来自iOS的Android AES解密和数据:javax.crypto.BadPaddingException:pad块损坏

我尝试解密从iOS发送的Android上的备份,并且在方法doFinal中显示exceptionjavax.crypto.BadPaddingException: pad block corrupted

 public String decrypt(byte[] cipherText, SecretKey key, byte [] initialVector) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector); cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec); cipherText = cipher.doFinal(cipherText); return new String(cipherText, "UTF-8"); } 

key和initialVector是从iOS以base64字符串发送的。 相关代码:

 public static byte[] decodeWebSafe(String s) throws Base64DecoderException { byte[] bytes = s.getBytes(); return decodeWebSafe(bytes, 0, bytes.length); } byte[] iv = Base64.decodeWebSafe(enciv); byte[] salt = Base64.decodeWebSafe(encsalt); byte[] data = Base64.decodeWebSafe(encdata); SecretKey key = Security.getExistingKey(password, salt); String original = aes.decrypt(data, key, iv); 

关于Security.getExistingKey:

 public static SecretKey getExistingKey(String password, byte[] salt) throws Exception{ SecretKey key= null; KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] keyBytes=new byte[32]; keyBytes = keyFactory.generateSecret(keySpec).getEncoded(); key= new SecretKeySpec(keyBytes, "AES"); return key; } 

Thx适用于任何解决方案。

PST这就是我们在iOS中设置加密的方式:

 CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, self.encryptionKey.bytes, kCCKeySizeAES128, self.encryptionIV.bytes, [rawData bytes], dataLength, /* input */buffer, bufferSize, /* output */&numBytesEncrypted); 

