一. 简介

1.1 主要内容

本文将基于strongswan,详细阐述IKE(Internet Key Exchange,网络密钥交换)协议中的以下关键问题:

  • 为了避免密钥泄漏,两个站点如何使用DH算法,衍生出一个共同的密钥shared Diffie Hellman secret
  • 如何使用一系列衍生密钥,在IKE_AUTH阶段保护双方的身份信息
  • 如何通过证书认证,确保对端的合法身份。

本文将使用SM2国密证书,通过IKEv1协议,建立IKE SA。

1.2 全文结构

  • 创建根CA证书,并为两个站点颁发国密证书。
  • 使用SM3摘要算法、SM4加密算法、SM2 DH算法协商IKE SA
  • 展示SM2 DH算法在两个站点生成共享密钥g_xy,并使用衍生密钥KaIKE_AUTH阶段加密。
  • 分析ISAKMP的IKE_AUTH阶段的报文
  • 双方如何通过证书来验证对端的合法身份。

1.3 网络拓扑

本为旨在分析IKE SA的建立过程,所以使用最基础的host-host场景

1
2
| 10.1.0.1 |    ===    | 10.1.0.2 |
sun moon

1.4 参考链接

IKEv1 RFC 22409
strongswan官网文档
非对称加密算法与CA机构

二. 创建证书

本节所有证书的密钥对都将使用SM2算法生成、并使用SM2-with-SM3作为证书的签名算法。

2.1 创建自签名CA根证书

  • 使用SM2算法生成CA的私钥
1
2
3
4
5
6
7
8
zrf@debian:/tmp/sm2_cert$ pki --gen --type sm2 --outform pem > caKey.pem
zrf@debian:/tmp/sm2_cert$
zrf@debian:/tmp/sm2_cert$ cat caKey.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIFEWgyAmZ1TN8C6X1rlFwYdbetwuP/ieFgBbk9g+8ZTWoAoGCCqBHM9V
AYItoUQDQgAEox4kzxpegfjQ+R+Kp8VXytbAFAF4QSKKrdeOVdo0MFWi3FJtChsL
7ZbHsJk4US7D+NDFeadzsZ7kDVVWKWh0aA==
-----END EC PRIVATE KEY-----
  • 创建自签名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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
zrf@debian:/tmp/sm2_cert$ pki --self --ca --lifetime 3652 --in caKey.pem --dn "C=CN, O=zrf, CN=zrf CA" --outform pem > caCert.pem
zrf@debian:/tmp/sm2_cert$
zrf@debian:/tmp/sm2_cert$ openssl x509 --in caCert.pem -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 6144011213922486727 (0x5543e9a58dddc9c7)
Signature Algorithm: SM2-with-SM3
Issuer: C=CN, O=zrf, CN=zrf CA
Validity
Not Before: Mar 30 08:01:08 2024 GMT
Not After : Mar 30 08:01:08 2034 GMT
Subject: C=CN, O=zrf, CN=zrf CA
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:a3:1e:24:cf:1a:5e:81:f8:d0:f9:1f:8a:a7:c5:
57:ca:d6:c0:14:01:78:41:22:8a:ad:d7:8e:55:da:
34:30:55:a2:dc:52:6d:0a:1b:0b:ed:96:c7:b0:99:
38:51:2e:c3:f8:d0:c5:79:a7:73:b1:9e:e4:0d:55:
56:29:68:74:68
ASN1 OID: SM2
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
X509v3 Subject Key Identifier:
8A:12:AF:31:8E:83:B5:4B:2C:BB:B1:5D:DE:FC:4F:3B:63:A8:2A:F9
Signature Algorithm: SM2-with-SM3
Signature Value:
2d:43:ea:e0:51:10:b5:5e:54:0e:38:9f:37:5d:4d:8b:91:4e:
23:05:4f:c9:58:0d:b1:f9:d9:39:1a:bd:b5:31:0f:2a:ee:75:
b3:41:d8:8f:d3:a9:41:f9:2e:87:17:2f:c9:4d:81:3d:e6:d3:
49:25:b4:14:42:cd:a7:56:42:79
-----BEGIN CERTIFICATE-----
MIIBjTCCATigAwIBAgIIVUPppY3dyccwDAYIKoEcz1UBg3UFADAsMQswCQYDVQQG
EwJDTjEMMAoGA1UEChMDenJmMQ8wDQYDVQQDEwZ6cmYgQ0EwHhcNMjQwMzMwMDgw
MTA4WhcNMzQwMzMwMDgwMTA4WjAsMQswCQYDVQQGEwJDTjEMMAoGA1UEChMDenJm
MQ8wDQYDVQQDEwZ6cmYgQ0EwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAASjHiTP
Gl6B+ND5H4qnxVfK1sAUAXhBIoqt145V2jQwVaLcUm0KGwvtlsewmThRLsP40MV5
p3OxnuQNVVYpaHRoo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQUihKvMY6DtUssu7Fd3vxPO2OoKvkwDAYIKoEcz1UBg3UFAANB
AC1D6uBRELVeVA44nzddTYuRTiMFT8lYDbH52TkavbUxDyrudbNB2I/TqUH5LocX
L8lNgT3m00kltBRCzadWQnk=
-----END CERTIFICATE-----
zrf@debian:/tmp/sm2_cert$

2.2 为sun站点颁发证书

  • 创建sun站点私钥
