這是一張有關標題為 內網 HTTPS 無痛升級:使用 OpenSSL 建立自簽名憑證 的圖片

內網 HTTPS 無痛升級:使用 OpenSSL 建立自簽名憑證

使用 OpenSSL 建立自簽名 TLS 憑證,適用於 Nginx、Docker 等內部系統,並設定為系統信任的憑證,徹底解決瀏覽器不安全提示。

前言

在開發或內部測試階段,我們經常需要部署多種服務來滿足需求,例如:

  1. Nginx 搭建網頁伺服器,分發流量或提供靜態資源
  2. AdGuardHome 實現 DNS over HTTPS(DoH),阻擋廣告並提升隱私保護
  3. Node.js 或 Deno 部署動態應用(如 API 或全端框架 Next.js)
  4. Docker 容器服務

我們在實作內網相關服務後,在登入上如果遇到有要輸入密碼的頁面,瀏覽器會在未使用 HTTPS 協定時會提示此連線不安全,本文章將說明如何產生自簽名憑證(self-signed certificate)並應用於該服務,最終於系統中信任該憑證。

什麼是 HTTP(S)

HTTP(HyperText Transfer Protocol)與 HTTPS(HyperText Transfer Protocol Secure)是常用於資料傳輸的應用層協定,負責定義應用程式之間如何交換數據。其中,HTTP 是明文傳輸,容易被駭客或第三方攔截並分析未加密的數據,如帳號密碼或敏感資訊。相較之下,HTTPS 是基於 HTTP 並加入 TLS(Transport Layer Security)加密協定的版本,透過加密確保資料的機密性、完整性與身份驗證,即使數據遭到攔截,也無法被解讀或有效利用,大幅降低了敏感資訊外洩的風險。

TLS 是 SSL(Secure Sockets Layer)的後繼版本,從 1999 年起取代 SSL 成為網路加密的標準協定。相比已經廢棄的 SSL,TLS 提供更高效的握手流程、改進的加密算法,以及針對已知漏洞的修正。目前推薦使用 TLS 1.2(2008 年推出)或 TLS 1.3(2018 年推出),以確保通信安全性符合現代標準。雖然「SSL」這個術語仍被廣泛使用來泛指加密協定,但現今的 HTTPS 通訊都是基於 TLS。此外,像 OpenSSL 這樣的開源工具庫,是實現 TLS/SSL 協定的重要工具,支持憑證的產生、簽名與驗證等操作,是打造 HTTPS 安全環境的核心。

什麼是憑證

憑證是實現 HTTPS 安全性的核心要素之一。HTTPS 透過數位憑證來驗證伺服器的身份,這個數位憑證通常是 X.509 規範所實現的實體憑證,該規範訂定了伺服器的身份資訊(如域名)、公鑰、有效期限、簽發機構以及其他擴展屬性。受信任的憑證授權機構(certificate authority,CA)對這些資訊進行數位簽名,用於確保憑證的完整性與可信度,從而防止中間人攻擊與假冒伺服器的行為。

要產生 X.509 憑證,首先要產生一組密鑰對,其中包含:

  • 私鑰(Private Key):用於對資料進行加密或進行數位簽名,且必須妥善保管,不可公開。
  • 公鑰(Public Key):公鑰用於加密資料,或驗證由私鑰簽名的資料,確保數據的完整性與來源的真實性。公鑰是由私鑰通過非對稱加密算法計算得出,與私鑰配對使用,但無法從公鑰反推出私鑰。

使用私鑰產生的公鑰,我們可以建立憑證簽名請求(certificate signing request,CSR),CSR 是產生憑證的基礎文件。我們將 CSR 提交給 CA 後,CA 作為信任機構,會根據 CSR 中的資訊驗證伺服器的身份,並使用其中的公鑰簽名並產生正式的憑證,用於伺服器身份驗證和加密通訊。CSR 中包含:

  • 主機名稱(Common Name, CN):例如域名 wellstsai.com。
  • 組織資訊:包含公司名稱、國家、地區等相關資訊。
  • 公鑰:由私鑰產生,嵌入到 CSR 文件中,用於身份驗證與加密通訊。

如果是提交給 CA,CA 在完成伺服器身份驗證後,會使用他們自己的私鑰對 CSR 進行數位簽名,並設定憑證的有效期限,最終產生正式的 X.509 憑證。

