Swift中如何提取OpenSSL stack_st_X509中的证书内容?
从OpenSSL CMS签名中提取签名证书(Swift实现)
很高兴你已经搞定了CMS签名验证的部分!接下来要从CMS_get0_signers返回的栈中提取X509证书,其实就是利用OpenSSL提供的STACK操作函数结合Swift的不安全指针来遍历处理,我给你详细拆解下步骤和代码:
核心思路
OpenSSL的STACK_OF(X509)在Swift中被映射为UnsafeMutablePointer<stack_st_X509>,要提取里面的证书,需要用OpenSSL专门为X509栈提供的操作函数:
sk_X509_num():获取栈中证书的数量sk_X509_value():根据索引取出对应位置的X509证书指针
完整代码示例
首先确保你的项目已经正确桥接了OpenSSL的头文件(在桥接文件中添加#include <openssl/cms.h>和#include <openssl/x509.h>),然后可以用下面的代码处理:
// 假设你已经成功验证CMS签名,得到了cms指针:UnsafeMutablePointer<CMS_ContentInfo> guard let signersStack = CMS_get0_signers(cms) else { print("未找到任何签名证书") return } // 获取栈中证书的总数 let signerCount = sk_X509_num(signersStack) print("共找到 \(signerCount) 个签名证书") // 遍历栈中的每个证书 for index in 0..<signerCount { // 取出对应索引的X509证书指针 guard let x509Cert = sk_X509_value(signersStack, index) else { print("无法获取索引 \(index) 的签名证书") continue } // 示例:获取证书的主题名称并打印 // 方式1:使用X509_NAME_oneline(注意:返回的是静态缓冲区,无需手动释放,但线程不安全) if let subjectCStr = X509_NAME_oneline(X509_get_subject_name(x509Cert), nil, 0) { let subject = String(cString: subjectCStr) print("签名证书 \(index+1) 主题:\(subject)") } // 方式2:使用BIO读取(线程安全,推荐) let bio = BIO_new(BIO_s_mem()) defer { BIO_free(bio) } // 确保BIO资源被释放 if X509_NAME_print_ex(bio, X509_get_subject_name(x509Cert), 0, XN_FLAG_ONELINE) > 0 { let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 2048) defer { buffer.deallocate() } let readLength = BIO_read(bio, buffer, 2047) if readLength > 0 { buffer[readLength] = 0 // 手动添加字符串结束符 let subjectStr = String(cString: buffer) print("签名证书 \(index+1) 主题(安全方式):\(subjectStr)") } } // 如果你需要保留证书的引用,记得调用X509_up_ref(x509Cert)增加引用计数 // 后续不再使用时,调用X509_free(x509Cert)释放资源 }
关键注意事项
- 内存管理:
CMS_get0_signers返回的栈由CMS结构持有,不需要手动释放,当你调用CMS_ContentInfo_free(cms)时,栈会被自动清理。sk_X509_value返回的X509指针是栈内的引用,默认不需要手动X509_free,除非你调用了X509_up_ref增加了引用计数。
- 函数可用性:
确保你的OpenSSL版本支持这些函数,不同版本的函数名可能略有差异(比如旧版本可能需要用宏而不是直接函数调用,但现代版本都支持直接调用sk_X509_num和sk_X509_value)。 - 错误处理:
记得在每次获取指针后做非空判断,避免野指针导致的崩溃。
内容的提问来源于stack exchange,提问作者Shadowman
相关产品推荐
相关产品推荐

