惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 三国梦回

spring boot 项目中oracle datasource设置schema spring cloud项目中,在bootstrap.yml中指定了active的profile,结果不生效 线上服务重启后,从nacos取不到配置了,怎么回事 nginx location没学好,把自己坑了一把 技术问题记录20260125 最近遇到的两个技术问题记录 linux服务器文件上传失败 线上遇到的redis和数据库数据未同步问题、redisson内部实现问题 复杂业务系统线上问题排查过程 nacos中配了一个数字,springboot取回来怎么变了 一个java空指针异常的解决过程 简单记录下最近2个月完成的线上系统迁移工作 centos停服,迁移centos7.3系统到新搭建的openEuler 端口telnet不通排查过程 linux中如何判断一个rpm是手动安装还是通过yum安装的 对接服务升级后仅支持tls1.2,jdk1.7默认使用tls1.0,导致调用失败 网络抓包文件太大,如何切分 分页查询不加排序有问题,加了排序怎么还有问题 利用mybatis拦截器记录sql,辅助我们建立索引(二) 利用mybatis拦截器记录sql,辅助我们建立索引(一) sql server版本太老,java客户端连接失败问题定位
https证书中的subject alternative name字段作用及如何生成含该字段的证书
三国梦回 · 2025-03-15 · via 博客园 - 三国梦回

背景

最近,某个运维同事找到我,说测试环境的某个域名(他也在负责维护),假设域名为test.baidu.com,以前呢,证书都是用的生产的证书,最近不让用了。问为啥呢,说不安全,现在在整改了,因为证书和私钥肯定要一起部署在服务器上,既然测试环境的服务器部署了生产上的证书,那也就是说私钥也部署在测试服务器上,那自然是不安全。咱们小公司嘛,就是这个样子的,管理不是很规范。

那现在也就是说,要给这个test.baidu.com这个域名,弄一个测试环境的证书。然后,他是运维嘛,办公电脑上装软件的限制比较多,就问我,能不能帮忙生成个test.baidu.com的证书给他,就那种自签名的就行了。

问题

我一想,我们之前有个环境测试https,搞过一个自签名证书,直接发给他就行了,然后他就拿去部署到nginx了。

ssl     on;
ssl_certificate              comkey.crt;
ssl_certificate_key          comkey.key;
ssl_session_timeout          5m;
ssl_protocols                 TLSv1.2;

image-20250315122705781

结果,过了两天,他说部署上去后,有个客户端调用的时候报错,说证书里有个字段的值没对。

image-20250315122836268

我给他的证书里,如上图所示,subject alternative name的内容为:DNS NAME = ma-dev.example.com.cn,而他拿去部署的网站的域名为test.example.com.cn,导致客户端校验出错了,说能不能重新生成下证书给他。

我本来吧,不想弄的,因为我也忘了之前这个证书,怎么会包含这个字段,我也不知道这个字段啥意思。

到后面,还是接了这个事。

尝试

网上找了个教程:https://cloud.tencent.com/document/product/1552/111585

image-20250315124359901

可惜,生成出来的证书,根本就没有那个certifacate subject alternative name字段。

他这个生成证书,其实分了三个核心步骤。证书呢,里面一般是包含了公钥,这个是公开的,浏览器拿到证书后,会检查证书,有一堆规则,比如:验证证书是由合法的root CA签发的,没过期,没吊销等,如果都没问题,就认为证书有效,就会取出证书中包含的各个信息,如公钥信息。

这个公钥和服务端私密保存的私钥(也就是key文件,一般会配置到nginx里),是一一对应的,一般私钥发生了泄露,这个证书就算是废了。

所以,回到我们的证书生成环节,也是我们先生成一个私密的key,如上面的openssl genrsa -out server.key 2048这步。

然后呢,用这个key,去生成一个证书签名请求文件(CSR)。

