React Native动态获取HTTPS服务器证书指纹实现SSL固定
在React Native中实现指定OpenSSL命令的功能(动态获取SSL公钥哈希)
你给出的OpenSSL命令作用是:连接目标服务器获取SSL证书,提取证书公钥,将其转为DER格式后计算SHA256哈希,最终输出base64编码的结果——这正是SSL公钥固定所需的验证值。
React Native无法直接调用终端命令,以下是两种可行实现方案:
方案一:原生模块实现(推荐,完全匹配命令逻辑)
分别在iOS和Android端编写原生代码,通过RN桥接暴露给JS层调用,是兼容性和准确性最高的方案。
iOS端实现(Objective-C)
// SSLPublicKeyManager.m #import <Foundation/Foundation.h> #import <React/RCTBridgeModule.h> @interface SSLPublicKeyManager : NSObject <RCTBridgeModule> @end @implementation SSLPublicKeyManager RCT_EXPORT_MODULE(); RCT_EXPORT_METHOD(getPublicKeyHash:(NSString *)serverName resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { self.resolveBlock = resolve; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", serverName]]; NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { reject(@"ERROR_FETCH", @"Failed to fetch server certificate", error); return; } }]; [task resume]; } #pragma mark - NSURLSessionDelegate - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0); // 提取公钥 SecKeyRef publicKey = SecCertificateCopyPublicKey(certificate); CFErrorRef error = NULL; NSData *publicKeyData = (__bridge NSData *)SecKeyCopyExternalRepresentation(publicKey, &error); if (error) { CFRelease(error); completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); return; } // 计算SHA256哈希并转base64 unsigned char hash[CC_SHA256_DIGEST_LENGTH]; CC_SHA256(publicKeyData.bytes, (CC_LONG)publicKeyData.length, hash); NSData *hashData = [NSData dataWithBytes:hash length:CC_SHA256_DIGEST_LENGTH]; NSString *base64Hash = [hashData base64EncodedStringWithOptions:0]; if (self.resolveBlock) { self.resolveBlock(base64Hash); self.resolveBlock = nil; } CFRelease(publicKey); CFRelease(certificate); } completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); } @end
Android端实现(Kotlin)
// SSLPublicKeyManager.kt package com.your_app_package_name import android.util.Base64 import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import java.security.MessageDigest import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import javax.net.ssl.SSLSocket import javax.net.ssl.SSLSocketFactory class SSLPublicKeyManager(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { override fun getName(): String = "SSLPublicKeyManager" @ReactMethod fun getPublicKeyHash(serverName: String, promise: Promise) { Thread { try { val socketFactory: SSLSocketFactory = SSLSocketFactory.getDefault() as SSLSocketFactory val socket: SSLSocket = socketFactory.createSocket(serverName, 443) as SSLSocket socket.startHandshake() val certificates = socket.session.peerCertificates val certFactory = CertificateFactory.getInstance("X.509") val x509Cert = certFactory.generateCertificate(certificates[0].inputStream) as X509Certificate // 提取公钥DER格式数据 val publicKeyDer = x509Cert.publicKey.encoded // 计算SHA256哈希并转base64 val md = MessageDigest.getInstance("SHA-256") val hashBytes = md.digest(publicKeyDer) val base64Hash = Base64.encodeToString(hashBytes, Base64.DEFAULT).trim() promise.resolve(base64Hash) socket.close() } catch (e: Exception) { promise.reject("ERROR_FETCH", "Failed to fetch public key hash", e) } }.start() } }
JS层调用
import { NativeModules } from 'react-native'; const { SSLPublicKeyManager } = NativeModules; async function fetchPublicKeyHash(serverName) { try { const hash = await SSLPublicKeyManager.getPublicKeyHash(serverName); console.log('公钥哈希:', hash); return hash; } catch (error) { console.error('获取失败:', error); return null; } } // 使用示例 fetchPublicKeyHash('serverName.com');
方案二:纯JS方案(局限性较大)
纯JS方案依赖第三方库解析证书,兼容性不如原生方案,仅作参考:
import CryptoJS from 'crypto-js'; import asn1js from 'asn1js'; import { fromBER } from 'pkijs'; async function fetchPublicKeyHash(serverName) { try { // 需自定义网络请求获取证书原始数据,此处省略请求逻辑 const certDerData = ...; // 从服务器获取的证书DER格式数据 // 解析证书提取公钥 const asn1 = asn1js.fromBER(certDerData); const cert = new fromBER(asn1.result); const publicKey = cert.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex; // 计算SHA256哈希并转base64 const hash = CryptoJS.SHA256(CryptoJS.lib.WordArray.create(publicKey)); const base64Hash = hash.toString(CryptoJS.enc.Base64); return base64Hash; } catch (error) { console.error('获取失败:', error); return null; } }
内容的提问来源于stack exchange,提问作者Nemesis
相关产品推荐
相关产品推荐

