生成自签名SSL证书

  |  
cover

平常我们在开发的时候,经常需要使用到SSL证书,比如说使用https协议,或者使用双向认证等,这时候我们就需要生成SSL证书,这里介绍一下如何生成自签名的SSL证书。

证书的基本概念

证书的基本格式

  • x509: 证书的格式,它是一种标准,定义了证书的格式
  • pem和der: 两种编码格式,pem是base64编码,der是二进制编码
  • crt和cer: 两种后缀名,crt一般是pem编码,cer一般是der编码
  • csr: 证书签名请求,这个并不是证书,而是向证书颁发机构申请证书的文件,包含了申请者的信息和公钥
  • key: 常见的私钥的后缀名

数字证书和公钥的关系

数字证书是由证书颁发机构(CA)签发的,证书颁发机构(CA)是一个可信任的第三方机构,它会对证书申请者的身份进行验证,然后签发证书。

证书颁发机构(CA)会对证书申请者的身份进行验证,然后签发证书,证书中包含了申请者的公钥,证书颁发机构(CA)会对申请者的公钥进行签名,这样就形成了数字证书。

当客户端需要验证服务端的身份时,客户端会向服务端请求证书,服务端会将证书发送给客户端,客户端会验证证书的合法性,验证的过程是这样的:

  1. 客户端会从证书中获取证书颁发机构(CA)的公钥
  2. 客户端会使用证书颁发机构(CA)的公钥对证书中的签名进行验证,如果验证通过,则证明证书是合法的
  3. 客户端会从证书中获取服务端的公钥
  4. 客户端会使用服务端的公钥对数据进行加密,然后发送给服务端
  5. 服务端会使用自己的私钥对数据进行解密,然后进行处理
  6. 服务端会使用客户端的公钥对数据进行加密,然后发送给客户端
  7. 客户端会使用自己的私钥对数据进行解密,然后进行处理
  8. 服务端和客户端就可以进行通信了
  9. 服务端和客户端会使用对称加密算法对数据进行加密,然后进行通信

x509证书

x.509 是一种证书格式,它是一种标准,定义了证书的格式。它其实是作为x.500标准的一部分而出现的,x.500标准定义了唯一标识一个实体的信息,该实体可以是机构、组织、个人或一台服务器。而x.509则把该实体与公钥绑定在一起,从而提供了通信实体的鉴别机制,也就是目前最流行的x.509数字证书。

通常说的证书,就是指x.509数字证书,它是一种非常通用的证书格式,几乎所有的证书都是基于x.509的,如果不特别说明,都是指x.509 v3版本的证书。

https

http

HyperText Transfer Protocol,超文本传输协议,是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。HTTP传输的是明文,未加密的数据,因此不安全。

TCP端口默认为80

https

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。HTTPS传输的数据是加密的,保证会话过程的安全性。

TCP端口默认为443

SSL

SSL(Secure Sockets Layer),安全套接层,位于可靠的面向连接的网络层协议和应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层:SSL记录协议(SSL Record Protocol)和SSL握手协议(SSL Handshake Protocol)。

SSL协议既用到了对称加密也用到了非对称加密(公钥加密),在建立传输链路时,SSL首先对对称加密的密钥使用公钥进行非对称加密,链路建立好之后,SSL对传输内容使用对称加密。

  • 对称加密:速度高,可加密内容较大,用来加密会话过程中的消息
  • 公钥加密:加密速度较慢,但能提供更好的身份认证技术,用来加密对称加密的密钥

对称加密和非对称加密

对称加密就是加密和解密使用的是同一个密钥,非对称加密就是加密和解密使用的是不同的密钥。

对称加密和非对称加密的优缺点

对称加密的优点是加密和解密速度快,缺点是密钥的传输比较麻烦,因为密钥的传输也需要加密,如果密钥也被泄露了,那么加密就没有意义了。

非对称加密的优点是密钥的传输比较方便,因为加密和解密使用的是不同的密钥,缺点是加密和解密速度比较慢。

非对称加密

非对称加密的密钥分为公钥和私钥,公钥可以公开,私钥不能公开,公钥加密的数据只能使用私钥解密,私钥加密的数据只能使用公钥解密。

因为公钥是公开的,因此私钥加密的数据就都会被知道了。所以一般情况下,私钥加密的数据被用来作身份验证使用,比如说数字签名。

非对称加密已然存在安全隐患,比如此时也有一个中间人,他也生成了自己的公钥和私钥,并且先一步与客户端和服务器端建立了连接,获取了服务器端的公钥又把自己的公钥发送给了客户端,那么此时中间人已经可以获取甚至篡改客户端和服务器端的所有通信数据了。

为了解决这个问题,就出现了另外一个办法,需要一个权威的第三方平台,这个平台叫做证书颁发机构(Certificate Authority,CA),这个方法有个前提,就是客户端需要知道CA的公钥。 CA会对申请者的身份进行验证,然后签发证书,证书中包含了申请者的公钥,证书颁发机构(CA)会对申请者的公钥进行签名,这样就形成了数字证书。

CA工作流程如下:

  1. CA用自己的私钥对证书中的信息进行签名,生成自签名证书
  2. 服务端给CA发送请求,CA用自己的私钥和证书,对服务端的私钥进行签名,生成证书,证书中包含了服务端的公钥信息
  3. 服务端把自己的证书发送给客户端,客户端使用CA的公钥对证书中的签名进行验证,如果验证通过,则证明证书是合法的
  4. 最后客户端就可以使用这个证书和服务端进行通信了

