Эллиптическая кривая в Swift подписывает и проверяет результаты ошибки -9809 - PullRequest
2 голосов
/ 09 мая 2019

Я пишу этот вопрос, потому что зашел в тупик по поводу следующей проблемы.

Я пытаюсь подписать кусок данных с помощью закрытого ключа, а затем проверить эти данные с помощью открытогоключ, но когда я выполняю SecKeyRawVerify , он возвращает код ошибки -9809 , который Apple описывает как «Обнаружена скрытая криптографическая ошибка.» , что совсем не полезно.

Я думаю, что главная проблема в моем проверочном коде, который как-то не работает должным образом.Возможно, я ошибаюсь.
Минимальная целевая версия iOS - iOS10 , если это поможет.Я думаю, что на iOS10 Apple сделала различные изменения в структуре безопасности, но я не уверен на 100%.

Осторожно, код довольно длинный (извините за это).

Код, которыйгенерирует пару ключей, используемых для шифрования эллиптической кривой, следующим образом.

    private func generateKeyPair(tagPrivate: String, tagPublic: String, typeAuthentication: KeystoreServiceConstants.Authentication, keyType: CFString, KeySize: Int, container: KeystoreServiceConstants.Container) throws {

        var privateKeyAttr: [String: Any]
        var publicKeyAttr: [String: Any]
        var generatePairKeyAttr: [String: Any]

        var accessControl: AccessControl

        switch typeAuthentication {
        case KeystoreServiceConstants.Authentication.none:
            do {
                accessControl = try AccessControl(protection: .whenUnlocked, policy: [])
            } catch {
                throw error
            }
        case KeystoreServiceConstants.Authentication.biometricAuthentication:
            do {
                accessControl = try AccessControl(protection: .whenUnlocked, policy: [.touchIDCurrentSet, .privateKeyUsage])
            } catch {
                throw error
            }
        }

        publicKeyAttr = createPublicKeyParams(tagPublic: tagPublic)
        privateKeyAttr = createPrivateKeyParams(tagPrivate: tagPrivate, accessControl: accessControl)
        generatePairKeyAttr = createGeneratePairKeyParams(publicKeyAttr: publicKeyAttr, privateKeyAttr: privateKeyAttr, keyType: keyType, KeySize: KeySize, container: container)

        var publicKey, privateKey: SecKey?

        let status = SecKeyGeneratePair(generatePairKeyAttr as CFDictionary, &publicKey, &privateKey)

        if status != errSecSuccess {
            throw handlingError(status: status)
        }

        do {
            try forceSaveInKeyChain(tagPublic: tagPublic, publicKey: publicKey!, keyType: keyType, keyClass: kSecAttrKeyClassPublic)
        } catch {
            throw error
        }
    }

    private func createPublicKeyParams(tagPublic: String) -> [String: Any] {

        let publicKeyParameters: [String: Any] = [
            kSecAttrIsPermanent as String: false,
            kSecAttrApplicationTag as String: KeystoreServiceConstants.Tag.application,
            kSecAttrLabel as String: tagPublic
        ]
        return publicKeyParameters
    }

    private func createPrivateKeyParams(tagPrivate: String, accessControl: AccessControl) -> [String: Any] {

        let context = LAContext()

        let privateKeyParams: [String: Any] = [
            kSecAttrIsPermanent as String: true,
            kSecAttrApplicationTag as String: KeystoreServiceConstants.Tag.application,
            kSecAttrLabel as String: tagPrivate,
            kSecUseAuthenticationContext as String: context,
            kSecUseAuthenticationUI as String: kSecUseAuthenticationUIAllow,
            kSecAttrAccessControl as String: accessControl.accessControl
        ]
        return privateKeyParams
    }

    private func createGeneratePairKeyParams(publicKeyAttr: [String: Any], privateKeyAttr: [String: Any], keyType: CFString, KeySize: Int, container: KeystoreServiceConstants.Container) -> [String: Any] {
        var params: [String: Any] = [
            kSecAttrKeyType as String: keyType,
            kSecAttrKeySizeInBits as String: KeySize,
            kSecPublicKeyAttrs as String: publicKeyAttr,
            kSecPrivateKeyAttrs as String: privateKeyAttr,
        ]
        if container == .secureEnclave {
            params[kSecAttrTokenID as String] = kSecAttrTokenIDSecureEnclave
        }
        return params
    }

    private func forceSaveInKeyChain(tagPublic: String, publicKey: SecKey, keyType: CFString, keyClass: CFString) throws {


        let query: [String: Any] = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyType as String: keyType,
            kSecAttrKeyClass as String: keyClass,
            kSecAttrLabel as String: tagPublic,
            kSecAttrApplicationTag as String: KeystoreServiceConstants.Tag.application,
            kSecValueRef as String: publicKey,
            kSecAttrIsPermanent as String: true,
            kSecReturnData as String: true,
            ]

        var raw: CFTypeRef?
        var status = SecItemAdd(query as CFDictionary, &raw)

        if status == errSecDuplicateItem {
            status = SecItemDelete(query as CFDictionary)
            status = SecItemAdd(query as CFDictionary, &raw)
        }

        guard status == errSecSuccess else {
            throw KeystoreServiceConstants.KeyError.saveFailureInKeychain
        }
    }

