一. 简介

加密解密过程都会用到密钥,根据在加密解密过程中使用的密钥是否相同,我们将加密解密算法分为两种:对称加密算法和非对称加密算法。本文首先介绍两种加密算法——“对称加密算法”和”非对称加密算法”。

但今天的主角是非对称加密算法,我们将介绍非对称加密算法的一个前提两个特性

  • 一个前提:公钥可公开,私钥只有自己持有
  • 两个特性:公钥加密、私钥解密;私钥加密、公钥解密

我们将以最简单的公钥加密私钥解密场景来入手,使用图解的方式分析通信双方是如何安全地进行交流。

在此基础上,我们会分析这种场景下的两大安全隐患——消息源的身份认证公钥的归属性
由于无法确认消息源身份,就会导致消息被他人篡改或者消息源否认是自己发送的消息。
由于无法确认公钥的归属性,会导致公钥被调包,从而被第三方窃取消息。

我们针对这两个问题,很自然的引出了数字签名技术CA机构

最后,我们将使用strongswan提供的PKI工具,自己创建一个CA机构,使用自签名创建CA机构的根证书。使用CA机构的证书和私钥来为一个实体终端颁发一个”可信任的证书”。并为此证书验证合法性。

二. 对称加密算法

2.1 对称加密算法定义

对称加密算法使用相同的密钥进行加密和解密。假设明文为P,密文为C,密钥为K,EK是加密算法,DK是解密算法,则:

  • 加密过程可以这样表示:C = EK(P)
  • 解密过程可以这样表示:P = DK(C)

可以看到,加密和解密过程都使用密钥K,我们称这个密钥K为共享密钥

2.2 对称加密算法使用场景

我们假设A与B想要进行加密交流,那么他们一定会有一个分享密钥K的过程。

1
A           ----共享密钥K----->         B         

此后A将使用共享密钥K对信息进行加密,再发给B。
B收到密文后,先用共享密钥进行解密,就可以得到明文了。

1
2
3
4
5
A  
C = E(P)
----密文传输------>
P = D(C)
B

2.3 隐患

如果有一个窃听者C,截获了共享密钥K,那么他不仅可以查看明文,甚至可以修改明文。
所以对称加密中对共享密钥的保护是至关重要的

三. 非对称加密算法

3.1 非对称加密算法定义

非对称加密算法有两个密钥,称为公钥(K)和私钥(K)。
非对称加密算法有两个重要的特性

  • 对明文使用公钥加密、可以使用私钥进行解密
  • 对明文使用私钥加密、可以使用公钥进行解密

非对称加密算法还有一个重要前提,公钥是可以公开给所有人的,私钥只有自己持有,他人不应该获得。

我们接下来介绍使用非对称加密方式来保护通信安全的场景。

3.2 加密场景

加密场景使用的特性是公钥加密、私钥解密
根据非对称加密算法的重要前提——公钥可公开、私钥只有自己持有。这意味着A、B、C都持有B的公钥。

1
2
3
4
5
        A                   B                       C
|-----------| |-----------| |-----------|
| pubkey(B) | | pubkey(B) | | pubkey(B) |
| | | prikey(B) | | |
|-----------| |-----------| |-----------|

A想要向B发送一条加密消息,只需要使用B的公钥对消息进行加密,发送给B即可。
B收到消息后使用自己的私钥解密,就可以得到明文。而C由于没有B的私钥,所以无法解密,这样就保证了通信的安全。

1
2
3
4
5
6
7
8
9
A
使用公钥B加密消息
------------>
使用私钥B解密
B

------------>
没有私钥B、无法解密
C

3.3 加密场景的隐患

以上场景并不是无懈可击的,C无法解密的前提的A在加密时使用了正确的公钥B,如果在A加密前,C将A手中的公钥B调包为了公钥C又会如何呢?

1
2
3
4
5
        A                   B                       C
|-----------| |-----------| |-----------|
| pubkey(C) | | pubkey(B) | | pubkey(B) |
| | | prikey(B) | | prikey(C) |
|-----------| |-----------| |-----------|

A在使用公钥加密时使用了错误的公钥C,那么C就可以使用自己得私钥C来解密这条消息(如下图)。

