跳转到内容

唐玥璨 | 博客

我 的 故 事

唐玥璨 | 博客

我 的 故 事

局域网内搭建浏览器可信任的SSL证书

2021年12月17日 技术、推荐博文唐玥璨51条留言

首先是为什么要干这个事情，你可能会说随便搞个自签名证书难道不能用吗？答案是还真的不能用，的确对于开发来说搞个自签名的证书就行了。但是一旦放到生产环境浏览器对证书有效性进行验证的时候便是不可信状态，这时就必须要用户点击一下继续访问，但是对于我们即将实施项目的自动化要求来说没法这样干。你可能又会说了现在这个环境在阿里云、华为云这些平台上随便申请一个免费的证书难道不行吗？答案是真的不行，因为项目的特殊要求最终我们部署的环境是完全没有外网访问的，就只能在局域网环境下运行及意味着不光是SSL证书的问题我们连DNS服务器都要自己建。这时候你可能又要说了那么直接用http访问就可以了，干嘛要用ssl证书呀？答案是这个项目需要使用WebRTC进行音视频多人会议，而WebRTC只能在https下运行。

其实上面的说法有一个点需要更正一下，自签名证书其实也可以但是一旦对超过100个客户端进行分发简直是要命的事情，所以我们通过Windows域控的方式统一对下属计算机进行证书分发保证可用性。

1.原理

SSL证书的信任机制其实是非常简单的，第一需要一个机构证书，第二是需要服务端证书，一般来说机构证书被称为CA证书，而服务端证书就称为服务器证书吧。那么为啥https非常安全呢？答案其实不复杂，下面就是一段逻辑性描述来说明为啥https是安全的。

通常情况下我们在给Nginx、Tomcat、IIS上配置的证书便是服务器证书，那么它是怎么保证客户端访问的地址绝对没有被拦截修改的呢？其实也不复杂，当我们的浏览器发起一个请求的时候到服务端上时，对应web服务器会通过证书的秘钥将http响应值进行一次加密，然后将密文与明文同时返回出来，客户端浏览器接收到响应之后会将密文对称解码然后和明文进行对比，这样一来便可以保证响应值没有被串改。

这个时候逻辑上稍微厉害一点都会发现一个问题，客户端是怎么解码的？这里的答案就是服务端在响应的时候同时会将证书的公钥也返回，这个公钥只能解码对应私钥加密的信息，同时这个公钥无法加密只能解密，这样一来如果如果某人想要拦截http请求便必须知道对应的私钥才行，否则浏览器一旦发现解密信息对不上便知道了响应数据已经被拦截修改过了。

如果你反应过来了你会发现一个新的问题，那么假设拦截这自己搞了一对有效的私钥和公钥然后伪装为服务器不就行了，恭喜你盲生发现了华点。这里就需要CA证书来处理了。其实服务器证书的公钥是由CA证书的秘钥配对加密来的，这样一来当请求返回的服务器公钥和通过CA证书进行验证时便会发现这个公钥是不是由机构签发的公钥，一旦对应不上则说明服务器不是原来CA证书签发服务器证书，这就证明你的请求被第三方拦截了。同时CA证书对于浏览器而言只有公钥，也就是说安装证书时本质上就是将CA证书的公钥导入到你的电脑上了，至此除开CA机构的证书发放者没有知道CA证书的秘钥是什么这样一来便可以保证下面几个非常关键的安全性：

你请求的服务绝对是官方的服务器，绝对不是黑客自建的服务器。

服务器响应给你的数据绝对是正确的，期间黑客绝对无法对其进行修改。

证书的结构如下：

