Contents

產生自簽憑證筆記

上次上班花很多時間處理憑證問題
為了碰到下次不要花費太多時間設備根憑證不正確導致不能打API問題 | 程式狂想筆記
今天要還債(技術債)了
主要試試看建立憑證

建立根憑證

這邊範例是照如何使用 OpenSSL 建立開發測試用途的自簽憑證 (Self-Signed Certificate) | The Will Will Web
可以找個 VM 來測試看環境

  1. 安裝 openssl

ubuntu 預設就會安裝

  1. 設定 conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[req]
prompt = no
default_md = sha256
default_bits = 2048
distinguished_name = dn
x509_extensions = v3_req

[dn]
C = TW
ST = Taiwan
L = Taipei
O = Duotify Inc.
OU = IT Department
emailAddress = admin@example.com
CN = localhost

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = *.localhost
DNS.2 = localhost
IP.1 = 192.168.2.100
  1. 產生私鑰、公鑰
1
openssl req -x509 -new -nodes -sha256 -utf8 -days 3650 -newkey rsa:2048 -keyout server.key -out server.crt -config ssl.conf
  1. 丟到系統憑證區

Ubuntu

1
2
sudo cp server.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
  1. 測試結果
    沒錯,就這麼簡單
    可以找一個隨便 Web Server 建立

這邊我用的是 Apache 安裝
這邊小卡了一下,因為我是第一次設定XD

啟用 ssl 模組
a2enmod ssl
啟用 ssl 虛擬站台
a2ensite default-ssl
重啟 apache
sudo service apache2 restart
修改憑證路徑(private.key,public.crt/pem)
vim /etc/apache2/sites-enabled/default-ssl.conf

1
2
SSLCertificateFile      /home/vagrant/server.crt
SSLCertificateKeyFile /home/vagrant/server.key

就可以測試結果了

1
2
3
4
5
6
7
curl https://localhost
# ok
echo ""|openssl s_client  -connect localhost:443 2>&1  | less
# 會有Can't use SSL_get_servername
# 不過還是過了,verify return:1 
echo ""|openssl s_client -servername localhost -connect localhost:443 2>&1  | less
# 就不會有這個問題 Can't use SSL_get_servername

相關教學

OpenSSL 簽發憑證方式 [蔡宗融個人網站]
使用這個做,會有[Sat Jul 18 11:12:43.084807 2020] [ssl:emerg] [pid 27557:tid 140210043268160] SSL Library Error: error:140AB18E:SSL routines:SSL_CTX_use_certificate:ca md too weak AH00016: Configuration Failed
後來參考:Raspberry Pi 的實作 - 產生一張自己核發的 SSL 憑證 | IT 技術家
使用1024會有ee key too small
nginx:SSL: error:140AB18F:SSL routines:SSL_CTX_use_certificate:ee key too small - 八戒vs - 博客园
改成 2048 就可以

原本我根憑證想簽下面憑證,產生出來設定在Apache
竟然都失敗了
這時候我突然想到,每一張都可以簽,不就沒有管理機制?

其他欄位比較重要的是basic constraints的CA:true和key usage的key cert sign,表示這個憑證可以再往下簽(總不可能讓它無限簽吧)。
參考來源:TU的雜七雜八筆記本: OpenSSL Certificate Chain (included SAN)

HAProxy 整合 SSL/TLS Client Certificate 教學 - Soul & Shell Blog
這邊沒有提到CA:true

回頭看我產出來的

1
openssl x509 -noout -text -in server.crt

真的沒有CA:true

我 Google 到這篇如何使用 OpenSSL 簽發中介 CA
照他的產生 Root CA 步驟結果有看到 CA:true
估計是保哥使用x509_extensions = v3_req關係
所以沒有開到 CA:true

產生中繼憑證

產生授權根憑證

如何使用 OpenSSL 簽發中介 CA備份圖

建立 Key 應該是整個環節中最簡單的步驟了,利用簡單的 openssl genrsa 即可,這邊採用 AES256 加密 Key 並將長度設為 4096(如果你不想要加上密碼,就不要下 -aes256,但你的 Key 就少了一層保護):

我不想打密碼就拿掉了

1
openssl genrsa  -out ca.key 4096

相關資訊打完(備註: CN 我打 localhost)
複製到系統安全憑證區

