HTTPS 驗證解析

時常在開發任何應用服務時,會利用 HTTP 協定來串接服務與服務之間的通訊,然而這樣的方式可能存在著一定的風險,因此就有人會想是否可以在傳輸過程中加入驗證機制,來提高資料的安全性,進而衍伸出 TLS 雙向驗證機制。

筆者在了解其中運作原理時,意外在網路上看到一篇文章,用故事方式來描述這之間的關係,覺得還不錯分享給讀者們。

大意如下:

有一天 A 公司的業務要去 B 公司進行業務接洽,當業務到達 B 公司時,被 B 公司的警衛攔下,並要求業務出示身份。不過 B 公司沒有人認識這位業務,因此業務只能拿出蓋有 A 公司印章的文件來證明自己是 A 公司的員工。

聽完以上的敘述雖然可能大致上沒什麼問題,不過有人可能會想到,如果這樣 B 公司有 100 的合作廠商,不就需要記住 100 個公司的印章才能身任身份驗證的任務。

因此,C 公司看準了這個商機,成為了第三方的驗證機構。在 B 公司信任 C 公司與 C 公司信任 A 的的基礎上,只要 A 公司向 B 公司提出自己與 C 公司的印章,則為合法。如此一來 B 公司只要記得 C 公司印章即可。

上述 C 公司就是 CA 的角色,驗證的過程如故事情境,接著我們就實際來建置完整的驗證流程。

cfssl 簡介

我們使用 cfssl 這個工具來產生金鑰與憑證,cfssl 是 CloudFlare 公司是基於 Golang 開源的 PKI/TLS 工具。當前僅支援 ECDSARSA 兩種方式。

安裝

首先要先安裝 cfssl 工具

1
2
3
4
$ export CFSSL_URL=https://pkg.cfssl.org/R1.2
$ wget ${CFSSL_URL}/cfssl_linux-amd64 -O /usr/local/bin/cfssl
$ wget ${CFSSL_URL}/cfssljson_linux-amd64 -O /usr/local/bin/cfssljson
$ chmod +x /usr/local/bin/cfssl /usr/local/bin/cfssljson

安裝完成後,利用指令檢查當前安裝版本。

1
2
3
4
5
$ cfssl version

Version: 1.2.0
Revision: dev
Runtime: go1.6

配置

在建立服務憑證前,我們需要先建立 ca/ca-csr.json 檔案來產生 CA 憑證與金鑰。ca/ca-csr.json 大致上會描述如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ mkdir ca
$ vim ca-csr.json

{
"CN": "james-blog.com",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "TW",
"ST": "Taipei",
"L": "Taipei",
"O": "james company",
"OU": "james company"
}
]
}
  • CN(Common Name) 用來識別使用的 Domain。
  • Key 描述用 RSA 進行加密,並且長度為 2048(當前僅支援 2048 - 8192)
  • Name.C 表示國家
  • Name.ST 與 L 表示城市
  • Name.O 表示公司
  • Name.OU 表示公司部門

建立完成後,初始化 CA 憑證與金鑰。

1
2
3
4
5
6
7
8
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca/ca

2021/02/08 07:49:22 [INFO] generating a new CA key and certificate from CSR
2021/02/08 07:49:22 [INFO] generate received request
2021/02/08 07:49:22 [INFO] received CSR
2021/02/08 07:49:22 [INFO] generating key: rsa-2048
2021/02/08 07:49:23 [INFO] encoded CSR
2021/02/08 07:49:23 [INFO] signed certificate with serial number 512432585453518655479838519048416241349248297204

檢視當前 ca/ 目錄內的檔案,即 ca-key.pem 為金鑰,ca.pem 為憑證。

1
2
3
$ ls ca/

ca.csr ca-key.pem ca.pem

完成後,接著利用 CA 來建立服務的金耀與憑證。我們利用 nginx 作為示範,建立 nginx/nginx-csr.json 檔案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ mkdir nginx
$ vim nginx/nginx-csr.json

