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

Alamofire公钥固定失效问题排查及相关疑问求助

Alamofire + Moya 公钥固定问题排查与解决方案

一、证书读取失败:Bundle.main.af.publicKeys为空

核心原因

Alamofire 的 Bundle.af.publicKeys 仅支持 DER 格式 的 .cer/.crt 证书,PEM 格式证书会因为 SecCertificateCreateWithData 解析失败,无法生成公钥对象,导致返回空数组。

解决步骤

  1. 正确转换证书格式
    使用 OpenSSL 将 PEM 证书转为 DER 格式,命令如下:
    openssl x509 -in 原证书.pem -outform der -out 目标证书.cer
    
  2. 确保证书正确导入项目
    • 将转换后的 .cer 文件拖入 Xcode 项目,勾选「Copy items if needed」
    • 检查 Target 的 Build Phases > Copy Bundle Resources 中是否包含该证书,确保打包时能被放入 Bundle

二、证书正常但请求返回 405,!pinnedKeysInServerKeys 为 false

关键概念区分

  • 405 Method Not Allowed:这是 HTTP 层状态码,说明服务器已完成 TLS 握手(公钥校验大概率已通过),但请求使用的 HTTP 方法(如 GET/POST)不符合服务器接口要求。
  • pinnedKeysInServerKeys 返回 false:表示本地固定的公钥与服务器返回的公钥无匹配,理论上会触发 TLS 信任评估失败(抛出证书不信任错误),但如果仍能拿到 405,说明公钥校验逻辑可能未生效。

排查与解决

  1. 先排除 HTTP 方法错误
    用 Postman 或 curl 测试同一接口,确认使用的 HTTP 方法是否符合服务器要求(比如接口仅支持 POST,但你用了 GET)。

  2. 检查公钥固定配置是否生效

    • 确认 ServerTrustManager 已正确关联到 Alamofire Session,且域名匹配:
      // 初始化公钥评估器
      let evaluator = PublicKeysTrustEvaluator(
          keys: Bundle.main.af.publicKeys,
          performDefaultValidation: true,
          validateHost: true
      )
      // 绑定目标域名
      let serverTrustManager = ServerTrustManager(evaluators: [
          "your-api-domain.com": evaluator
      ])
      // 创建带信任管理的 Session
      let session = Session(serverTrustManager: serverTrustManager)
      // 传入 Moya Provider
      let provider = MoyaProvider<YourAPI>(session: session)
      
    • 打印 Bundle.main.af.publicKeys.count,确认公钥已成功加载(数量 > 0)。
  3. 验证公钥校验逻辑是否触发
    自定义信任评估器添加日志,确认校验过程:

    class LoggingPublicKeysEvaluator: PublicKeysTrustEvaluator {
        override func evaluate(_ trust: SecTrust, forHost host: String) throws {
            let serverKeys = try ServerTrustPolicy.publicKeys(for: trust)
            let pinnedKeysCount = self.keys.count
            let serverKeysCount = serverKeys.count
            let isMatch = pinnedKeysInServerKeys(self.keys, serverKeys)
            
            print("本地公钥数:\(pinnedKeysCount),服务器公钥数:\(serverKeysCount)")
            print("公钥匹配结果:\(isMatch)")
            
            try super.evaluate(trust, forHost: host)
        }
    }
    

    替换原评估器后,查看日志输出,确认是否真的触发了公钥校验,以及匹配结果是否符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 08:03:57