【问题标题】:Java Android AES decrypt result is truncated if only with long string如果仅使用长字符串,Java Android AES 解密结果将被截断
【发布时间】:2017-09-08 08:32:33
【问题描述】:

我正在尝试使用带有密码的 AES("AES/CBC/PKCS7Padding") 加密和解密纯文本。
由 KeyGenerator 制作 secretKey 并用它加密/解密。 一开始效果很好。

但是,如果纯文本是一个很大的字符串(例如183244个字符),它的一部分被解密而不是全部。

“部分”是指
如果纯文本为“abcdefghijklmn”,则解密文本为“abcdefgh”
(这是一个例子。实际上这种情况只发生在太长的字符上)

代码

        // get key from Android kyestore
        SecretKey secretKey = (SecretKey)mKeyStore.getKey(KEY_ALIAS, null);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);

        IvParameterSpec ivParams = cipher.getParameters().getParameterSpec(IvParameterSpec.class);

        //
        // Encrypt
        //
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
        cipherOutputStream.write(plainString.getBytes("UTF-8"));
        cipherOutputStream.close();

        String encryptedString = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);

        //
        // Decrypt
        //
        Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, ivParams);

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.decode(encryptedString, Base64.DEFAULT));
        CipherInputStream cipherInputStream = new CipherInputStream(byteArrayInputStream, decryptCipher);
        outputStream = new ByteArrayOutputStream();

        int b;
        while ((b = cipherInputStream.read()) != -1) {
            outputStream.write(b);
        }
        outputStream.close();

        String decryptedString =  outputStream.toString("UTF-8");

        //
        // results. decrypted string is part of source string and short.
        //
        int sourceStringLength = plainString.length(); // is 183244;
        int decryptedStringLength = decryptedString.length()); // is UNEXPECTED 68553;

您能告诉我解决这个问题的要点吗?

已编辑

我检查了加密/解密的字节数。

加密输出字节数:“outputStream.toByteArray().length”为 68560
解密输入字节:“Base64.decode(encryptedString, Base64.DEFAULT).length”也是68560

我认为这意味着解密过程不会丢失任何部分,并且加密过程可能会截断部分输入纯文本。 这是因为AES算法的限制吗?

已解决

正如@zaph 评论的那样,并非所有字节都被写入steam。

更换

  cipherOutputStream.write(plainString.getBytes("UTF-8"));
  cipherOutputStream.close();

        // NOTE: workaround. too bit bytes doesn't writted correctly.
        byte[] bytes = plainString.getBytes("UTF-8");

        int oneBulkSize = 1024;// temp value for proof of concept. might be bigger one.
        int numOfBulk = (bytes.length / oneBulkSize);

        for (int i = 0; i < numOfBulk; i++) {
            cipherOutputStream.write(bytes, oneBulkSize * i, oneBulkSize);
        }

        if ((bytes.length % oneBulkSize) != 0) {
            cipherOutputStream.write(bytes, oneBulkSize * numOfBulk, bytes.length % oneBulkSize);
        }
        cipherOutputStream.close();

解决了这个问题。

【问题讨论】:

  • (Base64.decode(encryptedString, Base64.DEFAULT) 的大小是多少?
  • 加密时你有Cipher.getInstance("AES/CBC/PKCS7Padding");,但解密时你有Cipher.getInstance(ALGORITHM);。那么ALGORITHM是什么?
  • 当我对算法使用“AES/CBC/PKCS7Padding”时,代码对我有用。
  • @JamesKPolk 我已经编辑了有问题的代码。算法是“AES/CBC/PKCS7Padding”。但不适用于长纯文本输入。
  • @MaartenBodewes 我检查了加密/解密的字节数。加密:“outputStream.toByteArray().length”是 68560 解密:“Base64.decode(encryptedString, Base64.DEFAULT).length”也是 68560 我认为这意味着加密过程会截断部分纯文本。这是AES算法的限制吗?

标签: java android encryption cryptography aes


【解决方案1】:

AES 没有数据长度限制。加密数据的长度应与输入的长度相同,再加上最多 16 个字节用于填充。

在加密循环中添加一些调试代码并验证是否正在读取整个输入。也许加密流和解密流一样需要循环。

另一种可能性是 183244 是 UTF-16 编码的字节大小,但您正在读取为 UTF-8,大多数 UTF-16 字符可以作为一个 UTF-8 字节。

以下是 OP @nsht 的实际解决方案代码,出于完整性考虑(来自@nsht 删除的答案):

更换

  cipherOutputStream.write(plainString.getBytes("UTF-8"));
  cipherOutputStream.close();

        // NOTE: workaround. too bit bytes doesn't writted correctly.
        byte[] bytes = plainString.getBytes("UTF-8");

        int oneBulkSize = 1024;// temp value for proof of concept. might be bigger one.
        int numOfBulk = (bytes.length / oneBulkSize);

        for (int i = 0; i < numOfBulk; i++) {
            cipherOutputStream.write(bytes, oneBulkSize * i, oneBulkSize);
        }

        if ((bytes.length % oneBulkSize) != 0) {
            cipherOutputStream.write(bytes, oneBulkSize * numOfBulk, bytes.length % oneBulkSize);
        }
        cipherOutputStream.close();

解决了这个问题。

【讨论】:

    猜你喜欢
    • 2014-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多