1
2
sudo cp ca.pem /usr/local/share/ca-certificates/ca.srt  # 這邊要改成srt
sudo update-ca-certificates

openssl x509 -noout -text -in ca.pem
確實有看到 CA: true
先到 Apache 測試 https://localhost
curl 成功

產生中繼憑證

1
openssl genrsa  -out intermediate.key 4096

一樣我AES256拿掉
我就是不想打密碼,我就爛XD

1
2
3
#CN 我設定 a.localhost 網域
openssl req -sha256 -new -key intermediate.key -out intermediate.csr
vim intermediate.ext

intermediate.ext內容貼上去

1
2
3
4
[ intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true, pathlen:0

為什麼要自己定義呢? 用原本的 v3_ca 不好嗎?
當然,如果你想要讓這個 CA 跟 Root CA 一樣可以繼續往下簽 Intermediate CA 的話,那你可以選擇繼續使用 v3_ca。
上面定義的 intermediate_ca 相較預設的 v3_ca 多了一個 pathlen:0,表示他往下還可以有 0 個 Intermediate CA(也就是沒有的意思)。

1
2
3
4
5
6
openssl x509 -req -in intermediate.csr \
               -CA ca.pem -CAkey ca.key \
               -CAserial ca.serial -CAcreateserial \
               -days 730 \
               -extensions intermediate_ca -extfile intermediate.ext \
               -out intermediate.pem

其中有幾個選項需要介紹一下:

-CAserial ca.serial: 每個 CA 簽發憑證時都需要一個流水號,這裡是指出流水號記錄檔檔名
-CAcreateserial: 只有當 CA 初次簽發時需要,他會初始化一個流水號出來,第二次之後簽發就不要帶這個參數,以免流水號被重置
-extensions intermediate_ca: 我們想要指定剛剛我們建立的 extension
-extfile intermediate.ext: 需要指定包含 extension 的設定檔

1
2
# 查看中繼憑證
openssl x509 -noout -text -in intermediate.pem 

可以看到 Issuer 是我們 Root CA 的名稱,而 Subject 是 CSR 中填寫的內容。
並且有將 pathlen 設為 0,表示這個 CA 只能用來簽發終端憑證。

另外,我們還可以透過 openssl verify 驗證這個憑證是不是由 Root CA 一路發下來的。

1
2
openssl verify -CAfile ca.pem intermediate.pem
# intermediate.pem: OK

一樣我把中繼憑證複製到 Apache 測試 https://a.localhost OK(更換憑證設定我就不寫了)

產生終端憑證

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# -aes256 拿掉,不解釋
openssl genrsa  -out endentity.key 4096
# 這次我 CN = a.a.localhost
openssl req -sha256 -new -key endentity.key -out endentity.csr
# 中繼憑證簽發終端憑證
openssl x509 -req -in endentity.csr \
               -CA intermediate.pem -CAkey intermediate.key \
               -CAserial intermediate.serial -CAcreateserial \
               -days 365 \
               -out endentity.pem
# 查看憑證
openssl x509 -noout -text -in endentity.pem
# 檢查憑證鍊
openssl verify -CAfile ca.pem -untrusted intermediate.pem endentity.pem

這邊因為有 Apache/nginx 中繼憑證
所以在設定上會不太一樣
Apache 有特別設定方法,這邊就不仔細說原因了

1
2
3
SSLCertificateFile      /home/vagrant/test/endentity.pem
SSLCertificateKeyFile /home/vagrant/test/endentity.key
SSLCertificateChainFile /home/vagrant/test/intermediate.pem

Nginx/Apache 都可以把中繼憑證包在一起

1
2
3
# cat endentity.pem > web.crt
# cat intermediate.pem >> web.crt
cat endentity.pem intermediate.pem > web.crt

Apache 設定

1
2
3
SSLCertificateFile      /home/vagrant/test/web.crt
SSLCertificateKeyFile /home/vagrant/test/endentity.key
#SSLCertificateChainFile /home/vagrant/test/intermediate.pem

curl https://a.a.localhost/ 顯示正常

wildcard

以下教學使用使用 OpenSSL 製作萬用字元 SSL 憑證-黑暗執行緒

2017 年 Chrome 58 / Firefox 48 開始要求憑證必須改用 SubjectAltName 欄位提供主機名稱 ,否則將顯示為不安全

========= 犯錯區 Start =========

這邊修改 *.localhost,*.*.localhost,*.*.*.localhost 網域

這邊後續我有繼續做,一直失敗,差點要放棄
過程是這樣的

  1. 我把localhost-ext.cnf 把 *.localhost 單獨一個
    結果失敗了

  2. 我把 SNA 只留 *.localhost
    還是失敗了

  3. 我把 SNA 放 *.localhost, localhost
    也是失敗了

  4. 我把 Apache conf 設定家 Servername localhost Serveralias localhost *.localhost
    還是失敗了


  5. Generating and Installing Wildcard and Multi-Domain SSL Certificates | DeveloperSide.NET

openssl verify -CAfile ca.pem -untrusted intermediate.pem localhost.crt
這一段沒有錯,但還是跑出這個錯誤
curl -v https://a.a.localhost

  • subjectAltName does not match a.a.localhost
  • SSL: no alternative certificate subject name matches target host name ‘a.a.localhost’
    跟別的網站不一樣
  • subjectAltName: host “xxx.xxxxx.com.tw” matched cert’s “*.xxxxx.com.tw”

apache error log
AH01909: 127.0.1.1:443:0 server certificate does NOT include an ID which matches the server name

後來求助偉大的谷歌,發現原來是nginx的TLS SNI support功能沒開,SNI (服務器名字指示)不開啟的話,一個IP只支持一個SSL證書,不支持多個證書,而服務器是yum安裝的nginx,默認TLS SNI support是關閉的,重新編譯nginx並指定openssl庫後,TLS SNI support 開啟了,訪問就正常了
記錄一次升級https走過的坑-czwanga-51CTO博客


….
…..

總之努力不一定會成功,不努力就一定很輕鬆
各位 Bye
剛好最近看到【Fun科學】靈壓探測器(潛伏身邊的不明危機!) - YouTube

為了不浪費大家的時間,我最後解決了,整理一下發生了什麼原因

這邊修改 *.localhost,*.*.localhost,*.*.*.localhost 網域
這邊我犯了很嚴重錯誤
這邊萬用網域也不是可以隨便用網址
需要使用一級網域做萬用憑證 EX:*.localhost.com <==== 這邊一級網域應該是指 萬用憑證 不是只 SNA
萬用憑證也只能用一層,所以*.*.localhost.com,*.*.*.localhost.com (應該不行,我沒有實驗)
正確來說 現在萬用憑證不看 CN 了,現在瀏覽器都會看 SNA 是否正確
SNA 算是 憑證 x509 插件
有實驗 *.a.localhost.com 是可以跑了

因為 localhost.com 網域 DNS 會抓不到
所以我們先手動設定 /etc/hosts 設定 127.0.0.1 localhost.com
家如要新增 a.localhost.com 也要獨立新增 127.0.0.1 a.localhost.com
SNA 也能設定 *.a.localhost.com ,沒有一級網域限制

以上是我實驗結果

參考資料

萬用字元憑證只能覆蓋一級子域
萬用字元憑證 - 維基百科,自由的百科全書

Wildcard Domains: Wildcard domains such as *.example.com are useful when protecting multiple services on one domain such as www.example.com, mail.example.com, mx.example.com, ftp.example.com, blog.example.com, etc. however it has several limitations:
Non-zero length subdomain: first is that many sites will use a combination of www.example.com and example.com and a *.example.com wildcard will not match the latter.
Only flat subdomain support: wildcards will not support multiple subdomains, for example *.m.example.com will not be matched by *.example.com.
Only one domain: finally, *.example.com will not support an entirely different subdomain such as foobar.com
Subject Alternative Name: Using the X.509 subjectAltName extension has been useful to address some of the limiations of wildcard domains, namely they can contain multiple FQDNs of all types so names with differing numbers of subdomains and entirely different domains can be suppored. To make SANs even more useful, the goal of this effort was to validate the support for using wildcard domain names in the SAN.
參考來源:Wildcard Subject Alternate Name SSL/TLS Certificates

========= 犯錯區 End =========

這邊拿剛剛上面中繼憑證繼續用

1
2
3
4
# 建立私鑰
openssl genrsa -out localhost.key 2048
# 設定 localhost.cnf
vim localhost.cnf

localhost.cnf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
C=TW
ST=Taiwan
L=Taipei
O=localhost
OU=localhost CA
emailAddress=nobody@localhost
CN=*.localhost.com
1
2
3
4
# 產出 CSR
openssl req -new -sha256 -nodes -key localhost.key -out localhost.csr -config localhost.cnf

vim localhost-v3.cnf

localhost-v3.cnf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
keyUsage                = critical,digitalSignature,keyEncipherment
extendedKeyUsage        = serverAuth,clientAuth
subjectKeyIdentifier    = hash
basicConstraints=CA:FALSE
subjectAltName = @alt_names

[alt_names]
DNS.1 = *.localhost.com
DNS.2 = localhost.com
DNS.3 = *.a.localhost.com

這邊因為中途失敗一直嘗試更改,所以跟darkthread-net-v3.ext 不太一樣

1
2
3
4
5
6
7
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = *.darkthread.net
1
2
3
#  拿掉 -CAcreateserial
#  -CAserial intermediate.serial 需要指定序號,後面參數指的是序號檔名
openssl x509 -req -in localhost.csr -CA intermediate.pem -CAkey intermediate.key  -out localhost.crt -days 365 -sha256 -CAserial intermediate.serial -extfile localhost-v3.ext

基本上放在 Apache 之前設定跟之前差不多
我就不說了

https://a.localhost.com
https://a.a.localhost.com

curl 都可以順利跑

TWCA 憑證為什麼有三層,四層串到同一個憑證呢?

我目前猜想其中兩邊憑證都是吃同一把私鑰
產生兩組 crt(public key)
所以憑證都可以用

1
2
3
4
5
6
openssl x509 -req -in intermediate.csr \
               -CA ca.pem -CAkey ca.key \
               -CAserial ca.serial \
               -days 730 \
               -extensions intermediate_ca -extfile intermediate.ext \
               -out intermediate2.pem

2020-07-22
最後這篇有和我同學討論
https://malagege.github.io/blog/posts/%E8%A8%AD%E5%82%99%E6%A0%B9%E6%86%91%E8%AD%89%E4%B8%8D%E6%AD%A3%E7%A2%BA%E5%B0%8E%E8%87%B4%E4%B8%8D%E8%83%BD%E6%89%93API%E5%95%8F%E9%A1%8C/cgh.png
我認為跟用同一個私金鑰產生有關係
他認為公鑰解析出來應該跟 pin sha256 一樣有關係 (當時看圖片沒注意到)
不過我們加密學都不是很懂,這邊就不繼續研究了

至於抓兩個路徑
我目前猜測四層憑證第二層對應上去都是 TWCA Global Root CA
除了中繼憑證跟電腦跟憑證 Mapping 到名子對應上去剛好符合驗證解密內容
四層憑證在 Windows 看是四層,但用在 Firefox,Linux(Chrome),Android(Chrome) 來看竟然是抓到三層

總結心得
當出現網站不安全,都是無法串連(client)根憑證有關係
根憑證正確就往下追中繼憑證,中繼憑證順序都需要注意
中繼憑證可以不用附根憑證(畢竟裝置有根憑證,沒必要再傳送過去)
但我這個案例比較特別,正常應該不會遇到
裝置有兩個 TWCA 根憑證,正常應該不會遇到問題
就算裝置有三層根憑證,也通吃
但是裝置只有四層根憑證,就會出現不安全!!!(像 LINE Webhook 就沒有 TWCA 三層憑證)
不過還是看到滿多網站都掛四層憑證居多
可以到很多銀行、醫院網站看看憑證

把 apache 設定 endentity.pem 對應 intermediate2.pem 真得可以用

測試檔案

所有範例下載

/vagrant/ <====== 第一段建立憑證範例
/vagrant/test/ <====== 第二、三段建立中繼、萬用憑證範例
hosts 參考 dns 設定檔
default-ssl.conf apache 設定檔

查看 web 上驗憑證內容

1
2
3
openssl s_client  -connect localhost.com:443 </dev/null  |  openssl x509 -noout -text  | less

openssl s_client  -connect youtube.com:443 </dev/null  |  openssl x509 -noout -text  | less

其他資源

被撤 SSL Certificate