在咱們不管是對服務器仍是客戶端進行 HTTPS 進行配置時,首先須要準備好的確定是相關證書文件了,而證書文件是什麼又從哪裏能夠獲取到相關證書,而且它們又是什麼關係,最後它們怎麼在通信中起做用呢?可能不少人都不是很系統的清楚這一塊;趁如今有空整理出來給你們入門瞭解下。node
爲了獲取證書前,咱們須要生成本身的一對公鑰和私鑰。在這裏咱們會使用到一個叫作 OpenSSL 的工具庫。git
# 生成私鑰文件 $ openssl genrsa -out server.key 2048 # 也能夠經過指定加密算法來生成加密的文件,經過 'openssl genrsa -help' 查看支持的算法 $ openssl genrsa -aes128 -out server.key 2048 # 生成證書文件 $ openssl req -new -x509 -days 3650 \ -subj "/C=CN/L=Guangzhou/O=Guangzhou Example Technology Co., Ltd/CN=example.com" \ -key server.key -out server.crt # e.g. 百度的證書中的身份信息 CN = baidu.com # Common Name(證書所請求的域名) O = Beijing Baidu Netcom Science Technology Co., Ltd # Organization Name OU = service operation department # Organization Unit Name L = beijing # Locality Name S = beijing # Sate or Province Name C = CN # Country Name
# 生成 CA 根證書 $ openssl genrsa -out ca.key 2048 $ openssl req -new -x509 -days 3650 \ -subj "/C=CN/L=Guangzhou/O=CA Technology Co., Ltd/CN=XXX Global Root CA" \ -key ca.key -out ca.crt # 生成 .csr 證書籤名請求文件 $ openssl req -new \ -subj "/C=CN/L=Guangzhou/O=Guangzhou Example Technology Co., Ltd/CN=example.com" \ -key server.key \ -out server.csr # 使用 CA 根證書頒發服務器證書 $ openssl x509 -req -sha256 \ -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 \ -in server.csr \ -out server.crt
在 HTTP 協議傳輸下,數據都是以明文進行傳輸,數據安全性得不到保障;使用 HTTPS 加密通信後,數據保密和不可篡都獲得進一步的保障;根據 HTTPS 服務器不一樣的配置方式,安全性也是不盡相同,下面就是常見的 3 種方式:算法
這種方式須要提早將服務器的證書告知客戶端,這樣客戶端在連接服務器時才能進行對服務器證書認證。shell
通信過程(簡化)瀏覽器
client ----(tcp three way handshake)-----> server client ----(Client Hello)-----> server client <----(Server Hello,public key)----- server client (經過事先保存本地的 server.crt 和服務器發過來的 server.crt 進行比較) client ----(客戶端生成對稱密鑰,經過 server.crt 提取 public key 進行加密發送)-----> server client ----(密鑰交換後,按照對稱密鑰進行加密通信)-----> server
在複雜的網絡環境中,服務器證書的傳輸自己也是一個很是危險的問題。若是在中間某個環節,服務器證書被監聽或替換那麼對服務器的認證也將再也不可靠。tomcat
爲了不證書的傳遞過程當中被篡改,能夠經過一個安全可靠的 CA 根證書分別對服務器和客戶端的證書進行簽名。這樣客戶端或服務器在收到對方的證書後能夠經過根證書進行驗證證書的有效性。安全
通信過程(簡化)服務器
client ----(tcp three way handshake)-----> server client ----(Client Hello)-----> server client <----(Server Hello,public key)----- server client (經過 CA 根證書對服務器發過來的 server.crt 進行合法性驗證) client ----(客戶端生成對稱密鑰,經過 public key 進行加密發送)-----> server client ----(密鑰交換後,按照對稱密鑰進行加密通信)-----> server
上面 2 種都是由客戶端單向驗證的,這種則是客戶端服務器雙向相互驗證。網絡
客戶端經過引入一個 CA 根證書和服務器的名字來實現對服務器進行驗證。客戶端在鏈接服務器時會首先請求服務器的證書,而後使用 CA 根證書對收到的服務器端證書進行驗證。客戶端的證書也採用 CA 根證書籤名,服務器端對客戶端進行證書認證。session
通信過程(簡化)
client ----(tcp three way handshake)-----> server client ----(Client Hello)-----> server client <----(Server Hello,public key)----- server client (經過 CA 根證書對服務器發過來的 server.crt 進行合法性驗證) client ----(客戶端生成對稱密鑰,經過 public key 進行加密發送,併發送客戶端 client.crt)-----> server server (服務端使用 CA 根證書對 client.crt 進行作法性校驗) client ----(密鑰交換後,按照對稱密鑰進行加密通信)-----> server
這種常見於網銀系統等交易系統的網站或者 API 使用,確保客戶端攜帶證書進行訪問;當無證書的客戶端沒法進行訪問,當訪問時會出現 400 Bad Reques <No required SSL certificate was sent >
。
瀏覽器證書導入須要裝換爲 PKCS #12 證書文件才能導入,命令以下:
openssl pkcs12 -export -inkey client.key -in client.crt -out client.pfx # 輸入口令,導入證書後再請求則會彈出選擇證書的選項就正常訪問了
說明:JKS 文件擴展名是 .jks 或 .keystore
keytool -list -keystore server.jks
keytool -importkeystore -srckeystore server.jks -srcalias tomcat -destkeystore server.p12 -deststoretype PKCS12
server.crt
、server.key
、ca.crt
文件openssl pkcs12 -in server.p12 -nodes -nocerts -out server.key openssl pkcs12 -in server.p12 -nodes -nokeys -clcerts -out server.crt openssl pkcs12 -in server.p12 -nodes -nokeys -cacerts -out ca.crt
使用加密的私鑰配置 Nginx 支持 SSL 時,並使用下面命令去除私鑰必須的口令(不然 start、reload 都得輸入密碼)
$ cp server.key server.key.org $ openssl rsa -in server.key.org -out server.key
server { listen 443 ssl; server_name localhost; ssl on; ssl_certificate ../cert/server.crt; ssl_certificate_key ../cert/server.key; ssl_client_certificate ../cert/ca.crt; ssl_verify_client on; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!AESGCM; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://localhost:8080; } }