CSR(Cerificate Signing Request),即证书请求文件,是证书申请者在申请数字证书时,生成私钥的同时生成证书请求文件。证书申请者把CSR文件提交给证书颁发机构后,证书颁发机构使用其根证书私钥签名就生成了证书公钥文件。

这个CSR里,就要包含我们希望放进证书的各种内容,比如我们本文提到的certifacate subject alternative name这些字段等。CSR准备好了后,提交给各个CA机构,然后CA机构理论上来说,会核查你、你的网站等各种身份信息,核查无误后,就会用它这家CA的私钥,对你的CSR进行签名,然后就生成了一个它这家CA认可的证书了。

后续,你就拿着这个证书,传输给用户的浏览器,用户浏览器中,有这家CA的公钥,自然就能发现你这个证书确实是这家CA的私钥签发的,就认可你的证书,后续就能正常通信。

回到我们的问题,首先,我们就必须要保证CSR文件中,包含我们的certifacate subject alternative name字段才行。

生成csr

由于这会在家里,连不上公司vpn了(正好到期了),也没法演示之前的各种错误了。所以就直接记录一下各种正确的方法。

方法1: addext选项

这个选项是openssl的1.1.1版本才有的。查看版本:openssl version。一般来说,linux服务器的openssl版本升级还是非常慎重的,如果发现版本不支持,那么,有个好办法,直接在windows上安装高版本的openssl来生成csr也是一样的: http://slproweb.com/products/Win32OpenSSL.html 这里可以下载,如(Win64 OpenSSL v3.4.1)。

这个选项比较方便,选项的注释如下:

G:\openssl-test>openssl req -h
 -addext val   Additional cert extension key=value pair (may be given more than once)
openssl genrsa -out server.key 2048

保留原有的交互式方式,同时又能指定subjectAltName这些扩展字段。
openssl req -new -out server.csr -key server.key -addext "subjectAltName = DNS:test.baidu.com.cn"

生成的csr文件,我们可以查看:

openssl req -in server.csr -noout -text

image-20250315143136127

网上也可以解码查看一个csr文件的内容:

https://www.sslshopper.com/csr-decoder.html

方法2: 配置文件

这个方法,我试了下,我的老版本OpenSSL 1.0.2k-fips也是支持的。

本来参考了:https://support.huawei.com/enterprise/zh/doc/EDOC1100296985?section=j025

结果发现行不通。

后面自己调整成全从配置文件中读了:

csr.conf:

[ req ]
default_bits        = 2048
default_md          = sha256
prompt              = no
distinguished_name  = req_distinguished_name
req_extensions      = SAN

[ req_distinguished_name ]
C  = CN
ST = Sichuan
L  = Chengdu
O  = BAIDU
OU = IT
CN = test.baidu.com

