Using CryptoTokenKit on iOS to read SmartCard certificates

I am working on an iOS application that deals with scanning SmartCards at entry points to see who is entering a facility. We are currently using proprietary smartcard readers from a company and their SDK to directly access the reader and issue APDU commands to get the smartcard information, such as certificate, expiration date, etc. The plan is to be able to have a person insert a smartcard, the system recognize it, validate the expiration date and cert chain, record who it is and then tell the user to pull the card and move on. This process needs to be fast. PIN entries are not required.

We are trying to move away from the 3rd party SDK and proprietary card reader to be able to use any CCID compliant reader and CryptoTokenKit from Apple. Information on this seems to be very limited from what searching.

I've started with some simple code (not complete):

var card : TKSmartCard? = nil
let card = slot?.makeSmartCard()
if (card != nil && card?.isValid != nil)
{
	card?.beginSession(reply: { something, error in
	let nistEndpoint : [UInt8] = [0x00, 0xA4, 0x04, 0x00, 0x0B, 0xA0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00]
	let nistRequest = Data.init(bytes: nistEndpoint)

	card?.transmit(nistRequest, reply: { data, error in
    		if error == nil {
            // Do stuff
        }else {
            // log the error
        }
    })
}

When I run this on an iPad with a USB connected card reader, I am seeing the card reader, getting its name, seeing that a card is in the reader and the 'card?.isValid' is coming back. The card?.transmit is throwing an error however and returns TKError.Code.tokenNotFound (-7).

Questions:

  1. I see that the CryptoTokenKit API requires the entitlement of com.apple.security.smartcard, but no where during the creation of a provisioning profile process in my dev account give me the option to add that specific entitlement. Is this something that has be specially assigned by Apple?
  2. Is what I'm trying to accomplish possible using CryptoTokenKit on an iOS device?
  3. Does anyone know of any tutorials or examples of this?

Thank you in advance.

Replies

Most folks who need to work with hardware-based credentials don’t need to send APDU commands to the token. Rather, they work with the SecItem API to get a list of certificates or digital identities and then, in the digital identity case, extract the private key from the digital identity and either sign or decrypt with that using the SecKey API. You wrote:

The plan is to be able to have a person insert a smartcard, the system recognize it, validate the expiration date and cert chain, record who it is and then tell the user to pull the card and move on.

It sounds like it’ll be useful to use CTK to hear about inserts and removes. Do that using TKTokenWatcher. Once the token is inserted, use SecItemCopyMatching to search for certificates on that token. Here’s a snippet of code that I use for that:

let w = TKTokenWatcher()
self.watcher = w
// The insertion handler is called once for each existing token. It’d be
// nice to debounce this but that requires either Combine (don’t want
// more of that code) or custom logic (too hard!) or a future Swift
// Async Algorithms package (not yet actually implemented).
w.setInsertionHandler() { tokenID in
    // Insertion and removal handlers are called on something that’s not
    // the main queue, so we bounce to the main queue before calling
    // refresh.
    self.refresh()
    w.addRemovalHandler({ tokenID in
        self.refresh()
    }, forTokenID: tokenID)
}

I see that the CryptoTokenKit API requires the entitlement of com.apple.security.smartcard

Yeah, entitlements here are a little confusing. There’s actually two:

  • com.apple.security.smartcard is only needed on macOS. See here. It has the com.apple.security. prefix, making it an unrestricted entitlement, so you don’t need a provisioning profile to authorise its use. For more background on that, see TN3125 Inside Code Signing: Provisioning Profiles.

  • com.apple.token is a special keychain access group. iOS and its child platforms require you to list this in your keychain-access-groups entitlement in order to access token-based credentials. iOS provisioning profiles always include this in their keychain-access-groups allowlist, so you don’t have to do anything special to authorise its use, you just have to claim it.

When querying for token-based credentials, make sure to set the kSecAttrAccessGroup attribute in your query dictionary to kSecAttrAccessGroupToken (whose value is com.apple.token). Here’s some code I use for that:

let attrsForIdentities = try secCall { SecItemCopyMatching([
    kSecClass: kSecClassIdentity,
    kSecAttrAccessGroup: kSecAttrAccessGroupToken,
    kSecMatchLimit: kSecMatchLimitAll,
    kSecReturnRef: true,
    kSecReturnPersistentRef: true,
] as NSDictionary, $0) } as! [[String: Any]]
for attrs in attrsForIdentities {
    let identity = attrs[kSecValueRef as String] as! SecIdentity
    let persistentRef = attrs[kSecValuePersistentRef as String] as! Data
    let cert = try! secCall { SecIdentityCopyCertificate(identity, $0) }
    let name = try! secCall { SecCertificateCopySubjectSummary(cert) } as String
    … use the above …
}

This uses my standard Security framework helpers.

Share and Enjoy

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

Thank you for the informative reply. I'm still wondering about using SecItem API over sending APDU commands to the card. I didn't mention that the app also needs to pull facial data off the card to display on screen. I'll start working with what you have shared and hopefully get something built. Thank you again.