iOS使用SecureEnclave保护密钥对生成无效签名问题排查
iOS签名验证异常问题(0.01%用户受影响,iOS 17-18.1)
我们有0.01%的iOS用户(系统版本覆盖17至18.1)遇到签名异常:用户可正常生成密钥对,但使用对应公钥验证签名时,结果始终无效。
签名生成Swift代码
@objc public func signData( _ text: String, resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock ) -> Void { guard let privateKey = self.getPrivateKey() else { reject(nil, "error fetching private key", nil) return } let data = text.data(using: .utf8)! var error: Unmanaged<CFError>? let signature = SecKeyCreateSignature(privateKey, .ecdsaSignatureMessageX962SHA256, data as CFData, &error) as Data? if error != nil { reject(nil, "Error in creating signature", nil) return } resolve(signature!.base64EncodedString()) }
密钥对生成Swift代码
@objc public func generateKeyPair( _ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock ) -> Void { let access = SecAccessControlCreateWithFlags( kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, .privateKeyUsage, nil)! let attributes: NSDictionary = [ kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeySizeInBits: 256, kSecAttrTokenID: kSecAttrTokenIDSecureEnclave, // Enable secure enclave kSecPrivateKeyAttrs: [ kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, // Do not share across devices with same appleid kSecAttrIsPermanent: true, kSecAttrApplicationTag: privateKeyTag, kSecAttrAccessControl: access ], kSecPublicKeyAttrs: [ kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, // Do not share across devices with same appleid kSecAttrIsPermanent: true, kSecAttrApplicationTag: publicKeyTag, ] ] var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateRandomKey(attributes, &error) else { reject(nil, "error creating private key", error!.takeRetainedValue() as Error) return } let publicKey = SecKeyCopyPublicKey(privateKey) if publicKey == nil { reject(nil, "error creating public key", nil) return } resolve(self.PEMFormattedPublicKey(key: publicKey!)) }
公钥PEM格式化代码
private func PEMFormattedPublicKey(key: SecKey) -> String? { guard let publicKeyData = self.dataForKey(key: key) else { return nil } // ASN.1 头部格式,适用于256位EC密钥 let ecHeader: [UInt8] = [ 0x30, 0x59, // Sequence 0x30, 0x13, // Algorithm identifier 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, // (ANSI X9.62 public key type) 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, // (ANSI X9.62 named elliptic curve) 0x07, 0x03, 0x42, 0x00 // bit headers ] var asn1 = Data() asn1.append(Data(ecHeader)) asn1.append(publicKeyData as Data) let encoded = asn1.base64EncodedString(options: .lineLength64Characters) let pemString = "-----BEGIN PUBLIC KEY-----\r\n\(encoded)\r\n-----END PUBLIC KEY-----\r\n" return pemString }
后端验证Node.js代码
const publicKey = Buffer.from(publicKeyB64, 'base64').toString('utf-8'); const key = { key: publicKey, }; const verifier = createVerify('SHA256'); verifier.update(challenge); return verifier.verify(key, digest, 'base64');
异常示例数据
- challenge:
ac13288a-3ef7-4eb8-9198-5cb7f3a18665 - signature:
MEUCIEQMq6unQLES+vBMhalDG0ZxTX9TNWukb63Qqd7W4ReRAiEAyqTwMMQC/HkU4AbLmwbBTm6DBFDpA8yo+3Cigg0H0VA= - publicKeyB64:
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRXJENjNrSXdRaC83bVNia1R1QllUUEJtSGdOVmwNCnJueE4rNGVER2pHSDNRZmdVaGhKNlZYTjVSWTZhVW9iRjd3MjdFQXMwZ0JHaTdjOXdPdndQbmhxNEE9PQ0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tDQo=
内容的提问来源于stack exchange,提问作者Max Fratane
相关产品推荐
相关产品推荐

