flutter 中 ssl 双向证书校验

ops/2024/9/24 10:34:25/

SSL 证书:

        在处理 https 请求的时候,通常可以使用 中间人攻击的方式   获取 https 请求以及响应参数。应为通常我们是 SSL 单向认证,服务器并没有验证我们的客户端的证书。为了防止这种中间人攻击的情况。我么可以通过 ssl 双向认证的方式。即服务端要验证客户端请求的证书合法性。

SSL 单向认证

      

        无需客户端拥有证书,只需服务端拥有证书  客户端也需要验证服务端证书;SSL 单向认证相对于 SSL 双向认证的认证过程,无需在服务端验证客户端证书、以及协商加密方案,服务端发送给客户端也是未加密的密码方案(并不影响 SSL 认证过程的安全性)

SSL 双向认证


        需要客户端 和 服务端双方都拥有证书。并且验证是双向的,即服务端 需要验证 客户端证书,客户端也需要验证服务端证书。当然服务器 也需要开启认证 客户端证书的操作并且证书如果放在客户端 如何保证证书的安全性也是一个很好的话题,否则意义不会太大

生成 客户端 ssl 证书

因为 Flutter 不支持 .p12 格式的,这里需要生成  .pem 格式的证书

1. 准备环境

        首先,确保你已经安装了 OpenSSL 工具。OpenSSL 是处理证书的常用工具,可以在大多数操作系统上安装。

2. 生成客户端证书密钥对

客户端证书需要一个公私钥对。首先,你需要生成一个私钥文件 过程需要输入秘钥

openssl genpkey -algorithm RSA -out client_key.pem -aes256# client_key.pem 是你生成的私钥文件。-aes256 参数表示私钥文件将使用 AES-256 加密。
3. 创建客户端证书签名请求 (CSR)

使用生成的私钥来创建一个客户端证书签名请求(CSR):

openssl req -new -key client_key.pem -out client_csr.pem#在执行这个命令时,你需要提供一些信息,例如国家、州、城市、组织名等。这些信息将被包含在 CSR 中。

4. 签署客户端证书
选项 1: 使用自签名证书

如果你使用自签名证书(即没有使用正式的 CA),可以通过 OpenSSL 自行签署客户端证书:

首先,生成一个自签名证书:

openssl x509 -req -in client_csr.pem -signkey client_key.pem -out client_cert.pem##### 这个证书是自签名的,仅在你的环境中有效,不适用于生产环境。

选项 2: 通过服务端证书签署

如果你希望使用服务端证书签署客户端证书通常服务端证书由一个 CA 签发 即你服务器SSL 证书提供商,你需要以下步骤:

  • 将客户端 CSR 提交给 CA: 将 client_csr.pem 提交给负责签署客户端证书的 CA。CA 将使用其私钥来签署客户端 CSR,生成一个客户端证书。

  • CA 签署客户端证书: CA 使用它的私钥对 CSR 进行签名,生成客户端证书。假设 CA 使用 ca-cert.pemca-key.pem 文件签署证书,你可以使用如下命令

openssl x509 -req -in client_csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client_cert.pem -days 365##这里,ca-cert.pem 是 CA 的证书,ca-key.pem 是 CA 的私钥,-CAcreateserial 参数表示如果没有 CA 序列号文件,则创建一个。

Flutter 中使用客户端 ssl 证书

如果是全局配置:main.dart 中

HttpOverrides.global = await AppHttpCert().createHttpProxy();

核心代理就这里:

///证书校验,Flutter  暂不支持 .p12
@override
HttpClient createHttpClient(SecurityContext? context) {// TODO: implement createHttpClient//return super.createHttpClient(context);final securityContext = SecurityContext(withTrustedRoots: false)..useCertificateChainBytes(clientCert.buffer.asUint8List(),password: 'https://pan.baidu.com/j/1WtKrUBVVZS')..usePrivateKeyBytes(clientKey.buffer.asUint8List(),password: 'https://pan.baidu.com/j/1WtKrUBVVZS');HttpClient client = super.createHttpClient(securityContext);client.badCertificateCallback = _badCertificateCallback;return client;
}///用于处理不受信任的证书。通过这个回调函数,你可以根据自定义逻辑决定是否接受一个不受信任的证书
bool _badCertificateCallback(X509Certificate cert, String host, int port) {// 打印证书信息print('Received certificate from host: $host, port: $port');print('Certificate subject: ${cert.subject}');print('Certificate issuer: ${cert.issuer}');print('Certificate valid from: ${cert.startValidity}');print('Certificate valid until: ${cert.endValidity}');// return true;if (kReleaseMode) {return false;} else {return true;}
}

完整代码:因为我这里在开发阶段使用代理抓包,不需要的可以直接略过

