Let's Encrypt 支援 ACME-CAA,可以再進一步限縮可以申請的使用人

前幾天在 Hacker News 上看到 Let's Encrypt 支援 ACME-CAA 的新聞:「Let's Encrypt now supports ACME-CAA: closing the DV loophole (devever.net)」,原文在「Let's Encrypt now supports ACME-CAA: closing the DV loophole」。

先前的「RFC 6844 - DNS Certification Authority Authorization (CAA) Resource Record」已經先定義了 DNS 上 CAA record 的規範,另外在 CA/Browser ForumBaseline Requirements 裡面也要求了 CA 簽發單位必須遵守 CAA 設定。

但這邊還是有一些風險,像是當網站被其他人控制後 (或是中間有 BGP hijacking 的方式取得 TCP 層的控制權),控制人就可以透過 http-01 的方式通過認證申請到 SSL certificate。而這次 Let's Encrypt 實做的 ACME-CAA 則是試著降低這個風險。

第一個是 accounturi 參數,可以指定只有某個 CA 裡的某個帳號可以申請,像是這樣:

example.com. IN CAA 0 issue "letsencrypt.org; accounturi=https://some/lets-encrypt/account-id"

第二個是限制 validationmethods 參數,限制只有某些方式可以申請,像是這邊限制只能透過 dns-01 申請:

example.com. IN CAA 0 issue "letsencrypt.org; validationmethods=dns-01"

不過支援 http-01 的不只 Let's Encrypt,至少也還有 ZeroSSLBuypass,後續可以看看其他家會不會跟上,以及會不會放到 Baseline Requirements 裡面...

Amazon CA 在 renew 時將引入動態的 Intermediate CA

上個月 AWS 發的公告,其實已經生效了,但整理的時候才發現還沒寫:「Amazon introduces dynamic intermediate certificate authorities」。

先介紹一下 Amazon CA,這是 Amazon 自己維護的 Root CA,有走過 CA/Browser Forum 的規範與稽核,以及各家瀏覽器額外的要求,所以是個用戶端預設都有信任的 CA。

這個服務後來也被用在 AWS Certificate Manager (ACM) 上,由 ACM 申請到的憑證也都可以掛到 AWS 的各種服務上。

通常 root CA 的憑證不會直接拿來簽最終使用者使用的憑證 (leaf certificate),而是 root CA 的憑證先簽 intermediate CA 的憑證,然後 intermediate CA 可能有好幾層一路簽下來,到最後面再用 intermediate CA 的憑證簽最終使用者使用的憑證。

這次公告的內容就題到了,之前的 intermediate CA 是一個固定範圍的量,而且會確保 renew 時用的 intermediate CA 跟先前的是相同的:

Before this change, Amazon maintained a limited number of intermediate CAs and issued and renewed certificates from the same intermediate CAs.

這次則是會變成動態:

With this change, leaf certificates issued to you will be signed by different intermediate CAs.

生效日期是這個月十一日,其實已經生效了:

Starting October 11, 2022 at 9:00 AM Pacific Time, public certificates obtained through ACM will be issued from one of the multiple intermediate CAs that Amazon manages.

一般的用戶端 (像是瀏覽器) 基本上應該是不會有問題,因為大多數預設都是信任 root CA,而非 intermediate CA,而這次的改變還是可以從 root CA 產生出對應的 trust chain。

官方有提到一個有可能的情況:如果你的應用程式有設定 certificate pinning 的話,應該是對 root CA 設定,而非對 intermediate CA 或是 leaf 做:

If you use intermediate CA information through certificate pinning, you will need to make changes and pin to an Amazon Trust Services root CA instead of an intermediate CA or leaf certificate.

這個也算通則,因為就 certificate pinning 想要做到的效果,對 root CA 做就就行了...

Let's Encrypt 更新了 ToS

在「Let's Encrypt’s subscriber agreement changes on Sept 21 (letsencrypt.org)」這邊看到的,Let's Encrypt 有提供 diff 的內容,在「LE-SA-v1.2-v1.3-diff.docx」這邊,你也可以用 Google Docs Viewer 看:「LE-SA-v1.2-v1.3-diff.docx」。

看起來主要是用語上的改變 (可能是律師的建議?),除了 revoke 的章節外看起來沒什麼大變化。而 revoke 的章節部份增加了這兩段文字:

You warrant to ISRG and the public-at-large, and You agree, that before providing a reason for revoking Your Certificate, you will have reviewed the revocation guidelines found in the “Revoking Certificates” section of the Let’s Encrypt documentation available at https://letsencrypt.org/docs/ , and that you will provide Your corresponding revocation reason code with awareness of such guidelines.

