Java Android AES 解密结果如果仅使用长字符串,则会被截断



我正在尝试使用密码使用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();

解决了这个问题。

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();

解决了这个问题。

最新更新