认证过程

认证方式分为两种,一种是单向认证,一种是双向认证。具体使用哪种认证方式,是由服务端决定的。

单向认证

单向认证就是服务端向客户端证明自己的身份,客户端不需要证明自己的身份。

single-authentication

认证过程如下:

  1. 客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
  2. 服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书
  3. 客户端使用服务端返回的信息验证服务器的合法性,包括:
    • 证书是否过期
    • 发行服务器证书的CA是否可靠
    • 返回的公钥是否能正确解开返回证书中的数字签名
    • 服务器证书上的域名是否和服务器的实际域名相匹配
    • 验证通过后,将继续进行通信,否则,终止通信
  4. 客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择
  5. 服务器端在客户端提供的加密方案中选择加密程度最高的加密方式
  6. 服务器将选择好的加密方案通过明文方式返回给客户端
  7. 客户端接收到服务端返回的加密方式后,使用该加密方式生成产生随机码,用作通信过程中对称加密的密钥,使用服务端返回的公钥进行加密,将加密后的随机码发送至服务器
  8. 服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获取对称加密密钥。 在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全

双向认证

双向认证就是服务端向客户端证明自己的身份,客户端也需要证明自己的身份。

double-authentication

双向认证和单向认证原理基本差不多,只是除了客户端需要认证服务端以外,增加了服务端对客户端的认证,具体过程如下:

  1. 客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
  2. 服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书
  3. 客户端使用服务端返回的信息验证服务器的合法性,包括:
    • 证书是否过期
    • 发行服务器证书的CA是否可靠
    • 返回的公钥是否能正确解开返回证书中的数字签名
    • 服务器证书上的域名是否和服务器的实际域名相匹配
    • 验证通过后,将继续进行通信,否则,终止通信
  4. 服务端要求客户端发送客户端的证书,客户端会将自己的证书发送至服务端
  5. 验证客户端的证书,通过验证后,会获得客户端的公钥
  6. 客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择
  7. 服务器端在客户端提供的加密方案中选择加密程度最高的加密方式
  8. 将加密方案通过使用之前获取到的公钥进行加密,返回给客户端
  9. 客户端收到服务端返回的加密方案密文后,使用自己的私钥进行解密,获取具体加密方式,而后,产生该加密方式的随机码,用作加密过程中的密钥,使用之前从服务端证书中获取到的公钥进行加密后,发送给服务端
  10. 服务端收到客户端发送的消息后,使用自己的私钥进行解密,获取对称加密的密钥,在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。

生成自签名SSL证书

我们平常使用的SSL证书,一般都是通过证书颁发机构(CA)签发的。这些证书都是内装在操作系统或者浏览器中,CA机构要么会向操作系统或者浏览器厂商支付一定的费用,要么足够强大到物理消灭项目,因此这些证书都是内装的。

既然机构能够将根证书内装了,那么他们就可以自己售卖签发的证书了,这些证书就会进行高昂的收费。如果我们安全性并没有那么高,我们可以自己生成自签名证书,这样就可以免费使用了。

生成证书过程

生成证书的过程如下:

  1. 生成私钥(.key)
  2. 生成证书签名请求(.csr)
  3. 将证书请求文件(.csr)提交给证书颁发机构(CA),CA会对提交的证书请求中的所有信息生成一个摘要,然后使用CA根证书对应的私钥对摘要进行加密,生成签名,然后将签名和证书请求文件(.csr)一起打包,生成证书(.crt或.cer)
  4. 用户拿到签发后的证书,可能需要导入到自己的密钥库中,或根据需要再进行各种格式的转换(.pem、.pfx、.p12等)

生成自签名SSL证书有两个工具可以使用,一个是openssl,另一个是cfssl

openssl

根证书私钥

根证书就是自己的CA证书,它是自签名的,因此需要自己生成私钥,然后用私钥生成自签名证书。

如果只是一个网站,那么直接生成一个证书就可以了,因为网站不验证用户的身份。但是,如果是一个需要双向认证的系统,比如物联网设备等,每个连接都单独生成证书的话就太多了,这时候通过CA生成证书就比较方便了。

这里我们模仿的是需要认证机构的场景,因此需要生成CA证书。

1
2
3
4
5
6
7
8
9
10
11
# 生成私钥
# genrsa: 生成RSA私有密钥,不会生成公钥,因为公钥是由私钥生成的。生成时可以指定私钥的长度,一般为1024位或2048位。如果需要查看公钥或者生成公钥,可以使用 openssl ras
# -out: 指定生成的私钥文件的路径
# -des|-des3|-idea: 指定私钥的加密算法,如果不指定,则不会对私钥进行加密。如果指定,则每次使用私钥时都需要输入密码,太麻烦所以一般不指定
# -passout: 指定私钥的密码,如果不指定,则提示输入密码。
# numbits: 指定私钥的长度,默认为1024,这里指定的是8192,一般4096就已经无法被太阳系总算力在几千年内破解了。这个参数必须为命令行参数的最后一个参数
$ openssl genrsa -out ca-key.pem 8192
Generating RSA private key, 8192 bit long modulus (2 primes)
......................................+++
..................................................+++
e is 65537 (0x010001)

此时直接查看私钥内容,会发现一段ASCII码,具体的私钥内容需要使用以下命令查看:

