AES Encryption and decryption for android and iOS gives wrong output

Viewed 126

Android code:

private static final String ALGORITHM = "AES/ECB/PKCS5Padding";

private static final String KEY = "SixteenCharacter";

Encryption:

private static byte[] encrypt(final byte[] key, final byte[] originalData) {
    try {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        SecretKeySpec skeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(originalData);
        return encrypted;
    } catch (Exception ex) {
       ex.printStackTrace();
       return new byte[0];
    }
}

Decryption:

private static byte[] decrypt(final byte[] key, final byte[] encryptedData) {
    try {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        SecretKeySpec skeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] original = cipher.doFinal(encryptedData);
        return original;
    } catch (Exception ex) {
        ex.printStackTrace();
        return new byte[0];
    }
}

iOS code:

static let KEY = "SixteenCharacter"

private static let iv: Array = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]

Encryption:

static func getEncryptedString(normal: String?, key: String, options: Int = kCCOptionPKCS7Padding) -> String {
    guard let normal = normal else { return "" }
    if let keyData = key.data(using: String.Encoding.utf8),
       let data = normal.data(using: String.Encoding.utf8),
       let cryptData    = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) {
        let keyLength              = size_t(kCCKeySizeAES128)
        let operation: CCOperation = UInt32(kCCEncrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
        let options:   CCOptions   = UInt32(options)
        var numBytesEncrypted :size_t = 0
        let cryptStatus = CCCrypt(operation,
                                  algoritm,
                                  options,
                                  (keyData as NSData).bytes, keyLength,
                                  iv,
                                  (data as NSData).bytes, data.count,
                                  cryptData.mutableBytes, cryptData.length,
                                  &numBytesEncrypted)
        
        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            cryptData.length = Int(numBytesEncrypted)
            var base64cryptString = cryptData.base64EncodedString(options: .lineLength76Characters)
            //base64cryptString = base64cryptString.replacingOccurrences(of: "/", with: "_")
                        .replacingOccurrences(of: "+", with: "-")
            return base64cryptString
        }
        else {
            return normal
        }
    }
    return normal
}

Decryption:

static func getDecryptedString(normal: String?, key: String, options: Int = kCCOptionPKCS7Padding) -> String {
    guard var normal = normal else { return "" }
    //normal = normal.replacingOccurrences(of: "_", with: "/").replacingOccurrences(of: "-", with: "+")
    if let keyData = key.data(using: .utf8),
       let data = NSData(base64Encoded: normal, options: .ignoreUnknownCharacters),
       let cryptData    = NSMutableData(length: Int((data.length)) + kCCBlockSizeAES128) {
        let keyLength              = size_t(kCCKeySizeAES128)
        let operation: CCOperation = UInt32(kCCDecrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
        let options:   CCOptions   = UInt32(options)
        var numBytesEncrypted :size_t = 0
                    let cryptStatus = CCCrypt(operation,
                                  algoritm,
                                  options,
                                  (keyData as NSData).bytes, keyLength,
                                  iv,
                                  data.bytes, data.length,
                                  cryptData.mutableBytes, cryptData.length,
                                  &numBytesEncrypted)
        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            cryptData.length = Int(numBytesEncrypted)
            let unencryptedMessage = String(data: cryptData as Data, encoding:String.Encoding.utf8)
            if let unencryptedMessage = unencryptedMessage, !unencryptedMessage.isEmpty {
                return unencryptedMessage
            }
        }
        else {
            return normal
        }
    }
    return normal
}

As I haven't worked with AES encryption before, I have no idea where am I going wrong I have seen so many solutions on the internet but no luck yet

Android code is already done and the only way I have is to modify the iOS code!

Thanks in advance!

1 Answers

You are working across two different systems. That means you cannot rely on system defaults, they may be different. You need to specify everything: Key, IV, padding, character encoding, etc. Crypto is designed to fail with even a small mismatch, so you have to check everything to make sure it matches across both systems. For instance, IIRC iOS and Java use different end-of-line characters. Something as simple as that might throw off the decryption.

If there is nothing obvious then look at all the inputs to both sides in binary/hex to make sure everything matches exactly.

You are using ECB mode. This is insecure and shouldn't be used except for very specific and rare situations. Better to use CBC or CTR mode. If you need authentication as well then try GCM mode.

Related