You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 07:35:00