密钥和IV推导方法:

 (NSData *)keyForPassword:(NSString *)password salt:(NSData *)salt { NSMutableData * derivedKey = [NSMutableData dataWithLength:kCCKeySizeAES128]; int result = CCKeyDerivationPBKDF(kCCPBKDF2, // algorithm password.UTF8String, password.length, salt.bytes, // salt salt.length, // saltLen kCCPRFHmacAlgSHA1, // PRF kPBKDFRounds, // rounds derivedKey.mutableBytes, // derivedKey derivedKey.length); // derivedKeyLen } 

我可以看到生成密钥的方式有几点不同:

  • 在iOS中,生成16字节/ 128位的密钥; 在Android中它是256位。
  • 在iOS中,密码是UTF-8编码,而Android要么是低8位,要么是每个字符的完整16位(我不知道具体算法的细节)。
  • 在iOS中,为密码传递无效长度(字符数,而不是UTF-8编码中的字节数)。

您最好花一些时间更好地匹配密钥生成并在解密之前比较密钥。

这里是Android版本,它生成用于解密/加密消息的字符串,它使用Cipher并生成正确的向量以产生与iOS相同的结果。 这个在这个post中对应于@亚历山大的iOS版本。

 public class MyCrypter { private static String TAG = "MyCrypter"; public MyCrypter() { } /** * Encodes a String in AES-128 with a given key * * @param context * @param password * @param text * @return String Base64 and AES encoded String * @throws NoPassGivenException * @throws NoTextGivenException */ public String encode(Context context, String password, String text) throws NoPassGivenException, NoTextGivenException { if (password.length() == 0 || password == null) { throw new NoPassGivenException("Please give Password"); } if (text.length() == 0 || text == null) { throw new NoTextGivenException("Please give text"); } try { SecretKeySpec skeySpec = getKey(password); byte[] clearText = text.getBytes("UTF8"); //IMPORTANT TO GET SAME RESULTS ON iOS and ANDROID final byte[] iv = new byte[16]; Arrays.fill(iv, (byte) 0x00); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); // Cipher is not thread safe Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec); String encrypedValue = Base64.encodeToString( cipher.doFinal(clearText), Base64.DEFAULT); Log.d(TAG, "Encrypted: " + text + " -> " + encrypedValue); return encrypedValue; } catch (InvalidKeyException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } return ""; } /** * Decodes a String using AES-128 and Base64 * * @param context * @param password * @param text * @return desoded String * @throws NoPassGivenException * @throws NoTextGivenException */ public String decode(Context context, String password, String text) throws NoPassGivenException, NoTextGivenException { if (password.length() == 0 || password == null) { throw new NoPassGivenException("Please give Password"); } if (text.length() == 0 || text == null) { throw new NoTextGivenException("Please give text"); } try { SecretKey key = getKey(password); //IMPORTANT TO GET SAME RESULTS ON iOS and ANDROID final byte[] iv = new byte[16]; Arrays.fill(iv, (byte) 0x00); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); byte[] encrypedPwdBytes = Base64.decode(text, Base64.DEFAULT); // cipher is not thread safe Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec); byte[] decrypedValueBytes = (cipher.doFinal(encrypedPwdBytes)); String decrypedValue = new String(decrypedValueBytes); Log.d(TAG, "Decrypted: " + text + " -> " + decrypedValue); return decrypedValue; } catch (InvalidKeyException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } return ""; } /** * Generates a SecretKeySpec for given password * @param password * @return SecretKeySpec * @throws UnsupportedEncodingException */ public SecretKeySpec getKey(String password) throws UnsupportedEncodingException { int keyLength = 128; byte[] keyBytes = new byte[keyLength / 8]; // explicitly fill with zeros Arrays.fill(keyBytes, (byte) 0x0); // if password is shorter then key length, it will be zero-padded // to key length byte[] passwordBytes = password.getBytes("UTF-8"); int length = passwordBytes.length < keyBytes.length ? passwordBytes.length : keyBytes.length; System.arraycopy(passwordBytes, 0, keyBytes, 0, length); SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); return key; } public class NoTextGivenException extends Exception { public NoTextGivenException(String message) { super(message); } } public class NoPassGivenException extends Exception { public NoPassGivenException(String message) { super(message); } } } 

我和我的一个朋友创建了一个可以加密消息的iOS和Android应用程序。 要使用它,您应该使用以下代码片段从该网站创建NSData的扩展:

 - (NSData *)AES128EncryptWithKey:(NSString *)key { // 'key' should be 32 bytes for AES256, // 16 bytes for AES256, will be null-padded otherwise char keyPtr[kCCKeySizeAES128 + [key length]]; // room for terminator (unused) bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding) // insert key in char array [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; NSUInteger dataLength = [self length]; size_t bufferSize = dataLength + kCCBlockSizeAES128; void *buffer = malloc(bufferSize); size_t numBytesEncrypted = 0; // the encryption method, use always same attributes in android and iPhone (fe PKCS7Padding) CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, keyPtr, kCCKeySizeAES128, NULL /* initialization vector (optional) */, [self bytes], dataLength, /* input */ buffer, bufferSize, /* output */ &numBytesEncrypted); if (cryptStatus == kCCSuccess) { return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; } free(buffer); return nil; } - (NSData *)AES128DecryptWithKey:(NSString *)key { // 'key' should be 32 bytes for AES256, will be null-padded otherwise char keyPtr[kCCKeySizeAES128 + [key length]]; // room for terminator (unused) bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding) // insert key in char array [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; NSUInteger dataLength = [self length]; size_t bufferSize = dataLength + kCCBlockSizeAES128; void *buffer = malloc(bufferSize); size_t numBytesDecrypted = 0; CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, keyPtr, kCCKeySizeAES128, NULL /* initialization vector (optional) */, [self bytes], dataLength, /* input */ buffer, bufferSize, /* output */ &numBytesDecrypted); if (cryptStatus == kCCSuccess) { return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted]; } free(buffer); return nil; } + (NSData *)base64DataFromString: (NSString *)string { unsigned long ixtext, lentext; unsigned char ch, inbuf[4], outbuf[3]; short i, ixinbuf; Boolean flignore, flendtext = false; const unsigned char *tempcstring; NSMutableData *theData; if (string == nil){ return [NSData data]; } ixtext = 0; tempcstring = (const unsigned char *)[string UTF8String]; lentext = [string length]; theData = [NSMutableData dataWithCapacity: lentext]; ixinbuf = 0; while (true){ if (ixtext >= lentext){ break; } ch = tempcstring [ixtext++]; flignore = false; if ((ch >= 'A') && (ch <= 'Z')){ ch = ch - 'A'; } else if ((ch >= 'a') && (ch <= 'z')){ ch = ch - 'a' + 26; } else if ((ch >= '0') && (ch <= '9')){ ch = ch - '0' + 52; } else if (ch == '+'){ ch = 62; } else if (ch == '=') { flendtext = true; } else if (ch == '/') { ch = 63; } else { flignore = true; } if (!flignore){ short ctcharsinbuf = 3; Boolean flbreak = false; if (flendtext){ if (ixinbuf == 0){ break; } if ((ixinbuf == 1) || (ixinbuf == 2)) { ctcharsinbuf = 1; } else { ctcharsinbuf = 2; } ixinbuf = 3; flbreak = true; } inbuf [ixinbuf++] = ch; if (ixinbuf == 4){ ixinbuf = 0; outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4); outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2); outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F); for (i = 0; i < ctcharsinbuf; i++) { [theData appendBytes: &outbuf[i] length: 1]; } } if (flbreak) { break; } } } return theData; } 

然后在类中你想使用顶部的crypt方法插入:

 #import "NSData+Crypt.h" 

而不是加密你的字符串:

  NSData *value = [aString dataUsingEncoding:NSUTF8StringEncoding]; NSData *encryptedData = [value AES128EncryptWithKey:myKey]; NSString *myString = [encryptedData base64Encoding]; 

并解密这样的数据:

 NSData *myData = [NSData base64DataFromString:_textView.text]; NSData *decryptedData = [myData AES128DecryptWithKey:_textField.text]; NSString *myString2 = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding]; 

如果您使用,我使用Matt Gallagher网站上的方法base64DataFromString

 [[NSData alloc] base64EncodedDataWithOptions:NSUTF8StringEncoding]; 

该方法仅适用于> = iOS 7.0