0

I'm building a React-Native app that uses NotificationServiceExtension on iOS to intercept encrypted notifications and decrypt them before showing to the user.

On the react-native side, I encrypt the message using CryptoJS:

const originalText = 'This will be encrypted';
const theAESKeyWordArray = CryptoJS.enc.Hex.parse("000102030405060708090a0b0c0d0e0f");
const iv = CryptoJS.enc.Hex.parse("101112131415161718191a1b1c1d1e1f"); // Test iv text is in hexadecimal and converting it to wordArray
    
const ciphertext = CryptoJS.AES.encrypt(originalText, theAESKeyWordArray, {
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7,
            iv: iv
        }).toString();

On the NotificationServiceExtension side (Swift) using CryptoSwift, after receiving the notification, I'm trying to decode it using the following code:

The "encryptedBody" variable when printed out to console looks exactly the same as console.log(ciphertext) in react-native side. I can also see that when printed out the clearAESKey.string(encoding: .utf8) is the same as that which I used to to encrypt it on the react-native side.

let AESKey = Array<UInt8>(hex: try clearAESKey.string(encoding: .utf8))
let iv =  Array<UInt8>(hex: "101112131415161718191a1b1c1d1e1f")
let bufContent: [UInt8] = [UInt8] (encryptedBody.utf8)

let aes = try AES(key: AESKey, blockMode: CBC(iv: iv), padding: .pkcs7) 
let padded = Padding.pkcs7.add(to: bufContent, blockSize: AES.blockSize)
          
let decryptedBody = try aes.decrypt(padded)  
let decryptedData = Data(decryptedBody)
let decryptedString = String(decoding: decryptedBody, as: UTF8.self)

print(decryptedString)

The printed output of decryptedString is something like this "L����JtϑJ��E7sD��)�ga�J׊p�"

Any ideas what I am doing wrong? Thanks anyone for your help.

Chris
  • 13
  • 3
  • `ciphertext` in the CryptoJS code is Base64 encoded. Did you consider this on the Swift side, i.e. did you Base64 decode (it looks like you UTF-8 encode)? Also, the ciphertext on the Swift side must not be padded *before* decryption (instead, *un*padded *after* decryption). – Topaco Sep 19 '22 at 14:04
  • @Topaco Thanks for your prompt feedback. I'll try encoding in Base64 on Swift side. But regarding the padding before decryption... when i didn't do that, CryptoSwift complained that i needed padding. Looking at the CryptoSwift code it requires that (content bytes % blocksize = 0) so that's why i thought we needed to pad it. and i read this: https://github.com/krzyzanowskim/CryptoSwift#data-padding-1 – Chris Sep 19 '22 at 14:22

1 Answers1

0

Key and IV must be hex decoded, the ciphertext returned by the CryptoJS code must be Base64 decoded. Before decryption, no padding must be applied:

let AESKey = [UInt8](hex: "000102030405060708090a0b0c0d0e0f")
let iv = [UInt8](hex: "101112131415161718191a1b1c1d1e1f")
let bufContent = [UInt8](base64: "GErWj4t2vW0u1o1Z9iUNo8gxkO1O0Bl8l3amQ86EKKw=")

let aes = try AES(key: AESKey, blockMode: CBC(iv: iv), padding: .pkcs7)           
let decryptedBody = try aes.decrypt(bufContent)  
let decryptedString = String(bytes: decryptedBody, encoding: .utf8)!

print(decryptedString) // This will be encrypted

Keep in mind that a static IV is insecure (for testing purposes this is of course OK).

Topaco
  • 40,594
  • 4
  • 35
  • 62