如果是自簽名,就不需要 CA 的參與。此時我們可以使用自己的私鑰直接對 CSR 進行數位簽名,產生自簽名的 X.509 憑證。自簽名憑證雖然技術上具備完整功能,但不被公共信任機構認可,因此適用於內部測試環境或私有系統。

最終,在伺服器上設定所產生的私鑰與憑證,並將其整合到伺服器的 HTTPS 設置中(如 Nginx)。當使用者在瀏覽網頁時,瀏覽器會自動與伺服器進行數位憑證的認證過程。

瀏覽器會判斷憑證是否由受信任的 CA 簽名、是否仍在有效期內,以及是否與訪問的域名相符。如果這些條件都符合,瀏覽器會在網址列左上方顯示連線安全的鎖頭圖示,表示該網站的憑證有效且可信。隨後,瀏覽器與伺服器會進一步建立加密通訊通道。在建立通訊通道的過程中,瀏覽器會使用伺服器憑證中的公鑰,安全地產生並傳送一組對稱加密密鑰。此密鑰由伺服器解密後,雙方共享同一個對稱加密密鑰(速度較快),用於後續的數據傳輸加密。這樣一來,使用者與伺服器之間的通訊內容(如瀏覽資料、提交表單等)都會透過對稱加密與解密進行保護,防止第三方(如中間人或惡意攻擊者)竊取或篡改傳輸中的數據。

產生自簽名憑證

OpenSSL 環境檢查

確定是否安裝 OpenSSL,於終端機中輸入:

1
openssl version

系統如果有安裝 OpenSSL,輸出為:

1
2
3
4
5
# Windows 11 24H2
OpenSSL 1.1.1v  1 Aug 2023

# Ubuntu 24.04 (Homebrew Upgraded)
OpenSSL 3.4.0 22 Oct 2024 (Library: OpenSSL 3.4.0 22 Oct 2024)

如果沒有安裝的話,可透過 apt 進行安裝。

Windows 需要自行到 slproweb 自行下載安裝檔。

1
2
3
# Linux Only
sudo apt update
sudo apt install openssl

安裝後透過 openssl version 檢查版本號,應該要有版本輸出。

建立一鍵產生憑證腳本

使用熟悉的編輯器(如 VS Code、Nano 或 Vim),將以下內容複製並貼到編輯器中。根據需求修改下方對應的參數內容(例如國家代碼、城市名稱、域名、憑證期限等),然後將檔案儲存為 ~/generate_tls_cert.sh:

 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
55
56
57
58
#!/bin/bash

# 定義變數
OUTPUT_DIR="$HOME/certificates"
KEY_FILE="$OUTPUT_DIR/wells_server.key" # 私鑰
CSR_FILE="$OUTPUT_DIR/wells_server.csr" # CSR 文件
CRT_FILE="$OUTPUT_DIR/wells_server.crt" # 憑證
DAYS_VALID=365
COUNTRY="TW"
STATE="Taiwan"
CITY="Taipei"
ORG="WellsTsai"
OU="wells_server"
CN="wells_server"

# 憑證適用的域名或 IP
SAN="DNS:wells_server,DNS:localhost,IP:127.0.0.1,IP:192.168.1.100"

# 建立輸出目錄
mkdir -p "$OUTPUT_DIR" || { echo "無法建立目錄 $OUTPUT_DIR,腳本已終止"; exit 1; }

# 檢查並安裝 openssl
if ! command -v openssl &> /dev/null; then
    echo "openssl 不存在,腳本已終止"
    exit 1
fi

# 建立 ECC 私鑰
echo "建立 ECC 私鑰..."
openssl ecparam -genkey -name prime256v1 -out "$KEY_FILE" || { echo "ECC 私鑰建立失敗,腳本已終止"; exit 1; }

# 建立 CSR 文件
echo "建立 CSR 文件..."
openssl req -new -key "$KEY_FILE" -out "$CSR_FILE" \
    -subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORG/OU=$OU/CN=$CN" \
    -addext "subjectAltName=$SAN" || { echo "CSR 文件建立失敗,腳本已終止"; exit 1; }

# 建立自簽名憑證
echo "建立自簽名憑證..."

