Search code examples
iphoneencryptionaesnsdatacommoncrypto

implementing AES256 encryption into IOS


This is my java code. Now I want to implement same functionality in Objective-C.

Cipher encryptCipher;
IvParameterSpec iv = new IvParameterSpec(key);
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
encryptCipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = encryptCipher.doFinal(dataToEncrypt.getBytes());
Log.d("TAG", "encrypted string:"
        + Base64.encodeToString(encrypted, Base64.DEFAULT));
return Base64.encodeToString(encrypted, Base64.DEFAULT).trim();

This is my iOS implementation

- (NSData *)AES256EncryptWithKey:(NSString*)key
{
    char keyPtr[kCCKeySizeAES256 + 1];
    bzero(keyPtr, sizeof(keyPtr));
    [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;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                  kCCAlgorithmAES128,
                                  kCCOptionPKCS7Padding,
                                  keyPtr,
                                  kCCKeySizeAES256,
                                  NULL,
                                  [self bytes],
                                  dataLength,
                                  buffer,
                                  bufferSize, 
                                  &numBytesEncrypted);

    if (cryptStatus == kCCSuccess)
    {

        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer);
    return nil;
}

This is my hash key generating function. this function return same key in android and ios

int dkLen = 16;
    NSData *keyData = [hash_key dataUsingEncoding:NSUTF8StringEncoding];
    NSData *salt    = [saltKey dataUsingEncoding:NSUTF8StringEncoding];
    uint    rounds  = 1000;
    uint    keySize = kCCKeySizeAES128;

    NSMutableData *derivedKey = [NSMutableData dataWithLength:keySize];





    CCKeyDerivationPBKDF(kCCPBKDF2,               // algorithm
                         keyData.bytes,           // password
                         keyData.length,          // passwordLength
                         salt.bytes,              // salt
                         salt.length,             // saltLen
                         kCCPRFHmacAlgSHA1,       // PRF
                         rounds,                  // rounds
                         derivedKey.mutableBytes, // derivedKey
                         dkLen*8);
 return derivedKey;

I am getting a different output. I am doing anything wrong?.please help me to find out.


Solution

  • One problem is that the Java code used CBC mode, the iOS code used ECB mode.

    Next, from the referenced project:
    //result= yHbhApwTpQ2ZhE97AKF/g==
    is invalid Base64, it does not contain a multiple of 4 bytes.

    With these options: CBC, PKCS#7 padding

    inputs:  
       data in: "hello" which will be null padded to the block length of 16-bytes  
       key:  
          base64: VQQhu+dUdqXGoE7RZL2JWg==  
          hex: 550421bbe75476a5c6a04ed164bd895a  
       iv:   
          base64: VQQhu+dUdqXGoE7RZL2JWg==  
          hex: 550421bbe75476a5c6a04ed164bd895a  
    encrypted output:  
       hex: ff21db840a704e943666113dec0285fe  
       base64: /yHbhApwTpQ2ZhE97AKF/g==  
    

    This is the test code:

    NSString *base64Key  = @"VQQhu+dUdqXGoE7RZL2JWg==";
    NSString *dataString = @"hello";
    
    NSData *key  = [[NSData alloc] initWithBase64EncodedString:base64Key  options:0];
    NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    
    NSLog(@"key:  %@", key);
    NSLog(@"data: %@", data);
    
    NSData *encryptedData = [TestClass crypt:data
                                     iv:key
                                    key:key
                                context:kCCEncrypt];
    
    NSLog(@"encryptedData: %@", encryptedData);
    NSString *encryptedBase64Data = [encryptedData base64EncodedStringWithOptions:0];
    NSLog(@"encryptedBase64Data: %@", encryptedBase64Data);
    

    This is the encryption method (in the class TestClass):

    + (NSData *)crypt:(NSData *)dataIn
                      iv:(NSData *)iv
                     key:(NSData *)symmetricKey
                 context:(CCOperation)encryptOrDecrypt
    {
        CCCryptorStatus ccStatus   = kCCSuccess;
        size_t          cryptBytes = 0;    // Number of bytes moved to buffer.
        NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];
    
        ccStatus = CCCrypt( encryptOrDecrypt,
                           kCCAlgorithmAES128,
                           kCCOptionPKCS7Padding,
                           symmetricKey.bytes,
                           kCCKeySizeAES128,
                           iv.bytes,
                           dataIn.bytes,
                           dataIn.length,
                           dataOut.mutableBytes,
                           dataOut.length,
                           &cryptBytes);
    
        if (ccStatus != kCCSuccess) {
            NSLog(@"CCCrypt status: %d", ccStatus);
        }
    
        dataOut.length = cryptBytes;
    
        return dataOut;
    }
    

    Note: I keep separate the encryption and data conversions. Conflating them just makes testing more complicated.

    If you use an on-line encryption implementation the padding will probably not be PKCS#7 because mcrypt does not support it, instead it does non-standard null padding. Since the pad bytes are just the count of pad bytes the padding can be simulated in the input. Here is an example using AES – Symmetric Ciphers Online

    Note that "hello" PKCS#7 padded to a block size of 16 bytes adds 11 bytes of the uint8 value 11 or 0x0B: 68656c6c6f0B0B0B0B0B0B0B0B0B0B0B.

    Finally the question remains why the Java code does not produce this result?