Search code examples
iosencryptioncommoncrypto

CCCrypt padding option doesn't seem to affect decryption


I have a string that has been encrypted using Java

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(text.getBytes());    

(Note that in Java, PKCS5Padding is actually PKCS7Padding when using AES link)

And my decryption internal code is:

CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                      kCCAlgorithmAES128,
                                      0,
                                      key.bytes,
                                      kCCBlockSizeAES128,
                                      iv.bytes,
                                      [encrypted bytes],
                                      dataLength,
                                      buffer,
                                      bufferSize,
                                      &numBytesEncrypted);

called like:

let decryptedData = decryptor.AES128DecryptWithKeyString(key, encryptedString: encodedString) //this does the CCCrypt stuff
let string:NSString = NSString(data: decryptedData, encoding: NSUTF8StringEncoding) ?? ""
let data = NSData(base64EncodedString: string as String, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)

So even though it was encrypted using PKCS5Padding, my decryption works fine despite not giving a padding option. Additionally, if I change the 0 option to kCCOptionPKCS7Padding it also works to decrypt.

Is this the expected behavior? Is the padding option only relevant for kCCEncrypt and not kCCDecrypt?

Additionally, if we change the Java encryption to

Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");

and pad the payload manually with zero bytes, then it still decrypts properly regardless of whether I use 0 as an option or kCCOptionPKCS7Padding


Solution

  • When you say that the padding does not affect decryption are you looking at the output in a hex dump?

    The padding will be trailing characters in the range of 0x01 to 0x10 which are non printing ASCII characters so it may seem the same when printing a string that has been encrypted with PKCS#7 padding but decrypted with a padding option.

    Add sample output both ways as a hex dump to the question and you should see what is really happening.

    The return status does not indicate correct decryption or invalid padding, only gross calling errors.

    Example:

    let keyData     = Array("12345678901234567890123456789012".utf8)
    let messageData = Array("Don´t try".utf8)
    
    let encrypted = testCrypt(data:messageData, keyData:keyData, operation:kCCEncrypt, options:UInt32(kCCOptionPKCS7Padding))!
    print("encrypted:       \(encrypted)")
    
    let decryptedPKCS7 = testCrypt(data:encrypted, keyData:keyData, operation:kCCDecrypt, options:UInt32(kCCOptionPKCS7Padding))!
    print("decrypted PKCS7: \(decryptedPKCS7)")
    
    let decryptedNone = testCrypt(data:encrypted, keyData:keyData, operation:kCCDecrypt, options:UInt32(0))!
    print("decrypted None:  \(decryptedNone)")
    

    Output:

    encrypted:       [136, 95, 194,  76, 137, 107, 196,  39, 245, 108, 106, 84, 139, 49, 14, 139]  
    decrypted PKCS7: [68, 111, 110, 194, 180, 116,  32, 116, 114, 121]  
    decrypted None:  [68, 111, 110, 194, 180, 116,  32, 116, 114, 121,   6,  6,   6,  6,  6,   6]  
    

    The trailling six 0x06 bytes are the unremoved padding.