Search code examples
javaswiftswift2aes

How do I do a cross platform encryption method working in both IOS and android (AES encryption only..)?


I have been recently assigned a task of encrypting my links and parameters i pass in dataTaskWithRequest in swift. The main headache is it should produce the same output as Android platform. The android team has already created a backend using spring for decrypting the data. The java code is like this

class AESencrp {

private static final String ALGO = "AES";
 private static final byte[] keyValue =
        new byte[]{'T', 'h', 'e', 'B', 'e', 's', 't',
                'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'};

public static String encrypt(String Data) throws Exception {
    Key key = generateKey();
    Cipher c = Cipher.getInstance(ALGO);
    c.init(Cipher.ENCRYPT_MODE, key);
    byte[] encVal = c.doFinal(Data.getBytes());
    String encryptedValue = new BASE64Encoder().encode(encVal);
    return encryptedValue;
}

public static String decrypt(String encryptedData) throws Exception {
    Key key = generateKey();
    Cipher c = Cipher.getInstance(ALGO);
    c.init(Cipher.DECRYPT_MODE, key);
    byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
    byte[] decValue = c.doFinal(decordedValue);
    String decryptedValue = new String(decValue);

    return decryptedValue;
}

private static Key generateKey() throws Exception {
    Key key = new SecretKeySpec(keyValue, ALGO);
    return key;
}

} I cannot change the method used here and use the inbuilt encryption method of swift. Is there a way of doing AES encryption in swift which is efficient and matches the output of java code

I tried - POD Cryptoswift

   do {

         let encrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .ECB).encrypt(inputBytes)
         let encrypted2: [UInt8] = try AES(key: key, iv: iv, blockMode: .ECB, padding: PKCS5).encrypt(inputBytes)


        let encryptedNSData = NSData(bytes: encrypted, length: encrypted.count)
        encryptedBase64 = encryptedNSData.base64EncodedStringWithOptions([])

        let encryptedNSData2 = NSData(bytes: encrypted2, length: encrypted2.count)
        encryPadded = encryptedNSData2.base64EncodedStringWithOptions([])


        let decrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .ECB).decrypt(encrypted)
        let result = String(bytes: decrypted, encoding: NSUTF8StringEncoding)!
        print("result\t\(result )")

    } catch {
        // some error
    }
JAVA CODE OUTPUT = "eJvkXYGzEjJ6RbYSp4a3OQ=="

SWIFT CODE OUTPUT = "9UiyETvuHTsN7eIo0HfQ+w=="

As u can see there is a difference in both outputs. Why is this?


Solution

  • I did this by using crypto swift. You can add this pod through CocoaPods. It's been recommended that you find a native way to solve this issue as crypto swift may affect ur app Performance. Anyway, I couldn't figure a way out other than it.

    I was using swift 2.3 . So, Please convert the code if you are using the latest versions of swift

    Step 1 : pod 'CryptoSwift', '0.5.2' (0.5.2 is for swift 2.3 only.)

    Step 2 : create a string extension

    //Crypto Swift

    func AES_EncryptionKey() -> String {
        let date = NSDate()
        let calender = NSCalendar.currentCalendar()
        let components = calender.components([.Day,.Month,.Year], fromDate: NSDate())
        let year = components.year
        var day = String(components.day)
        var month = String(components.month)
        if day.characters.count == 1 {
            day = "0\(String(day))"
        }
        if month.characters.count == 1 {
            month = "0\(String(month))"
        }
        //Mark: Please change the key as per your requirment! I am using a dynamic key now  rather the one specified in question . i.e It changes everday
        let secretKey = "\(String(day))20\(month)u\(String(year))e"
        return secretKey
    
    }
    
    
    func AESencrypt() throws -> String  {
    
        //Mark: You have to do the same thing in Android too. If u skip this here skip in android too
        let secretKeyTest = AES_EncryptionKey().toBase64()!
    
    
    
        let inputBytes: [UInt8] = Array(self.utf8)
        let key:        [UInt8] = Array(secretKeyTest.utf8) //16
        let iv:         [UInt8] = Array("0000000000000000".utf8)  //16
    
        var encryptedBase64 = ""
        do
        {
            let encrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .ECB).encrypt(inputBytes)
            let encryptedNSData = NSData(bytes: encrypted, length: encrypted.count)
            encryptedBase64 = encryptedNSData.base64EncodedStringWithOptions([])
    
            //Mark: You have to do the same thing in Android too. If u skip this here skip in android too
            encryptedBase64=encryptedBase64.toBase64()!
    
            //Mark: Follow the same blockMode in both platform. ECB Mode is not recommended. I did it in ECB cuz it was already done in other platform
            let decrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .ECB).decrypt(encrypted)
            let result = String(bytes: decrypted, encoding: NSUTF8StringEncoding)!
            print("result\t\(result )")
        }
        catch
        {
            print("FAIL ENCRYPT")
        }
        print("encryptedBase64: \(encryptedBase64)")
    
        return encryptedBase64
    
    }
    
    func AESdecrypt() throws -> String {
        var decryptedString = "NIL"
        let secretKeyTest = AES_EncryptionKey().toBase64()!
    
        let key:        [UInt8] = Array(secretKeyTest.utf8) //16
        let iv:         [UInt8] = Array("0000000000000000".utf8)  //16
    
        //Step1
        let encryptedData = self.dataUsingEncoding(NSUTF8StringEncoding)!
        if let base64Decoded_ = NSData(base64EncodedData: encryptedData, options:  NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
        {
    
            if var stringBase64 = String(data:base64Decoded_, encoding: NSUTF8StringEncoding)
            {
                //Step2
                let encryptedDataSecond = stringBase64.dataUsingEncoding(NSUTF8StringEncoding)!
                let base64DecodedSecond_ = NSData(base64EncodedData: encryptedDataSecond, options:  NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
                //Step3
                let encrypted = Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(base64DecodedSecond_!.bytes), count: base64DecodedSecond_!.length))
                do
                {
                    let decryptedData = try AES(key: key, iv: iv, blockMode: .ECB).decrypt(encrypted)
                    decryptedString = String(bytes: decryptedData, encoding: NSUTF8StringEncoding)!
                    print("decryptedString: \(decryptedString)")
                    print("ALL DECRYPTED")
    
                }
                catch
                {
                    print("FAIL DECRYPT")
                }
            }
        }
    
    
        return decryptedString
    
    
    }
    
    
    
    func fromBase64() -> String? {
        guard let data = NSData(base64EncodedString: self, options: NSDataBase64DecodingOptions(rawValue: 0)) else {
            return nil
        }
    
        return String(data: data, encoding: NSUTF8StringEncoding)
    }
    
    func toBase64() -> String? {
        guard let data = self.dataUsingEncoding(NSUTF8StringEncoding) else {
            return nil
        }
    
        return data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
    }}
    

    Step 3 : The most important thing is cross checking the way of all your methods in both platforms. Do follow all the steps done in one platform in the other too..