iOS 8中如何获取单主机多证书的公钥哈希以实现证书固定?
iOS 8下获取单主机多证书公钥哈希的解决方案
针对你遇到的iOS 8与iOS 9+在多证书公钥获取上的差异问题,其实Apple有兼容iOS 8的API可以实现需求——核心是获取完整的服务器证书链,再逐个提取公钥计算哈希。下面是具体的实现思路和代码示例:
关键思路:遍历完整证书链
iOS 9+可能你能直接拿到所有证书,但iOS 8需要通过SecTrustCopyCertificateChain(iOS 3.0+就已支持)来获取服务器返回的完整证书链数组,之后遍历数组里的每个证书,单独提取公钥并计算哈希,再和你APP内存储的哈希字典对比,只要有一个匹配即可通过验证。
具体实现步骤(Objective-C)
- 获取完整证书链:从
SecTrustRef中提取所有证书 - 遍历每个证书:逐个获取证书的公钥
- 计算公钥哈希:用你存储哈希时对应的算法(比如SHA-256)生成哈希值
- 对比验证:检查哈希是否在你的预存字典中
BOOL isValid = NO; // 假设你已经有了serverTrust对象和预存的公钥哈希字典yourPublicKeyHashesDict CFArrayRef certificateChain = SecTrustCopyCertificateChain(serverTrust); if (certificateChain != NULL) { CFIndex certCount = CFArrayGetCount(certificateChain); for (CFIndex i = 0; i < certCount; i++) { // 取出链中的单个证书 SecCertificateRef cert = CFArrayGetValueAtIndex(certificateChain, i); // 提取该证书的公钥 SecKeyRef publicKey = SecCertificateCopyPublicKey(cert); if (publicKey != NULL) { // 将公钥转为外部表示数据 CFDataRef publicKeyData = SecKeyCopyExternalRepresentation(publicKey, NULL); if (publicKeyData != NULL) { // 计算SHA-256哈希(和你存储哈希时的算法保持一致) uint8_t digest[CC_SHA256_DIGEST_LENGTH]; CC_SHA256(CFDataGetBytePtr(publicKeyData), CFDataGetLength(publicKeyData), digest); // 转换为可对比的格式(这里用Base64字符串,和你存储的格式匹配即可) NSData *hashData = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH]; NSString *hashString = [hashData base64EncodedStringWithOptions:0]; // 检查是否在预存字典中 if ([yourPublicKeyHashesDict objectForKey:hashString] != nil) { isValid = YES; // 找到匹配项,提前退出循环 goto cleanup; } CFRelease(publicKeyData); } CFRelease(publicKey); } } cleanup: CFRelease(certificateChain); } // 最后根据isValid的值处理验证结果 if (isValid) { // 验证通过 } else { // 验证失败 }
替代方案:用SecTrustGetCertificateAtIndex遍历证书
除了SecTrustCopyCertificateChain,你也可以用SecTrustGetCertificateAtIndex逐个获取证书,直到返回NULL,效果是一样的:
BOOL isValid = NO; CFIndex certCount = SecTrustGetCertificateCount(serverTrust); for (CFIndex i = 0; i < certCount; i++) { SecCertificateRef cert = SecTrustGetCertificateAtIndex(serverTrust, i); // 后续提取公钥、计算哈希的逻辑和上面一致 }
注意事项
- 内存管理:所有CF类型的对象(
CFArrayRef、SecCertificateRef等)都需要手动调用CFRelease释放,避免内存泄漏。 - 算法一致性:计算公钥哈希的算法必须和你预存哈希时使用的算法完全一致(比如SHA-1或SHA-256),否则对比会失败。
- 证书链顺序:证书链通常是从服务器证书(索引0)到根证书,你可以根据需求决定是否需要遍历全部证书,或者只检查服务器证书层级的几个。
内容的提问来源于stack exchange,提问作者thoughtbreaker
相关产品推荐
相关产品推荐