Код, который подписывает сообщение, следующий:

   func signWithKey(data: String) throws -> String {
        let oneKeyPrivate = try getKeyTypeInSecureEnclave(tag: "TagKeyPrivate")
        let sign = try signWithPrivateKey(data, oneKeyPrivate)

        let dataSign = sign!.data(using: String.Encoding.utf8)!
        let testKeyPublicData = try getKeyTypeInKeyChain(tag: "TagKeyPublic", keyType: KeystoreServiceConstants.KeyType.EC)
        // The result is always false, because verifyString fails with code -9809
        let result = verifyString(string: data, signature: dataSign, publicKey: testKeyPublicData as! SecKey)

        // My 2nd try, with another method to get the public key
        let keyPrivate = try getKeyTypeInSecureEnclave(tag: "TagKeyPrivate")
        let publicKey = SecKeyCopyPublicKey(keyPrivate)
        let result2 = verifyString(string: data, signature: dataSign, publicKey: publicKey!)

        return sign!

    }



    private func getKeyTypeInKeyChain(tag: String, keyType: CFString) throws -> SecKey {

        let query: [CFString: Any] = [
            kSecClass: kSecClassKey,
            kSecAttrKeyType: keyType,
            kSecAttrApplicationTag: KeystoreServiceConstants.Tag.application,
            kSecAttrLabel: tag,
            kSecReturnRef: true
        ]
        var result: AnyObject?
        let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &result)
        switch status {
        case errSecSuccess:
            return result as! SecKey
        case errSecItemNotFound:
            throw KeystoreServiceConstants.KeyError.keyNotFound
        default:
            throw KeystoreServiceConstants.KeyError.keyNotFound
        }
    }


    private func getKeyTypeInSecureEnclave(tag: String) throws -> SecKey {

        let query: [String: Any] = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
            kSecAttrApplicationTag as String: KeystoreServiceConstants.Tag.application,
            kSecAttrLabel as String: tag,
            kSecReturnRef as String: true,
            kSecUseOperationPrompt as String: LocalizerManager.stringForKey(key: "accessControl_message") as CFString,
            kSecUseAuthenticationUI as String: kSecUseAuthenticationUISkip
            ]
        let raw = try getSecKeyWithQuery(query)
        return raw as! SecKey
    }

Код, который проверяет и проверяет подписанныйсообщение следующее:

     func verifyString(string: String, signature: Data, publicKey: SecKey) -> Bool {
        var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
        let stringData: Data = string.data(using: String.Encoding.utf8)!

        _ = digest.withUnsafeMutableBytes { (digestBytes) in
            stringData.withUnsafeBytes { (stringBytes) in
                CC_SHA256(stringBytes, CC_LONG(stringData.count), digestBytes)
            }
        }

        let mutdata = NSMutableData(data: signature)

        let err: OSStatus = SecKeyRawVerify(
            publicKey,
            SecPadding.PKCS1SHA256,
            [UInt8](digest),
            digest.count,
            mutdata.mutableBytes.assumingMemoryBound(to: UInt8.self),
            signature.count
        )
        switch err {
        case noErr:
            return true
        default:
            return false
        }
    }

Буду признателен за любую помощь или подсказки, которые вы можете предложить, потому что я в течение нескольких часов гуглял и искал в StackOverflow и не смог исправить свою проблемупока что.
И снова, простите за длинный код!

...