1
$ openssl rsa -in ca-key.pem -noout -text

生成根证书签发申请文件CSR

在正式的证书申请时,需要向证书颁发机构(CA)提交证书签发申请文件CSR,这个文件中包含了申请者的信息和公钥。

这里这个申请文件是用来生成自签名证书的,用来最后生成的证书作为整体认证使用。根证书信息可以不填,但是如果是为了演示的正常,可以填写一些信息。

生成证书签发申请文件CSR的命令如下:

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
# req: 生成证书签发申请文件CSR
# -new: 生成新的证书签发申请文件CSR
# -key: 指定私钥文件
# -out: 指定生成的证书签发申请文件CSR的路径
# -subj: 指定证书签发申请文件CSR中的信息,这里指定了国家、省份、城市、公司、部门、邮箱、域名等信息,如果不指定,则需要手动输入
# $ openssl req -new -key ca-key.pem -out ca.csr -subj "/C=CN/ST=beijing/L=beijing/O=test-on/OU=test-ou/CN=test-fqdn/emailAddress=test@example.com"
$ openssl req -new -key ca-key.pem -out ca.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:beijing
Locality Name (eg, city) []:beijing
Organization Name (eg, company) [Internet Widgits Pty Ltd]:test-on
Organizational Unit Name (eg, section) []:test-ou
Common Name (e.g. server FQDN or YOUR name) []:test-fqdn
Email Address []:test@example.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

如果报错 Can't load /home/aaron/.rnd into RNG,则需要执行以下命令:

1
2
cd /home/aaron
openssl rand -writerand .rnd

如果想查看证书签发申请文件CSR的内容,可以使用以下命令:

1
2
3
4
5
6
7
8
9
$ openssl req -in ca.csr -noout -text
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = CN, ST = beijing, L = beijing, O = test-on, OU = test-ou, CN = test-fqdn, emailAddress = test@example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (8192 bit)
......

生成自签名证书 CER

根证书的私钥、签发请求文件都有了,现在可以自己给自己办法一个证书了。这个生成的证书就是公钥,包含了证书持有组织的信息以及公钥、公钥的哈希校验。后面使用该根证书签发的证书都可以递归到该根证书,证明可信,从而实现信任链。

根证书需要存储到所有设备中,而且必须是通过”可信的“方式。因为其他证书能靠它来验证,但是它自己没办法验证自己。

生成自签名证书的命令如下:

1
2
3
4
5
6
7
8
9
10
11
# x509: 生成x509证书
# -req: 指定证书签发申请文件CER
# -in: 指定证书签发申请文件CSR
# -signkey: 指定私钥文件
# -out: 指定生成的证书文件CER的路径
# -days: 指定证书的有效期,单位为天,默认为30天
# -sha256: 指定签名算法为sha256,默认为sha1,sha1已经找到暴力撞值的方法,因此不安全了
$ openssl x509 -req -days 3650 -sha256 -signkey ca-key.pem -in ca.csr -out ca.cer
Signature ok
subject=C = CN, ST = beijing, L = beijing, O = test-on, OU = test-ou, CN = test-fqdn, emailAddress = test@example.com
Getting Private key

此时直接查看证书内容,也会发现是一段ASCII码,具体的证书内容使用以下命令查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ openssl x509 -in ca.cer -noout -text
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
67:6b:09:ad:8e:e9:05:60:9d:d3:f5:c5:2a:27:20:14:b0:59:14:a3
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = CN, ST = beijing, L = beijing, O = test-on, OU = test-ou, CN = test-fqdn, emailAddress = test@example.com
Validity
Not Before: Oct 24 15:24:37 2023 GMT
Not After : Oct 21 15:24:37 2033 GMT
Subject: C = CN, ST = beijing, L = beijing, O = test-on, OU = test-ou, CN = test-fqdn, emailAddress = test@example.com
Subject Public Key Info:
......

一步生成自签名证书

上面的方法使用了三步,生成私钥、生成证书签发申请文件、生成自签名证书。其实可以一步生成私钥以及自签名证书,命令如下:

1
2
3
4
5
6
7
8
9
# req: 生成证书文件
# -newkey: 生成新的私钥的命令,指定算法以及密钥长度,这里指定了rsa算法,长度为2048位
# -nodes: 表示私钥不加密,如果不带参数,则表示私钥加密,会提示输入密码
# -keyout: 指定生成的私钥文件的路径
# -x509: 指定生成的是x509证书,而不是证书签发申请文件
# -days: 指定证书的有效期,单位为天,默认为30天
# -out: 指定生成的证书文件的路径
# -subj: 指定证书中的信息,这里指定了国家、省份、城市、公司、部门、邮箱、域名等信息,如果不指定,则需要手动输入
$ openssl req -newkey rsa:2048 -nodes -keyout ca-key.pem -x509 -days 3650 -out ca.cer -subj "/C=CN/ST=beijing/L=beijing/O=test-on/OU=test-ou/CN=test-fqdn/emailAddress=test@example.com"

这一段只是一个扩展知识,因为命令太长了,我个人见用的人还是比较少的。

生成网站用私钥PEM

我们现在已经有了根证书,作为CA的存在。现在我们拿CA来给网站签发证书。这样网站就可以用CA来验证证书的合法性,进行https通信了。

