首页 > 解决方案 > 无法在 java 中解密 aes-256-gcm 加密数据

问题描述

我已经使用 OpenSSL aes-256-gcm 加密了一个文件。由于命令行不支持 aes-256-gcm,我已经安装了 LibreSSL,并且可以使用以下命令来加密文件的数据。

openssl enc -aes-256-gcm -K 61616161616161616161616161616161 -iv 768A5C31A97D5FE9 -e -in file.in -out file.out

我需要用 Java 解密 file.out 的数据,但我无法做到这一点。

示例代码:

    // Get Cipher Instance
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

    String key = "61616161616161616161616161616161";
    byte[] IV = "768A5C31A97D5FE9".getBytes();

    // Create SecretKeySpec
    SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

    // Create GCMParameterSpec
    GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV);

    // Initialize Cipher for DECRYPT_MODE
    cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);

    // Perform Decryption
    byte[] decryptedText = cipher.doFinal(cipherText); // for the data by reading file.out

但是,我收到一个异常说 javax.crypto.AEADBadTagException:标签不匹配!

标签: javaencryptionaes-gcm

解决方案


那应该行不通。命令行openssl enc不支持 AEAD 密码/模式,尽管 1.0.1 的早期版本(低于补丁 h,2012-2014 年)如果您错误地指定了这样的密码并默默地产生了错误的输出,则无法捕获。如果您实际上使用的是 LibreSSL 而不是 OpenSSL,它似乎继承了这个问题并且没有修复它,即使 LibreSSL 项目的全部意义在于他们将修复由无能的 OpenSSL 人员造成的所有错误。

如果这是一个在 OpenSSL(以及 Java)中正常工作的密码,比如 aes-256-ctr,那么你唯一的问题就是将openssl enc -K -iv它们的参数采用十六进制(适用于 shell 上下文),而 Java 加密是从代码中调用的可以处理二进制数据并期望它的参数以这种形式。因此,您提供给 OpenSSL 的值实际上是 16 字节(128 位)和 8 字节(64 位),而不是应有的 256 位和 128 位(对于 CTR;对于 GCM,96 位的 IV 是正确的,但如前所述,GCM 在这里不起作用)。openssl enc自动-K -iv用(二进制)零填充,但 Java 不会。因此,您需要更多类似的东西

 byte[] key = Arrays.copyOf( javax.xml.bind.DatatypeConverter.parseHexBinary("61616161616161616161616161616161"), 32);
 // Arrays.copyOf zero-pads when expanding an array
 // then use SecretKeySpec (key, "AES")
 // and IVParameterSpec (iv) instead of GCMParameterSpec

 // but after Java8 most of javax.xml is removed, so unless you
 // are using a library that contains this (e.g. Apache) 
 // or have already written your own, you need something like

 byte[] fromHex(String h){
   byte[] v = new byte[h.length()/2];
   for( int i = 0; i < h.length(); i += 2 ) v[i] = Integer.parseInt(h.substring(i,i+2),16);
   return v;
 }

对比AES加密和openssl命令行工具,Java解密, Blowfish在Java/Scala加密,bash解密(后者是反方向,但需要匹配的都是一样的)


推荐阅读