1
2
3
4
5
6
7
8
9
A
使用公钥C加密消息
------------>
使用私钥B解密
B (解密失败)

------------>
使用私钥C解密
C (窃听成功)

此时,C窃听到了A发给B的消息,更可怕的是C也有B的公钥,意味着C不仅可以窃听、还可以篡改消息(如下图),将篡改后的消息使用公钥B加密,发给B。

1
2
3
4
5
C(窃听者)
篡改消息,使用公钥B加密
------------>
使用私钥B解密
B (获取到被篡改的消息)

这其实是两种隐患,前一个隐患产生的原因是A无法核实手中的公钥的归属,而后者隐患产生的原因是B无法核实消息来源
接下来,我们将根据这两个安全隐患,一一进行解决.

四. 数字签名

4.1 签名介绍

数字签名技术就是为了解决信息完整性、身份认证和非否认性等问题而设计的。

签名场景使用了非对称加密算法的特性是私钥加密、公钥解密

4.2 使用场景

根据非对称加密算法的重要前提——公钥可公开、私钥只有自己持有。这意味着私钥A只有自己持有,B和C都没有。

1
2
3
4
5
        A                   B                       C
|-----------| |-----------| |-----------|
| pubkey(B) | | pubkey(B) | | pubkey(B) |
| prikey(A) | | prikey(B) | | prikey(C) |
|-----------| |-----------| |-----------|

B由于收到了被篡改的消息,但是A又不承认那个消息是自己发的,所以现在B要求,在开始会话前要首先验证会话发起者的身份,也就是要求A进行签名
签名的过程是这样的:

A将自己的身份”我是A”当作明文,使用仅自己持有的私钥A进行加密。密文发给B之后,B使用A的公钥进行解密,可以得到明文”我是A”。这样,A就无法抵赖了。同时B也确定了会话的发起者确实是A,不会被C误导。

1
2
3
4
5
6
7
8
9
10
A
使用私钥A加密"我是A"
------------>
使用公钥A解密
B (得到"我是A",确认对端身份)
C
使用私钥C加密"我是A"
------------>
使用私钥A解密
B (解密失败,确认对端身份存疑)

五. CA权威机构

5.1 解决公钥归属问题

我们使用数字签名可以校验数据的所有者。但是现在还剩下一个问题——如何确定公钥的归属?
这就需要引入一个第三方机构,该机构有足够的权威性,可以得到所有人的信任——CA机构。

还记得我们这个隐患是怎么产生的吗?
A使用了错误的公钥C(本应该使用公钥B),并将数据加密,被C截获流量后使用自己的私钥C进行解密导致了消息泄漏。

现在A吃一堑长一智,他在加密前会先向CA机构确认一下手里的公钥是否是B的,由于CA有足够的权威,如果A得到CA的确认,说这个公钥确实是公钥B,那么A就可以放心地使用这个公钥来加密数据。如果CA说这个公钥不是B的,A就意识到了自己持有一个不被信任的公钥。就不会使用这个公钥了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
A
这个公钥是pubkeyB吗?
------------------>
CA
这个公钥被我信任
<------------------
A
使用公钥加密数据
------------------>
使用私钥B解密
B (得到明文)

这个公钥是pubkeyB吗?
------------------>
CA
这个公钥我不认识
<------------------
A (异常公钥)

至此,关于公钥归属的问题通过引入一个权威机构CA解决了,但实际上针对公钥的归属问题,并不是向上文一样在使用时咨询CA机构。接下来我们介绍证书。

5.2 证书

