【发布时间】:2021-05-30 07:37:48
【问题描述】:
我正在尝试在 iOS (14.4) 上对用户数据进行哈希处理和签名,将其发送到我的服务器,并让服务器使用之前上传的公钥(在用户创建期间生成密钥对时发送)验证哈希和签名。似乎很多人都遇到了这个问题,但我能找到的所有答案都是very old,不要考虑使用 Apple 的 Secure Enclave,或者在同一个 iOS 上进行签名和验证设备。
一般的工作流程是:用户在 iOS 上创建一个帐户,在设备上创建一个随机密钥对,私钥留在 Secure Enclave 中,而公钥转换为 ASN.1 格式,PEM 编码并上传到服务器。当用户稍后对数据进行签名时,数据会经过 JSON 编码,使用 sha512 进行哈希处理,并由他们在 Secure Enclave 中的私钥进行签名。然后将其打包到 base64EncodedString 有效负载中,并发送到服务器进行验证。服务器首先使用 openssl_digest 验证哈希,然后使用 openssl_verify 检查签名。
我一直无法获取 openssl_verify 方法来成功验证签名。我还尝试使用 phpseclib 库(以更深入地了解验证失败的原因)但没有成功。我了解 phpseclib 使用 openssl 库(如果可用),但即使禁用此功能,phpseclib 的内部验证也会失败,因为模数后的结果值不匹配。有趣的是,phpseclib 将公钥转换为看起来像带有大量填充的 PKCS8 格式。
看来,openssl 正在正确解析和加载公钥,因为在验证之前正在创建正确的引用。但是,由于私钥是不透明的(位于 Secure Enclave 中),我无法从外部“检查”签名本身是如何生成/编码的,或者是否会在 iOS 设备之外创建相同的签名。我想知道我是否有编码错误,或者是否可以使用 Secure Enclave 中生成的密钥进行外部验证。
iOS 公钥上传方法- 我正在使用 CryptoExportImportManager 将原始字节转换为 DER,添加 ASN.1 标头,并添加 BEGIN 和 END 密钥标签。
public func convertPublicKeyForExport() -> String?
{
let keyData = SecKeyCopyExternalRepresentation(publicKey!, nil)! as Data
let keyType = kSecAttrKeyTypeECSECPrimeRandom
let keySize = 256
let exportManager = CryptoExportImportManager()
let exportablePEMKey = exportManager.exportECPublicKeyToPEM(keyData, keyType: keyType as String,
keySize: keySize)
return exportablePEMKey
}
上传后其中一个公钥的外观示例
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEf16tnH8YPjslaacdtdde4wRQs0PP
zj/nWgBC/JY5aeajHhbKAf75t6Umz6vFGBsdgM/AFMkeB4n2Qi96ePNjFg==
-----END PUBLIC KEY-----
let encoder = JSONEncoder()
guard let payloadJson = try? encoder.encode(["user_id": "\(user!.userID)", "random_id": randomID])
else
{
onCompletion(nil, NSError())
print("Failed creating data")
return
}
let hash = SHA512.hash(data: payloadJson)
guard let signature = signData(payload: payloadJson, key: (user?.userKey.privateKey)!) else
{
print("Could not sign data payload")
onCompletion(nil, NSError())
return
}
let params = Payload(
payload_hash: hash.hexString,
payload_json: payloadJson,
signatures: ["user": [
"signature": signature.base64EncodedString(),
"type": "ecdsa-sha512"
]]
)
let encoding = try? encoder.encode(params).base64EncodedString()
sign data 函数与 Apple 的文档代码非常接近,但我将其包括在内以供参考
private func signData(payload: Data, key: SecKey) -> Data?
{
var error: Unmanaged<CFError>?
guard let signature = SecKeyCreateSignature(key,
SecKeyAlgorithm.ecdsaSignatureMessageX962SHA512,
payload as CFData, &error)
else
{
print("Signing payload failed with \(error)")
return nil
}
print("Created signature as \(signature)")
return signature as Data
}
【问题讨论】:
标签: php ios swift phpseclib php-openssl