背景

在三方接口对接中,偶尔会遇到需要传递证书的情况,这种方式其实是在SSL握手过程中会同时验证客户端和服务器的身份,这就是我们常说的 双向认证

双向认证需要服务器和客户端提供身份认证,只能是服务器允许的客户方能访问,安全性相对于要高一些。

下面老黄用几个小例子来演示一下双向认证的简单应用。

准备工作

由于离不开证书,所以我们需要提前生成好几个证书,这里用 OpenSSL 来生成一个自签名的。

2 个根证书,1 个服务端证书,2个不是同一个根证书下面的客户端证书

# 根证书
openssl genrsa -out ca.key 4096
openssl req -new -key ca.key -out ca.csr -days 365
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt -days 365 # 服务端证书
openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr -days 365
openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365
openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 # 客户端证书
openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr -days 365
openssl x509 -req -in client.csr -out client.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365
openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12

最后会有下面几个文件要在后面的演示中用到: ca.crtserver.p12server.crtserver.keyclient.p12client2.p12

下面先来看看 ASP.NET Core 直接对外的情况,也就是不依赖 nginx 或 IIS 的情况。

ASP.NET Core

基于 minimal api 来演示,主要是在 ConfigureKestrel 做处理。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(x =>
{
x.Listen(IPAddress.Any, 443, listenOptions =>
{
var serverCertificate = new X509Certificate2("server.p12", "abc123");
var httpsConnectionAdapterOptions = new HttpsConnectionAdapterOptions()
{
// must provide a valid certificate for authentication
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
SslProtocols = System.Security.Authentication.SslProtocols.Tls12, ClientCertificateValidation = (cer, chain, error) =>
{
// valid the client certificate by you way.
return CusSSLLib.CaHelper.Valid(cer, chain, error);
},
ServerCertificate = serverCertificate
};
listenOptions.UseHttps(httpsConnectionAdapterOptions);
});
});

这里最核心的是 HttpsConnectionAdapterOptions

ServerCertificate 设置成我们上面生成的服务端证书。

ClientCertificateMode 设置成 RequireCertificate,表示客户端在调用的时候必须要传递证书。

ClientCertificateValidation 就是验证客户端证书的逻辑,这里可以自定义,示例里面的验证逻辑主要针对不被信任的根证书做了验证。

首先是从资源文件读取了根证书,然后再去判断客户端证书是否匹配。

internal static string CA_DATA = System.Text.Encoding.UTF8.GetString(CAResource.ca).Replace("-----BEGIN CERTIFICATE-----", "")
.Replace("-----END CERTIFICATE-----", "")
.Replace("\r", "")
.Replace("\n", ""); public static bool Valid(X509Certificate2 certificate, X509Chain chain, SslPolicyErrors policy)
{
// the root certificate
var validRootCertificates = new[]
{
Convert.FromBase64String(CA_DATA),
}; foreach (var element in chain.ChainElements)
{
foreach (var status in element.ChainElementStatus)
{
// untrusted root certificate
if (status.Status == X509ChainStatusFlags.UntrustedRoot)
{
if (validRootCertificates.Any(x => x.SequenceEqual(element.Certificate.RawData)))
{
continue;
}
} return false;
}
} return true;
}

到这里的话,服务端已经可以了。

这个时候从浏览器访问,大概会看到这个提示。

下面写个控制台用 HttpClient 来访问看看。

void DoOk()
{
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls | SslProtocols.None | SslProtocols.Tls11;
try
{
// add client certificate
var crt = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "client.p12"), "123456");
handler.ClientCertificates.Add(crt);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
} handler.ServerCertificateCustomValidationCallback = (message, cer, chain, errors) =>
{
// valid server certificate
return CusSSLLib.CaHelper.Valid(cer, chain, errors);
}; var client = new HttpClient(handler);
var url = "https://localhost/WeatherForecast";
var response = client.GetAsync(url).Result;
Console.WriteLine(response.IsSuccessStatusCode);
var result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);
}

