前言
在本地開發(fā)中,有時候我們經(jīng)常需要模擬https環(huán)境,比如PWA應用要求必須使用https訪問。在傳統(tǒng)的解決方案中,我們需要使用自簽證書,然后在http server中使用自簽證書。由于自簽證書瀏覽器不信任,為了解決瀏覽器信任問題我們需要將自簽證書使用的CA證書添加到系統(tǒng)或瀏覽器的可信CA證書中,來規(guī)避這個問題。
以前這些步驟需要一系列繁瑣的openssl命令生成,盡管有腳本化的方案幫助我們簡化輸入這些命令(可以參考以前的blog: Nginx SSL快速雙向認證配置)。但是仍然覺得對本地開發(fā)不那么友好,有些繁重了。本文將介紹一種更加簡單友好的方式生成本地https證書,并且信任自簽CA的方案——mkcert。
mkcert簡介
mkcert是一個使用go語言編寫的生成本地自簽證書的小程序,具有跨平臺,使用簡單,支持多域名,自動信任CA等一系列方便的特性可供本地開發(fā)時快速創(chuàng)建https環(huán)境使用。
安裝方式也非常簡單,由于go語言的靜態(tài)編譯和跨平臺的特性,官方提供各平臺預編譯的版本,直接下載到本地,給可執(zhí)行權限(Linux/Unix需要)就可以了。下載地址: https://github.com/FiloSottile/mkcert/releases/latest
此外,mkcert已經(jīng)推送至Homebrew, MacPorts, Linuxbrew, Chocolatey, Scoop等包管理平臺中,也可以直接借助對應的包管理平臺安裝。如:
brew install mkcert # Homebrew/Linuxbrew
choco install mkcert # Chocolatey
安裝成功后,應該可以使用mkcert命令了:
PS C:\Users\abcfy\projects> mkcert
Using the local CA at "C:\Users\abcfy\AppData\Local\mkcert" ?
Usage of mkcert:
$ mkcert -install
Install the local CA in the system trust store.
$ mkcert example.org
Generate "example.org.pem" and "example.org-key.pem".
$ mkcert example.com myapp.dev localhost 127.0.0.1 ::1
Generate "example.com+4.pem" and "example.com+4-key.pem".
$ mkcert "*.example.it"
Generate "_wildcard.example.it.pem" and "_wildcard.example.it-key.pem".
$ mkcert -uninstall
Uninstall the local CA (but do not delete it).
For more options, run "mkcert -help".
mkcert基本使用
從上面自帶的幫助輸出來看,mkcert已經(jīng)給出了一個基本的工作流,規(guī)避了繁雜的openssl命令,幾個簡單的參數(shù)就可以生成一個本地可信的https證書了。更詳細的用法直接看官方文檔就好。
將CA證書加入本地可信CA
$ mkcert -install
Using the local CA at "C:\Users\abcfy\AppData\Local\mkcert" ?
僅僅這么一條簡單的命令,就幫助我們將mkcert使用的根證書加入了本地可信CA中,以后由該CA簽發(fā)的證書在本地都是可信的。
在Windows的可信CA列表可以找到該證書:

在MacOS的證書列表同樣也可以找到:


同理,在Linux系統(tǒng)中也是類似的效果,這里就不演示了。
生成自簽證書
生成自簽證書的命令十分簡單:
mkcert domain1 [domain2 [...]]
直接跟多個要簽發(fā)的域名或ip就行了,比如簽發(fā)一個僅本機訪問的證書(可以通過127.0.0.1和localhost,以及ipv6地址::1訪問)
mkcert localhost 127.0.0.1 ::1
Using the local CA at "C:\Users\abcfy\AppData\Local\mkcert" ?
Created a new certificate valid for the following names ??
- "localhost"
- "127.0.0.1"
- "::1"
The certificate is at "./localhost+2.pem" and the key at "./localhost+2-key.pem" ?
通過輸出,我們可以看到成功生成了localhost+2.pem證書文件和localhost+2-key.pem私鑰文件,只要在web server上使用這兩個文件就可以了。
使用生成的證書文件
默認生成的證書格式為PEM(Privacy Enhanced Mail)格式,任何支持PEM格式證書的程序都可以使用。比如常見的Apache或Nginx等,這里我們用python自帶的SimpleHttpServer演示一下這個證書的效果(代碼參考來自: https://gist.github.com/RichardBronosky/644cdfea681518403f5409fa16823c1f):
python2版本(MacOS自帶的版本):
#!/usr/bin/env python2
import BaseHTTPServer, SimpleHTTPServer
import ssl
httpd = BaseHTTPServer.HTTPServer(('0.0.0.0', 443), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket(httpd.socket, certfile='./localhost+2.pem', keyfile='./localhost+2-key.pem', server_side=True, ssl_version=ssl.PROTOCOL_TLSv1_2)
httpd.serve_forever()
python3版本:
#!/usr/bin/env python3
import http.server
import ssl
httpd = http.server.HTTPServer(('0.0.0.0', 443), http.server.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket(httpd.socket, certfile='./localhost+2.pem', keyfile='./localhost+2-key.pem', server_side=True, ssl_version=ssl.PROTOCOL_TLSv1_2)
httpd.serve_forever()
使用python simple-https-server.py運行即可(注意Linux/Unix下綁定443端口需要root權限,你可能需要使用sudo提權)


局域網(wǎng)內(nèi)使用
有時候我們需要在局域網(wǎng)內(nèi)測試https應用,這種環(huán)境可能不對外,因此也無法使用像Let's encrypt這種免費證書的方案給局域網(wǎng)簽發(fā)一個可信的證書,而且Let's encrypt本身也不支持認證Ip。
先來回憶一下證書可信的三個要素:
- 由可信的CA機構簽發(fā)
- 訪問的地址跟證書認證地址相符
- 證書在有效期內(nèi)
如果期望我們自簽證書在局域網(wǎng)內(nèi)使用,以上三個條件都需要滿足。很明顯自簽證書一定可以滿足證書在有效期內(nèi),那么需要保證后兩條。我們簽發(fā)的證書必須匹配瀏覽器的地址欄,比如局域網(wǎng)的ip或者域名,此外還需要信任CA。
我們先重新簽發(fā)一下證書,加上本機的局域網(wǎng)ip認證:
mkcert localhost 127.0.0.1 ::1 192.168.31.170
Using the local CA at "C:\Users\abcfy\AppData\Local\mkcert" ?
Created a new certificate valid for the following names ??
- "localhost"
- "127.0.0.1"
- "::1"
- "192.168.31.170"
The certificate is at "./localhost+3.pem" and the key at "./localhost+3-key.pem" ?
再次驗證發(fā)現(xiàn)使用https://192.168.31.170本機訪問也是可信的。然后我們需要將CA證書發(fā)放給局域網(wǎng)內(nèi)其他的用戶。
mkcert -CAROOT
C:\Users\abcfy\AppData\Local\mkcert
使用mkcert -CAROOT命令可以列出CA證書的存放路徑
ls $(mkcert -CAROOT)
目錄: C:\Users\abcfy\AppData\Local\mkcert
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2019-04-09-星期二 上午 1 2484 rootCA-key.pem
2:16
-a---- 2019-04-09-星期二 上午 1 1651 rootCA.pem
2:16
可以看到CA路徑下有兩個文件rootCA-key.pem和rootCA.pem兩個文件,用戶需要信任rootCA.pem這個文件。將rootCA.pem拷貝一個副本,并命名為rootCA.crt(因為windows并不識別pem擴展名,并且Ubuntu也不會將pem擴展名作為CA證書文件對待),將rootCA.crt文件分發(fā)給其他用戶,手工導入。
windows導入證書的方法是雙擊這個文件,在證書導入向?qū)е袑⒆C書導入受信任的根證書頒發(fā)機構:

MacOS的做法也一樣,同樣選擇將CA證書導入到受信任的根證書辦法機構。
Ubuntu的做法可以將證書文件(必須是crt后綴)放入/usr/local/share/ca-certificates/,然后執(zhí)行sudo update-ca-certificates
Android和IOS信任CA證書的做法參考官方文檔。
在局域網(wǎng)其他計算機就可以訪問https而不報警了。我在另一臺虛擬機Ubuntu上使用curl測試結果:
$ curl -I https://192.168.31.170
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.6.8
Date: Tue, 09 Apr 2019 05:22:12 GMT
Content-type: text/html; charset=utf-8
Content-Length: 1794
無警告,加上-v參數(shù)輸出還會告訴證書是可信的。
其他一些高級用法
以上我們演示了一些mkcert最基本的用法,非常簡單。如果我們打開mkcert --help看幫助的話,還會發(fā)現(xiàn)很多高級用法。
比如-cert-file FILE, -key-file FILE, -p12-file FILE可以定義輸出的證書文件名。
-client可以產(chǎn)生客戶端認證證書,用于SSL雙向認證。之前的文章介紹過使用openssl腳本的(Nginx SSL快速雙向認證配置),可以對比下。
-pkcs12命令可以產(chǎn)生PKCS12格式的證書。java程序通常不支持PEM格式的證書,但是支持PKCS12格式的證書。通過這個程序我們可以很方便的產(chǎn)生PKCS12格式的證書直接給Java程序使用,這里就不演示了。
其他高級用法不做介紹了,各位有興趣可以根據(jù)自己的實際需求和場景進行發(fā)掘吧。
小結
本篇文章我們介紹了一個好用的小工具mkcert,簡化我們在本地搭建https環(huán)境的復雜性,無需操作繁雜的openssl實現(xiàn)自簽證書了,這個小程序就可以幫助我們自簽證書,在本機使用還會自動信任CA,非常方便。