You acknowledge and accept that ISRG may modify any revocation reason code provided by You if ISRG determines, in its sole discretion, that a different reason code for revocation is more appropriate or is required by industry standards.

不確定自動化的 client 需不需要重新再 accept 一次?

Let's Encrypt 撤銷了兩百萬個以 tls-alpn-01 驗證的憑證

前幾天 Let's Encrypt 決定撤銷大約兩百萬個以 tls-alpn-01 簽發的憑證:「2022.01.25 Issue with TLS-ALPN-01 Validation Method」,在 Hacker News 上的討論「Issue with TLS-ALPN-01 Validation Method (letsencrypt.org)」也可以翻一下。

這次主要是有兩個修改,第一個是要 TLSv1.2 以上,不支援舊版的 TLS 驗證:

First, we now guarantee that our client which reaches out to conduct the “acme-tls/1” handshake will negotiate TLS version 1.2 or higher. If your ACME client or integration only supports a maximum TLS version of 1.1 when conducting the TLS-ALPN-01 challenge, it will break. We are not aware of any ACME clients with this limitation.

另外一個是淘汰掉 legacy OID:

Second, we no longer support the legacy 1.3.6.1.5.5.7.1.30.1 OID which was used to identify the acmeIdentifier extension in earlier drafts of RFC 8737. We now only accept the standardized OID 1.3.6.1.5.5.7.1.31. If your client uses the wrong OID when constructing the certificate used for the TLS-ALPN-01 handshake, it will break. Please either update your client, or switch to using a different validation method.

目前還是以 http-01 與 dns-01 為主,暫時不用管,但如果有人用 tls-alpn-01 的稍微注意一下吧...

DST Root CA X3 將在今天 22:01:15 過期

先前提到 Let's Encrypt 發出的憑證在 9/30 會產生問題,主因是 IdenTrustDST Root CA X3 會在 9/30 過期,交叉簽名加上 OpenSSL 1.0.2 的判斷條件太嚴格導致的:「OpenSSL 1.0.2 與 Let's Encrypt 在這個月月底的相容性問題」。

本來以為是 UTC 的 2021/09/30 23:59:59 之類的時間,結果因為要面對這個問題,需要確認正確的時間,結果發現不是 UTC 的 2021/09/30 23:59:59,而是一個奇怪的時間:

Validity
    Not Before: Sep 30 21:12:19 2000 GMT
    Not After : Sep 30 14:01:15 2021 GMT

所以是 2021/09/30 22:01:15 (台灣時間) 會過期,今天晚上可以看一下情況...

OpenSSL 1.0.2 與 Let's Encrypt 在這個月月底的相容性問題

看到 OpenSSL 的官方居然特地寫一篇與 Let's Encrypt 的相容性問題:「Old Let’s Encrypt Root Certificate Expiration and OpenSSL 1.0.2」。

這邊提到的 OpenSSL 1.0.2 很舊了 (在 Ubuntu 16.04 內是 1.0.2g),理論上大多數的機器應該不太會遇到這個問題。

問題出自 Let's Encrypt 舊的 DST Root CA X3 將在這個月月底過期,這在 Let's Encrypt 的「DST Root CA X3 Expiration (September 2021)」這邊也有提到。

The currently recommended certificate chain as presented to Let’s Encrypt ACME clients when new certificates are issued contains an intermediate certificate (ISRG Root X1) that is signed by an old DST Root CA X3 certificate that expires on 2021-09-30.

理想上只有要任何一條 trust chain 成立,就應該會把這個憑證認為是合法的憑證,但這在 OpenSSL 1.0.2 (以及之前的版本) 不是這樣設計。

舊版的設計是只要有任何一條過期的憑證,就會把憑證認為過期而失效:

Unfortunately this does not apply to OpenSSL 1.0.2 which always prefers the untrusted chain and if that chain contains a path that leads to an expired trusted root certificate (DST Root CA X3), it will be selected for the certificate verification and the expiration will be reported.

OpenSSL 官方給了三個 workaround 可以做,另外我還有想到一個惡搞方式,是可以用其他家免費的憑證... 不過也是得測看看在 OpenSSL 1.0.2 下會不會動。

