Swift AES CBC 256 Encryption With Static 32bit Key and 32bit IV

We have the below Implementation in Android and the same has to be integrated into Swift.

Key :-  "d95acd54b4a821ff32c52825q931c194"

IV :-  "687b9509c25a34b8ad076346s8353d67"

Here Both the Key and IV are 32 bits and below is the android code.

public class AESEncryption {
    private static final String key = "d95acd54c6a821ff32c52825b931c194";
    private static final String initVector = "687b9509c25a14b8ad076346d8353d67";
    static byte[] bte = hexToBytes(initVector);
    public static String encrypt(String strToEncrypt) {
        try {
            CommonCode.showLog("log", bte.toString());
            IvParameterSpec iv = new IvParameterSpec(bte);
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            CommonCode.showLog("IV after logs", iv.toString());
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(strToEncrypt.getBytes());
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                return Base64.getEncoder().encodeToString(encrypted).trim();
            } else {
                return android.util.Base64.encodeToString(encrypted, android.util.Base64.DEFAULT).trim();
            }
        } catch (Exception e) {
            CommonCode.showLog("Error while encrypting: ", e.toString());
        }
        return null;
    }
    public static String decrypt(String strToDecrypt) {
        try {
            IvParameterSpec iv = new IvParameterSpec(bte);
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
            } else {
                return new String(cipher.doFinal(android.util.Base64.decode(strToDecrypt, android.util.Base64.DEFAULT)));
            }
        } catch (Exception e) {
            CommonCode.showLog("Error while decrypting: " , e.toString());
        }
        return null;
    }
}

How can we mimic the above in Swift? Here in Android they are using static byte[] bte = hexToBytes(initVector); to convert the 32bit IV into 16 bit Bytes Array

I Have Tried the same approach on Swift below are the code snippet


