使用 Java 的 AES-256-GCM 解密中的标签不匹配错误

Posted

技术标签:

【中文标题】使用 Java 的 AES-256-GCM 解密中的标签不匹配错误【英文标题】:Tag mismatch error in AES-256-GCM Decryption using Java 【发布时间】:2017-05-10 08:18:28 【问题描述】:

我有以下用 javascript 编写的函数,用于使用 aes-256-gcm 进行加密:

encrypt: function (text, masterkey)
    try 
        // random initialization vector
        var iv = crypto.randomBytes(12);

        // random salt
        var salt = crypto.randomBytes(64);

        // derive key: 32 byte key length - in assumption the masterkey is a cryptographic and NOT a password there is no need for
        // a large number of iterations. It may can replaced by HKDF
        var key = crypto.pbkdf2Sync(masterkey, salt, 2145, 32, 'sha512');

        // AES 256 GCM Mode
        var cipher = crypto.createCipheriv('aes-256-gcm', key, iv);

        // encrypt the given text
        var encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);

        // extract the auth tag
        var tag = cipher.getAuthTag();

        // generate output
        return Buffer.concat([salt, iv, tag, encrypted]).toString('base64');

    catch(e)
    

    // error
    return null;

上述函数的加密文本使用以下函数成功解密回来:

decrypt: function (data, masterkey)
    try 
        // base64 decoding
        var bData = new Buffer(data, 'base64');
        var salt = bData.slice(0, 64);
        var iv = bData.slice(64, 76);
        var tag = bData.slice(76, 92);
        var text = bData.slice(92);

        // derive key using; 32 byte key length
        var key = crypto.pbkdf2Sync(masterkey, salt , 2145, 32, 'sha512');
        // AES 256 GCM Mode
        var decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
        decipher.setAuthTag(tag);

        // decrypt the given text
        var decrypted = decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');

        return decrypted;

    catch(e)
    

    // error
    return null;

现在,我需要一个 Java 中的解密方法,它相当于上面的 Javascript 解密函数。以下是我为解密编写的Java代码:

public void decrypt(byte[] nkb, String crKey)
    //nkb is byte array formed by Base64 decoding of 'data' variable in the Javascript code
    //crKey corresponds to the 'masterkey' variable

    byte[] salt = Arrays.copyOfRange(nkb, 0, 64);
    byte[] iv = Arrays.copyOfRange(nkb, 64, 76);
    byte[] tag = Arrays.copyOfRange(nkb, 76, 92);
    byte[] text = Arrays.copyOfRange(nkb, 92, nkb.length);

    PBEKeySpec ks = new PBEKeySpec(crKey.toCharArray(), salt, iterations, 256);
    SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
    SecretKey pbeKey = skf.generateSecret(ks);

    byte[] decrypted = decrypt(iv, pbeKey.getEncoded(), text, tag);

    public static byte[] decrypt(byte[] ivBytes, byte[] keyBytes, byte[] textBytes, byte[] tagBytes)
        throws java.io.UnsupportedEncodingException,
        NoSuchAlgorithmException,
        NoSuchPaddingException,
        InvalidKeyException,
        InvalidAlgorithmParameterException,
        IllegalBlockSizeException,
        BadPaddingException,
        NoSuchProviderException 

        GCMParameterSpec ivSpec = new GCMParameterSpec(tagBytes.length*Byte.SIZE, ivBytes);

        SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
        return cipher.doFinal(textBytes); //getting tag mismatch error here
    

正如我在上面的代码中所评论的,我在最后一行得到了一个标签不匹配错误。如果能帮助我找出我做错了什么,我将不胜感激。

我在这行代码中有错误:

cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec)
java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
at javax.crypto.Cipher.implInit(Cipher.java:805)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1396)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.micropro.namwebservice.utils.CryptoUtils.decrypt(CryptoUtils.java:93)
at com.micropro.namwebservice.utils.CryptoUtils.decrypt(CryptoUtils.java:82)

【问题讨论】:

【参考方案1】:

您需要将标记提供给 Java GCM 代码,以便它可以检查消息是否真实。 Java API 期望将标签附加到密文中。更改代码以执行此操作的最简单方法是替换该行

return cipher.doFinal(textBytes);

两行:

cipher.update(textBytes);
return cipher.doFinal(tagBytes);

【讨论】:

它工作得很好,谢谢。这是否意味着标记字节被附加在文本字节的末尾? @AnishHirlekar 是的,Java 期望密文的字节紧跟标签字节,并检查标签是否匹配,如果不匹配则抛出异常。

以上是关于使用 Java 的 AES-256-GCM 解密中的标签不匹配错误的主要内容,如果未能解决你的问题,请参考以下文章

Nodejs AES-256-GCM 通过 webcrypto api 解密加密的客户端消息

Nodejs AES-256-GCM 在客户端通过浏览器 webcrypto api 加密和解密

跨平台AES 256 GCM Javascript和Elixir

golang 在golang中使用AES256 GCM加密文本

项目中使用libsodium扩展

有没有办法过滤 aes 256 gcm 加密数据库中的数据?