这里还有一个问题便是这些CA证书是哪来的，自己的电脑上又重来没有导入过什么证书。这里便是一个非常无耻躺着赚钱的商业模式了，微软、谷歌、苹果等公司提供了操作系统和浏览器，他们便是第一方的CA机构，他们的系统自己肯定信任自己对吧？所以系统安装的时候他们的CA公钥已经安装到你们的系统里面了，然后这几家巨头合伙说那么这些CA公钥在每种系统都有，然后就是一写第三方公司和这些巨头打成了合作，这些公司的机构证书也被巨头们信任所以理所当然的入库了，这些三方机构便是大名鼎鼎的Symantec、GeoTrust几个巨头，这些机构一个单域名的签名证书都敢直接拿出来卖，一年好几千，对他们而言无法就是给下发的证书进行一次签名而已，真正的躺着赚钱。

2.开始制作证书

这里我使用的证书工具是openssl，经典工具，坦白的说非常难用。

2.1创建CA证书

首先第一步肯定是制作一个机构证书也就是CA证书出来，这里有两种方案，第一是直接用openssl创建CA证书，另一种是windows域控生成域组织的CA证书，我们分开说。

2.1.1通过openssl创建CA证书

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

Plain text

Copy to clipboard

Open code in new window

EnlighterJS 3 Syntax Highlighter

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

openssl genrsa -out myCA.key 2048

# 通过rsa算法生成2048位长度的秘钥 openssl genrsa -out myCA.key 2048

# 通过rsa算法生成2048位长度的秘钥
openssl genrsa -out myCA.key 2048

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

Plain text

Copy to clipboard

Open code in new window

EnlighterJS 3 Syntax Highlighter

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

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

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

# 公钥包含了机构信息，在输入下面的指令之后会有一系列的信息输入，这些信息便是机构信息，公司名称地址什么的 # 这里还有一个过期信息，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 | 邮件地址

2.1.2通过windows域控创建CA证书

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

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

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

这里非常简单，我都不想说了，直接根据提示输入相关信息就行了，在过期时间那一步最好将时间拉长，我还是使用的100年。

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

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

2.2创建服务器证书

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

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

Plain text

Copy to clipboard

Open code in new window

EnlighterJS 3 Syntax Highlighter

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

openssl genrsa -out server.key 2048

# 通过RSA算法生成长度2048位的秘钥 openssl genrsa -out server.key 2048

# 通过RSA算法生成长度2048位的秘钥
openssl genrsa -out server.key 2048

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

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

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

Plain text

Copy to clipboard

Open code in new window

EnlighterJS 3 Syntax Highlighter

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

[ 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)

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 [ 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)

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

