【问题标题】:javax.crypto.IllegalBlockSizeException: last block incomplete in decryption - Decrypting an encrypted AES Stringjavax.crypto.IllegalBlockSizeException:解密中的最后一个块不完整 - 解密加密的 AES 字符串
【发布时间】:2012-11-06 23:49:38
【问题描述】:

我正在尝试解密从后端服务器收到的字符串"~9?8?m???=?T?G",该后端服务器使用 OpenSSL 使用 AES-256-CBC 加密字符串。有代码块:

public static String decryptText(String textToDecrypt) {
    try {

        byte[] base64TextToDecrypt = Base64.encodeBase64(textToDecrypt.getBytes("UTF-8"));

        byte[] guid = "fjakdsjkld;asfj".getBytes("UTF-8");

        byte[] iv = new byte[16];
        System.arraycopy(guid, 0, iv, 0, guid.length);
        IvParameterSpec ips = new IvParameterSpec(iv);

        byte[] secret = DECRYPTION_SECRET_HASH.getBytes("UTF-8");
        SecretKeySpec secretKey = new SecretKeySpec(secret, "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // decryption pass
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ips);
        byte[] converted = cipher.doFinal(base64TextToDecrypt);
        System.out.println(new String(converted));

    } catch (Exception e) {
        e.printStackTrace();
        Log.e(TAG, "Decipher error for " + textToDecrypt, e);
    }
    return "";
}

不幸的是,当我到达时

byte[] converted = cipher.doFinal(base64TextToDecrypt);

声明抛出以下异常:

javax.crypto.IllegalBlockSizeException: last block incomplete in decryption

有什么想法吗?

【问题讨论】:

  • 请注意,如果您从服务器接收到字符串 "~9?8?m???=?T?G",而不是基于 64 位编码的字符串或纯字节(从 InputStream 接收),则服务器不会生成有效密码文本。这些问号是不可打印的字符,当这些问号出现时,您可能会丢失信息。
  • 谢谢@owlstead。你提出了一个很好的观点。 ~9?8?m???=?T?G 最初是使用字符串 4 创建的,我们希望解密并检索 4,但没有成功。我希望得到一个基本的解密工作,然后从那里开始。这是第一步,我还没有完成。根据您的建议,我会根据您的建议进行更多尝试,了解更多信息后做出回应。
  • 如果您不能直接传输字节,您必须使用 base 64 对服务器上的结果进行编码才能使代码正常工作。

标签: java encryption encoding character-encoding aes


【解决方案1】:

您应该解码字符串,而不是在方法开始时对字符串的平台特定表示进行编码。

byte[] base64TextToDecrypt = Base64.decodeBase64(textToDecrypt);

或更准确地说:

byte[] bytesToDecrypt = Base64(base64TextToDecrypt);

如果您正确命名变量。

一般来说,每次您(感觉必须)使用String.getBytes(): byte[] 方法或String(byte[]) 构造函数时,您都可能做错了什么。您应该首先考虑您正在尝试做什么,如果您确实需要使用它,请指定

在您的情况下,converted 变量中的输出可能是字符编码的。所以你可以使用以下片段:

String plainText = new String(converted, StandardCharsets.UTF_8);
System.out.println(plainText);

而不是你现在拥有的。

【讨论】:

  • 感谢您先回复。嗯,你是说我不应该编码成一个基本的 64 字节数组吗?但是我不需要传入一个字节数组来解密吗?你对此有何建议?我只是不理解异常以及我可以做些什么来让它甚至返回一个空白或其他东西而不是抛出异常。我也遇到过 BadPaddingException。 @owlstead 您还有其他建议吗?
  • Base 64 不是字节数组。它是字节数组的字符中的编码。所以你需要 decode 你进入一个字节数组的字符串,然后解密它。 BadPaddingException 是当密文的最后一个块未解码为具有有效填充的纯文本块时遇到的问题。当最后一个密文块的字节不正确或密钥不正确时,就会发生这种情况。换句话说,它只是告诉你 something 是错误的(这本身并不是很有帮助 :P )。
  • stackoverflow.com/a/20417874/3763032,或者您可以使用此解决方法。首先对您的解/加密字符串进行完整解/编码
【解决方案2】:

感谢@owlstead,我能够找到解决方案。那是我犯了 Base64encoding 一个已经 Base64 编码的字符串的错误。以下是代码块。

public static String decryptText(String textToDecrypt) {
    try {
        byte[] decodedValue = Base64.decodeBase64(textToDecrypt.getBytes());

        byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        IvParameterSpec ips = new IvParameterSpec(iv);

        byte[] input = textToDecrypt.getBytes();

        Cipher cipher = Cipher.getInstance(ENCRYPTION_METHOD);

        // decryption pass
        cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY, ips);
        byte[] plainText = cipher.doFinal(decodedValue);

        return new String(plainText);
    } catch (Exception e) {
        e.printStackTrace();
        Log.e(TAG, "Decipher error for " + textToDecrypt, e);
    }

    return "";
}

对应的加密是这样的

public static String encryptText(String textToEncrypt) {
    try {
        byte[] guid = "1234567890123456".getBytes("UTF-8");

        byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        IvParameterSpec ips = new IvParameterSpec(iv);

        // The secret key from the server needs to be converted to byte array for encryption.
        byte[] secret = ENCRYPTION_SECRET_HASH.getBytes("UTF-8");

        // we generate a AES SecretKeySpec object which contains the secret key.
        // SecretKeySpec secretKey = new SecretKeySpec(secret, "AES");
        Cipher cipher = Cipher.getInstance(ENCRYPTION_METHOD);
        cipher.init(Cipher.ENCRYPT_MODE, SECRET_KEY, ips);

        byte[] cipherText = cipher.doFinal(textToEncrypt.getBytes());
        byte[] base64encodedSecretData = Base64.encodeBase64(cipherText);
        String secretString = new String(base64encodedSecretData);
        return secretString;
    } catch (Exception e) {
        e.printStackTrace();
        Log.e(TAG, "Encryption error for " + textToEncrypt, e);
    }
    return "";
}

【讨论】:

  • 您使用的是哪个 Base64 类?我在该行收到错误,方法 decodeBase64(byte[]) is undefined for the Base64
【解决方案3】:

在加密和解密字符串时要记住以下几点,

加密:

  1. 使用 toByteArray(Charsets.UTF-8) 将字符串转换为 byteArray,并始终使用 UTF-8 指定字符集。

  2. 使用 cipher.doFinal(byteArray) 加密上述 byteArray。

  3. 现在这还不够,您需要使用 Base64.encode(encryptedByteArray, Base64.DEFAULT) 对加密的 byteArray 进行 base64 编码

  4. 请记住,这再次返回 byteArray ,如果要转换为字符串,请使用 toString(Charsets.UTF-8) 最重要的是再次将字符集指定为 UTF-8,然后根据需要处理或存储在 DB 中。

解密:

1.获取加密字符串,解密的第一步是使用base64.decode(encryptedString.toByteArray(Charsets.UTF-8), Base64.DEFAULT)对加密字符串进行解码

  1. 现在使用 cipher.dofinal(decodedByteArray) 解密解码后的 byteArray。

  2. 使用 toString(Charsets.UTF-8) 将解密的 byteArray 转换为 String。注意:始终指定字符集。这将返回原始字符串。

我知道我没有共享任何代码,但相信我,流程是加密和解密字符串时的重要部分..

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-03
    • 1970-01-01
    • 1970-01-01
    • 2010-12-27
    • 2017-05-12
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    相关资源
    最近更新 更多