这里要注意,由于服务端用的证书也是自己签名的,所以这里的验证也要放开,想省事的话,可以直接 return true;,不过并不建议这样操作。

下面是运行的结果,是可以正常访问并返回结果的。

我们再换一张不是同一个根证书的客户端证书。

不出意外的不能正常访问。

不过上面这种情况在实际应用的时候会偏少一点,大部分还是会挂在反向代理或云负载均衡上面的。

下面先来看看 nginx 的吧。

nginx 反向代理

webapi 这一块,创建一个项目,有一个可以访问的接口即可,不用添加其他东西,因为证书这一块的内容都是在 nginx 那一层做了,webapi做原来该做的事情即可。

下面是 nginx 的配置文件

server {
listen 443 ssl;
server_name localhost; # server certificate
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key; # root certificate
ssl_client_certificate /etc/nginx/ssl/ca.crt;
# open client certificate verify
ssl_verify_client on;
ssl_session_timeout 5m; location / {
proxy_pass http://webapi;
index index.html;
}
}

重点关注 ssl_verify_clientssl_client_certificate

一个是配置开启客户端证书的认证,一个是验证的客户端证书的关键。

这里的 ssl_client_certificate 用了根证书,为的是可以验证多个客户端证书,当然这里也可以用客户端证书。

把 webapi 和 nginx 都运行起来。

这个时候访问,就会提示, No required SSL certificate was sent。

用上面的控制台程序,再访问看看。

正确的证书,可以正常返回,错误的证书会返回 400 The SSL certificate error。

基于反向代理的话,操作起来就简单了一点。

如果是云负载均衡,只需要按他们的要求上传对应的证书即可。

讲了 nginx,不讲讲 IIS,好像有点说不过去。

那就再看看 IIS 的配置吧。

IIS 部署

在 Windows 服务器安装好 IIS 和托管捆绑包后,要先把我们的根证书安装到可信的根证书里面。

然后进行部署,绑定好服务端证书后,确认可以正常访问。

然后进行双向认证的配置。

在对应站点上面的 SSL 配置,把 要求 SSL必需 两个勾上即可。

后面再访问的时候,就会提示选择证书

选择正确的证书后就可以正常访问了。

然后我们再用前面的控制台程序访问,结果如下。

可以发现和前面的结果是一样的,不同的是错误返回的内容不一样。

上面提到的都是一些自建的场景,其实对云负载均衡的结合使用也是 OK 的。

总结

双向认证,在一些安全要求比较高的场景下,用途还是比较大的,相比较单向认证的话会麻烦一些。

本文示例代码:https://github.com/catcherwong-archive/2023/tree/main/MutualTLSAuthentication

参考资料

