Reputation: 13
I tried to decrypt a backup on Android which is sent from iOS
, and the exception javax.crypto.BadPaddingException: pad block corrupted
is showed at method doFinal.
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");
}
The key and initialVector are sent from iOS in base64 string. Related code:
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);
And about the 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 for any solutions.
P.S.This is how we set the encryption in iOS:
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
self.encryptionKey.bytes, kCCKeySizeAES128,
self.encryptionIV.bytes, [rawData bytes], dataLength,
/* input */buffer, bufferSize, /* output */&numBytesEncrypted);
the key and IV derivation method:
(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
}
Upvotes: 1
Views: 2501
Reputation: 6052
A friend of mine and me created an iOS and Android app which can crypt messages. To use it, you should create an extension of NSData with following code Snippet from this website:
- (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 (f.e. 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;
}
Then inside the class you want to use the crypt methods insert at the top:
#import "NSData+Crypt.h"
And than encrypt your strings like that:
NSData *value = [aString dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData = [value AES128EncryptWithKey:myKey];
NSString *myString = [encryptedData base64Encoding];
And decrypt the data like this:
NSData *myData = [NSData base64DataFromString:_textView.text];
NSData *decryptedData = [myData AES128DecryptWithKey:_textField.text];
NSString *myString2 = [[NSString alloc] initWithData:decryptedData
encoding:NSUTF8StringEncoding];
I used the method base64DataFromString
from the website of Matt Gallagher otherwise if you use
[[NSData alloc] base64EncodedDataWithOptions:NSUTF8StringEncoding];
the method is just available on >= iOS 7.0
Upvotes: 1
Reputation: 4574
And here is the Android Version wich is generating the String for decrypt/encrypt Messages, it uses Cipher and generates the right vector to make the same result as iOS. This is corresponding to the iOS Version of @亚历山大 here in this thread.
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);
}
}
}
Upvotes: 2
Reputation: 78795
I can see several differences in the way you generate the key:
You better invest some time in better matching the key generation and comparing the keys before decryption.
Upvotes: 0