1
2
3
4
5
6
7
zrf@debian:/tmp/sm2_cert$ pki --gen --type sm2 --outform pem > sunKey.pem
zrf@debian:/tmp/sm2_cert$ cat sunKey.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIMLdr3KCdINvkTkxzuvCE+8QU5d4T6LlsxEbuBecTT7MoAoGCCqBHM9V
AYItoUQDQgAEFs+zA+xs+8UJPZfjuCwMLJ9BOrri2YlHJQXziNSJd+5K1/eAo+dM
62ioSnUXBOSOTiSwfZ1Ptp0nhr5G2lR6YQ==
-----END EC PRIVATE KEY-----
  • 提交证书申请
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
zrf@debian:/tmp/sm2_cert$ pki --req --type sm2 --in sunKey.pem --dn "C=CN, O=zrf, CN=zrf sun" --san 10.1.0.1 --outform pem > sunReq.pem
zrf@debian:/tmp/sm2_cert$
zrf@debian:/tmp/sm2_cert$ openssl req -in sunReq.pem -text
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C=CN, O=zrf, CN=zrf sun
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:16:cf:b3:03:ec:6c:fb:c5:09:3d:97:e3:b8:2c:
0c:2c:9f:41:3a:ba:e2:d9:89:47:25:05:f3:88:d4:
89:77:ee:4a:d7:f7:80:a3:e7:4c:eb:68:a8:4a:75:
17:04:e4:8e:4e:24:b0:7d:9d:4f:b6:9d:27:86:be:
46:da:54:7a:61
ASN1 OID: SM2
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
IP Address:10.1.0.1
Signature Algorithm: SM2-with-SM3
Signature Value:
64:0e:fe:fc:7d:3f:71:f9:60:a1:38:2f:af:94:18:66:15:ae:
b4:f3:ae:45:b7:72:01:2b:f8:69:6b:4e:33:d3:9f:f0:e4:59:
ea:32:c9:28:01:8c:fa:f3:68:ad:b5:83:a0:cc:e8:17:b8:40:
17:b3:ea:eb:d9:cf:16:db:03:49
-----BEGIN CERTIFICATE REQUEST-----
MIIBBTCBsQIBADAtMQswCQYDVQQGEwJDTjEMMAoGA1UEChMDenJmMRAwDgYDVQQD
Ewd6cmYgc3VuMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEFs+zA+xs+8UJPZfj
uCwMLJ9BOrri2YlHJQXziNSJd+5K1/eAo+dM62ioSnUXBOSOTiSwfZ1Ptp0nhr5G
2lR6YaAiMCAGCSqGSIb3DQEJDjETMBEwDwYDVR0RBAgwBocECgEAATAMBggqgRzP
VQGDdQUAA0EAZA7+/H0/cflgoTgvr5QYZhWutPOuRbdyASv4aWtOM9Of8ORZ6jLJ
KAGM+vNorbWDoMzoF7hAF7Pq69nPFtsDSQ==
-----END CERTIFICATE REQUEST-----
  • CA为sun颁发五年有效期的SM2国密证书
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
zrf@debian:/tmp/sm2_cert$ openssl x509 -in sunCert.pem -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: SM2-with-SM3
Issuer: C=CN, O=zrf, CN=zrf CA
Validity
Not Before: Mar 30 08:13:17 2024 GMT
Not After : Mar 30 08:13:17 2029 GMT
Subject: C=CN, O=zrf, CN=zrf sun
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:16:cf:b3:03:ec:6c:fb:c5:09:3d:97:e3:b8:2c:
0c:2c:9f:41:3a:ba:e2:d9:89:47:25:05:f3:88:d4:
89:77:ee:4a:d7:f7:80:a3:e7:4c:eb:68:a8:4a:75:
17:04:e4:8e:4e:24:b0:7d:9d:4f:b6:9d:27:86:be:
46:da:54:7a:61
ASN1 OID: SM2
X509v3 extensions:
X509v3 Authority Key Identifier:
8A:12:AF:31:8E:83:B5:4B:2C:BB:B1:5D:DE:FC:4F:3B:63:A8:2A:F9
X509v3 Subject Alternative Name:
IP Address:10.1.0.1
Signature Algorithm: SM2-with-SM3
Signature Value:
4f:88:b7:e0:00:f0:b9:8f:8c:72:74:fb:fd:97:20:72:98:2b:
bd:a5:31:fa:eb:6b:ee:15:91:7b:9d:5b:8a:5e:3b:a8:0f:1c:
44:c9:f3:13:f6:13:c7:82:19:af:b7:ca:80:d6:1c:f0:07:d7:
bd:17:9f:bd:45:70:25:c0:e2:d3
-----BEGIN CERTIFICATE-----
MIIBeTCCASSgAwIBAgIBATAMBggqgRzPVQGDdQUAMCwxCzAJBgNVBAYTAkNOMQww
CgYDVQQKEwN6cmYxDzANBgNVBAMTBnpyZiBDQTAeFw0yNDAzMzAwODEzMTdaFw0y
OTAzMzAwODEzMTdaMC0xCzAJBgNVBAYTAkNOMQwwCgYDVQQKEwN6cmYxEDAOBgNV
BAMTB3pyZiBzdW4wWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAQWz7MD7Gz7xQk9
l+O4LAwsn0E6uuLZiUclBfOI1Il37krX94Cj50zraKhKdRcE5I5OJLB9nU+2nSeG
vkbaVHphozQwMjAfBgNVHSMEGDAWgBSKEq8xjoO1Syy7sV3e/E87Y6gq+TAPBgNV
HREECDAGhwQKAQABMAwGCCqBHM9VAYN1BQADQQBPiLfgAPC5j4xydPv9lyBymCu9
pTH662vuFZF7nVuKXjuoDxxEyfMT9hPHghmvt8qA1hzwB9e9F5+9RXAlwOLT
-----END CERTIFICATE-----
  • 验证sun证书的合法性