import 'dart:io';import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';class AppHttpCert {String? host;String? port;MethodChannel _channel = MethodChannel('com.lm.http.proxy');Future<String?> _getProxyHost() async {return await _channel.invokeMethod('getProxyHost');}Future<String?> _getProxyPort() async {return await _channel.invokeMethod('getProxyPort');}Future<AppHttpOverrides> createHttpProxy() async {final clientCert = await rootBundle.load('assets/client_cert.pem');final clientKey = await rootBundle.load('assets/client_key.pem');final String? host = await _getProxyHost();final String? port = await _getProxyPort();return AppHttpOverrides(clientCert: clientCert,clientKey: clientKey,host: host,port: port,);}
}class AppHttpOverrides extends HttpOverrides {final String? host;final String? port;final ByteData clientCert;final ByteData clientKey;AppHttpOverrides({required this.clientCert,required this.clientKey,required this.host,required this.port});///证书校验,暂不支持 .p12@overrideHttpClient createHttpClient(SecurityContext? context) {// TODO: implement createHttpClient//return super.createHttpClient(context);final securityContext = SecurityContext(withTrustedRoots: false)..useCertificateChainBytes(clientCert.buffer.asUint8List(),password: 'https://pan.baidu.com/j/1WtKrUBVVZS')..usePrivateKeyBytes(clientKey.buffer.asUint8List(),password: 'https://pan.baidu.com/j/1WtKrUBVVZS');// // 设置受信任的服务器证书// securityContext//     .setTrustedCertificatesBytes(serverCert.buffer.asUint8List());HttpClient client = super.createHttpClient(securityContext);client.badCertificateCallback = _badCertificateCallback;return client;}///用于处理不受信任的证书。通过这个回调函数,你可以根据自定义逻辑决定是否接受一个不受信任的证书bool _badCertificateCallback(X509Certificate cert, String host, int port) {// 打印证书信息print('Received certificate from host: $host, port: $port');print('Certificate subject: ${cert.subject}');print('Certificate issuer: ${cert.issuer}');print('Certificate valid from: ${cert.startValidity}');print('Certificate valid until: ${cert.endValidity}');// return true;if (kReleaseMode) {return false;} else {return true;}}///是否使用代理@overrideString findProxyFromEnvironment(Uri url, Map<String, String>? environment) {// TODO: implement findProxyFromEnvironmentif (host == null) {return super.findProxyFromEnvironment(url, environment);}environment ??= {};if (port != null) {environment['http_proxy'] = '$host:$port';environment['https_proxy'] = '$host:$port';} else {environment['http_proxy'] = '$host:8888';environment['https_proxy'] = '$host:8888';}return super.findProxyFromEnvironment(url, environment);}
}


http://www.ppmy.cn/ops/99038.html

相关文章

设计模式-结构性模式-桥接模式

1.桥接模式定义 桥接模式就是将抽象部分与他的实现部分分离&#xff0c;使他们都可以独立的变化&#xff1b; 桥接模式用一种巧妙地方式处理多层继承存在的问题&#xff0c;用抽象关联来取代传统的多层继承&#xff0c;将类之间的静态继承关系转变为动态的组合关系&#xff0c;…

RocketMQ 如何保证消息不丢失?

RocketMQ 的消息想要确保不丢失&#xff0c;需要生产者、消费者以及 Broker 的共同努力&#xff0c;缺一不可。 生产者&#xff08;Producer&#xff09; 1、发送方式&#xff1a;选择同步发送 同步发送&#xff1a;发送消息后&#xff0c;需要阻塞等待 Broker 确认收到消息…

Docker-制作镜像

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、操作系统的组成&#xff08;一&#xff09;bootfs&#xff08;二&#xff09;rootfs&#xff08;三&#xff09;Liunx操作系统的启动过程&#xff08;1&…

Zustand:让React状态管理更简单、更高效

Zustand 这个单词在德语里是状态的意思&#xff08;发音&#xff1a;促stand&#xff09; 1. 下载zustand npm i zustand 或者 yarn add zustand2.创建一个store import { create } from zustandconst useBearStore create((set) > ({bears: 0,increasePopulation: …

wpf UniformGrid 动态加载数据

在WPF中&#xff0c;如果你想要在UniformGrid内部为每个Model对象放置一个Panel&#xff08;比如StackPanel或Grid&#xff09;&#xff0c;并且这些Panel是通过数据绑定动态生成的&#xff0c;你需要结合使用ItemsControl、DataTemplate以及UniformGrid。但是&#xff0c;由于…

hyperf 协程作用和相关的方法

什么是协程 协程是一种轻量级的线程&#xff0c;由用户代码来调度和管理&#xff0c;而不是由操作系统内核来进行调度&#xff0c;也就是在用户态进行 判断当前是否处于协程环境内 在一些情况下我们希望判断一些当前是否运行于协程环境内&#xff0c; 对于一些兼容协程环境与…

Meta被曝取消了自己的“Vision Pro计划”内部代号La Jolla

据媒体周五报道&#xff0c;美国科技巨头Meta已经取消了原定于2027年推出高端混合现实头显的计划&#xff0c;Meta原本计划利用这款设备与苹果的Vision Pro一较高下。据Meta的两名员工声称&#xff0c;在本周的一次产品审查会议后&#xff0c;公司管理层突然要求Reality Labs部…

设计模式(3)结构型模式

结构型模式 结构型模式1. Adapter&#xff08;适配器模式&#xff09;2. Bridge&#xff08;桥接模式&#xff09;3.Composite&#xff08;组合模式&#xff09;4.Decorator&#xff08;装饰模式&#xff09;5.Facade&#xff08;外观模式&#xff09;6.Flyweight&#xff08;享…