解密错误:Pad块损坏

我有以下代码。

byte[] input = etInput.getText().toString().getBytes(); byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }; SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC"); // encryption pass cipher.init(Cipher.ENCRYPT_MODE, key); byte[] cipherText = new byte[cipher.getOutputSize(input.length)]; int ctLength = cipher.update(input, 0, input.length, cipherText, 0); ctLength += cipher.doFinal(cipherText, ctLength); cipher.init(Cipher.DECRYPT_MODE, key); byte[] plainText = new byte[cipher.getOutputSize(ctLength)]; int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0); String strLength = new String(cipherText,"US-ASCII"); byte[] byteCiphterText = strLength.getBytes("US-ASCII"); Log.e("Decrypt", Integer.toString(byteCiphterText.length)); etOutput.setText(new String(cipherText,"US-ASCII")); cipherText = etOutput.getText().toString().getBytes("US-ASCII"); Log.e("Decrypt", Integer.toString(cipherText.length)); ptLength += cipher.doFinal(plainText, ptLength); Log.e("Decrypt", new String(plainText)); Log.e("Decrypt", Integer.toString(ptLength)); 

它完美地运作。 但是一旦我将它转换为课程。 它总是在这一行中遇到错误。

  ptLength += cipher.doFinal(plainText, ptLength); Error:Pad block corrupted 

我已经检查过,两个代码完全相同。 即使转换字符串中传递给字节的值也与上面的代码没有什么不同。 知道代码有什么问题吗?

 public String Encrypt(String strPlainText) throws Exception, NoSuchProviderException, NoSuchPaddingException { byte[] input = strPlainText.getBytes(); byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }; SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC"); // encryption pass cipher.init(Cipher.ENCRYPT_MODE, key); byte[] cipherText = new byte[cipher.getOutputSize(input.length)]; int ctLength = cipher.update(input, 0, input.length, cipherText, 0); ctLength += cipher.doFinal(cipherText, ctLength); return new String(cipherText, "US-ASCII"); } public String Decrypt(String strCipherText) throws Exception, NoSuchProviderException, NoSuchPaddingException { byte[] cipherText = strCipherText.getBytes("US-ASCII"); int ctLength = cipherText.length; byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }; SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC"); // decryption pass cipher.init(Cipher.DECRYPT_MODE, key); byte[] plainText = new byte[cipher.getOutputSize(ctLength)]; int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0); ptLength += cipher.doFinal(plainText, ptLength); return new String(plainText); } 

正如Yann Ramin所说,使用String是密码/输出的失败。 这是二进制数据

  • 可以包含0x00
  • 可以包含未定义或映射到所用编码中的奇怪位置的值

使用第一个示例中的plain byte []或者使用hex编码或base64编码byte []。

 // this is a quick example - dont use sun.misc inproduction // - go for some open source implementation String encryptedString = new sun.misc.BASE64Encoder.encodeBuffer(encryptedBytes); 

可以安全地传输此字符串并将其映射回字节。

编辑

处理长度问题的最安全的方法是始终使用流式实现(恕我直言):

 static public byte[] decrypt(Cipher cipher, SecretKey key, byte[]... bytes) throws GeneralSecurityException, IOException { cipher.init(Cipher.DECRYPT_MODE, key); ByteArrayOutputStream bos = new ByteArrayOutputStream(); for (int i = 0; i < bytes.length; i++) { bos.write(cipher.update(bytes[i])); } bos.write(cipher.doFinal()); return bos.toByteArray(); } 

您已指定PKCS7填充。 存储在String对象中时是否保留了填充? 你的字符串对象与密码输出的字节是否是1:1匹配? 通常,String不适合传递二进制数据,例如密码输出。

在你的情况下,密码使用填充,这意味着换句话说,输入数据将被填充/舍入到具有一些预定义大小的块(这取决于填充算法)。 假设您已经提供了500个字节进行加密,填充块大小为16个字节,因此加密数据的大小为512字节(32个块) – 将填充12个字节。

在您的代码中,您期望加密数组与输入数组大小相同,这会导致exception。 您需要重新计算输出数组大小,同时记住填充。