SecKeyGeneratePair giving error -4 (Function or operation not implemented)

Viewed 208

I'm using https://code.tutsplus.com/tutorials/creating-digital-signatures-with-swift--cms-29287 to create digital signatures and I'm following along with the ECDSA section.

I've created the following code:

@available(iOS 9.0, *)
class User
{
    public var publicKey : SecKey?
    private var privateKey : SecKey?
    private var recipient : User?

    init(withUserID id : String)
    {

        if let access = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage], nil)  //Keep private key on device
        {
            let privateTagString = "com.example.privateKey." + id
            let privateTag = privateTagString.data(using: .utf8)! //Store it as Data, not as a String
            let privateKeyParameters : [String : AnyObject] = [kSecAttrIsPermanent as String : true as AnyObject,
                                                               kSecAttrAccessControl as String : access as AnyObject,
                                                               kSecAttrApplicationTag as String : privateTag as AnyObject,
            ]

            let publicTagString = "com.example.publicKey." + id
            let publicTag = publicTagString.data(using: .utf8)! //Data, not String
            let publicKeyParameters : [String : AnyObject] = [kSecAttrIsPermanent as String : false as AnyObject,
                                                              kSecAttrApplicationTag as String : publicTag as AnyObject,
            ]

            let keyPairParameters : [String : AnyObject] = [kSecAttrKeySizeInBits as String : 256 as AnyObject,
                                                            kSecAttrKeyType as String : kSecAttrKeyTypeEC,
                                                            kSecPrivateKeyAttrs as String : privateKeyParameters as AnyObject,
                                                            kSecAttrTokenID as String : kSecAttrTokenIDSecureEnclave as AnyObject, //Store in Secure Enclave
                kSecPublicKeyAttrs as String : publicKeyParameters as AnyObject]

            let status = SecKeyGeneratePair(keyPairParameters as CFDictionary, &publicKey, &privateKey)
            if status != noErr
            {
                print("Key generation error \(status)")
            }
        }
}
}

Now each time I get an error -4, which I believe is https://opensource.apple.com/source/Security/Security-55471/sec/Security/SecBase.h.auto.html function or error not implemented.

How can I create the public and private keys? I've tried to manually set the public and private key by:

let keyBase64 = "MIIEpAIBAAKCAQEA5B7lqLrwVCFNUiCmwMr5Q48iuArOolxb7DAuclGnoZVX0SaJ8mrvCOtd6qY/VeBw227txWEPH7840qX/yGxxqTngdNCuDATqYrrbxFOGV30GZmg6NpZYKShTlsftkhiCsoXW0A7m5MCZUkH2/sNBC8oRHCNDXRlsU5bq/yPaAMt6xlBsUgLt/++INcuw+rx1Rm7LJv0FeukQmlekUOL/DMJXcLXCa05StTbvHPiAHOLej07pThCZoX3XHFpOTQ6379EsjvSZHtNhr67qrtRb8or2rX7wt5NWzXHbhUDlyzEcIBB/7G8ygqWhyZTEIMFiRMWSa3KGYZE3nZe5weC7SQIDAQABAoIBAQCjjxehA+++kmYK5YhKIP3Zl64QAQeo18m8rcsPgkZLj3V4a0Zq/orGfWNIE8zDePnSC1YFuBKM86D9P7IGdOKFsA6kEt9HlNqs0UczG6Pt5KGLGV3rt54cXGKacFyA7HwBHf8oDBc2mnUTymIaxcpEdqwP3aS2Ar1trX5uUrlC6UcZyspBZVYvMlU+uAKL1ZtFxjsv0EzuQQW1HX7b2WPUAoxp/yBC/EBRM9K8WbG9i7NB4FTFHAdTMt/EZLGUESizFgrai6lp3s96Apz5GvncRUI+UVP/7zbUaFYdRMW5lrcR8+PL9NACkL2rnQuLoyLKWZWPPlD3WEE9EzY4bH6FAoGBAPu8hL8goEbWMFDZuox04Ouy6EpXR8BDTq8ut6hmad6wpFgZD15Xu7pYEbbsPntdYODKDDAIJCBsiBgf2emL50BpiQkzMPhxyMsN5Pzry9Ys+AzPkJcQ7g+/Wbto9lCC+JmgxtGQ7JIibo1QH7BTsuK9+k72HnZne6oIfaYKbBZfAoGBAOf7/D2Q7NiNcEgxpZRn06+mnkHMb8PfCKfJf/BFf5WKXSkDBZ3XhWSPnZyQnE3gW3lzJjzUwHS+YDk8A0Xl2piAHa/d5O/8eoijB8wa6UGVDBIXqUnfM3Udfry78rM71FOpbzV3H48G7u4CUJMGwOpEqF0TfgtQr4uf8OurdH9XAoGAbdNhVsE1K7Jmgd97s6uKNUpobYaGlyrGOUd4eM+1gKIwEP9d5RsBm9qwX83RtKCYk3mSt6HVoQ+4kE3VFD8lNMTWNF1REBMUNwJo1K9KzrXvwicMPdv1AInK7ChuzdFWBDBQjT1c+KRs9tnt+U+Ky8F2Ytydjaq4GQZ7SuVhIqECgYBMsS+IovrJ9KhkFZWp5FFFRo4XLqDcXkWcQq87HZ66L03xGwCmV/PPdPMkKWKjFELpebnwbl1Zuv5QrZhfaUfFFsW5uF/RPuS7ezo+rb7jYYTmDlB3DYUTeLbHalMoEeV16xPK1yDlxeMDaFx+3sK0MBKBAsqurvP58txQ7RPMbQKBgQCzRcURopG0DF4VF4+xQJS8FpTcnQsQnO/2MJR35npA2iUb+ffs+0lgEdeWs4W46kvaF1iVEPbr6She+aKROzE9Bs25ZCgGLv97oUxDQo0IPvURX7ucN+xOUU1hw9oQDVdGKl1JZh93fn+bjtMTe+26asGLmM0r9YQX1P8qaw3KOg=="  
let keyData = Data(base64Encoded: keyBase64)!  
let key = SecKeyCreateWithData(keyData as NSData, [  
    kSecAttrKeyType: kSecAttrKeyTypeRSA,  
    kSecAttrKeyClass: kSecAttrKeyClassPrivate,  
] as NSDictionary, nil)!  
print(key) 

But this doesn't really make sense as SecKeyGeneratePair should generate the key pair!

1 Answers

The error status -4 has several meanings, not only errSecUnimplemented, but also errSecUnsupportedAlgorithm and errSecUnsupportedOperation, however, in this scenario, is is very likely it is the errSecUnimplemented.

The reason for this is that you try to retrieve the private key from the Secure Enclave, which is 'impossible'. Part of the security of the Secure Enclave is that it will not have the ability of exposing the private key to you. So there you go, that is why it is throwing a errSecUnimplemented error at you.

So if you want to use the Secure Enclave, I would suggest you look some more in the security documentation that Apple provides.

A solution I can present you is the following:

let someUserID = "111222333444"

if let access = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage], nil) {

    let privateTagString = "com.example.privateKey." + someUserID
    let privateTag = privateTagString.data(using: .utf8)!

    let keyPairSettings: [String: Any] = [
        String(kSecAttrKeyType): kSecAttrKeyTypeEC,
        String(kSecAttrKeySizeInBits): 256,
        String(kSecPrivateKeyAttrs): [
            String(kSecAttrApplicationTag): privateTag,
            String(kSecAttrIsPermanent): true,
            String(kSecAttrAccessControl): access
        ]
    ]

    var publicKey: SecKey?
    var privateKey: SecKey?
    let status = SecKeyGeneratePair(keyPairSettings as CFDictionary, &publicKey, &privateKey)

    if status != errSecSuccess {
        print("An error occured: \(status)")
    } else {
        print("Created keys!")
    }

}

This will store the private key in the KeyChain. Hope this helps!

Related