# 建立臨時文件
SAN_CONF=$(mktemp) || { echo "無法建立臨時文件,腳本已終止"; exit 1; }
chmod 600 "$SAN_CONF"

# 將 SAN 配置寫入臨時文件
printf "[v3_req]\nsubjectAltName=$SAN" > "$SAN_CONF"

openssl x509 -req -days "$DAYS_VALID" -in "$CSR_FILE" -signkey "$KEY_FILE" -out "$CRT_FILE" -extfile "$SAN_CONF" -extensions v3_req \
    || { echo "自簽署憑證建立失敗,腳本已終止"; rm -f "$SAN_CONF"; exit 1; }

# 清理臨時文件
rm -f "$SAN_CONF"

# 顯示完成訊息
echo "憑證已建立!"
echo "私鑰: $KEY_FILE"
echo "CSR 文件: $CSR_FILE"
echo "憑證: $CRT_FILE"

這邊我的本地端伺服器名 wells_server,如果有實際域名像是 wellstsai.com,則填寫對應的域名、IP。

從腳本中可得知,首先會建立私鑰,並產生 CSR 文件。CSR 文件需包含相關的域名、地理資訊。

其中 CSR 中的 subjectAltName(subject alternative name,SAN)是 X.509 憑證的擴展屬性,主要用於指定憑證適用的多個主機名、域名或 IP 地址。SAN 欄位中的這些名稱會被寫入憑證中,以確保瀏覽器或其他客戶端能正確驗證憑證是否與請求的資源相匹配。

自 2017 年 4 月起,Chrome 58 版本及其他基於 Chromium 的瀏覽器(如 Chrome 和 Edge)停止依賴憑證的 CN 欄位進行主機名稱驗證,而改為僅依賴 SAN 欄位。這使 SAN 成為現代憑證配置中的必要屬性。

最後產出 CSR 文件後,使用私鑰自簽名該憑證。

執行腳本

確認域名設定正確後,儲存修改,並為腳本新增執行權限:

1
chmod +x ~/generate_tls_cert.sh 

接著執行腳本以產生憑證:

1
~/generate_tls_cert.sh

執行腳本後,使用者目錄下的 ~/certificates/ 資料夾內會產生三個檔案,分別對應私鑰、CSR 文件與憑證。

1
2
3
4
5
6
7
wells@server:~$ ls -al ~/certificates/
total 20
drwxrwxr-x  2 wells wells 4096 12月  3 14:58 .
drwxr-x--- 29 wells wells 4096 12月  4 14:50 ..
-rw-rw-r--  1 wells wells  798 12月  4 14:50 wells_server.crt
-rw-rw-r--  1 wells wells  558 12月  4 14:50 wells_server.csr
-rw-------  1 wells wells  302 12月  4 14:50 wells_server.key

妥善保存這些檔案,並避免私鑰外洩以確保安全性。

Docker 中使用憑證

這邊以 portainer 為例子。由於我先前已建立 portainer,所以先透過 stop 與 rm 將其清除,並重新建立一次容器。

1
2
docker stop portainer
docker rm portainer

隨後,重新建立該容器,以下指令為參照官方說明,但帶上我所提供的私鑰與憑證。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
docker run -d \
--name=portainer \
--restart=always \
-p 9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
-v /home/wells/certificates:/certs \
portainer/portainer-ce:latest \
--ssl \
--sslcert /certs/wells_server.crt \
--sslkey /certs/wells_server.key

系統安裝自簽名憑證

使用自簽名憑證設定伺服器時,因未經 CA 認證,系統或瀏覽器不會自動信任該憑證。因此,需手動將憑證加入信任列表,否則系統或瀏覽器會警告連線不安全,並顯示憑證未被信任的提示。

以下以 Windows 為主,說明如何將自簽名憑證匯入系統。

透過 scp [使用者]@[伺服器]:[檔案路徑] [本地路徑] 或是 VS Code 把伺服器的檔案抓下來至本地。

1
scp wells@wells_server:~/certificates/wells_server.crt C:\Users\wells

下載完成後,可以透過圖形介面或是指令來安裝憑證。雙點 wells_server.crt,點選安裝憑證,選擇目前使用者作為存放範圍,並將憑證存放於受信任的根憑證授權單位

手動安裝憑證,需存放於受信任的根憑證授權單位

