Home:ALL Converter>Swift 5 AES Encryption/Decryption using CommonCrypto

Swift 5 AES Encryption/Decryption using CommonCrypto

Ask Time:2019-06-21T03:51:04         Author:ClockWise

Json Formatter

I've been trying to use the CommonCrypto in iOS to do a blockwise decryption of a larger Data object, but I can't get it to work. I've been reading the documentation, and looked at several examples from Objective-C - none of which I managed to get to work in an Objective-C environment. After 3 days of coding this I'm starting to wear out and I need some help.

I can decrypt and encrypt fine using the CCCrypto approach, but since I use rather large files this eats up too much memory for iOS to allow me to use this method. Thus I need to do this more efficiently and I decided to try an approach where I decipher one block at a time and then replace the block I deciphered with the resulting data block. The code runs, it seems to be working except for the fact that the data I get won't decode back to an UTF8 String.

Code for using CCCryptoCreate() with symmetric block deciphering (DataExtension)

mutating func decryptUsingCCCryptoCreate() {
    // Get the first 16 bytes as IV
    let IV = self.prefix(kCCBlockSizeAES128)

    // Get key as array of bytes
    let key = "ABCDEFGHIJKLMNOPQRSABCDEFGHIJKLM".data(using: .utf8) ?? Data()

    // Setup totalSize
    let totalSize = self.count

    let operation = kCCDecrypt
    let algorithm = kCCAlgorithmAES
    let options = kCCOptionPKCS7Padding
    var cryptorRef: CCCryptorRef?

    // Step one is to create the CCCryptor with correct parameters for AES128 decryption
    var status = CCCryptorCreate(CCOperation(operation), CCAlgorithm(algorithm), CCOptions(options), key.withUnsafeBytes { $0.baseAddress }, key.count, IV.withUnsafeBytes { $0.baseAddress }, &cryptorRef)

    if status != kCCSuccess {
        print("Failed on create: \(status.description)")
        return
    }

    var dataOutMoved: size_t = 0 // The actual data moved
    var dataInLength: size_t = kCCBlockSizeAES128 // The in size will always be the size of a kCCBlockSizeAES128
    var dataOutLength: size_t = CCCryptorGetOutputLength(cryptorRef, dataInLength, false) // DataOutLength is always less than or equal to the dataInLength

    var totalLength: size_t = 0 // The actual length of the deciphered data
    var filePtr: size_t = 0 // Keeps track of the current position in the deciphering process
    var startByte: Int = 0 // Increments each time with the kCCBlockSizeAES128 until we are done

    var dataIn = Data() // Buffer to store the encrypted block to be deciphered
    var dataOut = Data() // Buffer to store the decrypted block result

    // While startByte is less than totalSize we continue to decrypt the next block
    while startByte <= totalSize {
        if startByte + kCCBlockSizeAES128 > totalSize {
            dataInLength = totalSize - startByte
        } else {
            dataInLength = kCCBlockSizeAES128
        }

        // Next block to decrypt
        guard let rangeToDecrypt = Range(NSRange(location: startByte, length: dataInLength)) else { return }
        dataIn = self.subdata(in: rangeToDecrypt)

        // Decipher the block
        status = CCCryptorUpdate(cryptorRef, dataIn.withUnsafeBytes { $0.baseAddress }, dataInLength, dataOut.withUnsafeMutableBytes { $0.baseAddress }, dataOutLength, &dataOutMoved)
        if status != kCCSuccess {
            print("Failed on Update: \(status.description)")
            return
        }

        // Replace the encrypted block with the decrypted block
        let rangeToReplace = Range(NSRange(location: filePtr, length: dataOutMoved))!
        self.replaceSubrange(rangeToReplace, with: dataOut.withUnsafeBytes { $0.baseAddress! }, count: dataOutMoved)

        totalLength += dataOutMoved
        filePtr += dataOutMoved
        startByte += kCCBlockSizeAES128
    }

    // Finalize the deciphering
    status = CCCryptorFinal(cryptorRef, dataOut.withUnsafeMutableBytes { $0.baseAddress }, dataOutLength, &dataOutMoved)
    totalLength += dataOutMoved

    if status != kCCSuccess {
        print("Failed on final: \(status.description)")
        return
    }

    // We replace the final deciphered block
    let decryptedRange = Range(NSRange(location: filePtr, length: dataOutMoved))!
    self.replaceSubrange(decryptedRange, with: dataOut.withUnsafeBytes { $0.baseAddress! }, count: dataOut.count)

    // Since we are using padding the CCCryptorFinal can contain padding which needs to be truncated.
    self = self.prefix(totalLength)

    // Finish the CCCryptor process
    CCCryptorRelease(cryptorRef)
}

Code for the encryption using CCCrypto()

mutating func encryptUsingCCCrypto() {
    let sa = String(data: self, encoding: .utf8) ?? ""
    print("Before encryption: \(sa)")
    let now = Date()
    let key = "ABCDEFGHIJKLMNOPQRSABCDEFGHIJKLM".data(using: .utf8) ?? Data()
    let ivRandomData = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    let blockSize = kCCBlockSizeAES128
    let bufferSize = self.count + blockSize
    var encryptedSize = 0

    let cryptStatus = CCCrypt(UInt32(kCCEncrypt),
                              UInt32(kCCAlgorithmAES),
                              UInt32(kCCOptionPKCS7Padding),
                              key.withUnsafeBytes { $0.baseAddress },
                              key.count,
                              ivRandomData.withUnsafeBytes { $0.baseAddress },
                              self.withUnsafeBytes { $0.baseAddress },
                              self.count,
                              self.withUnsafeMutableBytes { $0.baseAddress },
                              bufferSize,
                              &encryptedSize)

    self = self.prefix(encryptedSize)
    let s = String(data: self, encoding: .utf8) ?? ""
    print("Result: \(s)")
}

I use the code like this:

        let string = "1234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234"
        var data = string.data(using: .utf8) ?? Data()
        data.encryptUsingCCCrypto()

        data.decryptUsingCCCryptoCreate() // I get a Data object with 112 bytes here
        let s = String(data: data, encoding: .utf8) ?? "" // String is nil, and is provided ""

Author:ClockWise,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/56692996/swift-5-aes-encryption-decryption-using-commoncrypto
yy