getting access to a certificate stored in a smartcard

Hi,

we have some code in our xcode project that needs to acces keychain's certificate to use them.

We use the SecItemCopyMatching method to get a list of SecCertificate like this:

var query = [String: Any]()
        query[String(kSecClass)] = kSecClassCertificate
        query[String(kSecMatchLimit)] = kSecMatchLimitAll
        
        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)

and we are now having troubles making this work with smartcards.

We use a Gemalto smartcard that contains a certificate. On our terminal application, when we use "security list-smartcard", we get a line with it's ID.

com.gemalto.Gemalto-Smart-Card-Token.PKCS11-Token:XXXXXXXXXXXXXXXX

The thing is, in our code, the certificate stored in the smartcard isn't retrieved by our current query.

We tried to do a more specific query like this:

let getquery: [String: Any] = [kSecClass as String: kSecClassKey,
                               kSecAttrTokenID as String: "com.gemalto.Gemalto-Smart-Card-Token.PKCS11-Token:XXXXXXXXXXXXXXXX",
                               kSecReturnPersistentRef as String: true]

but it seems we can't have access to it. results are nil.

We believed at first that it was because we didn't add the entitlement "com.apple.security.smartcard" to our project, but we get the same result enabling it.

Can somebody provide us some leads about getting SecCertificate that can be used to sign from a smartcard ?

Thank you.

Replies

we have some code in our Xcode project that needs to acces keychain's certificate to use them.

So, this is macOS, right?

Share and Enjoy

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

  • yes, this is for a mac OS application.

Add a Comment

Try setting kSecUseDataProtectionKeychain on your queries.

Share and Enjoy

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

sorry for the late reply. I did set the kSecUseDataProtectionKeychain in my query and now seem to get the certificate. now my request is

let getquery: [String: Any] = [kSecClass as String: kSecClassCertificate,
                               kSecUseDataProtectionKeychain  as String: true
                               kSecAttrTokenID as String: "com.gemalto.Gemalto-Smart-Card-Token.PKCS11-Token:XXXXXXXXXXXXXXXX",
                               kSecMatchList as String: kSecMatchLimitAll,
                               kSecReturnPersistentRef as String: true]

the issue now is that, the SecCertificate I get from this request doesn't seem to have any data: when I try to get some value , like the keyUsage doing:

certificate.getValueForKey(key: kSecOIDKeyUsage)

I got a EXC_BAD_ACCESS error.

I did also try to get the certificate.publicKey.getExternalRepresentation (I need it to identify my certificate) but it returns nil. Is there a way to fully acces the certificate in the smartcard ?

when I try to get some value, like the keyUsage doing

That snippet is calling some kind of wrapper function. What Apple API are you calling?

Share and Enjoy

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

sorry.

the code used to try gettin the keyUsage is:

    private func getValueForKey<T>(key: CFString) -> T? {
        if let copyValues = SecCertificateCopyValues(self, [key] as CFArray, nil) as? [String:Any],
            let valueData = copyValues[String(key)] as? [String:Any],
            let value = valueData[String(kSecPropertyKeyValue)] as? T {
            return value
        }
        return nil
    }

this code successfuly returns the keyUsage for all certificate that are installed in the keychained. But as mentionned in my previous post, the certificate we got with the request that include kSecUseDataProtectionKeychain  seems empty ? (certificate.publicKey.getExternalRepresentation returns nil)

I’m missing something here. The query dictionary you posted here requests a persistent reference (kSecReturnPersistentRef) but SecCertificateCopyValues takes an object reference (SecCertificateRef, which you’d get using kSecReturnRef).

Also, SecCertificateCopyValues is a Mac-specific API and thus probably tied to the old CDSA architecture, which is not compatible with CryptoTokenKit-based credentials. Just as a diagnostic test, what does SecCertificateCopyData return?

Share and Enjoy

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

Our current working code (that can't find any certificate stored in smarcard) is the following

var query = [String: Any]()
        query[String(kSecClass)] = kSecClassCertificate
        query[String(kSecMatchLimit)] = kSecMatchLimitAll
        
        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)

with this code, we get a list of SecCertificate and we have an extension of SecCertificate that can provide values of the object:

extension SecCertificate {

    private func getValueForKey<T>(key: CFString) -> T? {
        if let copyValues = SecCertificateCopyValues(self, [key] as CFArray, nil) as? [String:Any],
            let valueData = copyValues[String(key)] as? [String:Any],
            let value = valueData[String(kSecPropertyKeyValue)] as? T {
            return value
        }
        return nil
    }
}

so when doing

certificate.getValueForKey(key: kSecOIDKeyUsage)
var email: CFArray? = nil;
SecCertificateCopyEmailAddresses(certificate, &email)

we got a integer for the keyUsage, and the email sucessfully. Now when doing the same with my new query (that tries to get the object in smartcards) :

let getquery: [String: Any] = [kSecClass as String: kSecClassCertificate,
                               kSecUseDataProtectionKeychain  as String: true
                               kSecAttrTokenID as String: "com.gemalto.Gemalto-Smart-Card-Token.PKCS11-Token:XXXXXXXXXXXXXXXX",
                               kSecMatchList as String: kSecMatchLimitAll,
                               kSecReturnPersistentRef as String: true]

I got SecCertificate objects, but the values I got when getting keyUsage  or email are nil. I tried to remove the "kSecReturnPersistentRef " key as you said, but the query gives no result then. I really don't know how to access the certificates inside the smart cards, acces its public values like email , etc...

I tried to remove the kSecReturnPersistentRef key as you said, but the query gives no result then.

Right. If you don’t specify any kSecReturnXxx constants then you don’t get any results. My suggestion was that you switch from kSecReturnPersistentRef to kSecReturnRef (sorry that I was less than clear).

Share and Enjoy

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

thank you very much.

that was it, switched to kSecReturnRef  was the way to get and used the smartcard certificates.