Search code examples
iossecuritywatchos

Exporting SecKey from iOS to watchOS


I am trying to sync up private asymmetric keys between my iOS app and its watchOS equivalent. I have tried using SecKeyCopyExternalRepresentation to export it out as CFData and then send it to the watch using WatchConnectivity. However when it gets to the watch I have no way of converting the Data back into a SecKey. I tried using SecKeyCreateWithData in an attempt to recreate it, but it seems that that only works with symmetric keys, for when I tried it it crashed the watch app. Any ideas?

iOS Code:

func sendSharedKeyPair(keyPair: (publicKey: SecKey, privateKey: SecKey)) {
    var error: Unmanaged<CFError>?

    let publicKeyData = SecKeyCopyExternalRepresentation(keyPair.publicKey, &error)
    if let error = error {
        return print("Error sending shared key: \(error)")
    }
    let privateKeyData = SecKeyCopyExternalRepresentation(keyPair.privateKey, &error)
    if let error = error {
        return print("Error sending shared key: \(error)")
    }

    if let publicKeyData = publicKeyData, let privateKeyData = privateKeyData {
        session.sendMessage(["requestedCommand": WatchControllerCommands.sendSharedKeyPair.rawValue, "keyPair": ["publicKey": publicKeyData, "privateKey": privateKeyData]], replyHandler: nil, errorHandler: { error in
            print(error)
        })
    }


}

watchOS Code:

func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
    guard let requestedCommand = (message["requestedCommand"] as? String).flatMap({ WatchControllerCommands(rawValue: $0) }), requestedCommand == .sendSharedKeyPair else { return }

    guard let publicKeyData = (message["keyPair"] as? [String: Any])?["publicKey"].flatMap({ $0 as? Data }), let privateKeyData = (message["keyPair"] as? [String: Any])?["privateKey"].flatMap({ $0 as? Data }) else { return print("Couldn't parse keys") }

    let publicTag = "myAppTag"
    let privateTag = publicTag + ".private"

    let privateAttributes = [String(kSecAttrIsPermanent): true,
                             String(kSecAttrApplicationTag): privateTag] as [String : Any]
    let publicAttributes = [String(kSecAttrIsPermanent): true,
                            String(kSecAttrApplicationTag): publicTag] as [String : Any]

    var error: Unmanaged<CFError>?
    let publicCFData = publicKeyData as CFData
    let privateCFData = privateKeyData as CFData
    let publicCFDict = publicAttributes as CFDictionary
    let privateCFDict = privateAttributes as CFDictionary
    SecKeyCreateWithData(publicCFData, publicCFDict, &error)
    if let error = error {
        print(error)
    }
    SecKeyCreateWithData(privateCFData, privateCFDict, &error)
    if let error = error {
        print(error)
    }
}

Solution

  • From headerdocs around SecKeyCreateWithData:

    @param attributes Dictionary containing attributes describing the key to be imported. The keys in this dictionary are kSecAttr* constants from SecItem.h. Mandatory attributes are: * kSecAttrKeyType * kSecAttrKeyClass * kSecAttrKeySizeInBits

    Your code only defines kSecAttrIsPermanent and kSecAttrApplicationTag attributes.