[ SAN ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = test.baidu.com
IP.1  = 192.168.1.1

命令如下:

openssl req -new -key server.key  -out server1.csr   -config csr.conf 

目前这个调整后的配置,和我在下面这个教程中的差不多:

https://mp.weixin.qq.com/s/RYbdt2NY-kE3YE2THMnx9A

不过这个教程里的配置还要多一点点,且是通过指定环境变量OPENSSL_CONF的方式来指定配置文件:

[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
C = PH
ST = Manila
L = Quezon
O = HCL
CN = test.baidu.com

[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = test.baidu.com
这里明确说了,会覆盖 OPENSSL_CONF 环境变量中的配置。
-config filename
this allows an alternative configuration file to be specified, this overrides the compile time filename or any specified in the OPENSSL_CONF environment variable.

生成自签名证书

如果大家是生成csr,提交给CA去生成证书,就不涉及这部分。这部分主要是生成自签名证书的时候遇到的问题。

我之前的时候,遇到的最大问题其实就是生成证书,明明看到csr里面都是有我们需要的选项的,但是,证书里就没了。其实就是因为,没指定一个叫extfile的选项,这里说了,不指定的话,不会添加任何扩展到证书里。

man x509

-extfile filename
file containing certificate extensions to use. If not specified then no extensions are added to the certificate.

这里也说下正确的方法。

方法1

参考了:

https://security.stackexchange.com/questions/150078/missing-x509-extensions-with-an-openssl-generated-certificate

和csr一样,生成证书时,也可以指定一个配置文件:

# ssl-extensions-x509.cnf

[v3_ca]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
subjectAltName = IP:127.0.0.1, IP:192.168.73.120, IP:192.168.73.121

可以看到,文件里包含了一个段,叫:v3_ca

在下面的命令中,我们同时指定配置文件,且指定要使用的段名:

[root@VM-0-6-centos ssltest]# openssl x509 -req -in server.csr -out server.crt -signkey server.key  -days 3650 -extensions v3_ca -extfile ./ssl-extensions-x509.cnf
Signature ok
subject=/C=CN/ST=Sichuan/L=Chengdu/O=BAIDU/OU=IT/CN=test.baidu.com
Getting Private key

证书就生成了,我们查看下证书:

 openssl x509 -in server.crt -text -noout

image-20250315152949330

我们在linux中看下对应的注释:

man x509

image-20250315152531789

方法2

上面的方法1,是显式指定,也可以不指定具体的段,仅指定配置文件:

https://mp.weixin.qq.com/s/ce-QN78xwRSTN3ZgWIvQAg

创建一个文件:cert.conf

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = test.baidu.com
openssl x509 -req    -in server.csr  -out server.crt   -signkey server.key  -days 365     -sha256 -extfile cert.conf

我们查看下,也是ok的:

image-20250315153126162

jdk1.8中如何校验SAN字段

我这里拿deepseek网站来举例吧,我们通过浏览器,能看到它的证书的SAN字段:

image-20250315153916048

可以看到,内容为:

Not Critical
DNS Name: *.deepseek.com
DNS Name: deepseek.com

接下来,看如下的代码:

	public static void main(String[] args) throws IOException {
        URL url = new URL("https://www.deepseek.com/");
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setConnectTimeout(10000);
        connection.setReadTimeout(3000);
        int responseCode = connection.getResponseCode();
        System.out.println(responseCode);
    }

我们平时可能习惯了忽略证书校验,如:

image-20250315154112857

今天我们就简单看看,不忽略的话,是在什么地方校验的。

在收到服务端返回的证书的消息时,会进入以下代码:

image-20250315154236244

然后使用默认的trustManager(sun.security.ssl.X509TrustManagerImpl)来校验服务端证书:

image-20250315154340723

接着进入如下方法,校验server和校验client的方法一样,只是最后一个参数不同,false就是校验服务端:

image-20250315154447484

然后如下代码,就要开始校验:我们请求时的域名为www.deepseek.com,那么证书到底是不是匹配呢,是不是www.deepseek.com的证书呢?

image-20250315154538275

image-20250315154742190

image-20250315154810692

我们一路深入到如下方法,自始至终,参数就两个,一个是请求的域名(其实是取了客户端握手消息时携带的SNI字段,正好就是我们请求的域名,所以,就算我们使用https加密,网管也是知道我们在上什么网站的),一个是返回的证书:

image-20250315155348158

image-20250315154856142

接下来,从证书中获取了SAN字段,这是个集合:

image-20250315155017440

最后就是遍历这个集合,看看是否匹配我们请求的域名,最终呢,www.deepseek.com就匹配上了SAN中的*.deepseek.com这条,所以,这个校验就算是ok了。

image-20250315155102742

我们也就大概知道了SAN字段的用法,以及它为什么重要了。

参考文档

https://mp.weixin.qq.com/s/ce-QN78xwRSTN3ZgWIvQAg

https://stackoverflow.com/questions/6194236/openssl-certificate-version-3-with-subject-alternative-name

https://stackoverflow.com/questions/58845805/error-creating-csr-in-openssl-using-subjectaltname-othername

https://security.stackexchange.com/questions/150078/missing-x509-extensions-with-an-openssl-generated-certificate