[Contents.swift](https://developer.apple.com/forums/content/attachment/60fab4f2-1496-4003-9f37-c195de95e94a)

How can we mimic the above in Swift?

My recommendation would be to open a TSI for extra help here. If you do so, please include a focused sample project of the code that you have in Swift so far, and what you expect the result to be compared to what you are currently seeing.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

CryptoKit does not support AES-CBC [1]. The standard API for AES-CBC on our platforms is CommonCrypto. The CryptoCompatibility sample code has examples of how to use CommonCrypto to create AES-CBC results that match other common security frameworks, like OpenSSL.

With regards this specific question:

Here in Android they are using … hexToBytes

Swift does not have a nice API for converting between hex and binary (r. 28005863)-: However, in this case that’s not necessary because these are constant values and you can just save them as bytes:

let key: [UInt8] = [
    0xd9, 0x5a, 0xcd, 0x54, 0xc6, 0xa8, 0x21, 0xff, 
    0x32, 0xc5, 0x28, 0x25, 0xb9, 0x31, 0xc1, 0x94, 
]

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Because its focus is on algorithms that are secure by default, and AES-CBC is very easy to use insecurely. And apropos that, using a hard-wired IV is a really bad idea, security-wise.

So, I’m helping vishalfromnashik in another context but, as part of that, I created a Swift wrapper around CommonCrypto’s AES CBC support. I’ve pasted it in below for the benefit of all.

This includes a simple test suite, derived from the one in the CryptoCompatibility sample code (which is my go-to reference for this sort of thing).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

import Foundation
import CommonCrypto

/// Encrypts data using AES with PKCS#7 padding in CBC mode.
///
/// - note: PKCS#7 padding is also known as PKCS#5 padding.
///
/// - Parameters:
///   - key: The key to encrypt with; must be a supported size (128, 192, 256).
///   - iv: The initialisation vector; must be of size 16.
///   - plaintext: The data to encrypt; the PKCS#7 padding means there are no
///     constraints on its length.
/// - Returns: The encrypted data; it’s length with always be an even multiple of 16.

func QCCAESPadCBCEncrypt(key: [UInt8], iv: [UInt8], plaintext: [UInt8]) throws -> [UInt8] {

    // The key size must be 128, 192, or 256.
    //
    // The IV size must match the block size.

    guard
        [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256].contains(key.count),
        iv.count == kCCBlockSizeAES128
    else {
        throw QCCError(code: kCCParamError)
    }

    // Padding can expand the data, so we have to allocate space for that.  The
    // rule for block cyphers, like AES, is that the padding only adds space on
    // encryption (on decryption it can reduce space, obviously, but we don't
    // need to account for that) and it will only add at most one block size
    // worth of space.

    var cyphertext = [UInt8](repeating: 0, count: plaintext.count + kCCBlockSizeAES128)
    var cyphertextCount = 0
    let err = CCCrypt(
        CCOperation(kCCEncrypt),
        CCAlgorithm(kCCAlgorithmAES),
        CCOptions(kCCOptionPKCS7Padding),
        key, key.count,
        iv,
        plaintext, plaintext.count,
        &cyphertext, cyphertext.count,
        &cyphertextCount
    )
    guard err == kCCSuccess else {
        throw QCCError(code: err)
    }
    
    // The cyphertext can expand by up to one block but it doesn’t always use the full block,
    // so trim off any unused bytes.
    
    assert(cyphertextCount <= cyphertext.count)
    cyphertext.removeLast(cyphertext.count - cyphertextCount)
    assert(cyphertext.count.isMultiple(of: kCCBlockSizeAES128))

    return cyphertext
}

/// Decrypts data that was encrypted using AES with PKCS#7 padding in CBC mode.
///
/// - note: PKCS#7 padding is also known as PKCS#5 padding.
///
/// - Parameters:
///   - key: The key to encrypt with; must be a supported size (128, 192, 256).
///   - iv: The initialisation vector; must be of size 16.
///   - cyphertext: The encrypted data; it’s length must be an even multiple of
///     16.
/// - Returns: The decrypted data.

func QCCAESPadCBCDecrypt(key: [UInt8], iv: [UInt8], cyphertext: [UInt8]) throws -> [UInt8] {

    // The key size must be 128, 192, or 256.
    //
    // The IV size must match the block size.
    //
    // The ciphertext must be a multiple of the block size.

    guard
        [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256].contains(key.count),
        iv.count == kCCBlockSizeAES128,
        cyphertext.count.isMultiple(of: kCCBlockSizeAES128)
    else {
        throw QCCError(code: kCCParamError)
    }

    // Padding can expand the data on encryption, but on decryption the data can
    // only shrink so we use the cyphertext size as our plaintext size.

    var plaintext = [UInt8](repeating: 0, count: cyphertext.count)
    var plaintextCount = 0
    let err = CCCrypt(
        CCOperation(kCCDecrypt),
        CCAlgorithm(kCCAlgorithmAES),
        CCOptions(kCCOptionPKCS7Padding),
        key, key.count,
        iv,
        cyphertext, cyphertext.count,
        &plaintext, plaintext.count,
        &plaintextCount
    )
    guard err == kCCSuccess else {
        throw QCCError(code: err)
    }
    
    // Trim any unused bytes off the plaintext.
    
    assert(plaintextCount <= plaintext.count)
    plaintext.removeLast(plaintext.count - plaintextCount)

    return plaintext
}

/// Wraps `CCCryptorStatus` for use in Swift.

struct QCCError: Error {
    var code: CCCryptorStatus
}

extension QCCError {
    init(code: Int) {
        self.init(code: CCCryptorStatus(code))
    }
}

func main() {
    // This test case was taken from the CryptoCompatibility sample code, and
    // specifically the `-testAES256PadCBCEncryption` methods and
    // `-testAES256PadCBCDecryption` methods.
    //
    // <https://developer.apple.com/library/mac/#samplecode/CryptoCompatibility/>
    let key: [UInt8] = [
        0x0C, 0x10, 0x32, 0x52, 0x03, 0x02, 0xEC, 0x85,
        0x37, 0xA4, 0xA8, 0x2C, 0x4E, 0xF7, 0x57, 0x9D,
        0x2b, 0x88, 0xe4, 0x30, 0x96, 0x55, 0xeb, 0x40,
        0x70, 0x7d, 0xec, 0xdb, 0x14, 0x3e, 0x32, 0x8a,
    ]
    let iv: [UInt8] = [
        0xAB, 0x5B, 0xBE, 0xB4, 0x26, 0x01, 0x5D, 0xA7,
        0xEE, 0xDC, 0xEE, 0x8B, 0xEE, 0x3D, 0xFF, 0xB7,
    ]
    let plaintext332: [UInt8] = [
        0x54, 0x68, 0x65, 0x20, 0x41, 0x70, 0x70, 0x6C,
        0x65, 0x20, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61,
        0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x72,
        0x6F, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x62,
        0x79, 0x20, 0x41, 0x70, 0x70, 0x6C, 0x65, 0x20,
        0x6F, 0x6E, 0x20, 0x61, 0x6E, 0x20, 0x22, 0x41,
        0x53, 0x20, 0x49, 0x53, 0x22, 0x20, 0x62, 0x61,
        0x73, 0x69, 0x73, 0x2E, 0x20, 0x41, 0x50, 0x50,
        0x4C, 0x45, 0x20, 0x4D, 0x41, 0x4B, 0x45, 0x53,
        0x20, 0x4E, 0x4F, 0x20, 0x57, 0x41, 0x52, 0x52,
        0x41, 0x4E, 0x54, 0x49, 0x45, 0x53, 0x2C, 0x20,
        0x45, 0x58, 0x50, 0x52, 0x45, 0x53, 0x53, 0x20,
        0x4F, 0x52, 0x20, 0x49, 0x4D, 0x50, 0x4C, 0x49,
        0x45, 0x44, 0x2C, 0x20, 0x49, 0x4E, 0x43, 0x4C,
        0x55, 0x44, 0x49, 0x4E, 0x47, 0x20, 0x57, 0x49,
        0x54, 0x48, 0x4F, 0x55, 0x54, 0x20, 0x4C, 0x49,
        0x4D, 0x49, 0x54, 0x41, 0x54, 0x49, 0x4F, 0x4E,
        0x20, 0x54, 0x48, 0x45, 0x20, 0x49, 0x4D, 0x50,
        0x4C, 0x49, 0x45, 0x44, 0x20, 0x57, 0x41, 0x52,
        0x52, 0x41, 0x4E, 0x54, 0x49, 0x45, 0x53, 0x20,
        0x4F, 0x46, 0x20, 0x4E, 0x4F, 0x4E, 0x2D, 0x49,
        0x4E, 0x46, 0x52, 0x49, 0x4E, 0x47, 0x45, 0x4D,
        0x45, 0x4E, 0x54, 0x2C, 0x20, 0x4D, 0x45, 0x52,
        0x43, 0x48, 0x41, 0x4E, 0x54, 0x41, 0x42, 0x49,
        0x4C, 0x49, 0x54, 0x59, 0x20, 0x41, 0x4E, 0x44,
        0x20, 0x46, 0x49, 0x54, 0x4E, 0x45, 0x53, 0x53,
        0x20, 0x46, 0x4F, 0x52, 0x20, 0x41, 0x20, 0x50,
        0x41, 0x52, 0x54, 0x49, 0x43, 0x55, 0x4C, 0x41,
        0x52, 0x20, 0x50, 0x55, 0x52, 0x50, 0x4F, 0x53,
        0x45, 0x2C, 0x20, 0x52, 0x45, 0x47, 0x41, 0x52,
        0x44, 0x49, 0x4E, 0x47, 0x20, 0x54, 0x48, 0x45,
        0x20, 0x41, 0x50, 0x50, 0x4C, 0x45, 0x20, 0x53,
        0x4F, 0x46, 0x54, 0x57, 0x41, 0x52, 0x45, 0x20,
        0x4F, 0x52, 0x20, 0x49, 0x54, 0x53, 0x20, 0x55,
        0x53, 0x45, 0x20, 0x41, 0x4E, 0x44, 0x20, 0x4F,
        0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4F, 0x4E,
        0x20, 0x41, 0x4C, 0x4F, 0x4E, 0x45, 0x20, 0x4F,
        0x52, 0x20, 0x49, 0x4E, 0x20, 0x43, 0x4F, 0x4D,
        0x42, 0x49, 0x4E, 0x41, 0x54, 0x49, 0x4F, 0x4E,
        0x20, 0x57, 0x49, 0x54, 0x48, 0x20, 0x59, 0x4F,
        0x55, 0x52, 0x20, 0x50, 0x52, 0x4F, 0x44, 0x55,
        0x43, 0x54, 0x53, 0x2E
    ]
    let cyphertextAES256CBC332: [UInt8] = [
        0xDD, 0x7B, 0x23, 0x4D, 0x67, 0x05, 0xFE, 0xBF,
        0x28, 0x8D, 0x45, 0xFD, 0x93, 0x78, 0x6C, 0xEB,
        0x20, 0x22, 0xD8, 0x24, 0x89, 0x88, 0xAD, 0x1F,
        0x1C, 0x9E, 0x7E, 0x61, 0x6A, 0xE6, 0x4E, 0x79,
        0xA2, 0x47, 0x1B, 0xA3, 0x7C, 0xFE, 0x51, 0xED,
        0xF1, 0xEB, 0x9F, 0x5E, 0x01, 0x9B, 0x47, 0x30,
        0xD7, 0x2F, 0x87, 0x22, 0xDF, 0xA9, 0xEB, 0x1F,
        0x5C, 0xA3, 0x5F, 0xEA, 0x18, 0xDE, 0x2D, 0x18,
        0x08, 0x48, 0x8C, 0x84, 0xBC, 0xE9, 0x83, 0xA7,
        0x4E, 0x48, 0x92, 0xBB, 0x76, 0xA7, 0x9C, 0x71,
        0x27, 0x8B, 0x40, 0x82, 0x2E, 0xC3, 0xC5, 0x92,
        0xA5, 0x9F, 0x87, 0xC3, 0x90, 0xF2, 0x8E, 0x75,
        0xCB, 0xDB, 0x94, 0xC1, 0xBF, 0xC6, 0x60, 0xC7,
        0x6A, 0xBA, 0xAA, 0x35, 0x26, 0xB0, 0xE3, 0x49,
        0xCA, 0x26, 0x39, 0x72, 0xEB, 0x89, 0x01, 0x9F,
        0xC9, 0x44, 0x67, 0x74, 0x1F, 0xCD, 0xBA, 0x2C,
        0xB1, 0x95, 0x99, 0xFE, 0x1C, 0xDA, 0x4D, 0xE7,
        0x32, 0x6B, 0xE8, 0xCE, 0x66, 0x0F, 0xD3, 0x8E,
        0x25, 0x17, 0x82, 0x5A, 0x9C, 0x21, 0xA2, 0x28,
        0xF0, 0x71, 0x99, 0xD8, 0x4A, 0x5A, 0x58, 0x80,
        0x94, 0x36, 0x9A, 0x7F, 0xA5, 0xFA, 0xC1, 0x6E,
        0xA7, 0x04, 0x68, 0xF5, 0x2E, 0x38, 0x73, 0x01,
        0xF4, 0x99, 0xF8, 0x48, 0x12, 0x53, 0xCF, 0x06,
        0x67, 0x0B, 0x07, 0xF2, 0x45, 0x3A, 0xEE, 0xD7,
        0x2B, 0x45, 0x03, 0xE7, 0x50, 0x1D, 0x52, 0xD8,
        0xFA, 0x73, 0xC6, 0xBE, 0x79, 0x65, 0x01, 0x11,
        0x81, 0x0B, 0x94, 0x0E, 0xA4, 0x96, 0x20, 0xC9,
        0x60, 0xBE, 0xB4, 0xEA, 0x1F, 0xCC, 0xC4, 0x22,
        0x6A, 0xE6, 0x52, 0xDE, 0x62, 0x25, 0x61, 0xCF,
        0xE1, 0x2D, 0x1C, 0x0B, 0x65, 0xD3, 0xC3, 0x08,
        0xEE, 0x00, 0x5B, 0x61, 0x06, 0x72, 0xAA, 0xB6,
        0x2F, 0x3D, 0xC7, 0x23, 0xAE, 0xF7, 0xA7, 0xC3,
        0xA9, 0x7E, 0xBF, 0xC7, 0xF0, 0x3B, 0x68, 0xB6,
        0xEB, 0x22, 0xA2, 0x8E, 0x70, 0xE3, 0xD3, 0x27,
        0x83, 0xDB, 0x1C, 0xD2, 0x48, 0x3C, 0xEE, 0x63,
        0x36, 0xAA, 0x9D, 0xD7, 0x2D, 0x24, 0x7C, 0xD2,
        0x22, 0x1E, 0xFB, 0x42, 0xE4, 0x4F, 0xA7, 0x48,
        0xB2, 0x3F, 0xB6, 0xD3, 0x25, 0xAD, 0x93, 0x72,
        0x02, 0x8F, 0x0E, 0x70, 0x57, 0xF4, 0x74, 0xC9,
        0x2D, 0x1E, 0xEB, 0xC6, 0x4A, 0x78, 0x21, 0x33,
        0x4F, 0x2A, 0x41, 0x44, 0xD1, 0xC9, 0x45, 0x13,
        0x5A, 0x0D, 0x64, 0x4E, 0xD6, 0xF5, 0x54, 0x9E,
    ]
    let cyphertext = try! QCCAESPadCBCEncrypt(key: key, iv: iv, plaintext: plaintext332)
    assert(cyphertext == cyphertextAES256CBC332)
    let plaintext = try! QCCAESPadCBCDecrypt(key: key, iv: iv, cyphertext: cyphertext)
    assert(plaintext == plaintext332)
    print("Success!")
}

main()

Hello, I am developing this in ios but having one issue.

I am convert my string to encrypted string using AES encryption that's working fine and also and decrypt that encrypted string using same IV and Salt key. The issue is that i have one encrypted string that should be decrypt using IV and salt key but it's not It is returning nil instead of decrypted string.

I follow this code : "

"

Below are my string data : Encrypted string : "/ebetq2aI+VGtpb/YjFkF4+1w23r20cZh5O3w2yH8tpYHr7djnDs85yLWOUeFNU9DKPIBR5BDkdN+m/OsiNCdtdUdn9eA24heS1GB0k7o+MSxWMjYUtkcqhXPFxiUhue9utjq7FbogHv5QCbYLoXLPI1MDMAJEN2dyEyxintLCJ36Qb0JptBIfN79UVHnnZk/jD+ZaUhDzcJwFKhYPbSHV/g73xw+sn4mxxNVnRJz+yZb/f2ibNMpzzjO9b6vJru+NieeHV8tc6uPEJVKeNMO7KEBQ3qaW+bQMej9GD4gIkw9OkYYZBQ85HDFvSG5QT/9eC2paqUbHdh62obU3XbqniEuGyn/91EaIF2sHPMA9BC484gL0MeD8T7spXu3/IneqBBP/EidgoJ0OiyItzAGDPEFVaEALXJIuzycUUP+eWILbNSltfWaMKcUd06FxMWeiUVDZE7alwkIhZaMHDT7bw0Oo8KbDdZupdOW0KTWWBmXer0qTEC0HEryygO034kHIOL4ibYwAPeueYsePuVyIUY/I8O//dsNKM6MIrDCc8pTsIHIaBwUzZgpjYT9s8akX9Hkhrk17zg5QF6vP8QCd9Z2omnouS3MMIVEifimxr7yXXsAgEte3Ad+w/fat/Z+jft7goYy280Wue33CsQskZ2AJZZAqik21gRJD0qKiOPDLQMcpDKYQjRhn5WCk4fVZC4CCyuu/ujco7lKHTFMPy6X2akpyZKDO2iqDPmcSYpmyATcOxwRIc2CP7AbZFuCai/a1ah+4iG8NJTiRCW4lyql+zcGZZsEctwq3YLqzkG6w6yfbk3M8oSR598HgiUKjumMKMOVFgnHJ7Vu+k3OePEPgadAHxNGCS90jMuPVALtxocjUTX+oVYlakg5/irY2xBQRjTIn+fVp7Tw95h+hcRKNmnizBE5SA/l7vfmeRPQqDzyMtrM0rmFppHSvWM9lwp6orhdBL5rlOjlcnof/RZe7cezoQFMS9touCaX6o15Gvl/eNyJRV9v318E3j7pcVbAVJScZzox/hSZYh+354l28LDUA/G0h1DqSK528ZGbqbxYWtWx9PvgpdHWu++PuXCItBdsUby7jt7JkJPfpmfhzxkWwPUmUM0RNQ9N7uFyIeggd5OvzZq5B6sZuSK"

IV Key : "01234567890123456789012345678901" Salt Key : "df3ddbf43ec16823572c800d2b43c5b3"

Please help me out, Can you decrypt this encrypted string I am stuck in this issue over 1 week 😓😓

Thanks.

Are you sure that this is correct:

let statKey = Data("d95acd54c6a821ff32c52825b931c194".utf8)

That string looks like hex, so I suspect that you might want to convert it to data by decoding the hex than rendering the UTF-8.

Also, this looks weird:

let ivString = "687b9509c25a14b8ad076346d8353d67"

let string = "687b9509c25a14b8ad076346d8353d67"

Why are you declaring this string twice?

And the snippet you posted has a whole bunch of stuff that’s completely irrelevant to this issue, like the createKey(…) method and the stuff it relies on. It’d help if you tidied that up to focus on the issue you’re trying to debug.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi, @eskimo! I'm trying do decrypt some data using AES CBC. I found your swift wrapper around CommonCrypto’s AES CBC support. However I'm struggling to convert my key, iv to [UInt8] format:

I have:

       var encKey = "686d5e547e2d91f523878ba0bf344157"
       var cyphertext = "bad44962ac98600d00005ae917f1"
       var IV = "B43522F2228CD2FC41600D0000"

       var bytesKey = encKey. how to covert it
       var bytesCyphertext = cyphertext. how to covert it
       var bytesIV = IV. how to covert it

       let plaintext = try? QCCAESPadCBCDecrypt(key: bytesKey, iv: bytesIV, cyphertext: bytesCyphertext)

So I'm trying to use your QCCAESPadCBCDecrypt example function and I need to convert these strings to [UInt8].

Thank you very much for the example above!

Those looks like hex strings. Our platforms don’t have great APIs for dealing with hex strings )-: so I have my own helpers. I’ve pasted some snippets in below.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