在实际场景中,公钥是以一种称为证书的形式来公之于众的。证书记录了这个公钥的持有者的很多身份信息,例如国家、省市、IP、域名信息、合法日期等。
这里我举一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
zrf@ubuntu:~/git/strongswan/testing/hosts/moon/etc/swanctl/x509$ pki --print --in moonCert.pem 
subject: "C=CH, O=strongSwan Project, CN=moon.strongswan.org"
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
validity: not before Nov 29 11:21:11 2023, ok
not after Nov 29 11:21:11 2031, ok (expires in 2915 days)
serial: 03
altNames: moon.strongswan.org
flags:
CRL URIs: http://crl.strongswan.org/strongswan.crl
authkeyId: 77:9c:16:d3:9c:25:d1:c4:5e:84:7c:8b:40:a5:1e:c6:f9:c0:de:6a
subjkeyId: 84:81:42:19:2c:83:49:f2:8e:0b:cb:03:c0:f5:ac:a2:87:7e:b8:0c
pubkey: RSA 3072 bits
keyid: 5d:33:7b:8c:da:39:82:57:f6:67:6c:38:95:8c:15:ea:d7:e4:c8:3d
subjkey: 84:81:42:19:2c:83:49:f2:8e:0b:cb:03:c0:f5:ac:a2:87:7e:b8:0c

这个证书记录了公钥信息和公钥属主信息,但是为了确保这种信息的正确的,每个合法证书都由权威机构CA签发,并且在证书中也可以查得到签发机构。

例如上面的属于"C=CH, O=strongSwan Project, CN=moon.strongswan.org"的证书是由"C=CH, O=strongSwan Project, CN=strongSwan Root CA"签发的。可以看到还有合法日期not before Nov 29 11:21:11 2023not after Nov 29 11:21:11 2031。还包括了属主的域名信息moon.strongswan.org

5.3 颁发证书

在实际场景中,B会先生成自己的私钥,接下来将公钥和自己的身份进行绑定,生成一个未经CA机构签名的证书。

他需要向CA机构提交申请,希望得到CA的认证。

CA机构在审核之后如果发现公钥和公钥属主信息真实,就会使用自己的私钥对这个证书进行签名,B就得到了一个被权威机构信任的证书,此时,就可以向外界公布自己的证书了。

1
2
3
4
5
6
7
B
将公钥和个人信息绑定生成未经认证的证书
-------------------------------------->
审核证书
CA
使用CA的私钥签名,颁发合法证书
<-------------------------------------

5.4 验证证书

至此,我们了解到,对于验证者A,他并不是直接持有B的公钥,而是持有B的证书,B的证书中有B的身份信息和颁发证书的权威机构CA的名字。

所以当A使用这个证书时,会首先确认下这个证书是否合法——即验证这个证书否经过CA机构的认可

这个过程也很简单,每个被签发的证书都会被权威机构签名——权威机构使用自己的私钥加密这个证书的一部分。

A可以使用CA机构的公钥来验证签名,如果验签成功,意味着这个证书被权威机构认为是合法的正确的。所以,A就可以信任证书中的身份和公钥。

1
2
3
4
5
6
7
8
9
10
A
使用CA的公钥验签
------------------>
验签成功
证书可信任

使用CA的公钥验签
------------------>
验签不成功
证书不可信

5.5 信任链

当一个实体(例如,网站)提供其数字证书时,该证书通常由某个CA签发。为了建立对这个证书的信任,验证者需要检查证书是否由受信任的CA签发,以及中间是否有其他CA签发的证书。这一系列的证书链构成了信任链。

信任链的验证过程如下:

  • 根证书: 验证者首先查看证书链中的根证书,这是信任的起点。根证书是预先安装在操作系统或应用程序中的。

  • 中间证书: 验证者检查目标证书是否由某个受信任的CA签发。如果是,验证者将查找该CA的证书(中间证书),并验证它的合法性。

  • 目标证书: 最终,验证者检查目标证书,确保它是由中间证书签发的,并且中间证书是由根证书签发的。如果所有证书都是有效的,且目标证书的签名有效,则建立了信任。

六. 创建CA并颁布证书实验

5.1 创建一个CA

我使用的是strongswan提供的PKI命令行

  • 使用pki --gen生成一个类型的ed25519的密钥,其中type可选rsa|ecdsa|ed25519|ed448|bliss,这是CA机构的私钥,一定要保存好。
1
2
3
4
5
zrf@debian:/tmp$ pki --gen --type ed25519 --outform pem > strongswanKey.pem
zrf@debian:/tmp$ cat strongswanKey.pem
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIK0uCyvAxja4l+XcViuofSQwZXPjgAw4CTEfAk2q5vUe
-----END PRIVATE KEY-----
  • 使用pki --self命令将相应的公钥打包成自签名的CA证书,其生命周期为 10 年(3652 天)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
