AES 解密在结果中给出额外的零

AES decryption gives extra zeros in the result

我有以下代码使用密钥和随机 IV 加密解密字符串。但是在解密过程中,我的 IDE.

末尾有很多零
public class Example {

    private static final String AES_MODE = "AES/CBC/PKCS5Padding";
    private static final String CHARSET = "UTF-8";
    private static final String HASH_ALGORITHM = "SHA-256";
    private static final String KEY = "SUPER_SECURE_KEY";

    private static SecretKeySpec getSecretKey() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        final MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
        byte[] bytes = KEY.getBytes(CHARSET);
        digest.update(bytes, 0, bytes.length);
        byte[] key = digest.digest();

        return new SecretKeySpec(key, "AES");
    }

    public static String encrypt(String message) {
        if(message == null || message.isEmpty()) {
            return "";
        }

        try {
            final SecretKeySpec key = getSecretKey();
            byte[] cipherText = encrypt(key, message.getBytes(CHARSET));
            return Base64.getEncoder().encodeToString(cipherText);
        } catch (Exception e) {
            System.out.print(e.toString());
            return "";
        }
    }

    private static byte[] encrypt(final SecretKeySpec key, final byte[] message) throws GeneralSecurityException {

        final Cipher cipher = Cipher.getInstance(AES_MODE);

        byte[] iv = new byte[cipher.getBlockSize()];
        new SecureRandom().nextBytes(iv);

        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

        byte[] ciphertext = new byte[iv.length + cipher.getOutputSize(message.length)];
        System.arraycopy(iv, 0, ciphertext, 0, iv.length);
        cipher.doFinal(message, 0, message.length, ciphertext, iv.length);
        return ciphertext;
    }

    // ========================================================================================

    public static String decrypt(String base64EncodedCipherText) {
        if(base64EncodedCipherText == null || base64EncodedCipherText.isEmpty()) {
            return "";
        }

        try {
            final SecretKeySpec key = getSecretKey();

            byte[] decodedCipherText = Base64.getDecoder().decode(base64EncodedCipherText);
            byte[] decryptedBytes = decrypt(key, decodedCipherText);

            return new String(decryptedBytes, CHARSET);
        } catch (Exception e) {
            System.out.print(e.toString());
            return "";
        }
    }

    private static byte[] decrypt(final SecretKeySpec key, final byte[] decodedCipherText) throws GeneralSecurityException {
        final Cipher cipher = Cipher.getInstance(AES_MODE);

        IvParameterSpec ivSpec = new IvParameterSpec(decodedCipherText, 0, cipher.getBlockSize());
        cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

        int plainTextLength  = decodedCipherText.length - cipher.getBlockSize();
        byte[] plaintext = new byte[plainTextLength];

        cipher.doFinal(decodedCipherText, cipher.getBlockSize(), plainTextLength, plaintext, 0);

        return plaintext;

        // return cipher.doFinal(decodedCipherText);
    }

    // ========================================================================================

    public static void main(String[] args) {

        String message = "Message to encrypt.";

        String encryptedText = encrypt(message);
        System.out.println(encryptedText);

        String decryptedText= decrypt(encryptedText);
        System.out.println(decryptedText);

    }

}

我在 IntelliJ IDEA 中得到的输出是: here

我认为我正确地将 IV 与密文分开,并使用密钥和随机 IV 解密密文。但最终还是得到零。有什么地方出了问题吗?

读书是根本。 getOutputSize 的文档表明您不能将其用于此目的:

The actual output length of the next update or doFinal call may be smaller than the length returned by this method.

加密然后检查生成的字节数组,或者用 doFinal 方法的 return 值做一些事情(它实际上告诉你它产生了多少字节),或者制作一个 ByteArrayOutputStream 并从 doFinal 发送 iv 和字节(考虑到它 returns 的内容),然后向它询问 byte[],或使用 ByteBuffer。

请注意,CBC 是可疑的,使用 SHA-256 的传递散列也是如此。它有效,但它是 'too fast',黑客每秒尝试几十亿个密码是非常容易的。一般来说,你不应该手动处理这些东西。

通常使用的 CBC 模式需要填充,您的代码正确指定了填充,因此密文(在添加 IV 之前和删除 IV 之后)比明文长。您为这个较长的大小分配一个缓冲区,Cipher.doFinal 仅将实际的明文存储到其中,其余字节的值由 new byte[n] 初始化,该值(始终)为零。

  1. 您可以确定输出将使用的大小 ciper.getOutputSize(int) 就像您对加密所做的那样 这不起作用;马腾是对的。

  2. 您可以继续过度分配输出缓冲区,但保存 cipher.doFinal (input,off,len, output,off) 中的 return 值,这是一个 int 告诉您输出的字节数(解密),然后仅使用缓冲区中的那么多字节,例如new String (output, 0, outlen, charset)Arrays.copyOf(output, outlen)

  3. 但最简单的方法是使用 doFinal 重载分配缓冲区本身(具有正确的大小)并 returns 它: return cipher.doFinal(decodedCipherText, cipher.getBlockSize(), decodedCipherText.length - cipher.getBlockSize());

同意不在 密码 上使用简单哈希,但您的示例没有显示或说明您的 'key' 是否真的是密码(由人工处理) ,并且需要 'stretching') 或只是具有足够熵的文本形式,对此简单的散列就可以了。