React Native iOS项目如何嵌入客户端.pem格式SSL证书?
React Native iOS端嵌入PEM证书实现API访问(Objective-C方案)
步骤1:添加PEM证书到iOS项目
- 将你的
.pem证书文件拖到Xcode的React Native iOS项目目录(如ios/[项目名称]) - 勾选「Copy items if needed」,并确认证书已加入项目的「Build Phases > Copy Bundle Resources」列表
步骤2:编写Objective-C证书处理与校验代码
创建SSLPinningManager类,负责加载PEM证书并完成证书校验逻辑:
1. 加载并转换PEM证书
#import <Foundation/Foundation.h> #import <Security/Security.h> @interface SSLPinningManager : NSObject + (SecCertificateRef)loadPEMCertificate; @end @implementation SSLPinningManager + (SecCertificateRef)loadPEMCertificate { // 替换为你的证书文件名(不带.pem后缀) NSString *certPath = [[NSBundle mainBundle] pathForResource:@"your-cert-filename" ofType:@"pem"]; NSData *pemData = [NSData dataWithContentsOfFile:certPath]; if (!pemData) { NSLog(@"PEM证书加载失败"); return NULL; } // 将PEM格式转成Security框架可识别的DER格式 NSData *derData = [self convertPEMToDER:pemData]; if (!derData) return NULL; SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)derData); return cert; } + (NSData *)convertPEMToDER:(NSData *)pemData { NSString *pemString = [[NSString alloc] initWithData:pemData encoding:NSUTF8StringEncoding]; // 清理PEM头、尾和换行符 NSString *cleanedStr = [pemString stringByReplacingOccurrencesOfString:@"-----BEGIN CERTIFICATE-----" withString:@""]; cleanedStr = [cleanedStr stringByReplacingOccurrencesOfString:@"-----END CERTIFICATE-----" withString:@""]; cleanedStr = [cleanedStr stringByReplacingOccurrencesOfString:@"\n" withString:@""]; cleanedStr = [cleanedStr stringByReplacingOccurrencesOfString:@"\r" withString:@""]; // Base64解码得到DER数据 return [[NSData alloc] initWithBase64EncodedString:cleanedStr options:NSDataBase64DecodingIgnoreUnknownCharacters]; } @end
2. 配置NSURLSession证书校验
在发起API请求的类中实现NSURLSessionDelegate,完成证书钉扎校验:
- (void)sendAPIRequest { NSURL *apiUrl = [NSURL URLWithString:@"https://your-api-domain.com"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:apiUrl]; NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 处理API响应或错误 if (error) { NSLog(@"请求失败:%@", error.localizedDescription); return; } // 解析响应数据 }]; [task resume]; } // 实现证书校验代理方法 - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { SecCertificateRef pinnedCert = [SSLPinningManager loadPEMCertificate]; if (!pinnedCert) { completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); return; } SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; // 设置仅信任指定的钉扎证书 SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)@[(__bridge id)pinnedCert]); SecTrustSetAnchorCertificatesOnly(serverTrust, YES); SecTrustResultType trustResult; SecTrustEvaluate(serverTrust, &trustResult); if (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed) { NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential, credential); } else { completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); } CFRelease(pinnedCert); } else { completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); } }
3. AFNetworking适配(可选)
如果项目使用AFNetworking,可通过以下方式配置证书钉扎:
#import <AFNetworking/AFNetworking.h> - (void)setupAFNetworkingWithPinning { SecCertificateRef cert = [SSLPinningManager loadPEMCertificate]; if (!cert) return; AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; policy.pinnedCertificates = @[(__bridge id)cert]; policy.validatesDomainName = YES; // 根据需求开启/关闭域名校验 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.securityPolicy = policy; // 发起API请求 [manager GET:@"https://your-api-domain.com" parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { // 处理成功响应 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { // 处理请求错误 }]; CFRelease(cert); }
步骤3:React Native JS层调用(可选)
若需在JS层调用原生API,可创建React Native原生模块桥接:
- 新建
RNAPIModule.h和RNAPIModule.m文件 - 在
RNAPIModule.m中封装上述API请求逻辑,并通过RCT_EXPORT_METHOD暴露给JS层 - 在
AppDelegate.m中注册该原生模块
常见问题排查
- 403错误:确认证书SAN字段与API域名匹配,检查Cloudflare SSL设置是否为兼容模式,排查请求头是否符合API要求
- 证书加载失败:检查证书文件名是否正确、是否已加入Copy Bundle Resources,确认PEM文件格式完整(包含BEGIN/END标记)
- 校验失败:可尝试提前用
openssl x509 -in cert.pem -out cert.der -outform der命令将PEM转成DER格式后导入项目,跳过代码转换步骤
内容的提问来源于stack exchange,提问作者Speedy
相关产品推荐
相关产品推荐