1
2
3
4
5
zrf@debian:/tmp/sm2_cert$ pki -v -c caCert.pem -i sunCert.pem 
using certificate "C=CN, O=zrf, CN=zrf sun"
using trusted ca certificate "C=CN, O=zrf, CN=zrf CA"
reached self-signed root ca with a path length of 0
certificate trusted, lifetimes valid

2.3 为moon站点颁发证书

  • 创建moon站点私钥
1
2
3
4
5
6
7
8
zrf@debian:/tmp/sm2_cert$ pki --gen --type sm2 --outform pem > moonKey.pem
zrf@debian:/tmp/sm2_cert$
zrf@debian:/tmp/sm2_cert$ cat moonKey.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIFUaXdWOslMUud4+Uld8UrxLBtdx8MQAtFYgEtKXb+k0oAoGCCqBHM9V
AYItoUQDQgAEMZenVS79IO6SP1nEapgeHEduGQJ5BqmtaZQFJ5nhxX1dykYaZniq
xNVcK6HYwR3UTYcybbIsH7QpbV2+avbvbA==
-----END EC PRIVATE KEY-----
  • 提交证书申请
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
zrf@debian:/tmp/sm2_cert$ pki --req --type sm2 --in moonKey.pem --dn "C=CN, O=zrf, CN=zrf moon" --san 10.1.0.2 --outform pem > moonReq.pem
zrf@debian:/tmp/sm2_cert$
zrf@debian:/tmp/sm2_cert$ openssl req --in moonReq.pem -text
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C=CN, O=zrf, CN=zrf moon
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:31:97:a7:55:2e:fd:20:ee:92:3f:59:c4:6a:98:
1e:1c:47:6e:19:02:79:06:a9:ad:69:94:05:27:99:
e1:c5:7d:5d:ca:46:1a:66:78:aa:c4:d5:5c:2b:a1:
d8:c1:1d:d4:4d:87:32:6d:b2:2c:1f:b4:29:6d:5d:
be:6a:f6:ef:6c
ASN1 OID: SM2
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
IP Address:10.1.0.2
Signature Algorithm: SM2-with-SM3
Signature Value:
b0:e5:e7:43:40:0a:53:22:19:7f:c2:fc:9c:bd:e5:a5:12:17:
4e:6c:d2:cb:85:d4:af:aa:02:f4:e4:fa:a5:fb:e8:d9:b6:cb:
a5:81:f3:5b:76:4b:46:ba:31:d2:0c:97:ad:25:28:b5:f0:df:
14:73:d6:40:13:3b:f5:ef:31:7b
-----BEGIN CERTIFICATE REQUEST-----
MIIBBjCBsgIBADAuMQswCQYDVQQGEwJDTjEMMAoGA1UEChMDenJmMREwDwYDVQQD
Ewh6cmYgbW9vbjBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABDGXp1Uu/SDukj9Z
xGqYHhxHbhkCeQaprWmUBSeZ4cV9XcpGGmZ4qsTVXCuh2MEd1E2HMm2yLB+0KW1d
vmr272ygIjAgBgkqhkiG9w0BCQ4xEzARMA8GA1UdEQQIMAaHBAoBAAIwDAYIKoEc
z1UBg3UFAANBALDl50NAClMiGX/C/Jy95aUSF05s0suF1K+qAvTk+qX76Nm2y6WB
81t2S0a6MdIMl60lKLXw3xRz1kATO/XvMXs=
-----END CERTIFICATE REQUEST-----
  • CA为moon颁发五年有效期的SM2国密证书
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
zrf@debian:/tmp/sm2_cert$ pki --issue --cacert caCert.pem --cakey caKey.pem --type pkcs10 --in moonReq.pem --serial 02 --lifetime 1826 --outform pem > moonCert.pem
zrf@debian:/tmp/sm2_cert$
zrf@debian:/tmp/sm2_cert$ openssl x509 -in moonCert.pem -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: SM2-with-SM3
Issuer: C=CN, O=zrf, CN=zrf CA
Validity
Not Before: Mar 30 08:19:06 2024 GMT
Not After : Mar 30 08:19:06 2029 GMT
Subject: C=CN, O=zrf, CN=zrf moon
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:31:97:a7:55:2e:fd:20:ee:92:3f:59:c4:6a:98:
1e:1c:47:6e:19:02:79:06:a9:ad:69:94:05:27:99:
e1:c5:7d:5d:ca:46:1a:66:78:aa:c4:d5:5c:2b:a1:
d8:c1:1d:d4:4d:87:32:6d:b2:2c:1f:b4:29:6d:5d:
be:6a:f6:ef:6c
ASN1 OID: SM2
X509v3 extensions:
X509v3 Authority Key Identifier:
8A:12:AF:31:8E:83:B5:4B:2C:BB:B1:5D:DE:FC:4F:3B:63:A8:2A:F9
X509v3 Subject Alternative Name:
IP Address:10.1.0.2
Signature Algorithm: SM2-with-SM3
Signature Value:
4e:4a:cf:13:13:15:f8:5d:a0:85:90:67:ff:86:7c:d4:5c:14:
2d:06:cd:e7:19:d7:5b:ee:c2:b5:c0:fa:e3:44:a7:c3:eb:3d:
26:1e:4d:33:d9:43:2f:69:9b:5a:fc:f2:37:75:1e:cc:e4:c8:
98:76:36:ca:92:97:0f:ff:9f:58
-----BEGIN CERTIFICATE-----
MIIBejCCASWgAwIBAgIBAjAMBggqgRzPVQGDdQUAMCwxCzAJBgNVBAYTAkNOMQww
CgYDVQQKEwN6cmYxDzANBgNVBAMTBnpyZiBDQTAeFw0yNDAzMzAwODE5MDZaFw0y
OTAzMzAwODE5MDZaMC4xCzAJBgNVBAYTAkNOMQwwCgYDVQQKEwN6cmYxETAPBgNV
BAMTCHpyZiBtb29uMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEMZenVS79IO6S
P1nEapgeHEduGQJ5BqmtaZQFJ5nhxX1dykYaZniqxNVcK6HYwR3UTYcybbIsH7Qp
bV2+avbvbKM0MDIwHwYDVR0jBBgwFoAUihKvMY6DtUssu7Fd3vxPO2OoKvkwDwYD
VR0RBAgwBocECgEAAjAMBggqgRzPVQGDdQUAA0EATkrPExMV+F2ghZBn/4Z81FwU
LQbN5xnXW+7CtcD640Snw+s9Jh5NM9lDL2mbWvzyN3UezOTImHY2ypKXD/+fWA==
-----END CERTIFICATE-----
  • 验证moon证书的合法性
