You need to enable JavaScript to run this app. 如何使用生物识别/kSecAttrAccessControl保护SecIdentity?
优惠活动
大模型
产品
解决方案
定价
更多

如何使用生物识别/kSecAttrAccessControl保护SecIdentity?

根因说明

SecIdentity是钥匙串系统中私钥与对应X.509证书的逻辑关联对象,你遇到的失败主要是3个常见配置错误导致:

  • 私钥访问控制参数配置缺失:配置生物识别保护时未添加kSecAccessControlPrivateKeyUsage标识,导致URLAuthChallenge触发时,系统网络栈无法调用受保护的私钥完成签名验签流程
  • 私钥与证书关联属性不匹配:钥匙串需要通过完全一致的kSecAttrApplicationTag、kSecAttrLabel属性匹配私钥和证书,属性不一致时无法生成对应的SecIdentity对象
  • 查询SecIdentity时缺少鉴权参数:查询受生物识别保护的私钥关联的Identity时,未配置允许触发生物识别验证的参数,会被钥匙串直接拦截返回查询失败

正确实现步骤

1. 生成带生物识别保护的RSA密钥对

创建访问控制对象时必须添加.privateKeyUsage标识,同时固定私钥的关联标签后续给证书复用:

// 创建带生物识别的访问控制配置
guard let accessControl = SecAccessControlCreateWithFlags(
    kCFAllocatorDefault,
    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
    [.userPresence, .privateKeyUsage], // 必须添加privateKeyUsage允许私钥用于签名操作
    nil
) else {
    // 处理设备不支持生物识别/未设置密码的场景
    return
}

// 密钥对生成参数
let keyGenParams: [CFString: Any] = [
    kSecAttrKeyType: kSecAttrKeyTypeRSA,
    kSecAttrKeySizeInBits: 2048,
    kSecAttrAccessControl: accessControl,
    kSecAttrIsPermanent: true,
    kSecAttrApplicationTag: "com.your.bundle.id.rsa.tag".data(using: .utf8)! // 固定唯一标签,后续复用
]

var publicKey, privateKey: SecKey?
let status = SecKeyGeneratePair(keyGenParams as CFDictionary, &publicKey, &privateKey)
guard status == errSecSuccess, let publicKey = publicKey else {
    return
}
// 导出公钥发给服务器获取X.509证书

2. 导入服务器返回的X.509证书

导入证书时必须设置和私钥完全一致的关联标签,同时标记为身份证书:

let certData = // 服务器返回的X.509证书二进制数据
guard let cert = SecCertificateCreateWithData(nil, certData as CFData) else {
    return
}

// 可选:校验证书公钥与本地生成公钥是否一致,避免关联失败
guard let certPublicKey = SecCertificateCopyKey(cert),
      SecKeyIsEqualToKey(certPublicKey, publicKey) else {
    return
}

let certAddParams: [CFString: Any] = [
    kSecClass: kSecClassCertificate,
    kSecValueRef: cert,
    kSecAttrApplicationTag: "com.your.bundle.id.rsa.tag".data(using: .utf8)!, // 和私钥标签完全一致
    kSecAttrIsIdentity: kCFBooleanTrue!,
    kSecAttrAccessible: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
]

let addCertStatus = SecItemAdd(certAddParams as CFDictionary, nil)
guard addCertStatus == errSecSuccess || addCertStatus == errSecDuplicateItem else {
    return
}

3. 查询SecIdentity

查询时必须添加允许触发生物识别验证的参数:

let identityQuery: [CFString: Any] = [
    kSecClass: kSecClassIdentity,
    kSecAttrApplicationTag: "com.your.bundle.id.rsa.tag".data(using: .utf8)!,
    kSecReturnRef: kCFBooleanTrue!,
    kSecUseAuthenticationUI: kSecUseAuthenticationUIAllow // 允许触发生物识别弹窗
]

var identityRef: CFTypeRef?
let queryStatus = SecItemCopyMatching(identityQuery as CFDictionary, &identityRef)
guard queryStatus == errSecSuccess, let identity = identityRef as? SecIdentity else {
    // 处理查询失败,比如用户取消生物识别验证的场景
    return
}
// 拿到的identity可直接用于URLAuthChallenge处理

原有方案的问题修正

  • writeIdentityWithBiometrics方案:按上述步骤补充.privateKeyUsage标识、保证私钥和证书的关联标签一致即可正常使用
  • writePrivateKeyWithBiometrics方案:SecIdentity只是逻辑关联对象,本身无法直接配置访问控制,需要先查询到私钥的引用,再通过SecItemUpdate接口更新私钥的kSecAttrAccessControl属性,更新完成后再查询SecIdentity即可

注意:如果使用后台URLSession处理请求,需要提前用LAContext完成生物识别验证,将验证后的context传入查询参数的kSecUseAuthenticationContext字段,避免后台场景无法弹出生物识别验证窗口导致失败。

内容的提问来源于stack exchange,提问作者swalkner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 01:36:03