每个网站都有自己的子证书,证书可以层层嵌套,即使是子证书也可以再签发孙子证书,这样就形成了证书链。这里我们只需要二级就行了。

实际中,越靠近权威根证书,则证书越可信,如果花钱买的话,也就越贵。

生成业务用私钥PEM的命令如下:

1
2
3
4
5
$ openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
...........................................................+++++
..................................................................+++++
e is 65537 (0x010001)

一般来说,子证书的存续时间与加密强度都不如根证书。

子证书事需要颁发出去的。根证书的私钥只有签发机构有,但是子证书的私钥需要给各个业务使用,因此需要将子证书的私钥导出来。

1
2
# rsa: 生成RSA私钥。如果上一步使用的其他算法生成的私钥,这里需要执行。否则执行前后没有变化
$ openssl rsa -in server.key -out server.key

生成网站用证书签发申请文件CSR

注意,由于这里测试的是给网站使用的,证书的请求文件中的CN必须是域名,否则浏览器会报错。这里我们假定测试的域名为cert.galaxy.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ openssl req -new -key server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:BEIJING
Locality Name (eg, city) []:BEIJING
Organization Name (eg, company) [Internet Widgits Pty Ltd]:server-on
Organizational Unit Name (eg, section) []:server-ou
Common Name (e.g. server FQDN or YOUR name) []:cert.galaxy.com
Email Address []:cert@galaxy.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

准备配置文件

有时候,网站即使 Common Name 和域名是一样的,但是浏览器还是会报错 NET::ERR_CERT_COMMON_NAME_INVALID,这是因为证书中没有正确的嵌入域名信息,这时候就需要在配置文件中指定域名了。

1
2
3
4
5
6
7
8
9
# 生成配置文件,指定域名。
$ cat server.dns.ext
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName=@SubjectAlternativeName

[ SubjectAlternativeName ]
DNS.1=galaxy.com
DNS.2=cert.galaxy.com

配置文件格式如下:

1
2
3
4
5
6
7
8
9
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName=@SubjectAlternativeName

[SubjectAlternativeName]
DNS.1=your.domain.name
DNS.2=此处填写你的网站的域名.cn
DNS.3=如果有多个域名就这么增加.com
DNS.4=*.当然支持泛解析域名.net

如果没有域名,而是有固定IP,可以使用以下格式文件:

1
2
3
4
5
6
7
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName=@SubjectAlternativeName

[SubjectAlternativeName]
IP.1=192.168.100.1
IP.2=222.90.100.1

生成网站用证书 CER

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# x509: 生成x509证书
# -req: 指定证书签发申请文件CER
# -days: 指定证书的有效期,单位为天,默认为30天
# -CA: 指定CA证书
# -CAkey: 指定CA证书的私钥
# -CAcreateserial: 指定生成序列号文件。当签署证书时,CA需要为每个证书生成一个唯一的序列号,因此颁发者需要跟踪它以前使用过哪些序列号。这个选项会将一个随机数分配给签名证书,然后创建此序列号文件。
# -CAserial: 指定CA证书的序列号文件。将每个证书的唯一序列号都存储在文件中,以便在下次签署证书时使用。这样即使创建多个证书,也不会出现重复的序列号。
# -in: 指定证书签发申请文件CSR
# -out: 指定生成的证书文件CRT的路径
# -extfile: 指定配置文件
# -extensions: 指定配置文件中的扩展字段
$ openssl x509 -req -days 730 -sha256 -CA ca.cer -CAkey ca-key.pem -CAserial ca.srl -CAcreateserial -in server.csr -out server.cer -extfile server.dns.ext
Signature ok
subject=C = CN, ST = BEIJING, L = BEIJING, O = server-on, OU = server-ou, CN = cert.galaxy.com, emailAddress = cert@galaxy.com
Getting CA Private Key

以上是生成的证书全过程,如果只有IP,则将最后的配置文件改为记录IP的文件即可。

通过证书获取公钥的命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ openssl x509 -pubkey -in server.cer -noout
````

### 证书格式转换

可能有时候需要将证书转换为其他格式,比如说pfx格式,这时候就需要进行转换了。这里实验并不需要进行格式转换,因此转换操作仅是记录。