1
2
3
4
5
zrf@debian:/tmp/sm2_cert$ pki -v -c caCert.pem -i moonCert.pem 
using certificate "C=CN, O=zrf, CN=zrf moon"
using trusted ca certificate "C=CN, O=zrf, CN=zrf CA"
reached self-signed root ca with a path length of 0
certificate trusted, lifetimes valid

三. 使用国密算法协商IKE SA

3.1 网络拓扑

1
2
| 10.1.0.1 |    ===    | 10.1.0.2 |
sun moon
1
2
3
4
5
6
7
zrf@debian:/etc/netns/sun/swanctl$ sudo cp /tmp/sm2_cert/sunCert.pem x509/
zrf@debian:/etc/netns/sun/swanctl$ sudo cp /tmp/sm2_cert/sunKey.pem private/
zrf@debian:/etc/netns/sun/swanctl$ sudo cp /tmp/sm2_cert/caCert.pem x509ca/

zrf@debian:/etc/netns/moon/swanctl$ sudo cp /tmp/sm2_cert/moonCert.pem x509/
zrf@debian:/etc/netns/moon/swanctl$ sudo cp /tmp/sm2_cert/moonKey.pem private/
zrf@debian:/etc/netns/moon/swanctl$ sudo cp /tmp/sm2_cert/caCert.pem x509ca/

3.2 sun站点配置

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:~$ cat /etc/netns/sun/swanctl/conf.d/swanctl.conf 
connections {

h2h {
local_addrs = 10.1.0.1
remote_addrs = 10.1.0.2

local {
auth = pubkey
certs = sunCert.pem
id = "C=CN, O=zrf, CN=zrf sun"
}
remote {
auth = pubkey
id = "C=CN, O=zrf, CN=zrf moon"
}
children {
h2h {
start_action = trap
esp_proposals = aes128-sha1
remote_ts = 10.1.0.2/32
local_ts = 10.1.0.1/32
}
}
version = 1
proposals = sm2-sm3-sm4
mobike = no
}
}

3.3 moon站点配置

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
connections {

h2h {
local_addrs = 10.1.0.2
remote_addrs = 10.1.0.1

local {
auth = pubkey
certs = moonCert.pem
id = "C=CN, O=zrf, CN=zrf moon"
}
remote {
auth = pubkey
id = "C=CN, O=zrf, CN=zrf sun"
}
children {
h2h {
start_action = trap
esp_proposals = aes128-sha1
remote_ts = 10.1.0.1/32
local_ts = 10.1.0.2/32
}
}
version = 1
proposals = sm2-sm3-sm4
mobike = no
}
}

3.3 两个站点加载配置并读取证书

请注意,此时各自站点只有自己的证书和CA证书,并没有对端的证书,这意味着在协商IKE时会请求对端发送证书

  • sun
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
sudo ip netns exec sun  /usr/local/sbin/swanctl -q
loaded certificate from '/etc/swanctl/x509/sunCert.pem'
loaded certificate from '/etc/swanctl/x509ca/caCert.pem'
loaded SM2 key from '/etc/swanctl/private/sunKey.pem'
no authorities found, 0 unloaded
no pools found, 0 unloaded
loaded connection 'h2h'
successfully loaded 1 connections, 0 unloaded


zrf@debian:~$ sudo ip netns exec sun /usr/local/sbin/swanctl -x

List of X.509 End Entity Certificates

