无法使用 AES-GCM-256 解密在 Java 中加密的节点中的数据
unable to decrypt data in Node which is encrypted in Java using AES-GCM-256
我正在尝试在 node.js 中创建一个 API 来解密使用 AES-GCM-256 算法创建的输入,我在 JAVA 中使用相同的算法加密代码,但我无法使用 node.js
解密它
我已经尝试了很多方法,但我可能卡在标签部分并且出现错误 'Unsupported state or unable to authenticate data'
我的Java代码:
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES256GCMAlgo {
static String plainText = "This is a plain text which need to be encrypted by Java AES 256 GCM Encryption Algorithm";
public static final int AES_KEY_SIZE = 256;
public static final int GCM_IV_LENGTH = 12;
public static final int GCM_TAG_LENGTH = 16;
public static void main(String[] args) throws Exception
{
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(AES_KEY_SIZE);
// Generate Key
SecretKey key = keyGenerator.generateKey();
byte[] IV = new byte[GCM_IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
byte[] encoded = key.getEncoded();
String output = Base64.getEncoder().withoutPadding().encodeToString(encoded);
System.out.println("Keep it secret, keep it safe! " + output);
String ivoutput = Base64.getEncoder().withoutPadding().encodeToString(IV);
System.out.println("Keep ivoutput secret, keep it safe! " + ivoutput);
System.out.println("Original Text : " + plainText);
byte[] cipherText = encrypt(plainText.getBytes(), key, IV);
byte[] tagVal = Arrays.copyOfRange(cipherText, cipherText.length - (128 / Byte.SIZE), cipherText.length);
System.out.println("Encrypted Text : " + Base64.getEncoder().encodeToString(cipherText));
System.out.println("Tag Text : " + Base64.getEncoder().encodeToString(tagVal));
String input = output ;
byte[] deencoded = Base64.getDecoder().decode(output);
SecretKey aesKey = new SecretKeySpec(deencoded, "AES");
String ivinput = ivoutput;
byte[] ivdeencoded = Base64.getDecoder().decode(ivinput);
String decryptedText = decrypt(cipherText, aesKey, ivdeencoded);
System.out.println("DeCrypted Text : " + decryptedText);
}
public static byte[] encrypt(byte[] plaintext, SecretKey key, byte[] IV) throws Exception
{
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
return cipherText;
}
public static String decrypt(byte[] cipherText, SecretKey key, byte[] IV) throws Exception
{
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
}
我的Node.js代码
const crypto = require('crypto');
// input created by running above program in java
const ed = 'OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMYjc/vXd1ha/cElMBzFaIp9g==='
const key = 'HuzPEZgzqKOo8VwlnYhNUaPWTWSVDRQ2bMtY6aJAp8I'
const iv = 'kg5ILA0826hrew5w'
const tag = 'jc/vXd1ha/cElMBzFaIp9g==' // last 16 bytes extracted in java
function decrypt(encrypted, ik, iiv, it) {
let bData = Buffer.from(encrypted, 'base64');
// console.log(bData.length,bData.length - 64)
let tag1 = Buffer.from(tag, 'base64');
// let tag1 = bData.slice((bData.length - 16),bData.length) // also tried slicing last 16 bytes of buffer
console.log('00000000',tag1.length)
let iv1 = Buffer.from(iiv, 'base64');
let key1 = new Buffer(ik, 'base64');
console.log('aaaaaaaaa')
let decipher = crypto.createDecipheriv('aes-256-gcm', key1, iv1)
console.log('bbbbbbbbbbbbb')
decipher.setAuthTag(tag1);
console.log('ccccccc')
let dec = decipher.update(encrypted, 'binary', 'utf8')
dec += decipher.final('utf8');
return dec;
}
console.log('devryptedddddd',decrypt(ed,key,iv,tag))
我应该在 node.js 的控制台中得到 'This is a plain text which need to be encrypted by Java AES 256 GCM Encryption Algorithm' 但我收到 'Unsupported state or unable to authenticate data' 错误。
请帮忙。
您使用的 'ed' 不是明文、密钥和 IV 的 Java 代码的输出。我得到的值是 base64
OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMY3P713dYWv3BJTAcxWiKfY=
(最后 22 个字符不同)。但是这个值不是在 nodejs 中使用的正确值; Java 加密 returns GCM 标记作为密文的最后 N 个字节,并且您正确地将它从那里复制到一个单独的变量,但是您 没有 从密文中删除它。在 nodejs 中使用的正确密文是 base64:
OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMQ==
(短 20 个字符,最后 3 个字符不同)。
最后,您的 nodejs 执行 bData = Buffer.from(encrypted, 'base64')
但随后忽略 bData
并执行 decipher.update(encrypted, 'binary', 'utf8')
- 使用 base64 字符串作为二进制,但事实并非如此。有了这两个变化:
const crypto = require('crypto');
const ed = 'OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMQ=='
const key = 'HuzPEZgzqKOo8VwlnYhNUaPWTWSVDRQ2bMtY6aJAp8I'
const iv = 'kg5ILA0826hrew5w'
const tag = 'jc/vXd1ha/cElMBzFaIp9g==' // last 16 bytes extracted in java
function decrypt(encrypted, ik, iiv, it) {
let bData = Buffer.from(encrypted, 'base64');
let tag1 = Buffer.from(tag, 'base64');
let iv1 = Buffer.from(iiv, 'base64');
let key1 = new Buffer(ik, 'base64');
let decipher = crypto.createDecipheriv('aes-256-gcm', key1, iv1)
decipher.setAuthTag(tag1);
let dec = decipher.update(bData, 'utf8')
dec += decipher.final('utf8');
return dec;
}
console.log(decrypt(ed,key,iv,tag))
我得到了正确的输出,但也有一个警告说 new Buffer()
(用于 key1
)已被弃用; Buffer.from
现在首选用于其他变量。
我正在尝试在 node.js 中创建一个 API 来解密使用 AES-GCM-256 算法创建的输入,我在 JAVA 中使用相同的算法加密代码,但我无法使用 node.js
解密它我已经尝试了很多方法,但我可能卡在标签部分并且出现错误 'Unsupported state or unable to authenticate data'
我的Java代码:
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES256GCMAlgo {
static String plainText = "This is a plain text which need to be encrypted by Java AES 256 GCM Encryption Algorithm";
public static final int AES_KEY_SIZE = 256;
public static final int GCM_IV_LENGTH = 12;
public static final int GCM_TAG_LENGTH = 16;
public static void main(String[] args) throws Exception
{
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(AES_KEY_SIZE);
// Generate Key
SecretKey key = keyGenerator.generateKey();
byte[] IV = new byte[GCM_IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
byte[] encoded = key.getEncoded();
String output = Base64.getEncoder().withoutPadding().encodeToString(encoded);
System.out.println("Keep it secret, keep it safe! " + output);
String ivoutput = Base64.getEncoder().withoutPadding().encodeToString(IV);
System.out.println("Keep ivoutput secret, keep it safe! " + ivoutput);
System.out.println("Original Text : " + plainText);
byte[] cipherText = encrypt(plainText.getBytes(), key, IV);
byte[] tagVal = Arrays.copyOfRange(cipherText, cipherText.length - (128 / Byte.SIZE), cipherText.length);
System.out.println("Encrypted Text : " + Base64.getEncoder().encodeToString(cipherText));
System.out.println("Tag Text : " + Base64.getEncoder().encodeToString(tagVal));
String input = output ;
byte[] deencoded = Base64.getDecoder().decode(output);
SecretKey aesKey = new SecretKeySpec(deencoded, "AES");
String ivinput = ivoutput;
byte[] ivdeencoded = Base64.getDecoder().decode(ivinput);
String decryptedText = decrypt(cipherText, aesKey, ivdeencoded);
System.out.println("DeCrypted Text : " + decryptedText);
}
public static byte[] encrypt(byte[] plaintext, SecretKey key, byte[] IV) throws Exception
{
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
return cipherText;
}
public static String decrypt(byte[] cipherText, SecretKey key, byte[] IV) throws Exception
{
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
}
我的Node.js代码
const crypto = require('crypto');
// input created by running above program in java
const ed = 'OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMYjc/vXd1ha/cElMBzFaIp9g==='
const key = 'HuzPEZgzqKOo8VwlnYhNUaPWTWSVDRQ2bMtY6aJAp8I'
const iv = 'kg5ILA0826hrew5w'
const tag = 'jc/vXd1ha/cElMBzFaIp9g==' // last 16 bytes extracted in java
function decrypt(encrypted, ik, iiv, it) {
let bData = Buffer.from(encrypted, 'base64');
// console.log(bData.length,bData.length - 64)
let tag1 = Buffer.from(tag, 'base64');
// let tag1 = bData.slice((bData.length - 16),bData.length) // also tried slicing last 16 bytes of buffer
console.log('00000000',tag1.length)
let iv1 = Buffer.from(iiv, 'base64');
let key1 = new Buffer(ik, 'base64');
console.log('aaaaaaaaa')
let decipher = crypto.createDecipheriv('aes-256-gcm', key1, iv1)
console.log('bbbbbbbbbbbbb')
decipher.setAuthTag(tag1);
console.log('ccccccc')
let dec = decipher.update(encrypted, 'binary', 'utf8')
dec += decipher.final('utf8');
return dec;
}
console.log('devryptedddddd',decrypt(ed,key,iv,tag))
我应该在 node.js 的控制台中得到 'This is a plain text which need to be encrypted by Java AES 256 GCM Encryption Algorithm' 但我收到 'Unsupported state or unable to authenticate data' 错误。 请帮忙。
您使用的 'ed' 不是明文、密钥和 IV 的 Java 代码的输出。我得到的值是 base64
OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMY3P713dYWv3BJTAcxWiKfY=
(最后 22 个字符不同)。但是这个值不是在 nodejs 中使用的正确值; Java 加密 returns GCM 标记作为密文的最后 N 个字节,并且您正确地将它从那里复制到一个单独的变量,但是您 没有 从密文中删除它。在 nodejs 中使用的正确密文是 base64:
OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMQ==
(短 20 个字符,最后 3 个字符不同)。
最后,您的 nodejs 执行 bData = Buffer.from(encrypted, 'base64')
但随后忽略 bData
并执行 decipher.update(encrypted, 'binary', 'utf8')
- 使用 base64 字符串作为二进制,但事实并非如此。有了这两个变化:
const crypto = require('crypto');
const ed = 'OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMQ=='
const key = 'HuzPEZgzqKOo8VwlnYhNUaPWTWSVDRQ2bMtY6aJAp8I'
const iv = 'kg5ILA0826hrew5w'
const tag = 'jc/vXd1ha/cElMBzFaIp9g==' // last 16 bytes extracted in java
function decrypt(encrypted, ik, iiv, it) {
let bData = Buffer.from(encrypted, 'base64');
let tag1 = Buffer.from(tag, 'base64');
let iv1 = Buffer.from(iiv, 'base64');
let key1 = new Buffer(ik, 'base64');
let decipher = crypto.createDecipheriv('aes-256-gcm', key1, iv1)
decipher.setAuthTag(tag1);
let dec = decipher.update(bData, 'utf8')
dec += decipher.final('utf8');
return dec;
}
console.log(decrypt(ed,key,iv,tag))
我得到了正确的输出,但也有一个警告说 new Buffer()
(用于 key1
)已被弃用; Buffer.from
现在首选用于其他变量。