```bash
# 将证书转换为pfx格式
openssl pkcs12 -export -in server.cer -inkey server.key -out server.pfx
# 转换为pem格式,如果后缀名不标准的话还需要指定-inform der
openssl x509 -in server.cer -out server.pem -outform pem
# 转换为der格式,如果后缀名不标准的话还需要指定-outform pem
openssl x509 -in server.cer -out server.der -outform der

客户端证书

如果需要客户端证书,那么就需要生成客户端证书了。客户端证书的生成方式和网站证书的生成方式是一样的,只是在生成证书签发申请文件CSR时,需要将Common Name改为客户端的名称,比如说client.galaxy.com

简单来说,就是客户端证书和服务端证书是同级的两套不同的证书。

使用证书

经过上面的步骤,我们已经生成了根证书和网站证书,现在我们就可以使用这个证书了。

这里我使用的docker方式启动了一个nginx,然后将证书挂载到容器中,这样就可以使用https协议了。其他的服务也是类似的。

首先将所有的证书文件放到一个目录中,这里我放到了/data/docker/nginx/conf/ssl目录中。然后新建nginx配置文件,书写配置即可。

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
# 创建证书目录,将证书文件放到该目录中
$ mkdir -p /data/docker/nginx/conf/ssl
# 将证书文件放到该目录中,根据自己情况进行复制即可。我之前的证书都下载到了家目录下的cert目录中,因此从这里进行复制
$ cd ~/cert
$ cp server.cer server.key /data/docker/nginx/conf/ssl

# 创建nginx配置文件
$ vim /data/docker/nginx/conf/ssl-test.conf
$ cat /data/docker/nginx/conf/ssl-test.conf
server {
listen 443 ssl;
listen [::]:443 ssl;

# 网站域名
server_name cert.galaxy.com;

# 证书文件
ssl_certificate conf.d/ssl/server.cer;
# 证书私钥文件
ssl_certificate_key conf.d/ssl/server.key;

ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;

root /usr/share/nginx/html;

location / {
index index.html index.htm;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

启动nginx

1
$ docker run --name nginx-ssl -p 443:443 -v /data/docker/nginx/conf/:/etc/nginx/conf.d -d nginx:1.23-alpine

然后就可以使用https协议访问了。

访问截图

直接访问会提示警告,因为证书是自签名的,不被浏览器信任。

err cert authority invalid

点开高级,点击继续访问。

err cert authority invalid go on

此时访问成功,但是浏览器地址栏前面会提示不安全。

err cert authority invalid page

点击地址栏前面的提示信息,可以看到证书的警告信息,证书也提示是无效的。

err cert authority invalid warning

点击证书那个按钮,可以看到证书的详细信息。

err cert authority invalid cert

点击消息信息,可以看到证书的详细信息。可以看到证书层次结构中只有一个证书,就是自己这个证书。

err cert authority invalid cert struct

证书链

如果是自签名的证书,那么证书链就只有一个证书,就是自己的证书。如果是CA签发的证书,那么证书链就有多个证书,这些证书都是CA签发的,最后一个证书就是自己的证书。

对应我们之前的证书,现在地址栏会提示不安全,那怎么提示安全访问呢?这时候就需要将根证书导入到浏览器中,这样浏览器就会完成整个证书链的验证信任这个证书了。

首先,我们要导入根证书,这里我们使用chrome浏览器进行导入。打开浏览器的设置,然后隐私和安全侧边栏,然后点击管理证书,选择授权机构,然后点击导入。

cert with ca chrome manage

我们选择对应的ca证书文件,这里我们创建的是ca.cer,然后点击打开。

cert with ca chrome select

导入的时候会选择证书的身份,这里我们选择网站,然后点击确定。注意,这里会显示证书的FQDN信息,也就是创建的时候Common Name的信息。

cert with ca chrome load

导入证书之后。在列表中一直往下拉,名字组成是org-{Organizational Unit Name},点开就可以看到Organizational Unit NameCommon Name信息。

cert with ca chrome list

点击证书后面的操作按钮。如果之后想要删除证书,可以点击删除按钮。这里我们要看根证书的详细信息,因此点击视图按钮。

cert with ca chrome operation

可以看到这是一个自签名的证书,证书的层次结构中只有一个证书,就是自己的证书。

cert with ca chrome ca info

此时再访问 https://127.0.0.1 就会提示错误信息 NET::ERR_CERT_COMMON_NAME_INVALID,因为证书中的域名和访问的域名不一致。

cert with ca common name invalid

现在我们在本地的host中加入域名映射,然后再访问 https://cert.galaxy.com

此时访问直接就会成功了,而且浏览器地址栏前面也不会提示不安全了,地址栏前面取而代之的是一个锁型图标。

cert with ca success

点击地址栏前面的锁型图标,可以看到证书的信息。此时信息提示是安全的,同时提示证书是有效的。

cert with ca valid

点击证书按钮,查看证书信息。

cert with ca cert info

点击详细信息,可以看到证书的详细信息。可以看到证书层次结构中有两个证书,第一个证书就是自己的证书,第二个证书就是根证书。这里的根证书就是我们之前导入的证书。

因为证书链中的证书都是CA签发的,因此浏览器就会信任这个证书了。

cert with ca cert struct

cfssl

cfssl是一个云原生的证书管理工具,可以用来生成证书、签发证书、管理证书等。

安装

cfssl需要我们先下载二进制程序,然后才能进行使用。

1
2
3
4
5
6
7
8
9
10
11
12
# 下载cfssl
$ wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -O /usr/bin/cfssl
# 下载cfssljson
$ wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -O /usr/bin/cfssl-json
# 下载cfssl-certinfo
$ wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -O /usr/bin/cfssl-certinfo
# 设置文件权限
$ chmod +x /usr/bin/cfssl*

# 新建证书目录
$ mkdir -p /opt/certs
$ cd /opt/certs

生成根证书

生成证书配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ vim /opt/certs/ca-csr.json
$ cat /opt/certs/ca-csr.json
{
"CN": "ca.galaxy.com",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "beijing",
"L": "beijing",
"O": "test-on",
"OU": "test-ou"
}
],
"ca": {
"expiry": "87600h"
}
}

生成根证书

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
# gencert: 生成证书
# -initca: 指定生成根证书
# 只使用cfssl命令会将证书输出到标准输出,因此需要重定向到文件中
$ cfssl gencert -initca ca-csr.json
2023/10/30 21:20:06 [INFO] generating a new CA key and certificate from CSR
2023/10/30 21:20:06 [INFO] generate received request
2023/10/30 21:20:06 [INFO] received CSR
2023/10/30 21:20:06 [INFO] generating key: rsa-2048
2023/10/30 21:20:07 [INFO] encoded CSR
2023/10/30 21:20:07 [INFO] signed certificate with serial number 664542684350169622832034206885696124480833745240
{"cert":"-----BEGIN CERTIFICATE-----\nMIIDzjCCAragAwIBAgIUdGcf5FWyqUAx3gID1+gF9KMAcVgwDQYJKoZIhvcNAQEL\nBQAwbTELMAkGA1UEBhMCQ04xEDAOBgNVBAgTB2JlaWppbmcxEDAOBgNVBAcTB2Jl\naWppbmcxEDAOBgNVBAoTB3Rlc3Qtb24xEDAOBgNVBAsTB3Rlc3Qtb3UxFjAUBgNV\nBAMTDWNhLmdhbGF4eS5jb20wHhcNMjMxMDMwMTMxNTAwWhcNMzMxMDI3MTMxNTAw\nWjBtMQswCQYDVQQGEwJDTjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVp\namluZzEQMA4GA1UEChMHdGVzdC1vbjEQMA4GA1UECxMHdGVzdC1vdTEWMBQGA1UE\nAxMNY2EuZ2FsYXh5LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAKvYTErhsIaKNuA7nzen4ttbDKNih1trigiFottMLkxot9Iv3cks04RTAYxb4bmT\nmPF1GuA2WyfCkvvq8oJY4E8DzR54GLmkpNaGsMUIOjkaltnSiEYn4UjFkQDKveg+\nOdYmNvYTbMwl8Bs7F+REKbjxBKR/khRYcWE2NdW2OThSlvtNhcmWntD3Bw2tQekb\nUMCesD3uc7h3D+V8wrYGi0aTxZDMaV8R0e8bQPBDaSOxa/ViMbom82mnGq9ma51O\nSjr1wJmh2GUMZwsTXaTM8lB9AyUdFsBKmyuM0CltEJ/mRpb/uDPWjiQe+gL57m/y\nlMBkcbsBbnuEccK80cYzsesCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1Ud\nEwEB/wQIMAYBAf8CAQIwHQYDVR0OBBYEFLz7Dt3Drg9gb1VCbeboC6gh6Nt6MB8G\nA1UdIwQYMBaAFLz7Dt3Drg9gb1VCbeboC6gh6Nt6MA0GCSqGSIb3DQEBCwUAA4IB\nAQATDZKrQyrf77Lkj3TZsnDoXY5S3X4APRZVeat+hV39cO2ez+e1IG4gDUi2uxNR\nkfUh108HVA/nRqODrLfunBDCBAWOdeGuyFa2pOtG0T3CSngWfHN4LxzLOotYiFz4\nlcTYPAAew3MM4KmaKqofwG1mBkRCiRWNy/4cFFznCDMrW+srdt3DSAdJbqVneeGV\nusNpvgRXQZgME5+Ib0//FVaX3mxoiySw4RTuazqRuAHvLN9KJGHHuKzKI7+8GqpW\nttAJn4OD/XwYljJ15u48SKbJyP66d/spPDWLEiA60CXKrxWtpdmL0+XDXV2rLLoK\nrCk7sinyyBDYrMhSA3cJyHkC\n-----END CERTIFICATE-----\n","csr":"-----BEGIN CERTIFICATE REQUEST-----\nMIICsjCCAZoCAQAwbTELMAkGA1UEBhMCQ04xEDAOBgNVBAgTB2JlaWppbmcxEDAO\nBgNVBAcTB2JlaWppbmcxEDAOBgNVBAoTB3Rlc3Qtb24xEDAOBgNVBAsTB3Rlc3Qt\nb3UxFjAUBgNVBAMTDWNhLmdhbGF4eS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQCr2ExK4bCGijbgO583p+LbWwyjYodba4oIhaLbTC5MaLfSL93J\nLNOEUwGMW+G5k5jxdRrgNlsnwpL76vKCWOBPA80eeBi5pKTWhrDFCDo5GpbZ0ohG\nJ+FIxZEAyr3oPjnWJjb2E2zMJfAbOxfkRCm48QSkf5IUWHFhNjXVtjk4Upb7TYXJ\nlp7Q9wcNrUHpG1DAnrA97nO4dw/lfMK2BotGk8WQzGlfEdHvG0DwQ2kjsWv1YjG6\nJvNppxqvZmudTko69cCZodhlDGcLE12kzPJQfQMlHRbASpsrjNApbRCf5kaW/7gz\n1o4kHvoC+e5v8pTAZHG7AW57hHHCvNHGM7HrAgMBAAGgADANBgkqhkiG9w0BAQsF\nAAOCAQEAH8uavX8ZPlOfphmHTcXtz5u/uaDSdXSF/Ktamkgc9XHXZHu08QiuU0Ae\nhL4iW6M1jyBImD2Eu/FHFRusBTH/GVZCT3pVnf1ngmCeHRPStEHLnEqzLuA1JySD\nM+ahhldlmbM22eMYrj6U3y9KkD4okiXEfiLCrhZQyA5Om6Ux9k/GCUFhDJLcup3m\nGmZXv+0e9oFChJd7TJNj0WMuJUjikAwFqUHWqhrO6Tn0YUIOn382hi4mSvN50E6R\nGSM8JRgeSn46ERpKG1N6/z9a9vEyMAHYvGidYfz2aykUyB8Xs002ZCmk0BFx/93w\nmVC9NroN3CUDDcj2LWvpkwwnMEeApQ==\n-----END CERTIFICATE REQUEST-----\n","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAq9hMSuGwhoo24DufN6fi21sMo2KHW2uKCIWi20wuTGi30i/d\nySzThFMBjFvhuZOY8XUa4DZbJ8KS++rygljgTwPNHngYuaSk1oawxQg6ORqW2dKI\nRifhSMWRAMq96D451iY29hNszCXwGzsX5EQpuPEEpH+SFFhxYTY11bY5OFKW+02F\nyZae0PcHDa1B6RtQwJ6wPe5zuHcP5XzCtgaLRpPFkMxpXxHR7xtA8ENpI7Fr9WIx\nuibzaacar2ZrnU5KOvXAmaHYZQxnCxNdpMzyUH0DJR0WwEqbK4zQKW0Qn+ZGlv+4\nM9aOJB76Avnub/KUwGRxuwFue4RxwrzRxjOx6wIDAQABAoIBAGGnBqO+4Mtzm3+N\nIgtEkjvI38Ow5+5hjA0Ps94eymiNUXhVzxjVKlWVvdl/FSSZ5V3BCEbFXMOZZGFX\nv/umecEtDdD0ukg0cZ+e5rDw3fU5UOPzKZGEdBcgfigPDh/9zGwPR0hK/ZZ9MJao\n3AjRW0xHWjYIcICzSarOXYVWiemgxtCwj4RXu4Ub83BAUupImtOzKfnQdStZ8wpk\nII13+J4RsB+l7ME6cC9moBJ4jt7G9i6OlZY8/JX2nmTHCyltLzg9QdxViRjyFETk\nvdfhj4YiYlcklTqlZ/WC9rOmQOrDwbx2SBqR5ahLaNFwTcgeFJ3i766GDd+3WkTv\nLAGvWFECgYEA1iwpuyPapPRd0lXJVXCKeLyplDxvLVd+JdqxSd79Gs6vqXX60g3Z\nzojjGQrwkRk0G0fI3hHohvrNaOaZr7MtNY8vfns7bFYXQ0dKD1MWJ9qF9zVjfhiy\nv4Y2HJRNt1HIazRlpSZh5IXWZDTQMM0cwIGDj8I/v+7lQwn9w0hKcPcCgYEAzWfp\nNdVuVCb6dHdN4njYE/DHd/H5CJHIkufP5HKeVCW3t2fjo6sw08AHaMrMrrgkAIfz\nrVfNAuW/cn48IkO8W+qq+1Hc2b/wsNbZB0sM5e5lvdn7sZlDdJJHGJybxwGsbuYZ\nv1lwwbCIj3KtOzFjbXdmMwFycyjtXfUhiky5va0CgYAk+hz+XXNbdYFZVkxbfwG5\nVMFmgYSkbG2wNXDUkzZZ0YOMm30BlTicqw+ifDwKoTJY32zzwl3GKDkcumugZSwS\nCjWl/brFuptrlzxXJv41RUpJ4yLZW4RJAvAGwSgl1W3n7HT8LYNLRDw+ssubEV68\ncd/4Cw6coa9dgrUYaTvJAwKBgQC1c4PaoI50HHLHi9TrqWEITH2JAeLCpTYQQGOw\nJWikYSVoCYhYvxPFGy/wbKZf+h8jsPWcPaHHW3nCBK3OfxPYBvfAR9LXMO3I6iKS\nhMQCIpUSH4xumTuzsLzJix85r8rJtM8t8C7hi7c3MVDCp6BzxTQs/qxB+velNrTI\nXXr/iQKBgQCNXdas7MwBxg5TUAbRbiMbQsJqeatLDc34ojckNxhT9896GLhXILHb\nTBRlOwd5mePRk9RxuCPzXWs3080QWDsMtEqCm2aTRcAtZpM0lAFGDInPVfjbvQ3n\nrr4Psi4TR0dcq2n6xwKDxHQHBw4mMwa+50lkE6l0foJ4LAgO0044ig==\n-----END RSA PRIVATE KEY-----\n"}

# 查看没有任何文件生成
$ ls
ca-csr.json

# 通过cfssl-json命令将证书输出到文件中
# -bare: 指定输出文件的前缀
$ cfssl gencert -initca ca-csr.json | cfssl-json -bare ca
2023/10/30 21:20:31 [INFO] generating a new CA key and certificate from CSR
2023/10/30 21:20:31 [INFO] generate received request
2023/10/30 21:20:31 [INFO] received CSR
2023/10/30 21:20:31 [INFO] generating key: rsa-2048
2023/10/30 21:20:32 [INFO] encoded CSR
2023/10/30 21:20:32 [INFO] signed certificate with serial number 623569630703280871026003415413519558412232890993

# 此时查看就有证书文件了
$ ls
ca.csr ca-csr.json ca-key.pem ca.pem

接下来还要创建ca配置文件,这个配置文件用来指定证书的有效期、签名算法等信息。为了之后创建各种类型的证书做一个通用的配置。

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
$ vim /opt/certs/ca-config.json
$ cat /opt/certs/ca-config.json
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"server": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
},
"client": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"client auth"
]
}
}
}
}

各部分的含义如下:

  • signing: 证书签名配置
  • default: 默认配置
  • expiry: 证书有效期
  • profiles: 证书配置
  • server/client: 各配置的名称
  • usages: 证书用途
    • signing: 证书签名
    • key encipherment: 密钥加密
    • server auth: 服务器端认证
    • client auth: 客户端认证

服务器端证书

这里我们以生成k8s-apiserver的证书为例。

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
# 生成证书配置文件
$ vim /opt/certs/apiserver-csr.json
$ cat /opt/certs/apiserver-csr.json
{
"CN": "k8s-apiserver",
"hosts": [
"127.0.0.1",
"192.168.0.1",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local",
"10.4.7.10",
"10.4.7.21",
"10.4.7.22",
"10.4.7.23"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "beijing",
"L": "beijing",
"O": "test-on",
"OU": "test-ou"
}
]
}

各部分的含义如下:

  • CN: 证书的名称
  • hosts: 证书的域名或者IP地址。必须包含所有能访问的域名或者IP地址,否则会提示证书不安全。如果之后需要添加域名或者IP地址,那么就需要重新生成证书了。
  • key: 密钥配置
    • algo: 密钥算法
    • size: 密钥长度
  • names: 证书的其他信息
    • C: 国家
    • ST: 省份
    • L: 城市
    • O: 组织
    • OU: 组织单位

有了请求文件就可以生成证书了。

1
2
3
4
5
6
7
8
9
10
11
# 生成证书
# genceert: 生成证书
# -ca: 指定CA证书
# -ca-key: 指定CA证书的私钥
# -config: 指定配置文件
# -profile: 指定配置文件中的配置段
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server apiserver-csr.json | cfssl-json -bare apiserver

# 查看生成的证书
$ ls
apiserver.csr apiserver-csr.json apiserver-key.pem apiserver.pem ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem

在K8S中,提取出签发证书时指定的CN(Common Name),作为请求的用户名 (User Name), 从证书中提取O(Organization)字段作为请求用户所属的组 (Group),然后传递给后面的授权模块。

客户端证书

以下是生成客户端证书的步骤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 生成客户端证书配置文件
$ vim /opt/certs/client-csr.json
$ cat /opt/certs/client-csr.json
{
"CN": "k8s-node",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "beijing",
"L": "beijing",
"O": "test-on",
"OU": "test-ou"
}
]
}

接下来,生成客户端证书。

1
2
3
4
5
6
# 生成客户端证书
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client-csr.json | cfssl-json -bare client

# 查看生成的证书
$ ls
apiserver.csr apiserver-csr.json apiserver-key.pem apiserver.pem ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem client.csr client-csr.json client-key.pem client.pem

使用证书

如果我们的apiserver是二进制方式进行部署的,那么我们可以通过以下命令启动apiserver(假定kube-apiserver命令在/opt中)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
./kube-apiserver \
--apiserver-count 2 \
--audit-log-path /data/logs/kubernetes/kube-apiserver/audit-log \
--audit-policy-file ./conf/audit.yaml \
--authorization-mode RBAC \
--client-ca-file ./certs/ca.pem \
--requestheader-client-ca-file ./certs/ca.pem \
--enable-admission-plugins NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota \
--etcd-cafile ./certs/ca.pem \
--etcd-certfile ./certs/client.pem \
--etcd-keyfile ./certs/client-key.pem \
--etcd-servers https://10.4.7.12:2379,https://10.4.7.21:2379,https://10.4.7.22:2379 \
--service-account-key-file ./certs/ca-key.pem \
--service-cluster-ip-range 192.168.0.0/16 \
--service-node-port-range 3000-29999 \
--target-ram-mb=1024 \
--kubelet-client-certificate ./certs/client.pem \
--kubelet-client-key ./certs/client-key.pem \
--log-dir /data/logs/kubernetes/kube-apiserver \
--tls-cert-file ./certs/apiserver.pem \
--tls-private-key-file ./certs/apiserver-key.pem \
--v 2
文章目录
  1. 1. 证书的基本概念
    1. 1.1. 证书的基本格式
    2. 1.2. 数字证书和公钥的关系
      1. 1.2.1. x509证书
  2. 2. https
    1. 2.1. http
    2. 2.2. https
    3. 2.3. SSL
  3. 3. 对称加密和非对称加密
    1. 3.1. 对称加密和非对称加密的优缺点
    2. 3.2. 非对称加密
  4. 4. 认证过程
    1. 4.1. 单向认证
    2. 4.2. 双向认证
  5. 5. 生成自签名SSL证书
    1. 5.1. 生成证书过程
    2. 5.2. openssl
      1. 5.2.1. 根证书私钥
      2. 5.2.2. 生成根证书签发申请文件CSR
      3. 5.2.3. 生成自签名证书 CER
      4. 5.2.4. 一步生成自签名证书
      5. 5.2.5. 生成网站用私钥PEM
      6. 5.2.6. 生成网站用证书签发申请文件CSR
      7. 5.2.7. 准备配置文件
      8. 5.2.8. 生成网站用证书 CER
      9. 5.2.9. 客户端证书
    3. 5.3. 使用证书
      1. 5.3.1. 访问截图
      2. 5.3.2. 证书链
    4. 5.4. cfssl
      1. 5.4.1. 安装
      2. 5.4.2. 生成根证书
      3. 5.4.3. 服务器端证书
      4. 5.4.4. 客户端证书
      5. 5.4.5. 使用证书