ArticleCross platform encryption with AES between Swift and Node.js

While working on an application, I've come to a situation where I needed to transfer some highly sensitive data between the backend and the client. Even though theoretically, I should have used ECDH to transfer the keys of AES and then to do the decryption process on the mobile application itself, for the sake of simplicity and the delivery, I've omitted that part right now.

The problem

Simply, I wanted to share an object of configuration to the client app from the backend. The request was on SSL, but we all know that a MITM attack can be possible. So, I needed to encrypt the data on the backend and decrypt it on the mobile application. I'm trusting the compilation process of iOS for the security of the AES secret and the IV.

The solution

On the backend side, this is what I did:

1const PromiseRouter = require('express-router-wrapper')
2const router = new PromiseRouter()
3const crypto = require('crypto')
4
5const iv = process.env.VPN_AES_IV
6const secret = process.env.VPN_AES_SECRET
7
8router
9  .get('/', async (req, res) => {
10    const text = {key: "value"}
11
12    const cipher = crypto.createCipheriv(
13      'aes-256-cbc', 
14      Buffer.from(secret), 
15      Buffer.from(iv))
16
17    let encrypted = cipher.update(Buffer.from(text), 'utf8', 'hex')
18    encrypted += cipher.final('hex')
19
20    return {
21      data: encrypted.toString('hex')
22    }
23  })
24
25module.exports = router.getOriginal()

On the iOS Swift (4.2) side, please do this:

1import CryptoSwift
2
3let AES_IV: String = ""
4let AES_SECRET: String = ""
5
6func dataToByteArray(data: NSData) -> [UInt8] {
7    let pointer = data.bytes.assumingMemoryBound(to: UInt8.self)
8    
9    let buffer = UnsafeBufferPointer(start: pointer, count: data.length)
10    
11    return Array<UInt8>(buffer)
12}
13
14extension String {
15    func aesEncrypt() throws -> String {
16        let encrypted = try AES(key: AES_SECRET, iv: AES_IV).encrypt([UInt8](self.data(using: .utf8)!))
17        return Data(encrypted).base64EncodedString()
18    }
19    
20    func aesDecrypt() throws -> String {
21        let key: [UInt8] = Array(AES_SECRET.utf8)
22        let iv: [UInt8] = Array(AES_IV.utf8)
23        
24        let encryptedData: NSData = self.hexStringToData()
25        let encryptedBytes: [UInt8] = dataToByteArray(data: encryptedData)
26        let decryptedBytes: [UInt8] = try AES(key: key, blockMode: CBC(iv: iv)).decrypt(encryptedBytes)
27        
28        return String(bytes: decryptedBytes, encoding: .utf8)!
29    }
30    
31    func hexStringToData() -> NSData {
32        let data = NSMutableData()
33        var temp = ""
34        for char in self {
35            temp += String(char)
36            if temp.count == 2 {
37                let scanner = Scanner(string: temp)
38                var value: CUnsignedInt = 0
39                scanner.scanHexInt32(&value)
40                data.append(&value, length: 1)
41                temp = ""
42            }
43        }
44        return data as NSData
45    }
46}

Before running your application, make sure that you've set up a Podfile and added pod "CryptoSwift" into it.