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

Linux环境Swift 4提取CMS/PKCS#7证书并验证容器签名

我之前在Linux Swift项目里折腾过OpenSSL处理CMS签名,太懂这种对着C接口抓瞎的感觉了😅 既然你已经搞定了PKCS#7的类型校验,那接下来提取证书和验证签名的核心步骤我给你拆解清楚,结合Swift封装来实现:

第一步:从PKCS#7 SignedData中提取签名证书

PKCS7的签名数据存在PKCS7_SIGNED结构体里,我们需要先拿到这个结构体指针,再从中取出证书栈(STACK_OF(X509)),最后遍历栈提取单个证书。

假设你的PKCS7封装类已经持有了OpaquePointer类型的PKCS7指针,可以加一个这样的方法:

func extractSignerCertificates() throws -> [X509] {
    guard let pkcs7Ptr = self.pkcs7Ptr else {
        throw OpenSSLError.invalidPKCS7
    }
    
    // 二次校验类型(可选但稳妥)
    guard PKCS7_type_is_signed(pkcs7Ptr) == 1 else {
        throw OpenSSLError.notSignedData
    }
    
    // 获取SignedData结构体
    guard let signedData = PKCS7_get_signedData(pkcs7Ptr) else {
        throw OpenSSLError.failedToGetSignedData
    }
    
    // 取出证书栈
    guard let certStack = PKCS7_SIGNED_get_certs(signedData) else {
        throw OpenSSLError.noCertificatesFound
    }
    
    // 遍历栈转成X509封装对象
    var certs: [X509] = []
    let certCount = sk_X509_num(certStack)
    for i in 0..<certCount {
        if let certPtr = sk_X509_value(certStack, i) {
            // 假设你有X509封装类,这里初始化时接管指针所有权
            let cert = X509(pointer: certPtr)
            certs.append(cert)
        }
    }
    
    return certs
}

⚠️ 注意:OpenSSL的STACK_OF系列必须用对应的sk_*函数操作(比如sk_X509_num取数量、sk_X509_value取元素),而且X509对象的内存一定要在封装类的deinit里调用X509_free释放,避免内存泄漏。

第二步:验证CMS签名的完整性

核心用OpenSSL的PKCS7_verify函数,它需要区分内嵌式签名(签名里包含原始数据)和分离式签名(签名与数据分开存储)两种场景:

func verifySignature(originalData: Data? = nil, trustStore: X509Store? = nil) throws -> Bool {
    guard let pkcs7Ptr = self.pkcs7Ptr else {
        throw OpenSSLError.invalidPKCS7
    }
    
    // 准备原始数据的BIO(分离式签名需要传,内嵌式传nil)
    var inBio: OpaquePointer? = nil
    if let data = originalData {
        inBio = BIO_new_mem_buf(data.withUnsafeBytes { $0.baseAddress }, Int32(data.count))
        defer { BIO_free(inBio) }
    }
    
    // 准备信任存储(验证证书链用,不需要的话传nil)
    let storePtr: OpaquePointer?
    let shouldFreeStore: Bool
    if let store = trustStore {
        storePtr = store.storePtr
        shouldFreeStore = false
    } else {
        storePtr = X509_STORE_new()
        shouldFreeStore = true
    }
    defer {
        if shouldFreeStore, let ptr = storePtr {
            X509_STORE_free(ptr)
        }
    }
    
    // 设置验证标志:如果不需要验证证书链,用PKCS7_NOVERIFY;否则默认验证
    let flags = trustStore == nil ? PKCS7_NOVERIFY : 0
    
    // 执行签名验证
    let verifyResult = PKCS7_verify(pkcs7Ptr, nil, storePtr, inBio, nil, flags)
    guard verifyResult == 1 else {
        // 提取OpenSSL错误信息用于调试
        let errCode = ERR_get_error()
        var errBuffer = [Int8](repeating: 0, count: 1024)
        ERR_error_string(errCode, &errBuffer)
        throw OpenSSLError.signatureVerificationFailed(message: String(cString: errBuffer))
    }
    
    return true
}

关键细节:

  • 如果是内嵌式签名,直接传originalData: nil,PKCS7_verify会自动从PKCS7结构体里提取原始数据验证。
  • 如果要验证证书链的可信性,需要先构建X509_STORE,把信任的根证书加进去:
    // 构建信任存储示例
    let trustStore = X509Store()
    let rootCertData = try Data(contentsOf: URL(fileURLWithPath: "/path/to/root.cer"))
    let rootCert = try X509(derData: rootCertData)
    X509_STORE_add_cert(trustStore.storePtr, rootCert.certPtr)
    
  • 错误处理一定要拿ERR_get_error()提取OpenSSL的错误栈,否则根本不知道哪里出问题。
第三步:避坑提醒
  1. 内存管理是生命线:所有OpenSSL的C对象(BIO、PKCS7、X509等)都要手动释放,最好在Swift封装类的deinit里调用对应的*_free函数。
  2. 版本兼容性:不同Linux发行版的OpenSSL版本可能有差异(比如1.1.0和3.0的函数名略有变化),测试时要对应目标环境的版本。
  3. 编码格式适配:如果你的CMS负载是PEM格式,需要先用d2i_PKCS7_bio解码成PKCS7结构体再处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:54:48