iOS12+Swift:从PEM生成SHA256 Hex及根证书公钥固定实现与差异疑问
iOS Swift 实现ISRG Root X1公钥固定问题解答
1. 从PEM文件生成SHA256十六进制字符串
使用以下OpenSSL命令,直接从ISRG Root X1的PEM证书中提取公钥并计算SHA256十六进制哈希:
openssl x509 -in ISRG-Root-X1.pem -pubkey -noout | openssl rsa -pubin -inform PEM -outform DER | openssl dgst -sha256 -hex
命令说明:
- 第一部分提取证书中的公钥(去除证书其他内容)
- 第二部分将PEM格式的公钥转为二进制DER格式
- 第三部分计算DER数据的SHA256哈希,并输出十六进制字符串
2. Swift中基于URLSession实现公钥固定(无第三方库)
通过实现URLSessionDelegate的证书验证逻辑,完成公钥固定,核心代码如下:
import Foundation // 需确保项目已导入CommonCrypto:可通过桥接文件添加#include <CommonCrypto/CommonCrypto.h>,或在Build Settings中配置相关路径 class PinningDelegate: NSObject, URLSessionDelegate { // 替换为你生成的ISRG Root X1公钥SHA256十六进制字符串 private let trustedHashes = ["your-sha256-hex-string-here"] func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { // 仅处理服务器信任验证挑战 guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, let serverTrust = challenge.protectionSpace.serverTrust else { completionHandler(.performDefaultHandling, nil) return } var isTrusted = false let certCount = SecTrustGetCertificateCount(serverTrust) // 遍历证书链,检查是否有匹配的公钥哈希 for index in 0..<certCount { guard let cert = SecTrustGetCertificateAtIndex(serverTrust, index), let publicKey = SecCertificateCopyPublicKey(cert), let pubKeyData = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? else { continue } let calculatedHash = pubKeyData.sha256Hex() if trustedHashes.contains(calculatedHash) { isTrusted = true break } } if isTrusted { // 验证通过,使用服务器信任凭证 completionHandler(.useCredential, URLCredential(trust: serverTrust)) } else { // 验证失败,拒绝连接 completionHandler(.cancelAuthenticationChallenge, nil) } } } // Data扩展:计算SHA256并转为十六进制字符串 extension Data { func sha256Hex() -> String { var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) self.withUnsafeBytes { CC_SHA256($0.baseAddress, CC_LONG(self.count), &hash) } return hash.map { String(format: "%02x", $0) }.joined() } }
使用方式:创建URLSession时指定该delegate即可:
let session = URLSession(configuration: .default, delegate: PinningDelegate(), delegateQueue: nil)
3. OpenSSL命令结果与代码不一致的原因
你使用的命令openssl rsa -pubin -inform PEM -outform DER -in public_key.pem | openssl enc -base64没有计算SHA256哈希,而是将DER格式的公钥转成了Base64字符串,这和代码中计算的公钥SHA256十六进制哈希完全是两个不同的结果,因此不一致是命令错误导致的,不属于正常现象。
只要替换为问题1中给出的正确命令,生成的SHA256十六进制字符串就会和代码中计算的结果一致。
内容的提问来源于stack exchange,提问作者Sajal Gupta
相关产品推荐
相关产品推荐