聊一聊HTTPS双向认证的简单应用的更多相关文章

  1. HTTPS 双向认证构建移动设备安全体系

    HTTPS 双向认证构建移动设备安全体系 对于一些高安全性要求的企业内项目,我们有时希望能够对客户端进行验证.这个时候我们可以使用Https的双向认证机制来实现这个功能. 单向认证:保证server是 ...

  2. Tomcat 配置 HTTPS双向认证

    Tomcat 配置 HTTPS 双向认证指引说明: � 本文档仅提供 Linux 操作系统下的指引 � 在阅读本指引前请您在 Linux 部署 JDK 和 Tomcatserver为了 Tomcat ...

  3. httpd设置HTTPS双向认证

    去年用tomcat.jboss配置过HTTPS双向认证,那时候主要用的是JDK自带的keytool工具.这次是用httpd + openssl,区别比较大 在网上搜索了很多文章,发现全面介绍的不多,或 ...

  4. Https双向认证Android客户端配置

    Https .cer证书转换为BKS证书 公式https://blog.csdn.net/zww986736788/article/details/81708967 keytool -importce ...

  5. Android Https双向认证 + GRPC

    keywords:android https 双向认证android GRPC https 双向认证 ManagedChannel channel = OkHttpChannelBuilder.for ...

  6. 双向认证 HTTPS双向认证

    [微信支付]微信小程序支付开发者文档 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3 HTTPS双向认证使用说明 ...

  7. https双向认证訪问管理后台,採用USBKEY进行系统訪问的身份鉴别,KEY的证书长度大于128位,使用USBKEY登录

    近期项目需求,须要实现用USBKEY识别用户登录,採用https双向认证訪问管理后台管理界面,期间碰到过一些小问题,写出来给大家參考下. 1:前期准备工作 USBKEY 硬件:我买的是飞天诚信 epa ...

  8. nodejs之https双向认证

    说在前面 之前我们总结了https的相关知识,如果不懂可以看我另一篇文章:白话理解https 有关证书生成可以参考:自签证书生成 正题 今天使用nodejs来实现https双向认证 话不多说,直接进入 ...

  9. SpringBoot服务间使用自签名证书实现https双向认证

    SpringBoot服务间使用自签名证书实现https双向认证 以服务server-one和server-two之间使用RestTemplate以https调用为例 一.生成密钥 需要生成server ...

  10. Keytool配置 Tomcat的HTTPS双向认证

    Keytool配置 Tomcat的HTTPS双向认证 证书生成 keytool 简介 Keytool是一个Java数据证书的管理工具, Keytool将密钥(key)和证书(certificates) ...

随机推荐

  1. mysql 基础明细

    1.mysql 没有 TOP,用limit实现 2.mysql having 聚合之后,对组操作,和GROUP By搭配 mysql where  聚合之前,对表和视图操作 3.where 子句的作用 ...

  2. gdb不能使用mac

    先说问题:1.gdb不能使用,重新用homebrew install 了gdb 2.brew装的gdb可以用了,但是等start调试的时候报这些错误:        dyld: Library not ...

  3. 对于函数极限存在的充要条件“lim f(x)=A互推f(x)=A+a(x) lim a(x)=0”补充解释

    毫无疑问,这个定义适用于任何函数极限,诺f(x)有去间断点的时候,a(x)也为可去间断点函数. 例:

  4. Redux 的困扰与如何技术选型

    文章的名字我想了很久,备选项有"我再不推荐 Redux","Redux 为什么令我头疼","Redux 进化启示录"等等.通过这一系列名字我 ...

  5. 8 STL-stack

    ​ 重新系统学习c++语言,并将学习过程中的知识在这里抄录.总结.沉淀.同时希望对刷到的朋友有所帮助,一起加油哦!  生命就像一朵花,要拼尽全力绽放!死磕自个儿,身心愉悦! 写在前面,本篇章主要介绍S ...

  6. 【Java SE】Day07 API、Scanner类、Random类、ArrayList类

    一.API 1.概述: API(Application Programming Interface),应用程序编程接口 Java API:程序员的字典,是类的说明文档 2.使用步骤 帮助文档:JDK_ ...

  7. 为什么Git远程仓库中要配置公钥?

    最近在使用阿里云效平台代码管理,首次使用新建仓库,使用SSH时需要配置公钥.之前也在GitHub.Gitee上配置过,每次都能正常使用,也没有思考过为什么要配置公钥.这次记录一下其中的原理. 本地和远 ...

  8. 单例模式及pickle序列化模块

    内容回顾 目录 内容回顾 单列模式实现的多种方式 pickle序列化模块 根据类名或对象名如何获取到类的字符串名 选课系统需求分析 功能提炼 选课系统架构设计 选课系统目录搭建 单列模式实现的多种方式 ...

  9. uni-app生命周期和路由跳转

    生命周期分为:应用生命周期和页面生命周期 具体内容可参考:uni-app官网Api 应用生命周期(仅可在App.vue中监听) (1)onLaunch:当uni-app 初始化完成时触发(全局之触发一 ...

  10. 3、swagger-ui导出word接口文档

    参考 1.修改swagger2word项目的 application.yml 文件的 swagger.url 为Swagger Json资源的url地址(网址+端口): 例:swagger.url: ...