subject: "C=CN, O=zrf, CN=zrf sun"
issuer: "C=CN, O=zrf, CN=zrf CA"
validity: not before Mar 30 16:13:17 2024, ok
not after Mar 30 16:13:17 2029, ok (expires in 1825 days)
serial: 01
altNames: 10.1.0.1
flags:
authkeyId: 8a:12:af:31:8e:83:b5:4b:2c:bb:b1:5d:de:fc:4f:3b:63:a8:2a:f9
subjkeyId: 8c:8d:09:68:79:d1:71:d2:6f:78:fd:62:98:1b:7f:9b:92:dd:c7:9d
pubkey: SM2 256 bits, has private key
keyid: 67:ad:37:f5:6a:58:8c:ae:11:d9:ef:21:56:0b:5d:7f:0d:7f:8f:9a
subjkey: 8c:8d:09:68:79:d1:71:d2:6f:78:fd:62:98:1b:7f:9b:92:dd:c7:9d

List of X.509 CA Certificates

subject: "C=CN, O=zrf, CN=zrf CA"
issuer: "C=CN, O=zrf, CN=zrf CA"
validity: not before Mar 30 16:01:08 2024, ok
not after Mar 30 16:01:08 2034, ok (expires in 3651 days)
serial: 55:43:e9:a5:8d:dd:c9:c7
flags: CA CRLSign self-signed
subjkeyId: 8a:12:af:31:8e:83:b5:4b:2c:bb:b1:5d:de:fc:4f:3b:63:a8:2a:f9
pubkey: SM2 256 bits
keyid: e8:56:19:cf:d3:ec:b7:1e:2e:fc:78:d5:e7:ef:22:8a:dd:f1:53:3b
subjkey: 8a:12:af:31:8e:83:b5:4b:2c:bb:b1:5d:de:fc:4f:3b:63:a8:2a:f9
zrf@debian:~$
  • moon
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
zrf@debian:~$ sudo ip netns exec moon /usr/local/sbin/swanctl -q
loaded certificate from '/etc/swanctl/x509/moonCert.pem'
loaded certificate from '/etc/swanctl/x509ca/caCert.pem'
loaded SM2 key from '/etc/swanctl/private/moonKey.pem'
no authorities found, 0 unloaded
no pools found, 0 unloaded
loaded connection 'h2h'
successfully loaded 1 connections, 0 unloaded
zrf@debian:~$
zrf@debian:~$
zrf@debian:~$
zrf@debian:~$ sudo ip netns exec moon /usr/local/sbin/swanctl -x

List of X.509 End Entity Certificates

subject: "C=CN, O=zrf, CN=zrf moon"
issuer: "C=CN, O=zrf, CN=zrf CA"
validity: not before Mar 30 16:19:06 2024, ok
not after Mar 30 16:19:06 2029, ok (expires in 1825 days)
serial: 02
altNames: 10.1.0.2
flags:
authkeyId: 8a:12:af:31:8e:83:b5:4b:2c:bb:b1:5d:de:fc:4f:3b:63:a8:2a:f9
subjkeyId: c2:21:56:d6:6f:19:e0:75:4f:95:7e:1a:0f:2a:d2:66:29:2a:5a:cc
pubkey: SM2 256 bits, has private key
keyid: d7:b6:df:86:cf:dd:42:c5:1f:7b:64:c9:2e:a0:29:bb:f5:56:aa:aa
subjkey: c2:21:56:d6:6f:19:e0:75:4f:95:7e:1a:0f:2a:d2:66:29:2a:5a:cc

List of X.509 CA Certificates

subject: "C=CN, O=zrf, CN=zrf CA"
issuer: "C=CN, O=zrf, CN=zrf CA"
validity: not before Mar 30 16:01:08 2024, ok
not after Mar 30 16:01:08 2034, ok (expires in 3651 days)
serial: 55:43:e9:a5:8d:dd:c9:c7
flags: CA CRLSign self-signed
subjkeyId: 8a:12:af:31:8e:83:b5:4b:2c:bb:b1:5d:de:fc:4f:3b:63:a8:2a:f9
pubkey: SM2 256 bits
keyid: e8:56:19:cf:d3:ec:b7:1e:2e:fc:78:d5:e7:ef:22:8a:dd:f1:53:3b
subjkey: 8a:12:af:31:8e:83:b5:4b:2c:bb:b1:5d:de:fc:4f:3b:63:a8:2a:f9

3.4 协商IKE SA

