无法使用Java解密AES-256 GCM

4
我有一个能够使用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
}

Java类: 目前主方法仅用于测试,稍后将被移除。

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");
        }
    }
}

@NisheshPratap 我已经完成了。 - bitscuit
如果您想避免Oracle出口管制的限制,可以考虑使用BouncyCastle Java库。 - Alexandre Fenyo
@AlexandreFenyo 尝试避免使用第三方工具 - bitscuit
1个回答

5

抛出的异常是正常的,你的Java代码有两个问题:

1- 你的AES密钥没有正确解码:它被包装在十六进制表示中,而你在解码时没有这样做。当调用SecretKeySpec()时,需要将其从十六进制表示转换为字节。

请替换以下行:

SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

通过这个:

SecretKeySpec skeySpec = new SecretKeySpec(Hex.decodeHex(key.toCharArray()), "AES");

注意,如果您需要访问Hex类,则需要在类文件中导入org.apache.commons.codec.binary.Hex并在项目中包含相应的Apache commons-codec库。
2- 在将其转换为base64之前,您需要首先decrypt()方法开始时使用Base64.getDecoder().decode()对密文(sourceText)进行解码,然后再将其拆分成相应的字段。
如果您想了解如何在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()的方法。
这些方法由此处的主类调用:https://github.com/AlexandreFenyo/kif-idp-client/blob/master/src/kif/libfc/CommandLine.java

我知道密钥看起来是十六进制编码,这是我的疏忽没有澄清,但是密钥是明文的。至于第二点,问题已解决,谢谢! - bitscuit
你能否也看一下这个问题: https://stackoverflow.com/questions/46307621/cannot-decrypt-long-aes-256-gcm-message-with-java 相同的问题出现在更长的消息中,但短消息可以正常解密。 - bitscuit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接