This put me on my guard - the following code uses SpongyCastle encryption / decryption for Android - I'm trying to achieve cross-platform encryption / decryption for iOS.
The following code (from Android) works with AES 128bit CBC with PKCS7Padding, using the supplied salt and password, which salt is stored in the mysql database, the password is by the end user, the following code is adapted from this kelhoer answer.
The reason I used AES128bit is because AES256 is not available in iOS 4+, it was introduced in iOS5 +, and in order to generate a derivative key and initialization vector (iv), you had to dip your sock into using openssl as Apple found out that Apple rejects applications statically linked to the openssl library.
Since the platform is based on iOS 4.2+, resorting to linking and static linking the openssl library , it seems preferable to use the CommonCryptor library.
Here is the Android version with the Spongycastle code in place:
private static void encrypt(InputStream fin, OutputStream fout, String password, byte[] bSalt) { try { PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator( new SHA256Digest() ); char[] passwordChars = password.toCharArray(); final byte[] pkcs12PasswordBytes = PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars); pGen.init(pkcs12PasswordBytes, bSalt, ITERATIONS); CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine()); ParametersWithIV aesCBCParams = (ParametersWithIV) pGen.generateDerivedParameters(128, 128); aesCBC.init(true, aesCBCParams); PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding()); aesCipher.init(true, aesCBCParams); byte[] buf = new byte[BUF_SIZE]; // Read in the decrypted bytes and write the cleartext to out int numRead = 0; while ((numRead = fin.read(buf)) >= 0) { if (numRead == 1024) { byte[] plainTemp = new byte[ aesCipher.getUpdateOutputSize(numRead)]; int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0); final byte[] plain = new byte[offset]; System.arraycopy(plainTemp, 0, plain, 0, plain.length); fout.write(plain, 0, plain.length); } else { byte[] plainTemp = new byte[aesCipher.getOutputSize(numRead)]; int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0); int last = aesCipher.doFinal(plainTemp, offset); final byte[] plain = new byte[offset + last]; System.arraycopy(plainTemp, 0, plain, 0, plain.length); fout.write(plain, 0, plain.length); } } fout.close(); fin.close(); } catch (Exception e) { e.printStackTrace(); } } private static void decrypt(InputStream fin, OutputStream fout, String password, byte[] bSalt) { try { PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator( new SHA256Digest() ); char[] passwordChars = password.toCharArray(); final byte[] pkcs12PasswordBytes = PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars); pGen.init(pkcs12PasswordBytes, bSalt, ITERATIONS); CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine()); ParametersWithIV aesCBCParams = (ParametersWithIV) pGen.generateDerivedParameters(128, 128); aesCBC.init(false, aesCBCParams); PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding()); aesCipher.init(false, aesCBCParams); byte[] buf = new byte[BUF_SIZE]; // Read in the decrypted bytes and write the cleartext to out int numRead = 0; while ((numRead = fin.read(buf)) >= 0) { if (numRead == 1024) { byte[] plainTemp = new byte[ aesCipher.getUpdateOutputSize(numRead)]; int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0); // int last = aesCipher.doFinal(plainTemp, offset); final byte[] plain = new byte[offset]; System.arraycopy(plainTemp, 0, plain, 0, plain.length); fout.write(plain, 0, plain.length); } else { byte[] plainTemp = new byte[ aesCipher.getOutputSize(numRead)]; int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0); int last = aesCipher.doFinal(plainTemp, offset); final byte[] plain = new byte[offset + last]; System.arraycopy(plainTemp, 0, plain, 0, plain.length); fout.write(plain, 0, plain.length); } } fout.close(); fin.close(); } catch (Exception e) { e.printStackTrace(); } }
However, in iOS 4.2 (working with Xcode), I cannot figure out how to make an equivalent,
This is what I tried in Objective-C, with the goal of decrypting the data from the Android side stored in the mysql database, in order to verify this:
+(NSData*) decrypt:(NSData*)cipherData userPassword:(NSString*)argPassword genSalt:(NSData*)argPtrSalt{ size_t szPlainBufLen = cipherData.length + (kCCBlockSizeAES128); uint8_t *ptrPlainBuf = malloc(szPlainBufLen); // const unsigned char *ptrPasswd = (const unsigned char*)[argPassword cStringUsingEncoding:NSASCIIStringEncoding]; int ptrPasswdLen = strlen(ptrPasswd); // NSString *ptrSaltStr = [[NSString alloc] initWithData:argPtrSalt encoding:NSASCIIStringEncoding]; const unsigned char *ptrSalt = (const unsigned char *)[ptrSaltStr UTF8String]; NSString *ptrCipherStr = [[NSString alloc]initWithData:cipherData encoding:NSASCIIStringEncoding]; unsigned char *ptrCipher = (unsigned char *)[ptrCipherStr UTF8String]; unsigned char key[kCCKeySizeAES128]; unsigned char iv[kCCKeySizeAES128]; // //int EVP_BytesToKey(const EVP_CIPHER *type,const EVP_MD *md, //const unsigned char *salt, const unsigned char *data, //int datal, int count, unsigned char *key,unsigned char *iv); int i = EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha256(), ptrSalt, ptrPasswd, ptrPasswdLen, PBKDF2_ITERATIONS, key, iv); NSAssert(i == kCCKeySizeAES128, @"Unable to generate key for AES"); // size_t cipherLen = [cipherData length]; size_t outlength = 0; // CCCryptorStatus resultCCStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, key, kCCBlockSizeAES128, iv, ptrCipher, cipherLen, ptrPlainBuf, szPlainBufLen, &outlength); NSAssert(resultCCStatus == kCCSuccess, @"Unable to perform PBE AES128bit decryption: %d", errno); NSData *ns_dta_PlainData = nil; if (resultCCStatus == kCCSuccess){ ns_dta_PlainData = [NSData dataWithBytesNoCopy:ptrPlainBuf length:outlength]; }else{ return nil; } return ns_dta_PlainData; }
We CCCrypt user data and password and received a return code from CCCrypt as -4304 , which indicates unsuccessful and bad decoding.
I thought that perhaps the encoding scheme would discard the routing of CommonCryptor decryption, hence the long way to convert to NSASCIIStringEncoding .
The salt is stored along with encryption data and has a length of 32 bytes.
What I miss in this regard, bearing in mind the weakness in cryptography.