moon站点发起协商:

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
zrf@debian:~$ sudo ip netns exec moon /usr/local/sbin/swanctl -i -i h2h
[IKE] initiating Main Mode IKE_SA h2h[3] to 10.1.0.1
[ENC] generating ID_PROT request 0 [ SA V V V V V ]
[NET] sending packet: from 10.1.0.2[500] to 10.1.0.1[500] (176 bytes)
[NET] received packet: from 10.1.0.1[500] to 10.1.0.2[500] (156 bytes)
[ENC] parsed ID_PROT response 0 [ SA V V V V ]
[IKE] received XAuth vendor ID
[IKE] received DPD vendor ID
[IKE] received FRAGMENTATION vendor ID
[IKE] received NAT-T (RFC 3947) vendor ID
[CFG] selected proposal: IKE:SM4_CBC/HMAC_SM3/PRF_HMAC_SM3/CURVE_SM2
[ENC] generating ID_PROT request 0 [ KE No NAT-D NAT-D ]
[NET] sending packet: from 10.1.0.2[500] to 10.1.0.1[500] (204 bytes)
[NET] received packet: from 10.1.0.1[500] to 10.1.0.2[500] (255 bytes)
[ENC] parsed ID_PROT response 0 [ KE No CERTREQ NAT-D NAT-D ]
[IKE] received cert request for 'C=CN, O=zrf, CN=zrf CA'
[IKE] sending cert request for "C=CN, O=zrf, CN=zrf CA"
[IKE] authentication of 'C=CN, O=zrf, CN=zrf moon' (myself) successful
[IKE] sending end entity cert "C=CN, O=zrf, CN=zrf moon"
[ENC] generating ID_PROT request 0 [ ID CERT SIG CERTREQ N(INITIAL_CONTACT) ]
[NET] sending packet: from 10.1.0.2[500] to 10.1.0.1[500] (620 bytes)
[NET] received packet: from 10.1.0.1[500] to 10.1.0.2[500] (540 bytes)
[ENC] parsed ID_PROT response 0 [ ID CERT SIG ]
[IKE] received end entity cert "C=CN, O=zrf, CN=zrf sun"
[CFG] using certificate "C=CN, O=zrf, CN=zrf sun"
[CFG] using trusted ca certificate "C=CN, O=zrf, CN=zrf CA"
[CFG] reached self-signed root ca with a path length of 0
[CFG] checking certificate status of "C=CN, O=zrf, CN=zrf sun"
[CFG] certificate status is not available
[IKE] authentication of 'C=CN, O=zrf, CN=zrf sun' with SM2_WITH_SM2 successful
[IKE] IKE_SA h2h[3] established between 10.1.0.2[C=CN, O=zrf, CN=zrf moon]...10.1.0.1[C=CN, O=zrf, CN=zrf sun]
[IKE] scheduling rekeying in 14089s
[IKE] maximum IKE_SA lifetime 15529s
initiate completed successfully

查看IKE SA:

1
2
3
4
5
6
zrf@debian:~$ sudo ip netns exec moon /usr/local/sbin/swanctl -l
h2h: #3, ESTABLISHED, IKEv1, 68d381f382237777_i* ea6feabcf2a1a98f_r
local 'C=CN, O=zrf, CN=zrf moon' @ 10.1.0.2[500]
remote 'C=CN, O=zrf, CN=zrf sun' @ 10.1.0.1[500]
SM4_CBC/HMAC_SM3/PRF_HMAC_SM3/CURVE_SM2
established 25s ago, rekeying in 14064s

3.5 总结

我们使用SM4_CBC/HMAC_SM3/PF_HMAC_SM3/CURVE_SM2安全提案在sun站点moon站点之间成功建立的IKE SA

四. 报文分析

本节观察此次协商过程的报文信息。

ISAKMP Main Mode 三次交换

4.1 创建安全提案

通过第一次交换(一次请求加一次响应称为一次交换),双方决定使用SM4_CBC/HMAC_SM3/PF_HMAC_SM3/CURVE_SM2作为安全提案。

可见加密算法为SM4-CBC,摘要算法为SM3, DH算法为2049(我将SM2的DH算法定义为了2049),签名算法为12(我将SM2-with-SM3定义为了12)

Transform

4.2 交换DH公钥

本节将首先展示DH算法是如何不在网络间传输共享密钥的前提下,双方通过交换可公开的DH公钥,各自计算出共享密钥g_xy的。

其次分析strongswan的代码,研究一下如何通过共享密钥衍生出一系列密钥。

1. DH算法

strongswan对DH算法抽象出了三个最常用的方法。

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
/**
* Implementation of a key exchange algorithms (e.g. Diffie-Hellman).
*/
struct key_exchange_t {

/**
* Returns the shared secret of this key exchange method.
*
* @param secret shared secret (allocated)
* @return TRUE if shared secret computed successfully
*/
bool (*get_shared_secret)(key_exchange_t *this, chunk_t *secret)
__attribute__((warn_unused_result));

/**
* Sets the public key from the peer.
*
* @note This operation should be relatively quick. Costly public key
* validation operations or key derivation should be implemented in
* get_shared_secret().
*
* @param value public key of peer
* @return TRUE if other public key verified and set
*/
bool (*set_public_key)(key_exchange_t *this, chunk_t value)
__attribute__((warn_unused_result));

/**
* Gets the own public key to transmit.
*
* @param value public key (allocated)
* @return TRUE if public key retrieved
*/
bool (*get_public_key)(key_exchange_t *this, chunk_t *value)
__attribute__((warn_unused_result));

};

在生成DH算法实例时会创建出一对密钥——公钥和私钥
我们举一个例子,A和B之间想协商出一个共享密钥,他们需要这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

A B
| |
| v
v 创建DH实例DH_B(pub_b, pri_b)
| |
创建DH实例DH_A(pub_a, pri_a) |
| |
v |
|------发送pub_a------> |
| |
|<------发送pub_b-------|
| |
psk1 = sm2_dh(DH_A, pub_b) |
| |
| psk2 = sm2_dh(DH_B, pub_a)

pks1和psk2就是他们生成的共享密钥,psk1和psk2应该相等。

就像下面这段测试程序一样:

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
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <library.h>

#include "crypto/key_exchange.h"
#include "utils/chunk.h"

library_t *lib = NULL;