{
"CN": "james-blog.com",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "TW",
"ST": "Taipei",
"L": "Taipei",
"O": "james company",
"OU": "james company"
}
]
}

這邊我們就用與 CA 相同的檔案內容。

建立 ca-config.json 檔案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ vim ca-config.json

{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"nginx": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "87600h"
}
}
}
}
  • expiry 表示可用時間為一年
  • signing 表示可用於其他簽名證書
  • server auth 表示 Client 可以 CA 對 Server 提供的憑證進行驗證
  • client auth 表示 Server 可以 CA 對 Client 提供的憑證進行驗證

基於 CA 的憑證與金鑰,產生 nginx 服務憑證與金鑰。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cfssl gencert \
-ca=ca/ca.pem \
-ca-key=ca/ca-key.pem \
-config=ca-config.json \
-hostname=james-blog.com \
-profile=nginx \
nginx/nginx-csr.json | cfssljson -bare nginx/nginx

2021/02/08 08:03:09 [INFO] generate received request
2021/02/08 08:03:09 [INFO] received CSR
2021/02/08 08:03:09 [INFO] generating key: rsa-2048
2021/02/08 08:03:09 [INFO] encoded CSR
2021/02/08 08:03:09 [INFO] signed certificate with serial number 67599991771298537291786410610924862791459550062

檢視當前 nginx/ 目錄內的檔案,即 nginx-key.pem 為金鑰,nginx.pem 為憑證。

1
2
3
$ ls nginx/

nginx.csr nginx-csr.json nginx-key.pem nginx.pem

接著將 nginx/nginx.pemnginx/nginx-key.pem 內容合成一份名為 tls.pem 檔案以提供 HAProxy 配置使用。

1
2
$ cat nginx/nginx.pem > tls.pem
$ cat nginx/nginx-key.pem >> tls.pem

檢視當前 tls.pem 內容。

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
$ cat tls.pem

