[Mac/Linux] 用 openssl 下載 HTTPS 網站憑證,解決 curl 抱怨 self-signed certificate 的問題
今天專案遇到一個問題,用 curl 去連一個 HTTPS 網站時,
出現 SSL certificate problem: self signed certificate in certificate chain 的錯誤…
例如我去連趨勢科技的 Site Safety Center:
testuser@localhost ~ curl -v https://global.sitesafety.trendmicro.com/ ...... * TLSv1.2 (OUT), TLS alert, Server hello (2): * SSL certificate problem: self signed certificate in certificate chain * Marked for [closure]: Failed HTTPS connection ...... curl: (60) SSL certificate problem: self signed certificate in certificate chain More details here: https://curl.haxx.se/docs/sslcerts.html curl performs SSL certificate verification by default, using a "bundle" of Certificate Authority (CA) public keys (CA certs). If the default bundle file isn't adequate, you can specify an alternate file using the --cacert option. If this HTTPS server uses a certificate signed by a CA represented in the bundle, the certificate verification probably failed due to a problem with the certificate (it might be expired, or the name might not match the domain name in the URL). If you'd like to turn off curl's verification of the certificate, use the -k (or --insecure) option.
看起來是因為 curl 認不得這個網站的 HTTPS 憑證簽發者 (CA),
因此以為這個憑證是 self-signed…
平常的 Linux 平台上應該不會有這個問題,
但因為我們在一個沒有設定好 CA bundle (就是 CA 的憑證集合) 的機器上測試,
因此遇到了問題…
要解決這個問題,可以把網站憑證和其簽發者的憑證 (CA chain),
通通下載下來,再指定給 curl 使用~
可惜 curl 沒有指令可以下載網站憑證,因此只能用 openssl 的指令,例如:
testuser@localhost ~ echo -n | openssl s_client -connect global.sitesafety.trendmicro.com:443 -showcerts depth=2 C = US, O = AffirmTrust, CN = AffirmTrust Commercial verify error:num=19:self signed certificate in certificate chain verify return:0 --- Certificate chain 0 s:/C=US/ST=California/L=Cupertino/O=Trend Micro Inc./CN=*.sitesafety.trendmicro.com i:/C=US/O=Trend Micro Inc/CN=Trend Micro S2 CA -----BEGIN CERTIFICATE----- MIIF8zCCBNugAwIBAgIIAy0tVNSNHhcwDQYJKoZIhvcNAQELBQAwQzELMAkGA1UE ...... ...... qVkWXRfA7j5K/PDQle7Otl/d1aWApQd2XBkzWtIayb8xNc1Hd02V -----END CERTIFICATE----- 1 s:/C=US/O=Trend Micro Inc/CN=Trend Micro S2 CA i:/C=US/O=AffirmTrust/CN=AffirmTrust Commercial -----BEGIN CERTIFICATE----- MIIEeDCCA2CgAwIBAgIIW0aZkOx1nTQwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE ...... ...... dVgSPCvYa7GGG8U43fUASqXwshoOpnbetI+zRN5aH1TtTK71e2kTqcqEckM= -----END CERTIFICATE----- 2 s:/C=US/O=AffirmTrust/CN=AffirmTrust Commercial i:/C=US/O=AffirmTrust/CN=AffirmTrust Commercial -----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE ...... ...... nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= -----END CERTIFICATE----- --- Server certificate subject=/C=US/ST=California/L=Cupertino/O=Trend Micro Inc./CN=*.sitesafety.trendmicro.com issuer=/C=US/O=Trend Micro Inc/CN=Trend Micro S2 CA
在上面的 openssl 指令中,因為有加上 -showcerts,
因此會把網站憑證與其所有的簽發者 (CA chain) 都列出來,
我們可以看到:
- *.sitesafety.trendmicro.com 的憑證是由 Trend Micro S2 CA 簽發
- Trend Micro S2 CA的憑證是由 AffirmTrust Commercial 簽發
- AffirmTrust Commercial的憑證是由 AffirmTrust Commercial 簽發 (Root CA)
其中 —–BEGIN CERTIFICATE—– 和 —–END CERTIFICATE—– 包起來的,
就是一個 SSL 憑證,在此例中一共有三個 SSL 憑證。
我們可以利用 sed 指令,只將憑證的部分截取出來,儲存到一個檔案裡:
echo -n | openssl s_client -connect global.sitesafety.trendmicro.com:443 -showcerts | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > trendmicro.cert
截取出來的 trendmicro.cert 檔案就是所謂的 CA bundle file,
內容就是一堆簽章的集合:
-----BEGIN CERTIFICATE----- MIIF8zCCBNugAwIBAgIIAy0tVNSNHhcwDQYJKoZIhvcNAQELBQAwQzELMAkGA1UE ...... ...... qVkWXRfA7j5K/PDQle7Otl/d1aWApQd2XBkzWtIayb8xNc1Hd02V -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEeDCCA2CgAwIBAgIIW0aZkOx1nTQwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE ...... ...... dVgSPCvYa7GGG8U43fUASqXwshoOpnbetI+zRN5aH1TtTK71e2kTqcqEckM= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE ...... ...... nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= -----END CERTIFICATE-----
現在用 curl 加上 –cacert 參數指定 CA bundle file 的路徑,
再重新連線一次,可以看到這次連線就沒有 self-signed certificate 的問題了:
testuser@localhost ~ curl -v --cacert trendmicro.cert https://global.sitesafety.trendmicro.com/ ...... * successfully set certificate verify locations: * CAfile: trendmicro.cert ...... * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * Server certificate: * subject: C=US; ST=California; L=Cupertino; O=Trend Micro Inc.; CN=*.sitesafety.trendmicro.com * start date: Mar 2 14:02:51 2016 GMT * expire date: Mar 3 14:02:51 2018 GMT * subjectAltName: host "global.sitesafety.trendmicro.com" matched cert's "*.sitesafety.trendmicro.com" * issuer: C=US; O=Trend Micro Inc; CN=Trend Micro S2 CA * SSL certificate verify ok. > GET / HTTP/1.1 > Host: global.sitesafety.trendmicro.com > User-Agent: curl/7.51.0 > Accept: */* > < HTTP/1.1 200 OK ......
參考資料:how to download the ssl certificate from a website?