implementing AES256 encryption into IOS

2019-03-21 17:50发布

问题:

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.

回答1:

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?



回答2:

Well, how big is your key? kCCAlgorithmAES128 and kCCKeySizeAES256 assume different key sizes. I'm assuming that you're using a 16 byte key, because your Java code would throw an exception otherwise. If you're using a 128 bit key, then you should use kCCKeySizeAES128.

Additionally, you're not passing in any IV, so it will be assumed that the IV is filled with 0x00 bytes, but in Java, you're using the key as IV.

Don't use the key as IV. That diminishes the use of the IV in the first place that is there to randomize the ciphertext. You need to generate a random IV for each encryption and send it along with the ciphertext, for example by prepending it to the ciphertext.

Yes, PKCS#5 padding and PKCS#7 padding are the same thing.