尝试使用Secure Enclave中的EC私钥对数据进行签名时出现身份验证错误

时间:2016-11-14 18:22:43

标签: ios swift cryptography commoncrypto

我正在尝试在iOS应用程序中执行以下操作:

  1. 使用SecKeyGeneratePair生成密钥对,将私钥存储在Secure Enclave
  2. 使用私钥签署一些数据
  3. 如果我在第一次生成密钥时挂起私钥引用,它会起作用,但如果我在丢弃初始指针后尝试从Keychain中检索引用,则它不起作用。

    密钥生成如下:

    func generateKeyPair() -> Bool {
    
        if let access = SecAccessControlCreateWithFlags(nil,
                                                        kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                                                        [.userPresence, .privateKeyUsage],
                                                        nil) {
    
            let privateKeyAttr = [kSecAttrIsPermanent : 1,
                                  kSecAttrApplicationTag : privateTag,
                                  kSecAttrAccessControl as String: access
                ] as NSDictionary
    
            let publicKeyAttr = [kSecAttrIsPermanent : 0,
                                 kSecAttrApplicationTag : publicTag
                ] as NSDictionary
    
            let keyPairAttr = [kSecAttrKeySizeInBits : 256,
                               kSecAttrKeyType : kSecAttrKeyTypeEC,
                               kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
                               kSecPrivateKeyAttrs : privateKeyAttr,
                               kSecPublicKeyAttrs : publicKeyAttr] as NSDictionary
    
            let err = SecKeyGeneratePair(keyPairAttr, &publicKey, &privateKey)
            return err == noErr
    }
    

    签名方法如下:

    func signData(plainText: Data) -> NSData? {                
        guard privateKey != nil else {
            print("Private key unavailable")
            return nil
        }
    
        let digestToSign = self.sha1DigestForData(data: plainText as NSData) as Data
    
        let signature = UnsafeMutablePointer<UInt8>.allocate(capacity: 128)
        var signatureLength = 128
        let err = SecKeyRawSign(privateKey!,
                                .PKCS1SHA1,
                                [UInt8](digestToSign),
                                Int(CC_SHA1_DIGEST_LENGTH),
                                signature,
                                &signatureLength)
    
        print("Signature status: \(err)")
    
        let sigData = NSData(bytes: signature, length: Int(signatureLength))
    
        return sigData
    }
    
    func sha1DigestForData(data: NSData) -> NSData {
        let len = Int(CC_SHA1_DIGEST_LENGTH)
        let digest = UnsafeMutablePointer<UInt8>.allocate(capacity: len)
        CC_SHA1(data.bytes, CC_LONG(data.length), digest)
        return NSData(bytesNoCopy: UnsafeMutableRawPointer(digest), length: len)
    }
    

    这要求我的指纹和完美无瑕的工作。然后我使用另一种方法从Keychain获取关键引用:

    func getPrivateKeyRef() -> SecKey? {
    
        let parameters = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
            kSecAttrApplicationTag as String: privateTag,
            kSecReturnRef as String: true,
            ] as [String : Any]
        var ref: AnyObject?
        let status = SecItemCopyMatching(parameters as CFDictionary, &ref)
        print("Get key status: \(status)")
    
        if status == errSecSuccess { return ref as! SecKey? } else { return nil }
    }
    

    SecItemCopyMatching会返回成功状态,但尝试将生成的SecKey项目用作SecKeyRawSign中的私钥会导致错误-25293 Authorization/Authentication failed.此状态仅在我之后显示提供我的指纹,因此实际的指纹验证成功,但键仍然无法使用。

    使用Secure Enclave中存储的密钥对数据进行签名的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

看起来使用kSecAttrLabel属性是正确获取密钥所必需的。必须在生成密钥时以及通过SecItemCopyMatching提取密钥时指定它。

我获取私钥的工作解决方案:

func getPrivateKey() -> SecKey? {
    let parameters = [
        kSecClass as String: kSecClassKey,
        kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
        kSecAttrApplicationTag as String : "privateTag",
        kSecAttrLabel as String : "privateTag",
        kSecReturnRef as String: true,
        ] as [String : Any]
    var ref: AnyObject?
    let status = SecItemCopyMatching(parameters as CFDictionary, &ref)
    if status == errSecSuccess {
        return (ref as! SecKey)
    }
    return nil
}

密钥对的生成方式如下:

    var publicKey:SecKey?
    var privateKey:SecKey?
    if let access = SecAccessControlCreateWithFlags(nil,
                                                    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                                                    [.userPresence, .privateKeyUsage],
                                                    nil) {

        let privateKeyAttr = [kSecAttrIsPermanent : 1,
                              kSecAttrApplicationTag as String : "privateTag",
                              kSecAttrLabel as String : "privateTag",
                              kSecAttrAccessControl as String: access
            ] as NSDictionary

        let publicKeyAttr = [kSecAttrIsPermanent : 0,
                             kSecAttrApplicationTag as String : "publicTag",
                             kSecAttrLabel as String : "publicTag",
                             ] as NSDictionary

        // only 256 bit EC keys are supported in the Secure Enclave
        let keyPairAttr = [kSecAttrKeySizeInBits : 256,
                           kSecAttrKeyType : kSecAttrKeyTypeEC,
                           kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
                           kSecPrivateKeyAttrs : privateKeyAttr,
                           kSecPublicKeyAttrs : publicKeyAttr] as NSDictionary

        let err = SecKeyGeneratePair(keyPairAttr, &publicKey, &privateKey)