Service_CA

通过openssl创建CA证书

第一步是创建一个秘钥,这个便是CA证书的根本,之后所有的东西都来自这个秘钥:

# 通过rsa算法生成2048位长度的秘钥

openssl genrsa -out myCA.key 2048

第二步是通过秘钥加密机构信息形成公钥:

# 公钥包含了机构信息,在输入下面的指令之后会有一系列的信息输入,这些信息便是机构信息,公司名称地址什么的

# 这里还有一个过期信息,CA证书也会过期,openssl默认是一个月,我们直接搞到100年

openssl req -new -x509 -key myCA.key -out myCA.cer -days 36500

这一步需要输入的机构信息有点,分别说一下:

参数名称 参数值
Country Name 国家代码,比如中国就是CN
State or Province Name 省名称
Locality Name 城市名称
Organization Name 机构名称
Organizational Unit Name 机构单位名称
Common Name 重点参数:授权给什么,因为机构是根节点所以是授权给自己
Email Address 邮件地址

通过windows域控创建CA证书

这种便是我采用的方案,执行上比直接用openssl创建证书复杂多了,但是好处也非常多,一方面域控下级的所有计算机天然对域控服务就是信任状态,第二是域控制器能够通过组策略域内同步CA证书,本质上来讲相对于多了一个CA证书同步与分发的机制。我这边使用的Windows Server 2016,其他版本区别也不大。

第一步、在域控上启用证书服务

服务器角色 必选Active Directory 证书服务

角色服务选 【证书颁发机构】 【证书颁发机构Web注册】

第二步、安装完毕之后配置证书

非常简单,直接根据提示输入相关信息就行了,在过期时间那一步最好将时间拉长,建议使用的100年。

第三步、通过组策略进行分发

策略路径是:计算机策略/Windows设置/安全设置/公钥策略/受信任的根证书颁发机构计算机策略/Windows设置/安全设置/公钥策略/受信任的发布者证书。将上面创建的证书导出之后,在这里导入即可。

创建服务器证书

在得到CA证书之后,需要通过openssl工具对证书进行转换得到公钥(.crt文件)和密钥(.key文件),无论CA证书是怎么来的,到这里之后就没有任何区别了,服务器证书的制作流程相较CA证书要复杂一点点。

第一步通过openssl工具创建服务器的秘钥:

# 通过RSA算法生成长度2048位的秘钥

openssl genrsa -out server.key 2048

第二步这里是创建一个签名请求

需要将服务器信息写入到请求文件之中,然后通过CA机构证书对请求签名形成服务器证书公钥,这一步要复杂一些,很多网上的教程在这里都GG了主要原因没有把原理搞清楚。

首先https证书的公钥不同于自定义情况下的加密证书,这里需要安装浏览器标准进行配置,首先openssl默认的证书版本是V1,V1在支持https时部分浏览器依旧会认为不安全,所以需要使用V3版本;同时openssl即便是使用V3版本依旧没有附带V3的subjectAltName字段数据(这里是证书对应的IP地址或者域名,可以用通配符)。但是这些东西命令行没法指定所以需要配置文件,我这里准备了一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
tsa_policy2 = 1.2.3.4.5.6

tsa_policy3 = 1.2.3.4.5.7


[ ca ]

default_ca = CA_default # The default ca section


[ CA_default ]

dir = ./demoCA # Where everything is kept

certs = $dir/certs # Where the issued certs are kept

crl_dir = $dir/crl # Where the issued crl are kept

database = $dir/index.txt # database index file.

new_certs_dir = $dir/newcerts # default place for new certs.

certificate = $dir/cacert.pem # The CA certificate

serial = $dir/serial # The current serial number

crlnumber = $dir/crlnumber # the current crl number

crl = $dir/crl.pem # The current CRL

private_key = $dir/private/cakey.pem# The private key

RANDFILE = $dir/private/.rand # private random number file

x509_extensions = usr_cert # The extentions to add to the cert

name_opt = ca_default # Subject Name options

cert_opt = ca_default # Certificate field options

default_days = 365 # how long to certify for

