如何製作和使用自簽名證書

如何製作和使用自簽名證書
在計算機加密和安全領域中,我們會時常遇到:自簽名安全證書。

因爲自簽名證書籤發相對於商業證書流程簡單,費用低廉(除了電費幾乎不花錢),更新容易。所以在開發領域、甚至一些小衆場景下特別常見,比如 K8S / MySQL 集羣中的 TLS 認證,一些大的集團、公司的內網服務、網站安全證書、企業路由器設備的管理後臺、用於管理企業員工的“安全准入客戶端”等不乏使用這個方案。

本篇文章就來聊聊如何快速生成證書,以及如何安裝部署到不同的環境中。

寫在前面
經常有人說,使用自簽名證書不安全,會導致中間人***。這裏需要爲自簽名證書“正名”,如果你製作生成的證書被妥善保管(即不泄漏並被二次利用),並將其加入你的有限的設備(自用、團隊使用)的證書信任列表中,在明確你的設備訪問地址(不涉及DNS***),你是不會遇到中間人***的。

比如當你遇到類似下面的場景,不一定會遇到不安全的事情,有可能只是管理員忘記換掉過期證書、或者你自己生成證書後,使用了一臺沒有信任證書的設備進行訪問、也可能是管理員壓根沒有想在公網簽發證書,想做一個私有的網站:
如何製作和使用自簽名證書

多數時候我們看到的不安全的證書是因爲應用錯誤配置、有心人基於 DNS 地址***、證書過期造成、甚至是我們未曾正確配置證書信任白名單造成的。

一旦我們正確生成證書,在妥善保存證書後,進行了有限設備的白名單設置後,我們的證書和商業證書的使用是幾乎沒有差別的(除了無法使用 OCSP、EV 證書使用上存在一定額外工作外)。

如何製作和使用自簽名證書
那麼來聊聊如何快速生成證書。

使用命令行腳本生成自簽名證書
最常見和通用的做法便是安裝配置一個帶有 openssl 環境的系統,然後使用命令行執行類似下面這樣的命令:


openssl req -x509 -newkey rsa:2048 -keyout ssl/${fileName}.key -out ssl/${fileName}.crt -days 3600 -nodes ...

這裏如果你選擇不使用配置文件的話,得參考openssl 文檔,附帶一堆參數,或需要交互式的輸入一堆選項,並祈禱在中間每一步沒有輸入出錯,例如下面這樣:


enerating a RSA private key
....................................................................................................................................................................................................................................................................++++
.............................................................++++
writing new private key to 'example.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:XX
Locality Name (eg, city) []:XXXX
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example, Inc
Organizational Unit Name (eg, section) []:IT
Common Name (e.g. server FQDN or YOUR name) []:example.com
Email Address []:[email protected]

相比之下,使用類似下面的配置生成證書會稍微容易那麼一些:

#!/bin/sh

OUTPUT_FILENAME="lab.com"

printf "[req]
prompt                  = no
default_bits            = 4096
default_md              = sha256
encrypt_key             = no
string_mask             = utf8only

distinguished_name      = cert_distinguished_name
req_extensions          = req_x509v3_extensions
x509_extensions         = req_x509v3_extensions

[ cert_distinguished_name ]
C  = CN
ST = BJ
L  = BJ
O  = HomeLab
OU = HomeLab
CN = lab.com

[req_x509v3_extensions]
basicConstraints        = critical,CA:true
subjectKeyIdentifier    = hash
keyUsage                = critical,digitalSignature,keyCertSign,cRLSign #,keyEncipherment
extendedKeyUsage        = critical,serverAuth #, clientAuth
subjectAltName          = @alt_names

[alt_names]
DNS.1 = lab.com
DNS.2 = *.lab.com
DNS.3 = *.page.lab.com

">ssl/${OUTPUT_FILENAME}.conf

openssl req -x509 -newkey rsa:2048 -keyout ssl/$OUTPUT_FILENAME.key -out ssl/$OUTPUT_FILENAME.crt -days 3600 -nodes -config ssl/${OUTPUT_FILENAME}.conf

類似的腳本,我曾在 Traefik 示例腳本中提到過:


https://github.com/soulteary/traefik-example/blob/main/scripts/generate-certs.sh 。

還有更簡單的方案嗎?尤其是在有不斷修改 DNS、希望使用腳本自動化簽訂證書的場景下?

快速生成證書
爲此我寫了一個腳本,並使用容器進行封裝,以達到可以使用極其簡的命令行來生成證書的目的,並藉助容器簡化掉了本地需要安裝 openssl 依賴的問題,“開箱即用”。相關代碼我已經開源,項目地址

:https://github.com/soulteary/certs-maker

比如你想生成一個稍微複雜一些的站點證書,只需要執行下面這行命令就足夠了:


docker run --rm -it -e CERT_DNS="domain.com;*.domain.com;*.a.domain.com" -v `pwd`/certs:/ssl soulteary/certs-maker

執行完畢你將會看到類似下面的日誌:


User Input: { CERT_DNS: 'domain.com;*.domain.com;*.a.domain.com' }
Generating a RSA private key
................................................................................................................................................+++++
.........................................................+++++
writing new private key to 'ssl/domain.com.key'
-----