-----BEGIN CERTIFICATE-----
MIIEGjCCAwKgAwIBAgIUC9dJ2RtjdZYV/EvQvYWwUrl0M24wDQYJKoZIhvcNAQEL
BQAweDELMAkGA1UEBhMCVFcxDzANBgNVBAgTBlRhaXBlaTEPMA0GA1UEBxMGVGFp
cGVpMRYwFAYDVQQKEw1qYW1lcyBjb21wYW55MRYwFAYDVQQLEw1qYW1lcyBjb21w
YW55MRcwFQYDVQQDEw5qYW1lcy1ibG9nLmNvbTAeFw0yMTAyMDgwNzU4MDBaFw0z
MTAyMDYwNzU4MDBaMHgxCzAJBgNVBAYTAlRXMQ8wDQYDVQQIEwZUYWlwZWkxDzAN
BgNVBAcTBlRhaXBlaTEWMBQGA1UEChMNamFtZXMgY29tcGFueTEWMBQGA1UECxMN
amFtZXMgY29tcGFueTEXMBUGA1UEAxMOamFtZXMtYmxvZy5jb20wggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDxLh+7I7avapikDuiP9JQhHJu4hVlxrcvK
trMcXP9QcqiRkrgJ/Z2kWxM1KRq1TEFpArBCaLBxDpRVDuH3zG5b+gbZSF+uAK1d
WBy7ANFnSjj49/XtD54gU9mu0DUoGgQFU0zfI7RDOKwXbVmL04WBVI1avS88qxVa
jjaKCwvz4ubvIHrDhJRzVIzKrGuC5F7lRoahQTfL6Qu6kD89AgpiunPnAeMaQULU
LKt4ScdPrZsbnd6hlRBt8SsFx6ain/Z9J2FG/J27XwtV78nO409owZ4Is/ZwANQC
oI3BdvEO4i2p5B0f5517FKM8p13ZQ02WbFPHpch47XDUgpJWqQ8BAgMBAAGjgZsw
gZgwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSLQt4fZ6N7eBFxFRfGrb/Yy8sTOjAf
BgNVHSMEGDAWgBTgpE193bdCcTryuM5SoEVqxDpUFDAZBgNVHREEEjAQgg5qYW1l
cy1ibG9nLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEATsIqEmyayWvrfapcEMnzrirr
r1uIPRHlWYjzWx0HRtaxnWzk/mgtDSDUTzAVM7+unQFqy7ZsJoI7zsDuhfidv8WG
3ChdSs773grz5m6GB3Wy8n+P5LNmqp3uKN80e1A0fjccqoKcec29YdMjlCRWj/El
7OF9vOQECROFiGDiXvz9Ou0BsAB79GgTjMRQfwIVTjgVhh9rVR5Q4eDtaUqnOF5e
Wxo8erHBw8wIhfjrSI7Mfa09MWwF4ohC3U1tHnkb44gjdz6wfZ1xFv/7rw6LA1bG
bD5mj2FUKoMKnzL9FkQhoe8H5YBchdB5IUS6MZygmwAgQ4L89dByLes3QJAT6Q==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA8S4fuyO2r2qYpA7oj/SUIRybuIVZca3LyrazHFz/UHKokZK4
Cf2dpFsTNSkatUxBaQKwQmiwcQ6UVQ7h98xuW/oG2UhfrgCtXVgcuwDRZ0o4+Pf1
7Q+eIFPZrtA1KBoEBVNM3yO0QzisF21Zi9OFgVSNWr0vPKsVWo42igsL8+Lm7yB6
w4SUc1SMyqxrguRe5UaGoUE3y+kLupA/PQIKYrpz5wHjGkFC1CyreEnHT62bG53e
oZUQbfErBcemop/2fSdhRvydu18LVe/JzuNPaMGeCLP2cADUAqCNwXbxDuItqeQd
H+edexSjPKdd2UNNlmxTx6XIeO1w1IKSVqkPAQIDAQABAoH/QuOH6V7+S6hErTt1
RHeQnZ5Rkdtp8x1AZ/hDtJoWNTmXhsgqQpsUHYEk0pmTzrRXb8hPGhvu02w3t19p
TFBmSxwMyjZIUvndGwZU8YhIi10KcAJVMmoicNTQiqs0EuskMlXn+/rrA7m0AMtT
CnSfkj7g9UmC0FCim3rVpXNq3TiG9H+tFPnOFadTbRJi0CkADgMRrmywliBe/7Dd
B6Xcp/2jxHeoMiqqOcszDgIxG5CoE03mghqqfxalJXzioMexaMyMH4epoYFaKVsI
KfOIo1Fie8JPCtOP0DEi3BOolaFd9Nrj3RwgMIoQQ0ZeOdkJUvZRklOxJHwG3hEQ
8sNRAoGBAPJM9SueSnDqqhofjcRl8a5CKY9y7zwTiXy92VQFhsK21fG8EviEgs50
577EHE4Cb5gxQxsPSK54gxOOZ2ZbP11ynaKbI2ZbPUUgNppiaPc+ERG6xBL4e0/r
VzAqd5kAZZUov5oi2RW6cBtQeONhgdRGN3Fq0an1QCMaJe9TRcRlAoGBAP7Q8vLp
L7rAs8jqllD+Ijx7aDzLMDHD2k2iWatSe6uIXghyqOD7VmSI4goYYqtcipRv53iH
dwWm/c6BtqwHrsOgM12ZiqotDDOUhgJIe29IEdGHG+fJ9rfefAbeV53I8CadL+G/
wtmaMDzq9h6FZmMPT2lzvYj9oeBKQAfqw7BtAoGBAIjD5583Oc2Cp4EXTm4NHN2/
erX9qgu++1vtzT4f4HEHwrsv7YVZRnxCgIytJUzjExpUtAwSFHRmkZX4S2T7HEki
6NdfuhuMZIkgJbH+2kC0R+45/XK3zuLNc+k8D0XNc4k99uiJwv8AvUatpY6y+xVW
jPT31mCYjhtCJydvWXIBAoGBAMC0ECc3xgq7lLsK/WZ+6jFHOotPNkFMVhmD/8Aa
fsA4PrSw0ZpjOPCKvDbaPjRNpdef0TNLbu1tXl//pL/wh3AWBQJyDXWo36NaXQX4
/rAnlqIYRThDejuPG8it+SCwRz1MfluBA8BAZN6M6lgmlkmv2GRtTRb+iJ7wSAA0
wIz5AoGBAJhIzuMCMuUpywBPJr6L4zwWBswhqds/KZX31EcYCFVxL/rtkNQhjy/b
mL+JGNARMbYHadqP/wobwqhY2WI9ZtBb+A+mVKl/kO38q+hwUCigOumo2P3U76SR
9ofrV/klBKDE8Az+D5M0zwWvmosutoIQDpgiif20qlySdX1N9U7g
-----END RSA PRIVATE KEY-----

