flutter 中 ssl 双向证书校验

news2024/9/22 5:24:35

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
  @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');

    // // 设置受信任的服务器证书
    // 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;
    }
  }

  ///是否使用代理
  @override
  String findProxyFromEnvironment(Uri url, Map<String, String>? environment) {
    // TODO: implement findProxyFromEnvironment
    if (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.coloradmin.cn/o/2068934.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode JAVA刷刷站(91)解码方法

一、题目概述 二、思路方向 这个问题是一个典型的动态规划问题&#xff0c;其中我们可以使用一个数组来存储到达每个位置时的解码方法的总数。 我们定义一个数组 dp&#xff0c;其中 dp[i] 表示字符串 s 的前 i 个字符&#xff08;从索引 0 到 i-1&#xff09;的解码方法总数。…

企业数字化转型管控平台探索 ---基于流程的企业经络管理框架DEM

篇幅有限&#xff0c;获取完整内容、更多感兴趣的内容 见下图

OpenCV几何图像变换(8)调整图像大小的函数resize()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 resize 函数调整图像 src 的大小&#xff0c;使其缩小或放大至指定的大小。需要注意的是&#xff0c;初始的 dst 类型或大小不被考虑。相反&…

解决Jasper Studio报表工具中预览正常显示,但部署到服务器上面无法正常显示的问题

目录 1.1、错误描述 1.2、解决方案 1.1、错误描述 之前有遇到过一个Jasper Studio报表开发相关的问题&#xff0c;这里记录一下&#xff0c;方便其他小伙伴可以快速解决问题。问题是这样的&#xff1a;当我在Jasper Studio报表工具里面设计好样式之后&#xff0c;预览报表发…

[论文阅读] mobile aloha实验部分

DP:[1] CHI C, FENG S, DU Y, et al. Diffusion Policy: Visuomotor Policy Learning via Action Diffusion[J]. 2023. Diffusion Policy: Visuomotor Policy Learning via Action Diffusion精读笔记&#xff08;一&#xff09;-CSDN博客 VINN:[1] PARI J, SHAFIULLAH N, ARU…

视频达人的秘密武器:全能型剪辑软件深度剖析

剪辑视频&#xff0c;作为视频创作过程中的关键环节&#xff0c;其重要性不言而喻。无论是专业影视制作团队&#xff0c;还是热衷于Vlog创作的个人&#xff0c;都离不开一款强大且易用的视频剪辑工具。今天&#xff0c;就让我们一起踏上一场探索之旅&#xff0c;对市面上的视频…

java基础 之 关键字static

文章目录 前言1、特征2、修饰变量3、修饰方法4、修饰代码块优缺点应用场景代码理解 前言 本文主要是从类与对象的方向来讲&#xff0c;所以在文章开始前&#xff0c;我们先理解一下类和对象 类是一个模板&#xff0c;对象是一个实例。 如【手机】是一个类&#xff08;一个模板…

MySQL系统性的学习--基础

学习资料是黑马的mysql课程 Mysql概述 相关概念 数据模型 关系型数据库 数据模型 SQL SQL通用语法 SQL分类 DDL 数据库操作 表操作 查询 创建 数据类型 修改/删除 DML 添加数据INSERT 修改数据UPDATE 删除数据DELETE DQL 基础查询 条件查询 聚合函数 分组查询 排序查询 分…

Otterctf 2018 内存取证 (复现)

题目地址: https://otterctf.com/challenges 1 - What the password? 描述:you got a sample of ricks PCs memory. can you get his user password? 首先查看一下镜像的信息 python2 vol.py -f /home/kali/Desktop/OtterCTF.vmem imageinfo 题目描述需要获取密码, 使用mi…

el-form中使用v-model和prop实现动态校验

如何在Vue的el-form中使用v-model和prop实现动态校验&#xff0c;包括多个变量控制校验、数组循环校验和字段级条件显示。通过实例演示了如何配合rules和自定义验证函数来确保表单的完整性和有效性。 公式&#xff1a; 动态校验项的v-model的绑定值 el-form的属性 :model的值 …

PCSE不同播种时间的对比

目录 简介对比图源代码简介 设置为2022年10月15日播种,然后每隔5天往后播种一次,然后探究播种时间对于作物各个长势的影响 对比图 源代码 import sys, os import matplotlib from matplotlib import style matplotlib.style.use("seaborn-whitegrid") import ma…

ST 表算法

ST 表 ST 表&#xff0c;主要思想是空间换时间&#xff0c;用于解决可重复贡献问题和 RMQ 问题。 可重复贡献问题 指某个运算 o p op op&#xff0c;有 x o p x x x\ op\ x\ \ x x op x x 。例如 m a x ( x , x ) x m i n ( x , x ) x g c d ( x , x ) x max(x,x)x\…

Linux基础环境开发工具gcc/g++ make/Makefile git

1.Linux编译器-gcc/g使用 1. 预处理&#xff08;进行宏替换) 预处理功能主要包括宏定义,文件包含,条件编译,去注释等。 预处理指令是以#号开头的代码行。 实例: gcc –E hello.c –o hello.i 选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。 选项“-o”是指目标…

UE基础 —— Components

目录 Component Instancing Instanced Static Mesh Component Instanced Static Mesh Differences of an ISM and a Static Mesh Component Hierarchical Instanced Static Mesh Instancing Systems Working with ISMs Prefabrication Custom Data Creating and Edit…

吴恩达机器学习课后作业-04神经网络

神经网络 对y进行独立热编码处理&#xff08;one-hot处理&#xff09;序列化权重参数前向传播代价函数反向传播神经网络优化可视化隐藏层 对y进行独立热编码处理&#xff08;one-hot处理&#xff09; def one_hot_encoder(raw_y):result[]for i in raw_y:#1-10y_tempnp.zeros(1…

网络编程之初识

目录 ​前言 发展史 网络互连 局域网(LAN) 广域网(WAN) 网络通信基础 IP地址 特殊IP地址 端口号 网络协议 协议的作用 五元组 协议分层 含义 OSI七层模型 TCP/IP五层协议 网络设备所在分层 封装和分用 发送方 接收方 前言 在这个科技发达的时代&#xff0…

初识C语言指针(3)

目录 1. 数组名的理解 2. 使⽤指针访问数组 3. ⼀维数组传参的本质 4. 冒泡排序 5. 二级指针 6. 指针数组 7. 指针数组模拟⼆维数组 结语 1. 数组名的理解 对于数组名想必大家并不陌生&#xff0c;数组名就是该数组首元素的地址&#xff0c;设想有一个arr 数组。我们…

文本是否换行显示

多行文本 1、white-space: pre-wrap; 保留连续的空白符。在遇到换行符或 <br> 元素时&#xff0c;或者根据填充行框盒子的需要换行。 2、word-break: break-word; 如果单词太长而无法在当前行容纳&#xff0c;浏览器可以尝试在单词内部进行断行&#xff0c;以避免内容…

轻松获得ADSL代理服务

ADSL 代理服务接入常见问答 在当今激烈的网络爬虫与反爬虫斗争中&#xff0c;各大网站和应用程序采取的风险管理手段愈加严格&#xff0c;其中最常见的一种措施是 IP 封禁。 为了有效应对 IP 封禁带来的挑战&#xff0c;设置代理服务成为一种非常有效的解决方案。配置完代理后…

数论之高斯消元

高斯消元&#xff1a; 前置知识&#xff1a; 高斯消元五步骤法 枚举每一列c 找到绝对值最大的一行 将该行换到最上面&#xff08;第r行&#xff09; 将该行的第c列数字变为1 把该行下面的第c列数字全部变为0 代回求解 #include <bits/stdc.h>using namespace std;…