[ 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为最终部署需要的地址，支持通配符），然后执行创建签名申请文件即可，执行运行：

Plain text

Copy to clipboard

Open code in new window

EnlighterJS 3 Syntax Highlighter

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

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

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

# 和创建CA时一样这里需要输入一堆服务器信息，输入项也是相同的。 # 不过在输入Common Name（CN）最好直接输入服务器的IP地址或者域名。 openssl req -config openssl.cnf -new -out server.req -key server.key

# 和创建CA时一样这里需要输入一堆服务器信息，输入项也是相同的。
# 不过在输入Common Name（CN）最好直接输入服务器的IP地址或者域名。
openssl req -config openssl.cnf -new -out server.req -key server.key

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

Plain text

Copy to clipboard

Open code in new window

EnlighterJS 3 Syntax Highlighter

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

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

# 这里没有什么需要说的，本质上就是将签名请求文件进行签名最终得到服务器的公钥 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

# 这里没有什么需要说的，本质上就是将签名请求文件进行签名最终得到服务器的公钥
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就行了。

3.信任CA机构证书

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

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

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

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

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

4.总结

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

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

文章导航

上一篇文章WebRTC颠覆软件体系的一项技术

下一篇文章vue集成lib-jitsi-meet

《局域网内搭建浏览器可信任的SSL证书》有51条留言

Pingback： 免费的Let’s Encrypt证书 – 唐玥璨 | 博客

鹏哥是个好人说道：

2023年9月2日 12:14

哈哈，评论量好高，我在外面都给你推荐了这篇文章的。

回复

耿飞鹏说道：

2023年8月24日 12:50

请问nginx搭建的加密服务使用的自签名证书，虽然会证书报警但不会TLS握手失败，但是我自己写的加密服务直接报证书不合法，tls握手失败，把自签名证书添加到浏览器里面就合法了，这是有什么原理吗，如何才能让自签名报个警告但是握手还能成功呢

回复

唐玥璨说道：

2023年8月25日 00:28

建议你重新读一下这篇文章，你提到的问题已经在文章里面解释的很清楚了，校验逻辑不是你的自签名证书，而是这个证书的来源

回复

蒋先生说道：

2023年8月30日 18:32

你最后给 服务器证书进行签名认证 时使用的 CA证书是 使用 openssl 生成的呢 还是 通过 域控生成 导出来的 呢，

回复

刘子佳说道：

2023年7月28日 20:00

哇，绝了绝了，说的很有道理，网上一搜一堆没用的，要不就是几句话能说明白的，要搞一大堆

回复

paran3xus说道：

2023年7月4日 21:58

感谢大佬写blog，但是有个小问题
如果服务器没有域名，使用ip来访问，但是https的端口又不是443，填写common name和alt_names的时候需要加端口号吗？

回复

北里闻箫说道：

2023年6月29日 14:33

牛逼！一次成功。
给你点赞！

回复

唐玥璨说道：

2023年7月1日 15:25

我也给你点个赞，文章写到这种情况，这些问题应该是轻轻松松的才对；

回复

Sev7n说道：

2023年6月26日 15:56

感谢讲解！

回复

唐玥璨说道：

2023年7月1日 15:25

客气

回复

jack step说道：

2023年3月19日 17:25

我前几年看了一篇富途的文章手把手学会了制作ca，没想到时间都过去了五六年了，这些知识还是这么多人不会，可见国内的技术根本没发展。

回复

唐玥璨说道：

2023年3月23日 00:41

也不能这么说吧，毕竟行业这几年也有不少新人加入，而且对于大部分程序员来说SSL也不是什么面试的必要指标，大家不了解也很正常；我真正生气的点在于这么基础的东西，就没有几个文章能够讲清楚，过于离谱

回复

cooper说道：

2023年3月5日 01:10

我找到原因了，
1. 要先打开 keychain 并选择到 system 上 再导入， 不能在iCloud上导入。
2.服务证书的有效期100年会被认为无效，1年是可以的，CA证书有效期100年是可以的。

回复

cooper说道：

2023年3月5日 00:15

我在mac上导入CA证书时报错, “localhost” 是我CA证书的 Common Name ， 不知道是不是因为这个问题，CA证书的Common Name不应该是一个域名吧！

An error occurred. Unable to import “localhost”.

Error: -25294

回复

bingo说道：

2023年2月27日 14:53

我自定义了一个证书链，然后把根证书 导入了受信任的根证书，二级证书导入了中间证书机构，但是访问网站是，还是提示不安全，请问可能原因是什么？
CONNECTED(00000003)
depth=2 C = CN, ST = HB, L = WH, O = edu, OU = marvin, CN = Marvin Root Certificate Authority
verify error:num=19:self signed certificate in certificate chain
—
Certificate chain
0 s:/C=CN/ST=HB/L=WH/O=edu/OU=bingo/CN=*.od.com
i:/C=CN/ST=HB/O=edu/OU=marvin/CN=Marvin Intermediate Certificate Authority
1 s:/C=CN/ST=HB/O=edu/OU=marvin/CN=Marvin Intermediate Certificate Authority
i:/C=CN/ST=HB/L=WH/O=edu/OU=marvin/CN=Marvin Root Certificate Authority
2 s:/C=CN/ST=HB/L=WH/O=edu/OU=marvin/CN=Marvin Root Certificate Authority
i:/C=CN/ST=HB/L=WH/O=edu/OU=marvin/CN=Marvin Root Certificate Authority
—
Server certificate
—–BEGIN CERTIFICATE—–
MIIERjCCAi6gAwIBAgIJALD8x4SX70A8MA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV
BAYTAkNOMQswCQYDVQQIDAJIQjEMMAoGA1UECgwDZWR1MQ8wDQYDVQQLDAZtYXJ2
aW4xMjAwBgNVBAMMKU1hcnZpbiBJbnRlcm1lZGlhdGUgQ2VydGlmaWNhdGUgQXV0
aG9yaXR5MB4XDTIzMDIyNjA5MzMyNVoXDTI0MDMwNzA5MzMyNVowWDELMAkGA1UE
BhMCQ04xCzAJBgNVBAgMAkhCMQswCQYDVQQHDAJXSDEMMAoGA1UECgwDZWR1MQ4w
DAYDVQQLDAViaW5nbzERMA8GA1UEAwwIKi5vZC5jb20wggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDaZ4dQFfMFIc44gZoAqbppQ54u1udRIcKvHvIOZ4Ir
17DD7bPJBWIzB9q+yBlZ7SqfefpnsL3l8GhLknQy6f7B7BOe47HVu9xrheCG/tQj
IhoIbQd9ox9Ga3XfmaBue3p4SRSXWv4bvT8xD78ifEpYQDawCygY48CB4aRuEZN6
ZLPWmhLMrzlrliGJgbbAvAlbdSt3wZJNAl0EzMC7xSDDB6yLNiu9WMttl8arHO+U
orZkRHOAPLDtIa0TTzFADvJTuw3HeICnJVawSwtQJOZqAFPhTsHtR8shHO//5dtX
bzhJEAxQaKbOqWAgK0/wglj9CHRI5KA9PVl/ezA0Na95AgMBAAEwDQYJKoZIhvcN
AQELBQADggIBAHQnHbwJFDxa5+mRUPaipkC+af8kHkgk9aAJdKnp0gs8fbAjLV5D
OXYeFfSPsEEiUyVAjvW3ZVZYeJLWOHSR+XcAiABn/U02TjpREH7vO91fGbrYJamt
qFR3cq3qGn+c3QH0Yb0L/RQAD8S/ipDOsUnpCoSGUUMgnjc/yAdeeEygiIbPbEmx
plcp2OI1bMkVQuMl82jelWdp1hFLmFtg4LrxNM/3vO1Q57z9VbCOfp8Ke6bDArkO
WUVzwjaIEUfPHKR7ww1q1b2+3zw3kRSZtkeNcyEV0/aqbMqHH/WOa++nyjkj/0/K
LpYfUvsRV10D/yPSv2b55YkauRf/zbXF8bICP8d1rWpofjYOktEKxqsKsSor7XJl
5GDXAZulJBgEEUGIl+HNSHYy5V5MH76Q081TgQtiwcv3aFZppIcvx8IOMGmau+3w
n+NQl1nXjYKtpGx7hHNHqirZNXjLTNTLkVFUIQlEcf7XUQ9OQKuW7K8mYBPLXM9G
/WxoHth3GIBN3VMVjxTj6L5NtyKIHbLH8yK3rGlTt+p5nNz7O8Fh1++d1froSTb8
LkUuP75WwrSKyBzvRraS5YZbRYFvSd3V9PWaGmcoFFnNzAbE/dwsk6Z4v6Nr9TIL
SuoHZbmgJGxC4B8DcSAud5FJXxWiELKLa4rSbDVn7C8627nX/n34t0jI
—–END CERTIFICATE—–
subject=/C=CN/ST=HB/L=WH/O=edu/OU=bingo/CN=*.od.com
issuer=/C=CN/ST=HB/O=edu/OU=marvin/CN=Marvin Intermediate Certificate Authority
—
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
—
SSL handshake has read 4742 bytes and written 415 bytes
—
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: A23FF2C9688BD347D7AB77616A9D9E1108C6C6D0A44B17391D4825075025DCCE
Session-ID-ctx:
Master-Key: F1CBFA67E17EB265B6C911E9EDD0D24A991894CB19E0BD6570C9059BF4DA17BF7EA38CD89C43225C58C5A239414F6C0F
Key-Arg : None
Krb5 Principal: None
PSK identity: None
PSK identity hint: None
TLS session ticket lifetime hint: 300 (seconds)
TLS session ticket:
0000 – b4 62 a6 3a 43 66 84 16-9c 7f b5 d0 e0 63 5e e4 .b.:Cf…….c^.
0010 – 54 85 63 fc 44 d4 10 ac-79 ce 52 b7 bd 3a 0f fe T.c.D…y.R..:..
0020 – 3f 4b 69 20 64 41 9a f5-f5 29 a3 34 d8 4c 0a 46 ?Ki dA…).4.L.F
0030 – 22 e9 75 dc cb 97 26 b2-9f 19 29 f3 01 9c ba 71 “.u…&…)….q
0040 – 2c 5e 17 91 ab 5d 75 01-f5 5f f8 41 0c 79 36 03 ,^…]u.._.A.y6.
0050 – ef 60 70 9f b2 d6 2e 0a-8b af 20 bc 9c 18 59 c9 .`p……. …Y.
0060 – f4 aa e1 38 ff 89 14 35-8d a1 5b 63 44 d6 fd 8f …8…5..[cD…
0070 – 4b fb a5 4f d9 26 29 01-7d 7a 93 3c f1 7f 36 b8 K..O.&).}z.<..6.
0080 – cc 4b 99 0b e6 98 d2 c0-57 06 33 77 dc 29 d9 83 .K……W.3w.)..
0090 – c8 45 35 35 63 92 b7 4e-63 fd 72 16 90 4c 7e 33 .E55c..Nc.r..L~3
00a0 – 24 92 00 20 ed 71 c5 fa-37 c6 6d 44 4f 04 37 ca $.. .q..7.mDO.7.

Start Time: 1677479813
Timeout : 300 (sec)
Verify return code: 19 (self signed certificate in certificate chain)
—

回复

kevin说道：

2023年2月17日 03:30

如果你遇到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。

回复

kevin说道：

2023年2月17日 03:11

文章中所提供的openssl.cnf，在目前版本的火狐和chrome中已经不能用了，显示The certificate is not trusted because it was signed using a signature algorithm that was disabled because that algorithm is not secure.
问题在于低版本的sha1加密算法已经不被支持，请后来的同学使用更高版本的

回复

唐玥璨说道：

2023年2月24日 22:17

感谢指正，我等会把文章更新了

回复

五花肉说道：

2023年1月31日 15:09

内网环境中http站点里嵌套https页面也遇到同样问题，上级部门没有DNS，需要访问我们系统，没建域控，通过博主的文章找到了思路，打算写个脚本打开首页时安装证书。

回复

大白吃豆人说道：

2022年12月24日 19:08

PS C:\Program Files\OpenSSL-Win64\bin> .\openssl.exe x509 -req -extfile e:\ssl\openssl.conf -extensions v3_req -in e:\ssl\server.req -out e:\ssl\server.cer -CAkey e:\ssl\myCA.key -CA e:\ssl\myCA.cer -days 7200 -CAcreateserial -CAserial serial
Certificate request self-signature ok
subject=C = cn, ST = gd, L = sz, O = sundy, OU = sundy, CN = sundy
7C360000:error:80000005:system library:BIO_new_file:Input/output error:crypto\bio\bss_file.c:67:calling fopen(serial, w)
7C360000:error:10080002:BIO routines:BIO_new_file:system lib:crypto\bio\bss_file.c:77:

卡死在最后一步了。。。。。。

回复

Hong说道：

2022年11月30日 14:07

补充一下,为IP制作Server证书的时候
[alt name] 那里要写成
IP.1 = 192.168.10.1

回复

唐玥璨说道：

2022年12月3日 02:19

终于有人看懂了，不知道为什么现在搞技术的都这么浮夸，我这个文章算是几乎没有废话了都没几个有耐心看完

回复

3032834864说道：

2022年11月5日 16:27

openssl.cnf 是一个配置文件还是一个类似脚本的东西？
下面是报错的内容，我应该怎么做？
[root@localhost ssl]# openssl req -config openssl.cnf -new -out server.req -key server.key
Error Loading request extension section v3_req
140206333278016:error:2207507C:X509 V3 routines:v2i_GENERAL_NAME_ex:missing value:crypto/x509v3/v3_alt.c:512:
140206333278016:error:22098080:X509 V3 routines:X509V3_EXT_nconf:error in extension:crypto/x509v3/v3_conf.c:47:name=subjectAltName, value=nextcloud.net

回复

唐玥璨说道：

2022年11月7日 11:26

是一个配置文件

回复

雨晨说道：

2022年10月2日 18:49

在运行服务器请求文件是报错 ：Error Loading extension section v3_ca

回复

雨晨说道：

2022年10月2日 09:49

你好 麻烦问下 我是通过域环境下搭建的CA centos上的Apache服务现在需要ssl证书 centos通过OpenSS生成.csr请求文件 通过CA签名后 导入到Apache配置文件中 为什么还是不安全的啊 ？

回复

唐玥璨说道：

2022年10月11日 23:21

你难道连我这篇文章都没有耐心一字一句看完吗？那干脆别尝试了，早点换个行业发展吧

回复

你、说道：

2022年8月5日 15:09

我有一个问题，尽管将CA证书添加了认证之后，查询证书的相信信息的时候，可以看到“这个证书的目的是：所有应用程序策略”，但是Chrome浏览器还是会提示“证书无效”，这是什么原因啊？难道是Chrome浏览器自己还有一层校验么？

回复

唐玥璨说道：

2022年8月6日 18:45

不会，大概率是你的服务证书和机构证书不匹配

回复

moon说道：

2022年7月6日 17:40

大佬，我按照你的流程走了一遍，我的网站是 IP地址+端口的，不管是发布在IIS，还是nginx, edge浏览器还是提示不安全，是IP地址不行吗？必须用域名吗？

回复

唐玥璨说道：

2022年7月8日 20:22

IP是可以的，你再确定一下openssl里面配置的地址是不是正确的，不需要端口

回复

zhuanghc说道：

2022年9月29日 16:25

大佬！我今天试了下内网ip+端口，客户端机是win10，IP好像没有用，域名才有用，不知道是不是不同系统下对浏览器对证书域名的校验不一样？

回复

苏智明说道：

2022年6月16日 14:13

# 这里是重点，需要将里面配置为最终服务端需要的域名或者IP
# 这里可以写多个，能够自行添加DNS.X = XXXXXX
[ alt_names ]
DNS.1 = 内网服务器ip

我的网站地址是内网ip，是不是在这里配置了ip，浏览器就不会出现不安全的提示？

回复

唐玥璨说道：

2022年6月24日 01:22

只要机构证书被信任，IP也不会出现不安全提示

回复

你、说道：

2022年8月5日 16:28

并不是啊，Chrome只认域名啊，如果只用ip的话，尽管证书是没有问题的，但是Chrome还是会提示不安全。

回复

唐玥璨说道：

2022年8月6日 18:46

我的文章全文哪里出现chrome了？单纯告诉你SSL而已，你拿个浏览器和我杠有意思吗？

回复

毛说道：

2023年4月20日 09:33

楼主你自己有没有试过呀？确实chrome上会报不信任。

唐玥璨说道：

2023年4月23日 02:24

卧槽，SB玩意

daily说道：

2022年5月12日 11:11

请问第三步 信任CA机构证书这一步，应该是客户进行的吗

回复

唐玥璨说道：

2022年5月13日 00:28

是的，默认情况下客户信任的机构证书肯定是不包括我们生成的

回复

jack说道：

2022年5月12日 07:10

同时openssl即便是使用V3版本依旧没有附带V3的subjectAltName字段数据（这里是证书对应的IP地址或者域名，可以用通配符）。但是这些东西命令行没法指定所以需要配置文件。
可以不用配置文件的：
证书请求：
openssl req -new -sha256 -key server.key \
-subj “/C=CN/ST=HeNan/L=ZhengZhou/O=JACKADAM/OU=jack/CN=server.jackadam.ml/emailAddress=[email-masked]” \
-extensions v3_req \
-config <(cat /etc/pki/tls/openssl.cnf \
<(printf "[req]\nreq_extensions = req_ext\n[req_ext]\nsubjectAltName=DNS:server.jackadam.ml")) \
-out SANserver.csr
但是证书签名，不会带入请求中的SAN扩展，还得手动指定：
openssl ca -in SANserver.csr \
-policy policy_anything \
-cert rootCA.crt \
-keyfile rootCA.key \
-batch \
-extensions SAN \
-config <(cat /etc/pki/tls/openssl.cnf \
<(printf "[SAN]\nsubjectAltName=DNS:server.jackadam.ml")) \
-out ./SANserver.crt

而且你用的是X509自签名，不是CA签名。证书吊销以后，客户端是不会同步跟踪到这个证书被注销。
证书吊销列表（CRL）与证书状态在线查询协议（OCSP）无法使用。
我也在尝试，生成证书后，包含CRL OCSP,也不知道是放CA证书还是用户证书。

回复

唐玥璨说道：

2022年5月13日 00:29

请仔细看我给出的配置文件

回复

范宇说道：

2022年5月11日 16:11

博主您好，我在WindowsServer上运行了一个AzureDevOps，通过客户端浏览器访问时提示https不安全，显示证书无效；在服务器上的应用使用SMTP邮件提醒功能时也提示无法建立安全的SSL，远程证书无效。
搭建的过程就是按照教程里做的，OpenSSL生成CA证书，服务器证书，证书签名，在客户端信任CA证书。部署证书是在IIS上导入，绑定到网站服务。
操作的具体步骤和注意事项应该都注意到了，但还是不能建立安全的SSL连接，实在是找不到问题出在哪了。
服务器是一台虚拟机，使用的是内网ip地址，可以连接上外网。

回复

唐玥璨说道：

2022年5月13日 00:30

根据你说明的信息我完全无法分析哪一步出了问题，建议系统学习一下证书机制，通过客户端错误进行调试

回复

范宇说道：

2022年4月18日 23:07

求助博主
您好，请问这一步：第三步通过CA机构证书对服务器证书进行签名认证 ，中，需要向CA机构进行申请吗，因为我看到文章标题是局域网环境的https搭建；服务器如果没分配公网ip，不花钱向第三方机构申请也是可以参考这篇文章进行搭建的吗。提问不太专业，如果有幸被看到，感谢回复！

回复

唐玥璨说道：

2022年4月18日 23:13

CA机构可以是你自己呀，通过openssl就可以创建机构证书，公网或者内网都可以使用，无非就是这个证书是否被信任，我们自己签名的证书肯定是默认不被信任的需要自行给证书添加信任。线上的一些权威机构证书需要花钱，这一部分就是大家默认都信任的证书。

回复

范宇说道：

2022年4月19日 00:28

懂了，多谢多谢！

回复

唐玥璨说道：

2022年4月20日 22:16

客气

回复

范宇说道：

2022年4月12日 21:58

感谢您的这篇博客，我刚过了一遍，准备在测试服务器上配置https，太有用了！

回复

唐玥璨说道：

2022年4月18日 22:50

能够帮助到你我很高兴

回复

留下回复 取消回复

著作权 © 2023 唐玥璨 版权所有
备案号：蜀ICP备18036276号-1