zrf@debian:/tmp$ pki --self --ca --lifetime 3652 --in strongswanKey.pem \
--dn "C=CH, O=strongSwan, CN=strongSwan Root CA" \
--outform pem > strongswanCert.pem
zrf@debian:/tmp$ cat strongswanCert.pem
-----BEGIN CERTIFICATE-----
MIIBdjCCASigAwIBAgIIdpbqQE52YcUwBQYDK2VwMD8xCzAJBgNVBAYTAkNIMRMw
EQYDVQQKEwpzdHJvbmdTd2FuMRswGQYDVQQDExJzdHJvbmdTd2FuIFJvb3QgQ0Ew
HhcNMjMxMjA1MTEwNDA3WhcNMzMxMjA0MTEwNDA3WjA/MQswCQYDVQQGEwJDSDET
MBEGA1UEChMKc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBSb290IENB
MCowBQYDK2VwAyEA+g2agkMD3vbinYSC4zN+3sCxmURttTCW09yWKFkxmNKjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRc9qnq
ufZKbAfoGlKP6oidsEWPDTAFBgMrZXADQQA+7lHofkL3XZwBAyJ8HD+6RMw9LHlN
YcLRROfzUHma3m0ExR1I6mg5YNxUBNXc3jh2/CIOGY65Ma8Z9zjwWWIO
-----END CERTIFICATE-----
  • 使用pki --print命令打印证书:
1
2
3
4
5
6
7
8
9
10
11
zrf@debian:/tmp$ pki --print --in strongswanCert.pem
subject: "C=CH, O=strongSwan, CN=strongSwan Root CA"
issuer: "C=CH, O=strongSwan, CN=strongSwan Root CA"
validity: not before Dec 05 19:04:07 2023, ok
not after Dec 04 19:04:07 2033, ok (expires in 3651 days)
serial: 76:96:ea:40:4e:76:61:c5
flags: CA CRLSign self-signed
subjkeyId: 5c:f6:a9:ea:b9:f6:4a:6c:07:e8:1a:52:8f:ea:88:9d:b0:45:8f:0d
pubkey: ED25519 256 bits
keyid: 45:2f:1d:0b:28:7f:6f:9f:1d:09:96:7b:d6:ed:d1:02:d5:d1:b0:c8
subjkey: 5c:f6:a9:ea:b9:f6:4a:6c:07:e8:1a:52:8f:ea:88:9d:b0:45:8f:0d

至此,我们就生成了自签名的CA证书,作为我们信任链的根证书

5.2 为一个实体创建私钥

现在我们为一个实体moon创建证书

  • 使用pki --gen命令创建一个类型为ed25519的私钥
1
2
3
4
5
zrf@debian:/tmp$ pki --gen --type ed25519 --outform pem > moonKey.pem
zrf@debian:/tmp$ cat moonKey.pem
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEICK+zDZ22cxz0c1mqs76+3ujfkIN3rOAlMK7QNZavBSa
-----END PRIVATE KEY-----
  • 使用pki --print --type来打印私钥
1
2
3
4
zrf@debian:/tmp$ pki --print --type ed25519 --in moonKey.pem
privkey: ED25519 256 bits
keyid: 5b:4a:c8:a6:d4:21:8d:6c:08:1b:c2:17:c4:ef:e2:04:c3:40:42:48
subjkey: f3:02:c2:ba:0b:d7:97:53:b5:cc:ce:cb:8b:46:22:58:a4:2b:d6:c0

5.3 向CA提出证书请求

  • 使用pki --req向CA提出一个创建证书的请求,次过程中将私钥公钥和实体信息进行绑定