int main()
{
library_init(NULL, "sm2 dh test");
lib->plugins->load(lib->plugins, "gmssl");

key_exchange_t *DH_A; key_exchange_t *DH_B;
chunk_t pub_a, psk1; chunk_t pub_b, psk2;
pub_a = psk1 = chunk_empty; pub_b = psk2 = chunk_empty;

DH_A = lib->crypto->create_ke(lib->crypto, CURVE_SM2); DH_B = lib->crypto->create_ke(lib->crypto, CURVE_SM2);

assert(DH_A->get_public_key(DH_A, &pub_a) == TRUE); assert(DH_B->get_public_key(DH_B, &pub_b) == TRUE);

assert(DH_A->set_public_key(DH_A, pub_b) == TRUE); assert(DH_B->set_public_key(DH_B, pub_a) == TRUE);

assert(DH_A->get_shared_secret(DH_A, &psk1) == TRUE); assert(DH_B->get_shared_secret(DH_A, &psk2) == TRUE);

assert(chunk_equals(psk1, psk2) == TRUE);

return 0;
}

2. KEY EXCHANGE PAYLOAD

我们观察第二组交换:

KEY EXCHANGE PAYLOAD

KEY EXCHANGE的有效载荷其实就是各自DH实例的公钥,需要发送给对端。

不难想象,对端将此KE作为对端DH实例的公钥,就可以得到共享密钥了。

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
/* 发送方将KE实例的公钥拷贝到KE载荷中 */
ke_payload_t *ke_payload_create_from_key_exchange(payload_type_t type,
key_exchange_t *ke)
{
private_ke_payload_t *this;
chunk_t value;

/* 获取了KE实例的公钥 */
if (!ke->get_public_key(ke, &value))
{
return NULL;
}
this = (private_ke_payload_t*)ke_payload_create(type);
/* 并把公钥存放在KE的载荷中 */
this->key_exchange_data = value;
this->ke_method = ke->get_method(ke);
this->payload_length += this->key_exchange_data.len;

return &this->public;
}

/* 接收方收到KE后将dh_value设置为public key */
METHOD(phase1_t, get_nonce_ke, bool,
private_phase1_t *this, message_t *message)
{
ke_payload_t *ke_payload;

ke_payload = (ke_payload_t*)message->get_payload(message, PLV1_KEY_EXCHANGE);
this->dh_value = chunk_clone(ke_payload->get_key_exchange_data(ke_payload));
if (!this->dh->set_public_key(this->dh, this->dh_value))
{
DBG1(DBG_IKE, "unable to apply received KE value");
return FALSE;
}

return TRUE;
}

METHOD(keymat_v1_t, derive_ike_keys, bool,
private_keymat_v1_t *this, proposal_t *proposal, key_exchange_t *dh,
chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
auth_method_t auth, shared_key_t *shared_key)
{
chunk_t g_xy, g_xi, g_xr, dh_me, spi_i, spi_r, nonces, data, skeyid_e;
chunk_t skeyid, ka;
uint16_t alg;

/* 使用get_shared_secret方法,得到了共享密钥g_xy */
if (!dh->get_shared_secret(dh, &g_xy))
{
return FALSE;
}
DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &g_xy);
}

3. 总结

至此,我们已经通过DH算法令双方都获取到了共享密钥(g_xy)。但是这个共享密钥并不会直接作为加/解密的密钥。
下一节将分析用来保护IKE_AUTH阶段的密钥是如何产生的。

五. 保护IKE_AUTH的密钥是如何产生的

本节的内容就是按照IKEv1协议要求,来衍生密钥的。

5.1 SKEYID

IKE有证书认证和预共享密钥认证,这两种认证方法产生的SKEYID是不同的:

  • 预共享密钥:
    SKEYID = prf(pre-shared-key, Ni_b | Nr_b)
    其中pre-shared-key为预共享密钥,Ni_b和Nr_b为双方的Nonce值(同KE一起交换)
    将使用pre-shared-key为prf算法的key,Ni_b | Nr_b作为prf算法的data,得到一个HASH值,就是SKEYID

  • 证书认证:
    SKEYID = prf(Ni_b | Nr_b, g_xy)
    Ni_b和Nr_b为双方的Nonce值(同KE一起交换),g_xy就是通过DH算法交换的共享密钥。
    将使用Ni_b | Nr_b为prf算法的key,g_xy作为prf算法的data,得到一个HASH值,就是SKEYID

5.2 SKEYID_d

SKEYID_d = prf(SKEYID, g^xy | CKY-I | CKY-R | 0)
CKY-I/CKY-R就是发起者和响应者的SPI

5.3 SKEYID_a

SKEYID_a = prf(SKEYID, SKEYID_d | g^xy | CKY-I | CKY-R | 1)
CKY-I/CKY-R就是发起者和响应者的SPI

5.4 SKEYID_e

SKEYID_e = prf(SKEYID, SKEYID_a | g^xy | CKY-I | CKY-R | 2)
CKY-I/CKY-R就是发起者和响应者的SPI

5.5 KA

KA比较关键,他是由SKEYID_e衍生的:

K1 = prf(SKEYID_e, 0)
K2 = prf(SKEYID_e, K1)
K3 = prf(SKEYID_e, K2)
Ka = K1 | K2 | K3

这个KA将作为最终的密钥,对IKE_AUTH进行加密。

六. 证书认证

接下来,所有的ISAKMP的有效载荷将被KA加密,我们可以拿到KA和SPI对密文解密

ikev1_decryption_table有两个是因为网络空间的sun和moon都记录了解密信息,他们当然应当是一样的。

