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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 19:07:20