flutter 中 ssl 双向证书校验

server/2024/10/19 6:17:24/

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/server/106974.html

相关文章

JAVA设计模式之【单例模式】

1 类图 2 饿汉式单例 例如&#xff1a;静态块、静态成员 2.1 概念 类加载的时候就立即初始化&#xff0c;并且创建单例对象 2.2 优点 没有加任何的锁、执行效率比较高 2.3 缺点 类加载的时候就初始化&#xff0c;不管用与不用都占着空间&#xff0c;浪费了内存。 3 懒汉…

【三种循环结构】for循环、while循环和do-while循环

在C/C语言中&#xff0c;循环结构是编程中常用的控制结构之一&#xff0c;用于重复执行一段代码直到满足特定条件。C/C语言主要提供了三种循环结构&#xff1a;for循环、while循环和do-while循环。 1.for循环&#xff1a; for循环是最常用的循环结构之一&#xff0c;它的一般形…

项目中Redis常见的一些问题(缓存穿透,缓存雪崩,内存耗尽等)

缓存穿透 缓存穿透是指查询一个不存在的数据&#xff0c;导致每次请求查询这个不存在的数据 都会转发到数据库&#xff0c;可能导致数据库崩溃 通常都会用布隆过滤器来解决它 布隆过滤器 布隆过滤器主要是用于检索一个元素是否在一个集合中。 它的底层主要是先去初始化一个…

Rust: Reading and Writing Files

Reading and Writing Files We need some way to actually get data from the filesystem so we can process it, and write it back when we’re done 我们需要某种方法从文件系统中实际获取数据&#xff0c;以便处理它&#xff0c;并在完成后将其写回来 use std::fs;std::f…

80、k8s概念及组件介绍

一、k8s kubernetes:k8s----希腊语&#xff0c;舵手&#xff0c;飞行员 1.1、k8s作用&#xff1a; ​ 用于自动部署&#xff0c;扩展&#xff0c;管理容器化部署的应用程序。开源&#xff08;半开源。&#xff09; ​ k8s的底层语言是由go语言。 ​ k8s理解成负责自动化运…

PostgreSQL 内核资源管理

在高负载环境下&#xff0c;尤其是在同一系统上运行多个 PostgreSQL 实例或在大型安装环境中&#xff0c;PostgreSQL 有时可能会耗尽操作系统的资源限制。本文介绍了 PostgreSQL 使用的关键内核资源&#xff0c;以及如何解决与这些资源消耗相关的问题。 19.4.1. 共享内存和信号…

小程序常用的模板语法

WXML 文件 <!-- page.wxml --> <view><!-- 数据绑定: 将数据 message 绑定到视图中 --><view>{{message}}</view><!-- 条件渲染: 根据 isLoggedIn 的值显示不同的内容 --><view wx:if"{{isLoggedIn}}">Welcome back!<…

内网横向移动常用方法

横向移动 #横向移动含义 横向移动是以已经被攻陷的系统为跳板&#xff0c;通过收集跳板机的信息&#xff08;文档&#xff0c;存储的凭证&#xff0c;ipc连接记录等等信息&#xff09;来访问其他域内主机。#常见横向手段 1&#xff0c;通过相同的用户名密码批量ipc连接其他域内…