使用nodejs和java进行AES-GCM 256加解密

问题描述 投票:0回答:1

在应用程序中,我希望java前端应该从nodejs获取加密文本并提供固定的

SECRET_KEY
,它应该能够对其进行解码,反之亦然。任何帮助都是appriced。

我在 java 中使用 javax.crypto,在节点中使用 crypto。我发现 iv 没有问题,因为它们都使用 iv 从 crypto 创建密码,但标签出现问题。 Java 没有提供预先构建的方法来获取标签,但 Nodejs 有它,并且 Node 的加密字符串为 16 字节,而在 Java 中为 32 字节。我尝试过合并节点并为 java 切片末尾 16 个字节,但它会抛出无效标签。我也尝试过使用java的

updateAAD()
方法,但没有结果。请帮我解决这个问题,我从过去两天就被困住了。谢谢你。

JAVA编码器


import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Encoder3 {
    public static void main(String[] args) throws Exception {
        String base64Key = "XHgxl8qs5D6sejZncSsYg7OqPIeV0uQe5I9Zh+uHLcc=";
        String originalString = "Hello, NODE JS!";

        // Generate random IV
        byte[] iv = new byte[12];
        new SecureRandom().nextBytes(iv);
        
        byte[] aad = new byte[16];
        new SecureRandom().nextBytes(aad);
        
        // Generate a key from the base64 encoded key
        byte[] keyBytes = Base64.getDecoder().decode(base64Key);
        SecretKey key = new SecretKeySpec(keyBytes, "AES");

        // Create cipher instance
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

        // Generate a random IV
        cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(aad.length*8, iv));

        // Encrypt the string
        byte[] cipherText = cipher.doFinal(originalString.getBytes(StandardCharsets.UTF_8));

        String encodedString = Base64.getEncoder().encodeToString(cipherText);
        String ivString = Base64.getEncoder().encodeToString(iv);
        
        System.out.println("Encoded String: " + encodedString);
        System.out.println("IV String: " + ivString);
    }
}

Node.js 解码器

const crypto = require('crypto');

function decrypt(encodedString, iv, base64Key) {
    const key = Buffer.from(base64Key, 'base64');

    iv = Buffer.from(iv, 'base64'); 
    const buffer = Buffer.from(encodedString, 'base64');
    const encryptedString = buffer.slice(0, 16);
    const tag = buffer.slice(16, 32);

    // Create decipher instance
    const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
    decipher.setAuthTag(tag);
    
    // Decrypt the string
    let decrypted = decipher.update(encryptedString, 'base64', 'utf-8');
    decrypted += decipher.final('utf-8');

    return decrypted;
}

// Example usage
const base64Key = 'XHgxl8qs5D6sejZncSsYg7OqPIeV0uQe5I9Zh+uHLcc=';
const encodedStringFromJava = 'gifu7S5cqzb5etwmNGManhj/Y/pxLrT8DJ9OLuXAww==';
const iv = `Mk6vC6BfFfWtDevA`;

const decodedString = decrypt(encodedStringFromJava,iv, base64Key);
console.log('Decoded String:', decodedString);

显示

node:internal/crypto/cipher:193
  const ret = this[kHandle].final();
                            ^

Error: Unsupported state or unable to authenticate data
    at Decipheriv.final (node:internal/crypto/cipher:193:29)
    at decrypt (S:\My Practice Files\UI\app.js\decrypt.js:16:27)
    at Object.<anonymous> (S:\My Practice Files\UI\app.js\decrypt.js:26:23)
    at Module._compile (node:internal/modules/cjs/loader:1375:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1434:10)
    at Module.load (node:internal/modules/cjs/loader:1206:32)
    at Module._load (node:internal/modules/cjs/loader:1022:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:142:12)
    at node:internal/main/run_main_module:28:49

Node.js v21.5.0
java node.js caesar-cipher cryptoapi node-crypto
1个回答
0
投票
  1. AAD 错了。如果您在 AEAD 密码中使用 AAD,则两端(加密器和解密器)必须相同。如果您在加密器处随机生成它并且不发送它,则解密器永远无法得到正确的结果;如果你真的发送了它,你只是在浪费精力,一事无成。删除它。

    注意Java的

    GCMParameterSpec
    中的第一个参数是tag长度,而不是AAD长度。这些是完全不同的事情。

  2. 32 不是 31。正如您似乎已经猜到的那样,但可以通过查看文档了解到,Java 加密返回附加到密文的标签,并在解密时接受它;相反,nodejs crypto 将其作为单独的字段返回或接受。因此,Java“密文”的最后 16 个字节应该成为 Nodejs 标签,这是正确的。

    但是你说Java“密文”是32字节。它不是。它有 31 个字节——15 个字节的密文和 16 个字节的标签。因此,您需要将其切片为

    (0,15) (15,31)
    或更一般地为
    (0,-16) (-16 /* to end */)

如果我更改您的Java以删除AAD,并且仅出于测试目的*重用您发布的IV而不是新的IV,我会得到encodedString ='gifu7S5cqznzbbRBPX0a + YOrlrs9WQFOfr1aPV4W4A=='并且如果我更改您的JS以正确切片它正确解密该编码字符串。

*警告:这仅用于测试!在实际使用 GCM 时,不要对同一个密钥重复使用 IV/nonce——这会破坏安全性。

© www.soinside.com 2019 - 2024. All rights reserved.