java - 在 OpenSSL 中完成的 AES 加密成功解密,但在 Java 中加密时失败

标签 java encryption openssl aes

我已经使用下面的 OpenSSL 代码进行了 AES 加密,并在税务网站上成功解密

openssl rand 48 > 48byterandomvalue.bin
hexdump /bare 48byterandomvalue.bin > 48byterandomvalue.txt

set /a counter=0
for /f "tokens=* delims= " %%i in (48byterandomvalue.txt) do (
set /a counter=!counter!+1
set var=%%i
if "!counter!"=="1" (set aes1=%%i)
if "!counter!"=="2" (set aes2=%%i)
if "!counter!"=="3" (set iv=%%i)
)

set result1=%aes1:~0,50%
set result1=%result1: =%
set result2=%aes2:~0,50%
set result2=%result2: =%
set aeskey=%result1%%result2%
set initvector=%iv:~0,50%
set initvector=%initvector: =%

openssl aes-256-cbc -e -in PAYLOAD.zip -out PAYLOAD -K %aeskey% -iv %initvector%

openssl rsautl -encrypt -certin -inkey test_public.cer -in 
48byterandomvalue.bin -out 000000.00000.TA.840_Key

但作为迁移的一部分,我想在 Java 中做同样的事情,所以我使用了 javax.cryptojava.security 库,但解密失败我将文件上传到税务网站

//creating the random AES-256 secret key
SecureRandom srandom = new SecureRandom(); 
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey secretKey = keyGen.generateKey();
byte[] aesKeyb = secretKey.getEncoded();

//creating the initialization vector
byte[] iv = new byte[128/8];
srandom.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);

byte[] encoded = Files.readAllBytes(Paths.get(filePath));
str = new String(encoded, StandardCharsets.US_ASCII);

//fetching the Public Key from certificate
FileInputStream fin = new FileInputStream("test_public.cer");
CertificateFactory f = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)f.generateCertificate(fin);
PublicKey pk = certificate.getPublicKey();

//encrypting the AES Key with Public Key
Cipher RSACipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
RSACipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] RSAEncrypted = RSACipher.doFinal(aesKeyb);

FileOutputStream out = new FileOutputStream("000000.00000.TA.840_Key");
out.write(RSAEncrypted);
out.write(iv);
out.close();

另外,java生成的AES key 和openssl生成的不一样。你们能帮忙吗。

编辑 1: 以下是使用的 AES 加密代码:

Cipher AESCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AESCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
byte[] AESEncrypted = AESCipher.doFinal(str.getBytes("UTF-8"));
String encryptedStr = new String(AESEncrypted);

最佳答案

  • 脚本和Java代码用RSA加密的数据不同:

    该脚本生成一个随机的 48 字节序列并将其存储在文件 48byterandomvalue.bin 中。前 32 个字节用作 AES key ,最后 16 个字节用作 IV。 key 和 IV 用于在 CBC 模式下使用 AES-256 加密文件 PAYLOAD.zip,并将其存储为文件 PAYLOAD。文件 48byterandomvalue.bin 使用 RSA 加密并存储为文件 000000.00000.TA.840_Key

    在 Java 代码中,生成一个随机的 32 字节 AES key 和一个随机的 16 字节 IV。两者都用于在 CBC 模式下使用 AES-256 执行加密。 AES key 使用 RSA 加密,与未加密的 IV 连接,结果存储在文件 000000.00000.TA.840_Key 中。

    000000.00000.TA.840_Key 文件的内容对于脚本和 Java 代码是不同的。对于使用脚本逻辑生成 file 000000.00000.TA.840_Key 的 Java 代码,未加密 AES key 必须与未加密的 IV 和 this 结果必须使用 RSA 加密:

    ...
    //byte[] aesKeyb byte-array with random 32-bytes key
    //byte[] iv      byte-array with random 16-bytes iv
    byte[] key_iv = new byte[aesKeyb.length + iv.length];
    System.arraycopy(aesKeyb, 0, key_iv, 0, aesKeyb.length);
    System.arraycopy(iv, 0, key_iv, aesKeyb.length, iv.length);
    ...
    byte[] RSAEncrypted = RSACipher.doFinal(key_iv);
    FileOutputStream out = new FileOutputStream("000000.00000.TA.840_Key");
    out.write(RSAEncrypted);
    out.close();
    ...
    

    注意:IV 不必是 secret 的,因此不需要加密。只有在 Java 代码中生成脚本结果时才需要加密。

  • 另一个问题涉及将任意二进制数据转换为字符串。如果编码不合适(例如 ASCII 或 UTF8),这通常会导致数据损坏。因此

    ...
    byte[] encoded = Files.readAllBytes(Paths.get(filePath));
    str = new String(encoded, StandardCharsets.US_ASCII);           // Doesn't work: ASCII (7-bit) unsuitable for arbitrary bytes, *        
    ...
    byte[] AESEncrypted = AESCipher.doFinal(str.getBytes("UTF-8")); // Doesn't work: UTF-8 unsuitable for arbitrary bytes and additionally different from * 
    String encryptedStr = new String(AESEncrypted);                 // Doesn't work: UTF-8 unsuitable for arbitrary bytes
    ...
    

    应该替换为

    ...
    byte[] encoded = Files.readAllBytes(Paths.get(filePath));
    ...
    byte[] AESEncrypted = AESCipher.doFinal(encoded);
    FileOutputStream out = new FileOutputStream("PAYLOAD");
    out.write(AESEncrypted);
    out.close();
    ...
    

    在字符串中存储任意数据的合适编码是例如Base64,但在本例中这不是必需的,因为脚本中也未使用 Base64 编码。

  • 尝试这些改变。如果出现其他问题,最好分别测试 AES 加密、RSA 加密和 key_iv 生成。这使得隔离错误变得更加容易。

关于java - 在 OpenSSL 中完成的 AES 加密成功解密,但在 Java 中加密时失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58091189/

相关文章:

java:我想从内部类返回快照键但得到空变量

java - 扫描仪在使用 next() 或 nextFoo() 后跳过 nextLine()?

java - 拆分字符串并用标点符号和空格分隔

c++ - 在 C++ 中通过 cryptoapi 加密大文件

java - 将 Spring MVC 呈现为字符串或 PDF

Java AES解密BadPaddingException

c - 我需要重命名文件,但我不明白出了什么问题,文件已加密,但名称没有改变

ruby-on-rails - 尝试使用已在rails中加密的openssl/golang解密字符串

c++ - openSSL:PEM_write_RSAPublicKey 和 PEM_write_RSA_PUBKEY 之间的区别

python - 使用 Mercurial 克隆时 SSL 包装器出错