AES/CBC/NoPadding 和 IllegalBlockSizeException - 自定义填充处理

AES/CBC/NoPadding and IllegalBlockSizeException - Custom padding handling

更新:我删除了代码中的另一个问题以使问题更准确。

我需要使用 AES/CBC/NoPadding 加密可变长度的字符串,但我收到 IllegalBlockSizeException。 我必须使用 NoPadding 因为即使解密失败,输入的长度也应与输出的长度相同。 应该不可能确定它失败了。

之前我使用 AES/CBC/PKCS5Padding 没有任何问题,但这不是一个选项。所以我的问题是:

如何添加自定义填充以获得 16 字节的倍数或可能导致 IllegalBlockSizeException (DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH) 的原因?我还读到密文窃取是一种方法。我将不胜感激。

这是我当前的代码:

private static final String KEY_TRANSFORMATION_ALGORITHM_SYM = "AES/CBC/NoPadding";

@NonNull
static String encryptMessage(@NonNull String plainMessage,
                             @NonNull SharedPreferences storage,
                             @Nullable Key aesKey,
                             @NonNull String charset) {
    if (aesKey == null) {
        throw new RuntimeException("AES key is null", null);
    }
    try {
        // Cipher can not be re-used on Android
        Cipher cipher = Cipher.getInstance(KEY_TRANSFORMATION_ALGORITHM_SYM);
        cipher.init(Cipher.ENCRYPT_MODE, aesKey, new IvParameterSpec(getIV(storage, cipher, charset)));
        byte[] charsetEncryptedData = cipher.doFinal(plainMessage.getBytes(charset));
        return Base64.encodeToString(charsetEncryptedData, Base64.NO_WRAP);

    } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException | BadPaddingException | IllegalBlockSizeException | UnsupportedEncodingException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

@NonNull
static String decryptMessage(@NonNull String encryptedMessage,
                             @NonNull SharedPreferences storage,
                             @Nullable Key aesKey,
                             @NonNull String charset) {
    if (aesKey == null) {
        throw new RuntimeException("AES key is null", null);
    }
    try {
        //Cipher can not be re-used on Android
        Cipher cipher = Cipher.getInstance(KEY_TRANSFORMATION_ALGORITHM_SYM);
        cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(getIV(storage, cipher, charset)));

        byte[] decryptedData = Base64.decode(encryptedMessage.getBytes(charset), Base64.NO_WRAP);
        byte[] charsetEncryptedData = cipher.doFinal(decryptedData);
        return new String(charsetEncryptedData, charset);

    } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | BadPaddingException | NoSuchPaddingException | IllegalBlockSizeException | UnsupportedEncodingException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

我用下面的代码解决了我的问题。我必须添加带空格的自定义填充:

@NonNull
static String encryptMessage(@NonNull String plainMessage,
                             @NonNull SharedPreferences storage,
                             @Nullable Key aesKey,
                             @NonNull String charset) {
        //...
        // add spaces (custom padding) until the plainMessage.getBytes can be divided by 16 without rest --> this is the solution I was looking for
        while (plainMessage.getBytes().length % 16 != 0) {
            plainMessage += '\u0020';
        }
        //...
}

@NonNull
static String decryptMessage(@NonNull String encryptedMessage,
                             @NonNull SharedPreferences storage,
                             @Nullable Key aesKey,
                             @NonNull String charset) {
        //...
        // trim the String to get rid of the spaces
        return new String(charsetEncryptedData, charset).trim();
        //...
}