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

如何在Flutter中不使用插件实现SSL公钥固定?

Flutter 公钥固定实现方案(无插件)

一、Dart 原生方法实现(无需依赖插件)

Dart 自带的 dart:io 库提供了 SecurityContext 和 HttpClient,可直接实现公钥固定——核心逻辑是将服务器公钥的指纹加入可信列表,发起请求时验证服务器证书的公钥是否匹配。

步骤说明

  1. 提取服务器公钥的 SHA-256 指纹
    通过命令行工具(如 openssl)获取:

    openssl s_client -connect your-domain.com:443 < /dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
    

    输出的字符串即为公钥的 SHA-256 指纹(例如 abc123...xyz)。

  2. 配置自定义 SecurityContext 并发起请求
    创建带可信指纹验证的 HttpClient,代码示例:

    import 'dart:io';
    import 'dart:convert';
    import 'package:crypto/crypto.dart';
    
    Future<void> makeSecureRequest() async {
      // 替换为你的服务器公钥 SHA-256 指纹
      const trustedPublicKeyFingerprint = "your-public-key-fingerprint";
    
      final securityContext = SecurityContext();
      // 加载系统默认根证书(可选,避免仅信任单一公钥导致的兼容性问题)
      securityContext.setTrustedCertificatesBytes(await rootCertificates);
    
      final httpClient = HttpClient(context: securityContext);
    
      // 手动验证公钥指纹
      httpClient.badCertificateCallback = (X509Certificate cert, String host, int port) {
        // 计算当前证书公钥的 SHA-256 指纹
        final publicKeyBytes = base64Decode(cert.publicKey);
        final sha256Digest = sha256.convert(publicKeyBytes).bytes;
        final currentFingerprint = base64Encode(sha256Digest);
    
        // 对比指纹是否匹配,返回true表示信任该证书
        return currentFingerprint == trustedPublicKeyFingerprint;
      };
    
      try {
        final request = await httpClient.getUrl(Uri.parse("https://your-domain.com/api"));
        final response = await request.close();
        final responseBody = await utf8.decodeStream(response);
        print(responseBody);
      } catch (e) {
        print("请求失败: $e");
      } finally {
        httpClient.close();
      }
    }
    

二、通过 Method Channel 调用自定义原生代码实现

如果需要全局拦截网络请求或更底层的控制,可通过 Method Channel 让 Flutter 调用 Android/iOS 的原生网络逻辑,在原生层实现公钥固定。

Android 端实现(基于 OkHttp)

  1. 创建公钥验证拦截器

    class PublicKeyPinningInterceptor : Interceptor {
        // 替换为你的服务器公钥 SHA-256 指纹
        private val trustedFingerprint = "your-public-key-fingerprint"
    
        override fun intercept(chain: Interceptor.Chain): Response {
            val response = chain.proceed(chain.request())
            val certificate = response.handshake?.peerCertificates?.firstOrNull()
                ?: throw SSLException("无法获取服务器证书")
    
            // 提取公钥并计算 SHA-256 指纹
            val publicKeyBytes = certificate.publicKey.encoded
            val sha256Digest = MessageDigest.getInstance("SHA-256").digest(publicKeyBytes)
            val currentFingerprint = Base64.encodeToString(sha256Digest, Base64.NO_WRAP)
    
            if (currentFingerprint != trustedFingerprint) {
                throw SSLException("公钥指纹不匹配,请求被拦截")
            }
            return response
        }
    }
    
  2. 注册 Method Channel 处理 Flutter 请求

    class MainActivity : FlutterActivity() {
        private val CHANNEL = "com.example/secure_network"
    
        override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
            super.configureFlutterEngine(flutterEngine)
            MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
                if (call.method == "makeSecureRequest") {
                    val url = call.argument<String>("url") ?: run {
                        result.error("INVALID_URL", "URL 不能为空", null)
                        return@setMethodCallHandler
                    }
    
                    val okHttpClient = OkHttpClient.Builder()
                        .addInterceptor(PublicKeyPinningInterceptor())
                        .build()
    
                    val request = Request.Builder().url(url).build()
                    try {
                        val response = okHttpClient.newCall(request).execute()
                        val responseBody = response.body?.string() ?: ""
                        result.success(responseBody)
                    } catch (e: Exception) {
                        result.error("REQUEST_FAILED", e.message, null)
                    }
                } else {
                    result.notImplemented()
                }
            }
        }
    }
    

iOS 端实现(基于 NSURLSession)

  1. 创建带公钥验证的网络管理器

    class SecureNetworkManager: NSObject, URLSessionDelegate {
        // 替换为你的服务器公钥 SHA-256 指纹
        private let trustedFingerprint = "your-public-key-fingerprint"
    
        func makeRequest(url: String, completion: @escaping (String?, Error?) -> Void) {
            guard let url = URL(string: url) else {
                completion(nil, NSError(domain: "INVALID_URL", code: -1, userInfo: [NSLocalizedDescriptionKey: "URL 无效"]))
                return
            }
    
            let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
            let task = session.dataTask(with: url) { data, _, error in
                if let error = error {
                    completion(nil, error)
                    return
                }
                completion(data.flatMap { String(data: $0) } ?? "", nil)
            }
            task.resume()
        }
    
        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            guard let serverTrust = challenge.protectionSpace.serverTrust,
                  let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0),
                  let publicKey = SecCertificateCopyPublicKey(certificate),
                  let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? else {
                completionHandler(.cancelAuthenticationChallenge, nil)
                return
            }
    
            // 计算公钥 SHA-256 指纹
            let sha256Digest = SHA256.hash(data: publicKeyData)
            let currentFingerprint = Data(sha256Digest).base64EncodedString()
    
            if currentFingerprint == trustedFingerprint {
                completionHandler(.useCredential, URLCredential(trust: serverTrust))
            } else {
                completionHandler(.cancelAuthenticationChallenge, nil)
            }
        }
    }
    
  2. 注册 Method Channel 处理 Flutter 请求

    @UIApplicationMain
    @objc class AppDelegate: FlutterAppDelegate {
        private let CHANNEL = "com.example/secure_network"
    
        override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            let controller = window?.rootViewController as! FlutterViewController
            let methodChannel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger)
    
            methodChannel.setMethodCallHandler { call, result in
                if call.method == "makeSecureRequest" {
                    guard let url = call.arguments as? String else {
                        result.error("INVALID_URL", "URL 不能为空", nil)
                        return
                    }
    
                    let networkManager = SecureNetworkManager()
                    networkManager.makeRequest(url: url) { responseBody, error in
                        if let error = error {
                            result.error("REQUEST_FAILED", error.localizedDescription, nil)
                        } else {
                            result.success(responseBody)
                        }
                    }
                } else {
                    result.notImplemented()
                }
            }
    
            GeneratedPluginRegistrant.register(with: self)
            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
    }
    

Flutter 端调用 Method Channel

import 'package:flutter/services.dart';

class SecureNetworkService {
  static const MethodChannel _channel = MethodChannel('com.example/secure_network');

  static Future<String?> makeSecureRequest(String url) async {
    try {
      final String? response = await _channel.invokeMethod('makeSecureRequest', url);
      return response;
    } on PlatformException catch (e) {
      print("请求失败: ${e.message}");
      return null;
    }
  }
}

// 使用示例
void main() async {
  final response = await SecureNetworkService.makeSecureRequest("https://your-domain.com/api");
  print(response);
}

内容的提问来源于stack exchange,提问作者Nachiket Gohil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:16:00