關於各家 ACME client (或者說 Let's Encrypt client?)

在「Another free CA as an alternative to Let's Encrypt (scotthelme.co.uk)」這邊引用的文章本來在討論又多了一家免費的 SSL certificate 可以用,但結果討論的主力都在講除了 Certbot 外還有什麼比較好用...

大家之所以厭惡 Certbot,先不講他需要依賴一堆 Python 的套件包,最主要的問題在於現在 Certbot 官方建議的指引裡面都要求你裝 Snap,而 Snap 這東西超級吃資源...

既然是資源問題,裡面可以看到 Dehydrated 又被拿出來推薦了,另外也有提到 acme.sh,不過我個人不太愛 acme.sh,主要是預設值跑去用 ZeroSSL 的 CA。

這種單檔就可以跑的很適合包進像是 Ansible 這類的管理工具,至少目前用起來沒什麼大問題...

CloudFront 宣佈支援 ECDSA 的 Certificate

Amazon CloudFront 宣佈支援 ECDSA 的 certificate:「Amazon CloudFront now supports ECDSA certificates for HTTPS connections to viewers」。

用主要是讓 certificate 更小,讓 HTTPS 建立時的過程更快 (包括了傳輸的速度與計算的速度):

As a result, conducting TLS handshakes with ECDSA certificates requires less networking and computing resources making them a good option for IoT devices that have limited storage and processing capabilities.

很久以前好像有看到資料說 256 bits 的 EC 運算量跟 768~1024 bits 的 RSA 差不多,但一時間找不到資料...

目前 CloudFront 只支援 NIST P-256 (secp256r1,或稱作 prime256v1):

Starting today, you can use Elliptic Curve Digital Signature Algorithm (ECDSA) P256 certificates to negotiate HTTPS connections between your viewers and Amazon CloudFront.

但 NIST P-256 一直為人詬病,在「SafeCurves: choosing safe curves for elliptic-curve cryptography」這邊可以看到 NIST 宣稱的效率設計實際上都不是真的:

Subsequent research (and to some extent previous research) showed that essentially all of these efficiency-related decisions were suboptimal, that many of them actively damaged efficiency, and that some of them were bad for security.

但目前標準是往 NIST P-256、NIST P-384 與 NIST P-521 靠攏 (主要是受到 CA/Browser Forum 的限制),要其他 curve 的 certificate 也沒辦法生,目前可能還是繼續觀望...

Squid + GnuTLS 不支援 Intermediate Certificate 的問題

先前在「架設 Proxy over TLS」這邊提到了我用 Squid 架 Proxy over TLS 的服務起來用,本來在家裡跑得好好的,但到了公司發現卻不能用,追蹤後發現是目前 Ubuntu 裡面包的 Squid + GnuTLS 沒有辦法支援 intermediate certificate 的問題,而且有人問過了:「[squid-users] HTTPS_PORT AND SSL CERT」。

這邊先講測試的方法,然後後面再講解法。

測試的方式可以用 openssl s_client -connect hostname:port 測,正常的情況會可以看到兩層。

在這邊的例子裡,R3 簽了 home.gslin.orgDST Root CA X3 簽了 R3,而 DST Root CA X3 則在 root certificate 名單中:

$ openssl s_client -connect home.gslin.org:443
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = home.gslin.org
verify return:1
CONNECTED(00000003)
---
Certificate chain
 0 s:CN = home.gslin.org
   i:C = US, O = Let's Encrypt, CN = R3
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
---

如果沒有送出 Intermediate Certificate 的話就會導致信任鏈無法建立,像是我故意設計的 nointermediate.gslin.com 這樣,R3 簽了 nointermediate.gslin.com,但 R3 並沒有在 root certificate 的名單中:

$ openssl s_client -connect nointermediate.gslin.com:443
CONNECTED(00000003)
depth=0 CN = nointermediate.gslin.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = nointermediate.gslin.com
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 CN = nointermediate.gslin.com
verify return:1
---
Certificate chain
 0 s:CN = nointermediate.gslin.com
   i:C = US, O = Let's Encrypt, CN = R3                                
---

而想到的解法就是重新包一份 Squid 出來用,把本來的 --with-gnutls 改成 --with-openssl

這邊會先裝 Build-Depends 裡面指定的東西,然後加裝 libssl-dev,接著換掉 --with-gnutls 後編譯,最後產生 .deb

sudo apt install -y ed libltdl-dev pkg-config build-essential cdbs debhelper dpkg-dev lsb-release dh-apparmor libcppunit-dev libcap2-dev libdb-dev libecap3-dev libexpat1-dev libgnutls28-dev libkrb5-dev comerr-dev libldap2-dev libnetfilter-conntrack-dev libpam0g-dev libsasl2-dev libxml2-dev nettle-dev libssl-dev
apt-get source squid
cd squid/squid-4.10
sed -i -e 's/--with-gnutls/--with-openssl/' debian/rules
cd ..
dpkg-buildpackage -rfakeroot -uc -b

編好的 .deb 就可以拿到其他機器上裝了,然後就可以吐出 intermediate certificate 了...