无法解密 Node 中使用 AES-GCM-256 在 Java 中加密的数据

Dan*_*ara 2 java node.js aes-gcm

我正在尝试在node.js中创建一个API,它可以解密使用AES-GCM-256算法创建的输入,我在JAVA中使用相同的算法来加密代码,但我无法使用node来解密它。 js

\n\n

我尝试了很多方法,但我可能卡在标签部分并且收到错误“状态不受支持或无法验证数据”

\n\n

我的Java代码:

\n\n
import java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport javax.crypto.Cipher;\nimport javax.crypto.KeyGenerator;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.GCMParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\n\xe2\x80\x8b\npublic class AES256GCMAlgo {\n\xe2\x80\x8b\n\xe2\x80\x8b\n        static String plainText = "This is a plain text which need to be encrypted by Java AES 256 GCM Encryption Algorithm";\n        public static final int AES_KEY_SIZE = 256;\n        public static final int GCM_IV_LENGTH = 12;\n        public static final int GCM_TAG_LENGTH = 16;\n\xe2\x80\x8b\n        public static void main(String[] args) throws Exception\n        {\n            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");\n            keyGenerator.init(AES_KEY_SIZE);\n\xe2\x80\x8b\n            // Generate Key\n            SecretKey key = keyGenerator.generateKey();\n            byte[] IV = new byte[GCM_IV_LENGTH];\n            SecureRandom random = new SecureRandom();\n            random.nextBytes(IV);\n\xe2\x80\x8b\n            byte[] encoded = key.getEncoded();\n            String output = Base64.getEncoder().withoutPadding().encodeToString(encoded);\n            System.out.println("Keep it secret, keep it safe! " + output);\n\xe2\x80\x8b\n\xe2\x80\x8b\n            String ivoutput = Base64.getEncoder().withoutPadding().encodeToString(IV);\n            System.out.println("Keep ivoutput secret, keep it safe! " + ivoutput);\n\xe2\x80\x8b\n            System.out.println("Original Text : " + plainText);\n\xe2\x80\x8b\n            byte[] cipherText = encrypt(plainText.getBytes(), key, IV);\n\xe2\x80\x8b\n            byte[] tagVal = Arrays.copyOfRange(cipherText, cipherText.length - (128 / Byte.SIZE), cipherText.length);\n\xe2\x80\x8b\n            System.out.println("Encrypted Text : " + Base64.getEncoder().encodeToString(cipherText));\n\xe2\x80\x8b\n            System.out.println("Tag Text : " + Base64.getEncoder().encodeToString(tagVal));\n\xe2\x80\x8b\n\xe2\x80\x8b\n            String input = output ;\n            byte[] deencoded = Base64.getDecoder().decode(output);\n            SecretKey aesKey = new SecretKeySpec(deencoded, "AES");\n\xe2\x80\x8b\n            String ivinput = ivoutput;\n            byte[] ivdeencoded = Base64.getDecoder().decode(ivinput);\n\xe2\x80\x8b\n            String decryptedText = decrypt(cipherText, aesKey, ivdeencoded);\n            System.out.println("DeCrypted Text : " + decryptedText);\n        }\n\xe2\x80\x8b\n        public static byte[] encrypt(byte[] plaintext, SecretKey key, byte[] IV) throws Exception\n        {\n            // Get Cipher Instance\n            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");\n\xe2\x80\x8b\n            // Create SecretKeySpec\n            SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");\n\xe2\x80\x8b\n            // Create GCMParameterSpec\n            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);\n\xe2\x80\x8b\n            // Initialize Cipher for ENCRYPT_MODE\n            cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);\n\xe2\x80\x8b\n            // Perform Encryption\n            byte[] cipherText = cipher.doFinal(plaintext);\n\xe2\x80\x8b\n\xe2\x80\x8b\n\xe2\x80\x8b\n            return cipherText;\n        }\n\xe2\x80\x8b\n        public static String decrypt(byte[] cipherText, SecretKey key, byte[] IV) throws Exception\n        {\n            // Get Cipher Instance\n            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");\n\xe2\x80\x8b\n            // Create SecretKeySpec\n            SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");\n\xe2\x80\x8b\n            // Create GCMParameterSpec\n            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);\n\xe2\x80\x8b\n            // Initialize Cipher for DECRYPT_MODE\n            cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);\n\xe2\x80\x8b\n            // Perform Decryption\n            byte[] decryptedText = cipher.doFinal(cipherText);\n\xe2\x80\x8b\n            return new String(decryptedText);\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的 Node.js 代码

\n\n
const crypto = require(\'crypto\');\n// input created by running above program in java\nconst ed = \'OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMYjc/vXd1ha/cElMBzFaIp9g===\'\nconst key = \'HuzPEZgzqKOo8VwlnYhNUaPWTWSVDRQ2bMtY6aJAp8I\'\nconst iv = \'kg5ILA0826hrew5w\'\nconst tag = \'jc/vXd1ha/cElMBzFaIp9g==\' // last 16 bytes extracted in java\n\nfunction decrypt(encrypted, ik, iiv, it) {\n  let bData = Buffer.from(encrypted, \'base64\');\n  // console.log(bData.length,bData.length - 64)\n  let tag1 = Buffer.from(tag, \'base64\');\n  // let tag1 = bData.slice((bData.length - 16),bData.length) // also tried slicing last 16 bytes of buffer\n  console.log(\'00000000\',tag1.length)\n  let iv1 = Buffer.from(iiv, \'base64\');\n  let key1 = new Buffer(ik, \'base64\');\n  console.log(\'aaaaaaaaa\')\n  let decipher = crypto.createDecipheriv(\'aes-256-gcm\', key1, iv1)\n  console.log(\'bbbbbbbbbbbbb\')\n  decipher.setAuthTag(tag1);\n  console.log(\'ccccccc\')\n  let dec = decipher.update(encrypted, \'binary\', \'utf8\')\n  dec += decipher.final(\'utf8\');\n  return dec;\n}\n\nconsole.log(\'devryptedddddd\',decrypt(ed,key,iv,tag))\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

我应该在 Node.js 的控制台中收到“这是需要通过 Java AES 256 GCM 加密算法加密的纯文本”,但我收到“不受支持的状态或无法验证数据”错误。\n帮助。

\n

dav*_*085 5

您使用的“ed”不是该明文、密钥和 IV 的 Java 代码的输出。我得到的值是base64的

OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMY3P713dYWv3BJTAcxWiKfY=
Run Code Online (Sandbox Code Playgroud)

(最后 22 个字符不同)。但该值不是在 Nodejs 中使用的正确值;Java crypto 返回 GCM 标记作为密文的最后 N 个字节,并且您正确地将其从那里复制到单独的变量,但您没有将其从密文中删除。在nodejs中使用的正确密文是base64:

OGtANbvTLY6Cme2VNAxsiIhBLLwl29oVX7zC5DGmmq4hU/VqNKaGQuSp1Q8liQ94cW/B96OJoJJ2r67jRlQFI4qHCTWFU2qQ8QaNj6WehdVLsf5mDK2aMQ==
Run Code Online (Sandbox Code Playgroud)

(短 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))
Run Code Online (Sandbox Code Playgroud)

new Buffer()我得到了正确的输出,但也收到了(used for ) 已弃用的警告key1Buffer.from正如您用于其他变量的那样,现在是首选。