在应用程序中,我希望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
AAD 错了。如果您在 AEAD 密码中使用 AAD,则两端(加密器和解密器)必须相同。如果您在加密器处随机生成它并且不发送它,则解密器永远无法得到正确的结果;如果你真的发送了它,你只是在浪费精力,一事无成。删除它。
注意Java的
GCMParameterSpec
中的第一个参数是tag长度,而不是AAD长度。这些是完全不同的事情。
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——这会破坏安全性。