Я пишу этот вопрос, потому что зашел в тупик по поводу следующей проблемы.
Я пытаюсь подписать кусок данных с помощью закрытого ключа, а затем проверить эти данные с помощью открытогоключ, но когда я выполняю 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 и не смог исправить свою проблемупока что.
И снова, простите за длинный код!