如果使用的国际的加密和摘要算法,是可以通过ikev1_decryption_table表来解密ISAKMP报文的,但是我用的是SM3/SM4算法,wireshark无法解密,所以后文中的解密信息都是在使用sha1/aes128算法下的信息。

1
2
3
zrf@debian:~$ cat /tmp/ikev1_decryption_table 
68d381f382237777,4a843753e8fa6d3974ad2ae4e614b8a2
68d381f382237777,4a843753e8fa6d3974ad2ae4e614b8a2

6.1 证书请求

由于我们是证书认证,实际上在第四条报文中,会携带一个证书请求,期望对方发送被CA认可的证书:

Cert Req

6.2 IKE_AUTH

在进行IKE_AUTH时,解密后的有效载荷主要由这几部分组成:

IKE_AUTH

我们将主要查看他的Payload: Identification (5)字段Payload: Certificate (6)字段Payload: Signature (9)字段

1. Identification

本字段记录了发送端的ID信息,这个ID信息就是从moonCert.pem中获得的。

Identification

2. Certificate

本字段就是moonCert.pem证书,这是由于sun在第四个报文中发送了证书请求

Certificate

3. Signature

这个字段就比较关键了,关系到IKE_AUTH能否注册成功,我们将在下一节来分析签名是如何产生的、接收端将如何验签。

Signature

6.3 签名与验签

本节分两部分:

  • 分析发起者如何进行签名
  • 分析接受者如何验证签名

1. 签名

以下是构造签名的过程,请看注释

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
METHOD(authenticator_t, build, status_t,
private_pubkey_v1_authenticator_t *this, message_t *message)
{
/* 获取本端的ID */
id = this->ike_sa->get_my_id(this->ike_sa);
/* 获取本端的私钥 */
private = lib->credmgr->get_private(lib->credmgr, this->type, id, auth);

/* 获取本端DH实例的公钥 */
if (!this->dh->get_public_key(this->dh, &dh))
{
private->destroy(private);
return FAILED;
}
keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);

/* 计算这些值的摘要 */
if (!keymat->get_hash(keymat, this->initiator, dh, this->dh_value,
this->ike_sa->get_id(this->ike_sa), this->sa_payload,
this->id_payload, &hash, &scheme))
{
private->destroy(private);
free(dh.ptr);
return FAILED;
}
free(dh.ptr);

/* 对摘要进行私钥签名 */
if (private->sign(private, scheme, NULL, hash, &sig))
{
sig_payload = hash_payload_create(PLV1_SIGNATURE);
sig_payload->set_hash(sig_payload, sig);
free(sig.ptr);
message->add_payload(message, &sig_payload->payload_interface);
status = SUCCESS;
DBG1(DBG_IKE, "authentication of '%Y' (myself) successful", id);
}
return status;
}

2. 验签

验证签名的过程,请看注释

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
METHOD(authenticator_t, process, status_t,
private_pubkey_v1_authenticator_t *this, message_t *message)
{
sig_payload = (hash_payload_t*)message->get_payload(message, PLV1_SIGNATURE);

/* 获取到对端ID */
id = this->ike_sa->get_other_id(this->ike_sa);

/* 获取本端DH实例的公钥 */
if (!this->dh->get_public_key(this->dh, &dh))
{
return FAILED;
}
keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);

/* 计算这些值的摘要 */
if (!keymat->get_hash(keymat, !this->initiator, this->dh_value, dh,
this->ike_sa->get_id(this->ike_sa), this->sa_payload,
this->id_payload, &hash, &scheme))
{
free(dh.ptr);
return FAILED;
}
free(dh.ptr);

/* 获取到签名的载荷 */
sig = sig_payload->get_hash(sig_payload);
auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, this->type,
id, auth, TRUE);
while (enumerator->enumerate(enumerator, &public, &current_auth))
{
/* 使用信任的公钥进行验签 */
if (public->verify(public, scheme, NULL, hash, sig) &&
is_compliant_cert(current_auth))
{
DBG1(DBG_IKE, "authentication of '%Y' with %N successful",
id, signature_scheme_names, scheme);
status = SUCCESS;
auth->merge(auth, current_auth, FALSE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
break;
}
}

return status;
}

6.4 如何获得对端的公钥

在验证签名的时候我们可以发现,将使用对端的公钥进行验签,这个公钥就是通过证书请求获得的。

但是并不是随便的证书都可行,他需要被CA来验证。

我们可以看以下log:

1
2
3
4
5
6
7
8
9
12[CFG1] <h2h|3>   using certificate "C=CN, O=zrf, CN=zrf moon"
12[CFG2] <h2h|3> certificate "C=CN, O=zrf, CN=zrf moon" key: 256 bit SM2
12[CFG1] <h2h|3> using trusted ca certificate "C=CN, O=zrf, CN=zrf CA"
12[CFG2] <h2h|3> certificate "C=CN, O=zrf, CN=zrf CA" key: 256 bit SM2
12[CFG1] <h2h|3> reached self-signed root ca with a path length of 0
12[CFG1] <h2h|3> checking certificate status of "C=CN, O=zrf, CN=zrf moon"
12[CFG2] <h2h|3> ocsp check skipped, no ocsp found
12[CFG1] <h2h|3> certificate status is not available
12[IKE1] <h2h|3> authentication of 'C=CN, O=zrf, CN=zrf moon' with SM2_WITH_SM2 successful

只有被CA信任的证书才会作为验签公钥的候选。