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

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原生模块桥接:

  1. 新建RNAPIModule.h和RNAPIModule.m文件
  2. 在RNAPIModule.m中封装上述API请求逻辑,并通过RCT_EXPORT_METHOD暴露给JS层
  3. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 05:37:22