无法使用 Java 解密 AES-256 GCM
Cannot decrypt AES-256 GCM with Java
我有一个可以使用 AES-256 GCM 加密和解密的节点模块。现在我也尝试用 Java 解密节点模块加密的任何内容,但我不断收到 AEADBadTagException。
我已经自行测试了节点模块,可以确认它按预期工作。我知道 Java 假定身份验证标记是消息的最后一部分,因此我确保该标记是附加在节点模块中的最后一件事。
现在我只是在测试 "hello" 这个词。这是来自节点的加密消息:
Q10blKuyyYozaRf0RVYW7bave8mT5wrJzSdURQQa3lEqEQtgYM3ss825YpCQ70A7hpq5ECPafAxdLMSIBZCxzGbv/Cj4i6W4JCJXuS107rUy0tAAQVQQA2ZhbrQ0gNV9QA==
盐现在并没有真正被使用,因为为了测试目的我试图让事情变得简单
节点模块:
var crypto = require('crypto');
var encrypt = function(masterkey, plainText) {
// random initialization vector
var iv = crypto.randomBytes(12);
// random salt
var salt = crypto.randomBytes(64);
var key = masterkey;
// AES 256 GCM Mode
var cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
// encrypt the given text
var encrypted = Buffer.concat([cipher.update(plainText, 'utf8'), cipher.final()]);
// extract the auth tag
var tag = cipher.getAuthTag();
return Buffer.concat([salt, iv, encrypted, tag]).toString('base64');
};
var decrypt = function(masterkey, encryptedText) {
// base64 decoding
var bData = new Buffer(encryptedText, 'base64');
// convert data to buffers
var salt = bData.slice(0, 64);
var iv = bData.slice(64, 76);
var tag = bData.slice(bData.length - 16, bData.length);
var text = bData.slice(76, bData.length - 16);
var key = masterkey;
// AES 256 GCM Mode
var decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
// encrypt the given text
var decrypted = decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');
return decrypted;
};
module.exports = {
encrypt: encrypt,
decrypt: decrypt
}
JavaClass:
主要方法目前仅用于测试,稍后将被删除。
package decryption;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class DecryptAES256 {
private static String salt;
private static byte[] ivBase64;
private static String base64EncryptedText;
private static String key;
public static void main(String[] args) {
String key = "123456789aabbccddeefffffffffffff";
String sourceText = "Q10blKuyyYozaRf0RVYW7bave8mT5wrJzSdURQQa3lEqEQtgYM3ss825YpCQ70A7hpq5ECPafAxdLMSIBZCxzGbv/Cj4i6W4JCJXuS107rUy0tAAQVQQA2ZhbrQ0gNV9QA==";
System.out.println(decrypt(key, sourceText));
}
public static String decrypt(String masterkey, String encryptedText) {
byte[] parts = encryptedText.getBytes();
salt = new String(Arrays.copyOfRange(parts, 0, 64));
ivBase64 = Arrays.copyOfRange(parts, 64, 76);
ivBase64 = Base64.getDecoder().decode(ivBase64);
base64EncryptedText = new String(Arrays.copyOfRange(parts, 76, parts.length));
key = masterkey;
byte[] decipheredText = decodeAES_256_CBC();
return new String(decipheredText);
}
private static byte[] decodeAES_256_CBC() {
try {
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] text = base64EncryptedText.getBytes();
GCMParameterSpec params = new GCMParameterSpec(128, ivBase64, 0, ivBase64.length);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, params);
return cipher.doFinal(Base64.getDecoder().decode(text));
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Failed to decrypt");
}
}
}
异常抛出是正常的,你的Java代码有2个问题:
1- 您的 AES 密钥未正确解码:它以十六进制表示形式包装,您将其解码就好像它不是。在调用 SecretKeySpec()
.
时,需要将其从十六进制表示形式转换为字节形式
替换以下行:
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
通过这个:
SecretKeySpec skeySpec = new SecretKeySpec(Hex.decodeHex(key.toCharArray()), "AES");
请注意,要访问 Hex class,您需要在 class 文件中导入 org.apache.commons.codec.binary.Hex
并包含相应的 Apache commons-codec 项目中的库。
2- 您在将 base64 密文转换为 base64 之前拆分了它,这不是正确的执行顺序:
在您的 decrypt()
方法开始时,您需要 首先 在您的密文 (sourceText
) 上调用 Base64.getDecoder().decode()
,然后再拆分它进入相应的字段。
如果你想要一个在Java中使用AES-256-GCM的例子,你可以看看我几个月前写的下面的例子:https://github.com/AlexandreFenyo/kif-idp-client
加解密的源代码在以下文件中:https://github.com/AlexandreFenyo/kif-idp-client/blob/master/src/kif/libfc/Tools.java
查看名为 encodeGCM()
和 decodeGCM()
的方法。
这些方法由此处的主要 class 调用:https://github.com/AlexandreFenyo/kif-idp-client/blob/master/src/kif/libfc/CommandLine.java
我有一个可以使用 AES-256 GCM 加密和解密的节点模块。现在我也尝试用 Java 解密节点模块加密的任何内容,但我不断收到 AEADBadTagException。
我已经自行测试了节点模块,可以确认它按预期工作。我知道 Java 假定身份验证标记是消息的最后一部分,因此我确保该标记是附加在节点模块中的最后一件事。
现在我只是在测试 "hello" 这个词。这是来自节点的加密消息:
Q10blKuyyYozaRf0RVYW7bave8mT5wrJzSdURQQa3lEqEQtgYM3ss825YpCQ70A7hpq5ECPafAxdLMSIBZCxzGbv/Cj4i6W4JCJXuS107rUy0tAAQVQQA2ZhbrQ0gNV9QA==
盐现在并没有真正被使用,因为为了测试目的我试图让事情变得简单
节点模块:
var crypto = require('crypto');
var encrypt = function(masterkey, plainText) {
// random initialization vector
var iv = crypto.randomBytes(12);
// random salt
var salt = crypto.randomBytes(64);
var key = masterkey;
// AES 256 GCM Mode
var cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
// encrypt the given text
var encrypted = Buffer.concat([cipher.update(plainText, 'utf8'), cipher.final()]);
// extract the auth tag
var tag = cipher.getAuthTag();
return Buffer.concat([salt, iv, encrypted, tag]).toString('base64');
};
var decrypt = function(masterkey, encryptedText) {
// base64 decoding
var bData = new Buffer(encryptedText, 'base64');
// convert data to buffers
var salt = bData.slice(0, 64);
var iv = bData.slice(64, 76);
var tag = bData.slice(bData.length - 16, bData.length);
var text = bData.slice(76, bData.length - 16);
var key = masterkey;
// AES 256 GCM Mode
var decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
// encrypt the given text
var decrypted = decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');
return decrypted;
};
module.exports = {
encrypt: encrypt,
decrypt: decrypt
}
JavaClass: 主要方法目前仅用于测试,稍后将被删除。
package decryption;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class DecryptAES256 {
private static String salt;
private static byte[] ivBase64;
private static String base64EncryptedText;
private static String key;
public static void main(String[] args) {
String key = "123456789aabbccddeefffffffffffff";
String sourceText = "Q10blKuyyYozaRf0RVYW7bave8mT5wrJzSdURQQa3lEqEQtgYM3ss825YpCQ70A7hpq5ECPafAxdLMSIBZCxzGbv/Cj4i6W4JCJXuS107rUy0tAAQVQQA2ZhbrQ0gNV9QA==";
System.out.println(decrypt(key, sourceText));
}
public static String decrypt(String masterkey, String encryptedText) {
byte[] parts = encryptedText.getBytes();
salt = new String(Arrays.copyOfRange(parts, 0, 64));
ivBase64 = Arrays.copyOfRange(parts, 64, 76);
ivBase64 = Base64.getDecoder().decode(ivBase64);
base64EncryptedText = new String(Arrays.copyOfRange(parts, 76, parts.length));
key = masterkey;
byte[] decipheredText = decodeAES_256_CBC();
return new String(decipheredText);
}
private static byte[] decodeAES_256_CBC() {
try {
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] text = base64EncryptedText.getBytes();
GCMParameterSpec params = new GCMParameterSpec(128, ivBase64, 0, ivBase64.length);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, params);
return cipher.doFinal(Base64.getDecoder().decode(text));
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Failed to decrypt");
}
}
}
异常抛出是正常的,你的Java代码有2个问题:
1- 您的 AES 密钥未正确解码:它以十六进制表示形式包装,您将其解码就好像它不是。在调用 SecretKeySpec()
.
替换以下行:
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
通过这个:
SecretKeySpec skeySpec = new SecretKeySpec(Hex.decodeHex(key.toCharArray()), "AES");
请注意,要访问 Hex class,您需要在 class 文件中导入 org.apache.commons.codec.binary.Hex
并包含相应的 Apache commons-codec 项目中的库。
2- 您在将 base64 密文转换为 base64 之前拆分了它,这不是正确的执行顺序:
在您的 decrypt()
方法开始时,您需要 首先 在您的密文 (sourceText
) 上调用 Base64.getDecoder().decode()
,然后再拆分它进入相应的字段。
如果你想要一个在Java中使用AES-256-GCM的例子,你可以看看我几个月前写的下面的例子:https://github.com/AlexandreFenyo/kif-idp-client
加解密的源代码在以下文件中:https://github.com/AlexandreFenyo/kif-idp-client/blob/master/src/kif/libfc/Tools.java
查看名为 encodeGCM()
和 decodeGCM()
的方法。
这些方法由此处的主要 class 调用:https://github.com/AlexandreFenyo/kif-idp-client/blob/master/src/kif/libfc/CommandLine.java