iOS Security Tutorial — Part 2

Signature & Verification + Secure Enclave

Fady Derias
6 min readOct 24, 2019
Thanks to Lukas for the illustration

A digital signature is a guarantee to the receiver regarding the authenticity, integrity, and verification of the sender. To simply put, it’s equivalent to a handwritten signature. A digital signature is achieved using a different key pair than the one used for encryption/decryption.

Digital signatures are utilized in many electronic aspects, Xcode does digitally sign your code whenever you’re to submit your app for testing or to the app store. In general, this is how a sender signs the data to be sent:-

  1. The sender encrypts his/her data with a specific hash algorithm (ex: sha256). The output of that hash algorithm is labelled as a “digest” (ex: 6e04f289)
  2. The sender then encrypts the digest with his/her private key. The encrypted digest is known as the “digital signature” of the data.
  3. The sender sends the original data + the encrypted digest. Notice that the encrypted digest is utilized only for verifying the sender but the message itself is not encrypted by the same private key utilized for signature and verification. It’s encrypted via the private key created as similar to the above section.

This how a receiver verifies the received data:-

  1. The receiver receives the message alongside the digital signature.
  2. The receiver decrypts the digital signature using the sender’s public key
    -> Output is the digest.
  3. The receiver uses the same hashing algorithm similar to the one the sender used to hash the received plain data from the sender -> This will output a digest.
  4. If the output digest from step 2 (digital signature) = output digest from step 3 (hash of the received plain data) -> The receiver can validate the integrity of the data.

For this tutorial, the private key to be used for digital signature is to be stored in the secure enclave while the public key is to be stored outside of it and to be shared with the receiver.

The secure enclave based on Apple’s documentation is “a hardware-based key manager that’s isolated from the main processor to provide an extra layer of security. When you store a private key in the Secure Enclave, you never actually handle the key, making it difficult for the key to becoming compromised. Instead, you instruct the Secure Enclave to create the key, securely store it, and perform operations with it. You receive only the output of these operations, such as encrypted data or a cryptographic signature verification outcome.”

It’s important to point out that iOS’s secure enclave supports 256-bit elliptic curve keys (ECC) only. Notice that the private key inside the secure enclave can also be used for data encryption but that is limited to symmetric encryption using the eciesEncryptionCofactorX963SHA256AESGCM algorithm.

Let’s get coding.

The first thing to do is to generate a new key pair for signing and verification. The process is as similar to the last section, but with an extra step regarding setting the attributes for the private key. That is specifying what is known as an “Access Control”.

The access control object is what defines the mechanism to access the private key inside the secure enclave in order to sign data. One rule setting the accessibility level required when accessing the private key inside the secure enclave (ex: when the device is unlocked or when there’s a passcode set).

