1

我正在编写一个程序来加密和解密数据。为了加密,我使用keyGenerator. 我将密钥转移到密码中,并创建了密钥的字符串版本:

String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());

为了将其存储在配置文件中(这样我就可以在解密函数中检索密钥)。

现在,在解密函数中,我需要将该字符串恢复为密钥格式,因此我可以将其作为参数发送到 dercypt 模式下的密码。我以这种方式将其转换回密钥:

byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));
Key newkey = new SecretKeySpec(keyBytes,0,keyBytes.length, "AES"); 

我将其传输到密码并使用以下命令写入输出(解密的数据)CipherInputStream

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, newkey, newiv, SecureRandom.getInstance("SHA1PRNG"));
        CipherInputStream cipherInputStream = new CipherInputStream(
                new ByteArrayInputStream(encryptedBytes), cipher);


        ArrayList<Byte> decryptedVal = new ArrayList<>();
        int nextByte;
        while ((nextByte = cipherInputStream.read()) != -1) {
            decryptedVal.add((byte) nextByte);
        }

        byte[] bytes = new byte[decryptedVal.size()];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = decryptedVal.get(i);
        }
        String decryptedData = new String(bytes);
        cipherInputStream.close();
        System.out.println("decryptedData: " + decryptedData);

我收到此错误:

线程“主”java.io.IOException 中的异常:javax.crypto.BadPaddingException:给定最终块未正确填充。如果在解密期间使用了错误的密钥,则可能会出现此类问题。

所以我怀疑我对待钥匙的方式可能有问题。

有什么建议么?帮助将不胜感激!

4

2 回答 2

1

当然你会得到这个错误:首先你应用 base 64 编码:

String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());

然后您使用字符编码将其转换回字节:

byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));

它只是保持 base64 编码,可能将密钥大小从 16 字节扩展到 24 字节,这对应于 192 位密钥而不是 128 位密钥。或者当然是 32 字节密钥的 24 字节密钥 - 两者似乎都有效。

要解决这个问题,您需要使用Base64.getDecoder()解码密钥。

目前,您获得的密钥具有不同的大小和值。这意味着每个明文块,包括最后一个包含填充的明文,都将解密为随机明文。由于随机明文不太可能包含有效填充,因此您会看到一个BadPaddingException.


提醒:

  • ,例如 base 64 或 hex:将字节编码为文本字符串
  • ,例如 UTF-8 或 ASCII:将文本字符串编码为字节

它们不是对立的,分别是解码和字符解码。


评论:

  • 是的,听听Ashfin的声音;您需要在加密期间使用随机 IV,然后在解密期间使用它,例如将其添加到密文(未加密);
  • 不要使用ArrayList<Byte>;存储对每个单独字节(!)的引用 - 使用ByteArrayOutputStream或任何其他OutputStream代替;
  • 您可以更好地使用字节缓冲区并使用它来读取/写入流(请注意,读取函数可能不会填充缓冲区,即使在流的开始或中间) - 当时读取单个字节表现不佳;
  • 查找Java的 try-with-resources
  • 使用 aKeyStore可能比存储在配置文件中更好;
  • GCM 模式(AES/GCM/NoPadding)也验证数据,应该优先于 CBC 模式。
于 2018-05-12T12:56:42.640 回答
1

我想你还没有发送IV到解密功能。对于CBC模式下的解密,您必须提供IV加密过程中使用的哪个。

更新

IV只会影响CBC解密模式下的第一个块。因此,如果您的数据小于 1 个块,我的回答可能会影响取消填充。否则它只会更改第一个块的解密明文。

于 2018-05-12T12:15:48.630 回答