我之所以写这个问题,是因为我对下一个问题陷入僵局。
我试图用私钥对大量数据进行签名,然后使用公钥验证该数据,但是当我执行 SecKeyRawVerify 时,它返回错误代码- 9809 ,Apple将其描述为“ “遇到了潜在的密码错误。” ,这根本没有帮助。
我认为主要问题出在我的验证码中,该验证码无法正常工作。不过也许我弄错了。
如果有帮助,最低的iOS版本目标是 iOS10 。我认为Apple在iOS10上对安全框架进行了各种更改,但我不确定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
}
}
我会很感激您能提供的任何帮助或线索,因为我一直在Google StackOverflow上搜索和搜索了几个小时,而且还无法解决问题。
再说一遍,对不起,代码太长了!