//Creating the Access Control Object
let access =
SecAccessControlCreateWithFlags(kCFAllocatorDefault, //1kSecAttrAccessibleWhenUnlockedThisDeviceOnly, //2[.privateKeyUsage,.biometryAny], //3nil)! //4

The access object is created by invoking the SecAccessControlCreateWithFlags security API.

  1. The first parameter of the API is the allocator. Usually, this is set to the default allocator.
  2. Afterwards, the level of protection required to access the private key inside the secure enclave. This is set to kSecAttrAccessibleWhenUnlockedThisDeviceOnly, which means that this item is to be only accessed on the device that created it and only when the device is unlocked.
  3. An array of flags that are set to .privateKeyUsage which clearly defined that private key generated inside the secure enclave is to be used for signing and verification & .biometryAny that instructs the system to make the key only available when the system can authenticate the user with any available biometric authentication.
  4. Last is an error object to hold any error that might occur, but it’s ignored for now.

Next step is to assemble the key pair attributes as done in the previous sections. First thing is to assemble the private key parameters dictionary (which will include the created access control object)

let secEnclaveTag = secureEnclaveKeyTag.data(using: .utf8)! //1
let privateKeyParams: [String: AnyObject] = [
kSecAttrIsPermanent as String: true as AnyObject, //2kSecAttrApplicationTag as String: secEnclaveTag as AnyObject, //3kSecAttrAccessControl as String: access //4]
  1. Create a tag for the private key.
  2. kSecAttrIsPermanent: A key whose value is a boolean. If true, it indicates that the cryptographic key should be stored in the default keychain at creation time.
  3. kSecAttrApplicationTag: A key whose value is an attribute with a unique NSData value so that you can find and retrieve the private key later on from the key chain. It’s constructed from a string and preferable to be in reverse DNS notation.
  4. kSecAttrAccessControl: access control object generated to indicate how the key can be used.

Assemble the attributes dictionary that is to be utilized for generating the key pair as the last section.

let attributes = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, //1
kSecAttrKeySizeInBits as String: 256, //2kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, //3kSecPrivateKeyAttrs as String: privateKeyParams] as CFDictionary //4
  • kSecAttrKeyType: A key whose value indicates the type of cryptography algorithm used to produce the key pair (public and private keys). Set to ECC (since the secure enclave supports only ECC keys)
  • kSecAttrKeySizeInBits: A key whose value indicates the size of the generated key pair. Set to 256 bits (since the secure enclave supports only 256-bit keys)
  • ** kSecAttrTokenID: A key whose value is kSecAttrTokenIDSecureEnclave. This indicates that the generation operation for the private key should take place inside the Secure Enclave.
  • kSecPrivateKeyAttrs: A key whose value is set to the created sub-dictionary that does contain the specifications of the private key of the pair (that includes the access control object)

The last step is to generate the key pair using the attributes dictionary via the SecKeyCreateRandomKey API.

var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error}

The generated key is a reference to the private key stored inside the secure enclave. You can always obtain a reference to it, but can never inspect its data because it’s inside the secure enclave.

You can generate the public key afterwards from that reference of the private key that is to be distributed.

guard let publicKey = SecKeyCopyPublicKey(privateKey) else {print(“Public key generation error”)return””}

Digital Signature

let message = “4043 1710 2843 4577”guard let messageData = message.data(using: String.Encoding.utf8) else {print(“Invalid message to sign.”)} //1guard let signData = SecKeyCreateSignature(privateKey,SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256,messageData as CFData, nil) else {print(“Signing Error”)return nil} //2let signedData = signData as Datalet signedString = signedData.base64EncodedString(options: [])print(“Signed String”, signedString) //3
  1. Create/Retrieve the message required to sign and transform it into a Data object.
  2. Sign the data via the SecKeyCreateSignature security API. It takes the following parameters
  • The created private key via the secure enclave
  • The hashing algorithm required for encryption (SecKeyAlgorithm).
  • The message Data object.

3. In case you wanted to check the signed data, you can transform it to a Base 64 encoded string and print it.

Verification

let message = “4043 1710 2843 4577”guard let messageData = message.data(using: String.Encoding.utf8) else {print(“ECC bad message to verify”)return false} //1guard let signedMessageData = signatureString.data(using: String.Encoding.utf8) else {print(“Invalid message to verify.”)} //2let verify = SecKeyVerifySignature(publicKey,SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256,messageData as CFData,signatureData as CFData,nil) //3
  1. Create/Retrieve the message required to sign and transform it into a Data object.
  2. Get the signed string and transform it into “Data” object.
  3. Verify the data via the SecKeyVerifySignature security API. It takes the following parameters
  • The created public key via the private key in the secure enclave.
  • The hashing algorithm (SecKeyAlgorithm) should be exactly the same as the one used for signing.
  • The message Data object.
  • The signature Data object.
  • Error object.

The returned value is a boolean value that indicates whether the received data is verified or not.

Summary

In this tutorial you’ve learned:

  1. Symmetric vs Asymmetric Encryption & Decryption.
  2. How to apply Asymmetric Encryption in iOS apps using Swift Security APIs.
  3. iOS Secure Element/Enclave.
  4. Digital Signature and Verification.
  5. How to digitally sign and verify data via the secure enclave.

References

  1. Storing Keys in Secure Enclave.
  2. Signing and Verifying.
  3. Generating New Cryptographic Keys.
  4. iOS 10: How to Use Secure Enclave and Touch ID to Protect your Keys

--

--