Retrieve SecKey from NSData

8.1k Views Asked by At

I encountered an issue with creating SecKey from NSData. Basically my client-server communication is based on signature created with private key and verified on the server with public key.

I am implementing session transfer between two devices and in order to continue communication I need those keys to be transferred as well. I am converting SecKey to NSData and sending it via bluetooth, but on other side I cannot convert NSData to SecKey back to use encryption.

Could you help please?

3

There are 3 best solutions below

4
user3441734 On

what I use with success ...

func obtainKeyData(tag: String) -> NSData? {
    var keyRef: AnyObject?
    let query: Dictionary<String, AnyObject> = [
        String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
        String(kSecReturnData): kCFBooleanTrue as CFBoolean,
        String(kSecClass): kSecClassKey as CFStringRef,
        String(kSecAttrApplicationTag): tag as CFStringRef,
        ]

    let result: NSData?

    switch SecItemCopyMatching(query, &keyRef) {
    case noErr:
        result = keyRef as? NSData
    default:
        result = nil
    }

    return result
}

func insertPublicKey(publicTag: String, data: NSData) -> SecKeyRef? {
    let query: Dictionary<String, AnyObject> = [
        String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
        String(kSecClass): kSecClassKey as CFStringRef,
        String(kSecAttrApplicationTag): publicTag as CFStringRef,
        String(kSecValueData): data as CFDataRef,
        String(kSecReturnPersistentRef): true as CFBooleanRef]

    var persistentRef: AnyObject?
    let status = SecItemAdd(query, &persistentRef)

    if status != noErr && status != errSecDuplicateItem {
        return nil
    }

    return obtainKey(publicTag)
}
func obtainKey(tag: String) -> SecKey? {
    var keyRef: AnyObject?
    let query: Dictionary<String, AnyObject> = [
        String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
        String(kSecReturnRef): kCFBooleanTrue as CFBoolean,
        String(kSecClass): kSecClassKey as CFStringRef,
        String(kSecAttrApplicationTag): tag as CFStringRef,
        ]

    let status = SecItemCopyMatching(query, &keyRef)

    switch status {
    case noErr:
        if let ref = keyRef {
            return (ref as! SecKeyRef)
        }
    default:
        break
    }

    return nil
}

There is no easy way to transfer private part of the key pair( it is possible, but try to avoid it )

0
Mahmoud Adam On

Starting from iOS 10 you can use the following code:

let attributes: [String:Any] = [
            kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
            kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits as String: blockSize,
            ]
secKey = SecKeyCreateWithData(secKeyData as CFData, attributes as CFDictionary, nil)
0
John Robi On

More complete example (swift 4, iOS 10+) - assuming you have a Base64 encoded string. Note that the other side of the connection needs to also be creating key payloads using the same format (i.e. RSA - PKCS #1, also verify key size ). This function handles public or private keys (security caveats omitted for brevity).

// Extract secKey from encoded string - defaults to extracting public keys
func decodeSecKeyFromBase64(encodedKey: String, isPrivate: Bool = false) -> SecKey? {
    var keyClass = kSecAttrKeyClassPublic
    if isPrivate {
        keyClass = kSecAttrKeyClassPrivate
    }
    let attributes: [String:Any] =
    [
        kSecAttrKeyClass as String: keyClass,
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
        kSecAttrKeySizeInBits as String: 2048,
    ]

    guard let secKeyData = Data.init(base64Encoded: encodedKey) else {
        print("Error: invalid encodedKey, cannot extract data")
        return nil
    }
    guard let secKey = SecKeyCreateWithData(secKeyData as CFData, attributes as CFDictionary, nil) else {
        print("Error: Problem in SecKeyCreateWithData()")
        return nil
    }

    return secKey
}