1
2
3
4
5
6
7
8
9
10
11
12
zrf@debian:/tmp$ pki --req --type priv --in moonKey.pem \
--dn "C=CH, O=strongswan, CN=moon.strongswan.org" \
--san moon.strongswan.org --outform pem > moonReq.pem
zrf@debian:/tmp$ cat moonReq.pem
-----BEGIN CERTIFICATE REQUEST-----
MIHxMIGkAgEAMEAxCzAJBgNVBAYTAkNIMRMwEQYDVQQKEwpzdHJvbmdzd2FuMRww
GgYDVQQDExNtb29uLnN0cm9uZ3N3YW4ub3JnMCowBQYDK2VwAyEAaidKIJJYTIwx
HCnBX8YplBgMm0CcAuaytEsRyvXgMqqgMTAvBgkqhkiG9w0BCQ4xIjAgMB4GA1Ud
EQQXMBWCE21vb24uc3Ryb25nc3dhbi5vcmcwBQYDK2VwA0EARVPxH4pSTKqNeKdj
5cBopNMVk5h07BT2cM0U/J53rxf/jYFitRxvbHhMEumea53mnT9for2Mx6M3Yp0E
ARasDg==
-----END CERTIFICATE REQUEST-----

5.4 CA为实体颁发证书

  • 使用pki --issue命令,基于moonReq.pem请求,CA将颁发moon实体的证书,可以看到我们需要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
29
zrf@debian:/tmp$ pki --issue --cacert strongswanCert.pem --cakey strongswanKey.pem \
--type pkcs10 --in moonReq.pem --serial 01 --lifetime 1826 \
--outform pem > moonCert.pem

zrf@debian:/tmp$ cat moonCert.pem
-----BEGIN CERTIFICATE-----
MIIBcTCCASOgAwIBAgIBATAFBgMrZXAwPzELMAkGA1UEBhMCQ0gxEzARBgNVBAoT
CnN0cm9uZ1N3YW4xGzAZBgNVBAMTEnN0cm9uZ1N3YW4gUm9vdCBDQTAeFw0yMzEy
MDUxMTIwMzRaFw0yODEyMDQxMTIwMzRaMEAxCzAJBgNVBAYTAkNIMRMwEQYDVQQK
EwpzdHJvbmdzd2FuMRwwGgYDVQQDExNtb29uLnN0cm9uZ3N3YW4ub3JnMCowBQYD
K2VwAyEAaidKIJJYTIwxHCnBX8YplBgMm0CcAuaytEsRyvXgMqqjQzBBMB8GA1Ud
IwQYMBaAFFz2qeq59kpsB+gaUo/qiJ2wRY8NMB4GA1UdEQQXMBWCE21vb24uc3Ry
b25nc3dhbi5vcmcwBQYDK2VwA0EAYYzVbyuGOWHGCJ2DHcSl24+WzkaC/7D8kjg/
ymPYLF1of31Sdm/Z9zLT0gFUBBWZt5ckHGoaURgFYdOgEdgzCQ==
-----END CERTIFICATE-----

zrf@debian:/tmp$ pki --print --in moonCert.pem
subject: "C=CH, O=strongswan, CN=moon.strongswan.org"
issuer: "C=CH, O=strongSwan, CN=strongSwan Root CA"
validity: not before Dec 05 19:20:34 2023, ok
not after Dec 04 19:20:34 2028, ok (expires in 1825 days)
serial: 01
altNames: moon.strongswan.org
flags:
authkeyId: 5c:f6:a9:ea:b9:f6:4a:6c:07:e8:1a:52:8f:ea:88:9d:b0:45:8f:0d
subjkeyId: f3:02:c2:ba:0b:d7:97:53:b5:cc:ce:cb:8b:46:22:58:a4:2b:d6:c0
pubkey: ED25519 256 bits
keyid: 5b:4a:c8:a6:d4:21:8d:6c:08:1b:c2:17:c4:ef:e2:04:c3:40:42:48
subjkey: f3:02:c2:ba:0b:d7:97:53:b5:cc:ce:cb:8b:46:22:58:a4:2b:d6:c0

5.5 验证实体证书

  • 使用pki --verify来验证一个证书是否被CA信任
1
2
3
4
5
zrf@debian:/tmp$ pki --verify  --in moonCert.pem --cacert strongswanCert.pem 
using certificate "C=CH, O=strongswan, CN=moon.strongswan.org"
using trusted ca certificate "C=CH, O=strongSwan, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 0
certificate trusted, lifetimes valid

至此,我们就完成了CA的创建、使用CA为最终实体创建、颁发、验证证书的所有流程。