以及能夠在 ssl 目錄中看到我們生成的證書文件。

至於其他的使用方式,比如生成包含多個域名的混合證書、生成單個證書,只需要調整 CERT_DNS 參數的值即可。如果想進一步定製前文提到的證書細節,比如證書籤發國家、省份等信息,可以參考開源項目倉庫的使用方式,添加其他的參數,這裏就不過多贅述了。

使用 docker-compose 生成
如果你希望將命令保存下來,作爲代碼存儲在倉庫裏,也可以考慮編寫一個 compose 文件:


version: '2'

services:

  certs-maker:
    image: soulteary/certs-maker
    environment:
      - CERT_DNS=a.com;b.com;c.com;*.d.com;
    volumes:
      - ./certs:/ssl

將上面的內容保存爲 docker-compose.yml,然後執行 docker-compose up ,你會在 certs 目錄看到生成的證書文件。

使用證書
生成證書之後,來聊聊如何使用證書。

在各種系統上導入證書
導入證書可以參考下面的文檔,過程都很簡單,引導證書,然後重啓需要使用證書的應用即可。

  • Apple 文檔:在 iOS 和 iPadOS 中信任手動安裝的證書描述文件
  • VMware 文檔:在 Windows 主機上導入內部根 CA 證書
  • SSL 文檔: 生成證書籤名請求(CSR)在macOS鑰匙串訪問中
  • 羣暉文檔:使用自我簽署證書
    在 Java 應用中信任自簽名證書
    如果你使用的是 Java 應用訪問自簽名的網站,應用訪問過程會出現因爲證書錯誤而拒絕連接的錯誤。

解決這個問題並不複雜,只需要額外做一點點工作,將證書添加到 keystore 中,重啓 Java 應用即可:


sudo keytool -import -alias charles -file /Path-To-Certs/key.crt -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit

這個操作對於證書過期的情況,也同樣有效,早先有一篇文章有描述:《使用 Docker 和 Traefik v2 搭建 Confluence 7.3 》。

在 Debian / Ubuntu / Alpine 系統中信任證書
對於 Debian / Ubuntu 系統,信任證書相當簡單,只需要將證書拷貝到“待安裝目錄”,然後執行證書更新命令即可:


cp *.crt /usr/local/share/ca-certificates/
update-ca-certificates

Alpine 也是一樣,考慮我們經常在容器場景中使用它,所以這裏直接給出一個完整的Dockerfile 示例:

FROM alpine

RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*

ADD ./ssl/*.crt /usr/local/share/ca-certificates/

RUN update-ca-certificates --fresh

搭建配合安裝證書使用的 Web 服務
上文中如果想在客戶端(尤其是手機)上安裝證書,一定會遇到跨系統傳輸文件的問題。爲了方便分發和安裝,這裏可以使用 Nginx 啓動一個用於分享證書安裝的 Web 服務。

如何製作和使用自簽名證書
搭建通用服務
我們可以使用 Nginx 的 ngx_http_sub_module 和 ngx_http_autoindex_module 模塊構建一個能夠自動列舉證書目錄的服務:


server {
    listen 80;
    server_name localhost;
    location = /favicon.ico {
        empty_gif;
    }
    location / {
        root /public;
        autoindex on;
        sub_filter '<h1>Index of /</h1>'  '<h1>Get Certs</h1>';
        sub_filter_once on;
    }
}

將上面的內容保存爲 default.conf 後,再創建一個名爲 docker-compose.yml 的配置文件:


version: '2'

services:

  nginx:
    image: nginx:1.19.6-alpine
    ports:
      - 8080:80
    volumes: 
      - ./default.conf:/etc/nginx/templates/default.conf.template:ro
      - ./public:/public:ro

然後使用 docker-compose up 啓動服務後,使用手機訪問頁面,就能夠看到類似下面的頁面,然後使用手機訪問證書文件進行安裝和信任就可以啦。

更簡單的配置方式
上面的模式我們將配置和服務編排文件分拆成了兩個文件,考慮到這個 Nginx 配置十分簡單,那麼我們是否有辦法將其簡化呢?

答案是有的,通過對 command 命令進行調整,我們可以將 Nginx 配置的創建和服務啓動同時寫在 docker-compose.yml 編排文件中:

version: '2'

services:

  nginx:
    image: nginx:1.19.6-alpine
    ports:
      - 8080:80
    volumes: 
      - ./public:/public:ro
    command: >
      /bin/sh -c "
      echo \"daemon off;\" >> /etc/nginx/nginx.conf;
      echo \"server {
            listen 80;
            server_name localhost;
            location = /favicon.ico {
                empty_gif;
            }
            location / {
                root /public;
                autoindex on;
                sub_filter '<h1>Index of /</h1>'  '<h1>Get Certs</h1>';
                sub_filter_once on;
            }
        }\" > /etc/nginx/conf.d/default.conf; nginx;
      "

最後
最近入手了三根雷電數據線,快過年了,或許可以折騰一下雷電數據線組網開發。

--EOF

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章