default_crl_days= 30 # how long before next CRL

default_md = default # use public key default MD

preserve = no # keep passed DN ordering

policy = policy_match


[ policy_match ]

countryName = match

stateOrProvinceName = match

organizationName = match

organizationalUnitName = optional

commonName = supplied

emailAddress = optional


[ policy_anything ]

countryName = optional

stateOrProvinceName = optional

localityName = optional

organizationName = optional

organizationalUnitName = optional

commonName = supplied

emailAddress = optional


[ req ]

default_bits = 1024

default_keyfile = privkey.pem

distinguished_name = req_distinguished_name

attributes = req_attributes

x509_extensions = v3_ca # The extentions to add to the self signed cert

string_mask = utf8only

req_extensions = v3_req # The extensions to add to a certificate request

[ req_distinguished_name ]

countryName = Country Name (2 letter code)

countryName_default = CN

countryName_min = 2

countryName_max = 2

stateOrProvinceName = State or Province Name (full name)

stateOrProvinceName_default = BeiJing

localityName = Locality Name (eg, city)

0.organizationName = Organization Name (eg, company)

0.organizationName_default = myca

organizationalUnitName = Organizational Unit Name (eg, section)

commonName = Common Name (e.g. server FQDN or YOUR name)

commonName_max = 64

emailAddress = Email Address

emailAddress_max = 64


[ req_attributes ]

challengePassword = A challenge password

challengePassword_min = 4

challengePassword_max = 20

unstructuredName = An optional company name


[ usr_cert ]

basicConstraints=CA:FALSE

nsCertType = client, email, objsign

keyUsage = nonRepudiation, digitalSignature, keyEncipherment

nsComment = "OpenSSL Generated Certificate"

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid,issuer


[ svr_cert ]

basicConstraints=CA:FALSE

nsCertType = server

keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyAgreement

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid,issuer

extendedKeyUsage = serverAuth,clientAuth


[ v3_req ]

subjectAltName = @alt_names

\# 这里是重点,需要将里面配置为最终服务端需要的域名或者IP

\# 这里可以写多个,能够自行添加DNS.X = XXXXXX


[ alt_names ]

DNS.1 = xunshi.com

DNS.2 = *.xunshi.com

IP.1 = 192.168.0.2

IP.2 = 192.168.0.3


[ v3_ca ]

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid:always,issuer

basicConstraints = CA: true


[ crl_ext ]

authorityKeyIdentifier=keyid:always

[ proxy_cert_ext ]

basicConstraints=CA:FALSE

nsComment = "OpenSSL Generated Certificate"

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid,issuer

proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo


[ tsa ]

default_tsa = tsa_config1 # the default TSA section


[ tsa_config1 ]

dir = ./demoCA # TSA root directory

serial = $dir/tsaserial # The current serial number (mandatory)

crypto_device = builtin # OpenSSL engine to use for signing

signer_cert = $dir/tsacert.pem # The TSA signing certificate

​ \# (optional)

certs = $dir/cacert.pem # Certificate chain to include in reply

​ \# (optional)

signer_key = $dir/private/tsakey.pem # The TSA private key (optional)

default_policy = tsa_policy1 # Policy if request did not specify it

​ \# (optional)

other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)

digests = md5, sha1 # Acceptable message digests (mandatory)

accuracy = secs:1, millisecs:500, microsecs:100 # (optional)

clock_precision_digits = 0 # number of digits after dot. (optional)

ordering = yes # Is ordering defined for timestamps?

​ \# (optional, default: no)

tsa_name = yes # Must the TSA name be included in the reply?

​ \# (optional, default: no)

ess_cert_id_chain = no # Must the ESS cert id chain be included?

​ \# (optional, default: no)

将上面的配置内容保存为openssl.cnf放到生成的服务器证书文件的目录下(注意:修改alt_names里面的域名或者IP为最终部署需要的地址,支持通配符),然后执行创建签名申请文件即可,执行运行:

# 和创建CA时一样这里需要输入一堆服务器信息,输入项也是相同的。

# 不过在输入Common Name(CN)最好直接输入服务器的IP地址或者域名。

