You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Swift(Alamofire)中用certificate.pem和private_key.pem签名HTTP请求

嘿,我之前刚好解决过类似的问题——Postman里配置客户端证书确实很直观,但Swift + Alamofire这边因为涉及到iOS Security框架的细节,确实容易踩坑。下面是一步步的解决方案,亲测有效:

第一步:将PEM格式证书转换为iOS兼容的PKCS#12格式

iOS的Security框架对PKCS#12(.p12/.pfx)格式支持更好,我们需要把你的certificate.pemprivate_key.pem合并成这个格式。打开终端,用OpenSSL执行以下命令:

openssl pkcs12 -export -in certificate.pem -inkey private_key.pem -out client_cert.p12 -name "MyClientCert"

执行后会提示你设置一个密码,记得记下来,后面代码里会用到这个密码。

第二步:将.p12文件添加到Swift项目中

把生成的client_cert.p12拖进Xcode,勾选「Copy items if needed」,并确保它被添加到你的项目Target中(在Xcode右侧的「File Inspector」里确认Target Membership已勾选)。

第三步:配置Alamofire的SessionManager以支持客户端证书认证

下面是Swift 3的完整代码示例,我们会创建一个带证书认证的SessionManager,然后用它发送PUT请求:

import Alamofire

// 创建带客户端证书的SessionManager
func createCertifiedSession(p12Name: String, p12Password: String) -> SessionManager? {
    // 找到项目中的p12文件
    guard let p12Path = Bundle.main.path(forResource: p12Name, ofType: "p12") else {
        print("Error: 找不到p12证书文件")
        return nil
    }
    
    let p12Data = try! Data(contentsOf: URL(fileURLWithPath: p12Path))
    
    // 加载p12文件,提取身份信息
    let importOptions = [kSecImportExportPassphrase as String: p12Password] as CFDictionary
    var importedItems: CFArray?
    let importStatus = SecPKCS12Import(p12Data as CFData, importOptions, &importedItems)
    
    guard importStatus == errSecSuccess,
          let itemsArray = importedItems as? [[String: Any]],
          let identityDict = itemsArray.first,
          let identity = identityDict[kSecImportItemIdentity as String] as? SecIdentity else {
        print("Error: 加载p12证书失败,状态码:\(importStatus)")
        return nil
    }
    
    // 从身份信息中提取证书
    var certificate: SecCertificate?
    SecIdentityCopyCertificate(identity, &certificate)
    guard let clientCert = certificate else {
        print("Error: 无法从身份信息中提取证书")
        return nil
    }
    
    // 配置服务器信任策略(固定证书,防止中间人攻击)
    let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
        certificates: [clientCert],
        validateCertificateChain: true,
        validateHost: true
    )
    
    // 替换成你的API域名
    let trustPolicies = ["your-api-domain.com": serverTrustPolicy]
    let policyManager = ServerTrustPolicyManager(policies: trustPolicies)
    
    // 创建自定义SessionManager
    let sessionManager = SessionManager(serverTrustPolicyManager: policyManager)
    
    // 处理客户端证书认证挑战
    sessionManager.delegate.sessionDidReceiveChallenge = { session, challenge in
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
            let credential = URLCredential(identity: identity, certificates: [clientCert], persistence: .forSession)
            return (.useCredential, credential)
        } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            return (.performDefaultHandling, nil)
        } else {
            return (.cancelAuthenticationChallenge, nil)
        }
    }
    
    return sessionManager
}

// 使用示例:发送PUT请求
if let certifiedSession = createCertifiedSession(p12Name: "client_cert", p12Password: "你设置的p12密码") {
    let requestURL = "https://your-api-domain.com/your-endpoint"
    let parameters: [String: Any] = ["param1": "value1", "param2": "value2"]
    let headers: HTTPHeaders = ["Content-Type": "application/json"]
    
    certifiedSession.put(requestURL, parameters: parameters, encoding: JSONEncoding.default, headers: headers)
        .responseJSON { response in
            switch response.result {
            case .success(let responseData):
                print("请求成功:\(responseData)")
            case .failure(let error):
                print("请求失败:\(error.localizedDescription)")
            }
        }
}
常见问题排查
  • 密码错误:如果加载p12失败,先检查你设置的密码是否正确,大小写敏感。
  • 域名不匹配trustPolicies里的域名必须和API的域名完全一致(比如带不带www要统一)。
  • 证书链验证失败:如果你的服务器用的是自签名证书或者私有CA,可以把validateCertificateChain设为false(生产环境不建议这么做)。
  • ATS配置:iOS 9+默认开启ATS,若API是HTTP协议,需要在Info.plist中添加NSAppTransportSecurity配置关闭ATS;如果是HTTPS则无需额外操作。
  • 证书有效性:可以用以下命令验证你的PEM文件是否有效:
    # 验证证书
    openssl x509 -in certificate.pem -text -noout
    # 验证私钥
    openssl rsa -in private_key.pem -check
    

内容的提问来源于stack exchange,提问作者Mansur Muaz Ekici

火山引擎 最新活动