public enum QHexData {
    
    &#x2F;&#x2F;&#x2F; Parses a string as a sequence of hex bytes.
    &#x2F;&#x2F;&#x2F;
    &#x2F;&#x2F;&#x2F; - Parameter hex: The string to parse; supports both lower and upper case hex digits.
    &#x2F;&#x2F;&#x2F; - Returns: The bytes, or `nil` if the string contain non-hex characters or has an odd length.

    public static func optionalBytes&lt;S: StringProtocol>(_ hex: S) -> [UInt8]? {
        var result: [UInt8] = []
        var iter = hex.makeIterator()
        while let n1 = iter.next() {
            guard let n2 = iter.next() else { return nil }
            guard let b = UInt8("\(n1)\(n2)", radix:16) else { return nil }
            result.append(b)
        }
        return result
    }

    &#x2F;&#x2F;&#x2F; Parses a string as a sequence of hex bytes, trapping if things go wrong.
    &#x2F;&#x2F;&#x2F;
    &#x2F;&#x2F;&#x2F; - Parameter hex: The string to parse; supports both lower and upper case hex digits.
    &#x2F;&#x2F;&#x2F; - Returns: The bytes.

    public static func bytes&lt;S: StringProtocol>(_ hex: S) -> [UInt8] {
        return self.optionalBytes(hex)!
    }
}

Thank you for the provided helpers!

I recently found out that Apple's Swift Crypto open source package has a _CryptoExtras module that supports AES CBC encryption in Swift.

I have used it in my iOS app after I discovered it using Xcode's Add Package Dependencies… window under Apple Swift Packages.

It satisfied my AES CBC encryption needs perfectly, although it lacked proper documentation and has all of its types prefixed with an underscore. I shared some feedback about it in the pitch thread in Swift Forums.

Swift AES CBC 256 Encryption With Static 32bit Key and 32bit IV
 
 
Q