openssl req -config openssl.cnf -new -out server.req -key server.key

PS:上述配置文件使用sha1算法生产的证书,部分浏览器已经已经不信任该算法了,如果你使用的时候遇到sha1相关的问题,可以参考评论区的kevin同学提供的方案

如果你遇到sha1问题,用稍微新一点的openssl.cnf文件 https://github.com/openssl/openssl/blob/master/apps/openssl.cnf
同时还要在这个文件里稍微改一下,把下述的配置加入进去
[ v3_req ] subjectAltName = @alt_names \# 这里是重点,需要将里面配置为最终服务端需要的域名或者IP \# 这里可以写多个,能够自行添加DNS.X = XXXXXX [ alt_names ] DNS.1 = xunshi.com DNS.2 = *.xunshi.com “
加上。
最后用请求生成密钥的时候 用下面这个指令 使用sha384代替默认的sha1。

第三步通过CA机构证书对服务器证书进行签名认证

# 这里没有什么需要说的,本质上就是将签名请求文件进行签名最终得到服务器的公钥

openssl x509 -req -extfile openssl.cnf -extensions v3_req -in server.req -out server.cer -CAkey myCA.key -CA myCA.cer -days 36500 -CAcreateserial -CAserial serial

第四步部署证书

这里应该没有什么需要说的了,我们通过Nginx部署,最终得到server.key就是秘钥,server.cer文件就是公钥只需要配置给Nginx就行了。

信任CA机构证书

如果通过Windows域控创建的CA证书,其证书本身通过组策略便可以给每一个域下计算机添加机构信任。如果你没有域控只是通过openssl创建的CA证书也没有关系,只需要将CA证书的公钥(myCA.cer文件)导入到系统信任的根证书颁发机构里面就行了:

操作界面在windows的internet选型->内容->证书可以打开,导入即可,也可以直接双击cer文件进行证书安装,最终不光是windows系统,任何操作系统都可以安装证书来进行对CA机构的进行信任操作。

在对证书进行信任之后通过https打开浏览器进入内网DNS或者host配置的域名便可以得到没有任何警告的内容的安全连接:

如果是Mac系统访问逻辑也是一样的通过安装CA证书并且在钥匙串内添加信任之后依然可以正常访问:

在Android手机上也是一样,安装并且信任证书之后可以正常访问:

总结

本来对我对https的认证逻辑其实理解没有多深入,以前也只是用过SSL证书进行TCP传输加密而已,经过对openssl的学习现在至少在理解上达到了及格水平,不过这次学习论证与探索的过程我个人极其不愉快,本来这东西在有了理解之后大家都看得出来不是什么很难的东西,事实上我也只用了一天半就搞定了。但是网上充斥大量垃圾内容,不光没有什么正向内容甚至不少内容还TM起了误导的作用,整个中文互联网检索体系下就没有找到一篇文章稍微详细描述整个搭建逻辑与流程,简直了,最终我只能从https原理和openssl的官方文档开始看起,过于离谱了。基本上可以得到一个结论现在天天写一些所谓干货的博主简直就是滥竽充数,其内容千篇一律大多数也是抄袭来的基本上什么都没有说清楚简直浪费时间。

最后说一下https的原理,在解释清楚之后其实不是绝对上的安全,结合本文各位可以想一下怎样去伪造一个页面出来?假设我是黑客来搞入侵其实只需要一个小小的脚本就可以了,我们自行制作CA和服务证书之后,通过修改HOST文件对域名解析进行劫持将其引导到我们自己的服务器,然后将我们自己制作CA证书注入目标电脑的受信任证书组,这样一来对于被入侵者已经看到是安全连接但是其请求已经被我们拦截了。所以各位不要看到https就以为安全了,一旦你的电脑本身就被入侵了那么https也是形同虚设的,所以在执行高风险操作的时候最好还是点开站点的证书看看对应的CA机构是不是被修改过。


Service_CA
http://anximin.github.io/2024/03/01/Service-CA/
作者
Sylar
发布于
2024年3月1日
许可协议