或是透過終端機指令存放於目前使用者的受信任的根憑證授權單位:

1
2
3
4
5
6
7
8
9
# For Windows, CMD
certutil -user -addstore "Root" C:\Users\wells\wells_server.crt

# For macOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ~/wells_server.crt

# For Linux
sudo cp ~/wells_server.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

關閉瀏覽器,重新開啟後連接至相關服務的網址。此時該左上方瀏覽器中顯示的憑證狀態,應標示為受信任的憑證。

由於此電腦已信任自簽名憑證,在登入階段,不會跳出不安全

常見的憑證授權機構

最後簡要介紹一些常見的 CA。這些機構負責簽發並驗證憑證,確保網站的身份可信,進而提供安全的 HTTPS 通訊環境。有經過 CA 簽名的憑證,會自動被操作系統和瀏覽器信任(前提是該 CA 屬於系統的「受信任的根憑證庫」),使用者瀏覽網站時可直接建立加密連線,避免「不安全」的警告,並有效防範中間人攻擊(MITM),提升網站的信任度和用戶體驗。這通常會用在生產階段或是公開的網域中。

根據 W3Techs 統計資料(2024/12/04),以下是主要 CA 及其市場佔有情況:

  1. Let’s Encrypt 擁有 59.1% 的網站使用率,市場佔有率達 62.9%。其最大的特點在於提供免費自動化的憑證服務,特別受到中小型網站和個人開發者的青睞。
  2. GlobalSign 使用率為 20.3%,市場佔有率達 21.6%,主要服務對象為企業和政府機構,以其高安全性和穩定性著稱。
  3. Sectigo 使用率為 6.1%,市場佔有率達 6.4%,以提供多樣化的憑證產品和經濟實惠的價格吸引不同規模的企業用戶。
  4. GoDaddy 市場佔有率達 4.3%。該 CA 主打中小企業市場,提供便捷的域名與 SSL 憑證整合服務。
  5. DigiCert 使用率為 4.0%,市場佔有率達 4.2%,專注於高安全性和企業級需求,適用於金融和高風險行業的應用場景。

結論

本篇文章詳細說明了如何利用 OpenSSL 建立自簽名憑證,並將其應用於內部伺服器與容器服務(如 Nginx 和 Docker)。通過自簽名憑證,我們能夠在內網環境中快速啟用 HTTPS,加密通訊內容並提升安全性。雖然自簽名憑證不適用於公開網站,但在內部測試或開發階段,它是一個簡單、高效且低成本的解決方案。

對於內網環境的 IT 人員,只需在內網伺服器上統一部署自簽名憑證,並將其新增至內部系統的受信任憑證庫,即可快速完成 HTTPS 的全面升級,打造安全且高效的內部通訊環境。

憑證的主要目的是建立伺服器與使用者之間的安全連線。雖然自簽名憑證不被公共 CA 認可,但在手動信任正確憑證後,即使遭受中間人攻擊,由於攻擊者無法偽造與原憑證匹配的私鑰,瀏覽器將會因憑證不符而提示「不安全」的警告,從而有效避免中間人攻擊的威脅。

先前的文章:《深入解析臺灣 DNS RPZ 網路封鎖與解鎖策略》,也曾提到類似的憑證錯誤問題。該情境中,憑證錯誤是因原網站的憑證被 DNS 脅持所導致,這進一步說明了正確配置憑證與驗證其可信性的重要性。

參考文獻

  1. Rename OpenSSL to OpenTLS To Comply with RFC7568
  2. RFC 7568 - Deprecating Secure Sockets Layer Version 3.0
  3. RFC 2246 - The TLS Protocol Version 1.0
  4. TLS vs. SSL - Win32 apps | Microsoft Learn
  5. SSL vs TLS - Difference Between Communication Protocols - AWS
  6. X.509 - Wikipedia
  7. X.509 certificates | Microsoft Learn
  8. Deprecations and Removals in Chrome 58 | Chrome for Developers
  9. Security Changes in Chrome 58 - What You Need to Know
  10. Using your own SSL certificate with Portainer | Portainer Documentation
  11. How do you add a certificate authority (CA) to Ubuntu? - Super User
  12. Usage Statistics and Market Share of SSL Certificate Authorities for Websites, December 2024
主題 Stack 由 Jimmy 設計