最後,編輯 /etc/haproxy/haproxy.cfg 檔案,加入 TLS 配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ vim /etc/haproxy/haproxy.cfg

...
global
...
tune.ssl.default-dh-param 2048

frontend f_nginx
mode http
bind *:8443 ssl crt /home/vagrant/tls.pem
default_backend b_nginx

backend b_nginx
server nginx-1 192.168.10.10:80 check

在 global 加入 tune.ssl.default-dh-param 2048 並在最底部加入上述配置,其原因為 RSA 為長度 2048,預設 HAProxy 為 1024,若無調整則啟動時會產生警告。

重新啟動 HAProxy 服務。

1
$ systemctl restart haproxy

檢視當前 HAProxy 狀態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ systemctl status haproxy

● haproxy.service - HAProxy Load Balancer
Loaded: loaded (/lib/systemd/system/haproxy.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2021-02-08 08:11:44 UTC; 1s ago
Docs: man:haproxy(1)
file:/usr/share/doc/haproxy/configuration.txt.gz
Process: 15325 ExecStartPre=/usr/sbin/haproxy -f $CONFIG -c -q $EXTRAOPTS (code=exited, status=0/SUCCESS)
Main PID: 15336 (haproxy)
Tasks: 2 (limit: 1074)
Memory: 6.6M
CGroup: /system.slice/haproxy.service
├─15336 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock
└─15337 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock

Feb 08 08:11:44 ubuntu systemd[1]: Starting HAProxy Load Balancer...
Feb 08 08:11:44 ubuntu haproxy[15336]: Proxy f_nginx started.
Feb 08 08:11:44 ubuntu haproxy[15336]: Proxy f_nginx started.
Feb 08 08:11:44 ubuntu haproxy[15336]: Proxy b_nginx started.
Feb 08 08:11:44 ubuntu haproxy[15336]: Proxy b_nginx started.
Feb 08 08:11:44 ubuntu haproxy[15336]: [NOTICE] 038/081144 (15336) : New worker #1 (15337) forked
Feb 08 08:11:44 ubuntu systemd[1]: Started HAProxy Load Balancer.

結果

我們需要在 /etc/hosts 中加入 192.168.10.10 james-blog.com 讓系統認得這個 Domain。

1
2
3
4
$ vim /etc/hosts

...
192.168.10.10 james-blog.com

利用 cUrl 指令並帶入憑證進行驗證。

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
$ curl --cacert nginx/nginx.pem https://james-blog.com:8443

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

若想要透過瀏覽器進行測試,在 MacOS 系統就需要將憑證加入到”鑰匙圈存取”中,並且開啟”永遠信任”。

keynote1

keynote2

若成功,則利用 Chrome 瀏覽器可以看到網址左邊有一個”鎖”的 icon。

chrome-snapshot

問題

因為我們在建立 nginx 服務憑證時,有註明使用的 hostname 為 james-blog.com,因此若使用其他們 domain 會發生錯誤。

1
2
3
4
5
6
7
8
$ curl --cacert nginx/nginx.pem https://james1-blog.com:8443

curl: (60) SSL: no alternative certificate subject name matches target host name 'james1-blog.com'
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

參考

# TLS

評論

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×