1. 程式人生 > >openssl https證書

openssl https證書

address world 解密 cipher nginx acer frame agen detail

今天摸索了下 HTTPS 的證書生成,以及它在 Nginx 上的部署。由於博客托管在 github 上,沒辦法部署證書,先記錄下,後續有需要方便快捷操作。本文的闡述不一定完善,但是可以讓一個初學者了解大致的原理,同時跟著操作可以為自己的博客/網站部署一個 HTTPS 證書。
技術分享

網站部署 HTTPS 的重要性

看看下面,部分電信用戶訪問京東首頁的時候,會看到右下角有一個浮動廣告:
技術分享
小白用戶以為是京東有意放置的,細心的用戶會發現,這個 iframe 一層嵌一層的惡心廣告很明顯是電信/中間人通過 DNS 劫持註入進去的,十分惡心,沒有關閉按鈕。
隨著互聯網的快速發展,我們幾乎離不開網絡了,聊天、預訂酒店、購物等等,我們的隱私無時無刻不暴露在這龐大的網絡之中,HTTPS 能夠讓信息在網絡中的傳遞更加安全,增加了 haker 的攻擊成本。
HTTPS 區別於 HTTP,它多了加密(encryption),認證(verification),鑒定(identification)。它的安全源自非對稱加密以及第三方的 CA 認證。

簡述 HTTPS 的運作

技術分享


如上圖所示,簡述如下:
客戶端生成一個隨機數 random-client ,傳到服務器端(Say Hello)
服務器端生成一個隨機數 random-server ,和著公鑰,一起回饋給客戶端(I got it)
客戶端收到的東西原封不動,加上 premaster secret (通過 random-clientrandom-server 經過一定算法生成的東西),再一次送給服務器端,這次傳過去的東西會使用公鑰加密
服務器端先使用私鑰解密,拿到 premaster secret ,此時客戶端和服務器端都擁有了三個要素: random-clientrandom-server

premaster secret
此時安全通道已經建立,以後的交流都會校檢上面的三個要素通過算法算出的session key

CA 數字證書認證中心

如果網站只靠上圖運作,可能會被中間人攻擊,試想一下,在客戶端和服務端中間有一個中間人,兩者之間的傳輸對中間人來說是透明的,那麽中間人完全可以獲取兩端之間的任何數據,然後將數據原封不動的轉發給兩端,由於中間人也拿到了三要素和公鑰,它照樣可以解密傳輸內容,並且還可以篡改內容。

為了確保我們的數據安全,我們還需要一個 CA 數字證書。HTTPS的傳輸采用的是非對稱加密,一組非對稱加密密鑰包含公鑰和私鑰,通過公鑰加密的內容只有私鑰能夠解密。上面我們看到,整個傳輸過程,服務器端是沒有透露私鑰的。而 CA 數字認證涉及到私鑰,整個過程比較復雜,我也沒有很深入的了解,後續有詳細了解之後再補充下。

CA 認證分為三類:DV ( domain validation),OV ( organization validation),EV ( extended validation),證書申請難度從前往後遞增,貌似 EV 這種不僅僅是有錢就可以申請的。

對於一般的小型網站尤其是博客,可以使用自簽名證書來構建安全網絡,所謂自簽名證書,就是自己扮演 CA 機構,自己給自己的服務器頒發證書。

生成密鑰、證書

第一步,為服務器端和客戶端準備公鑰、私鑰

# 生成服務器端私鑰
openssl genrsa -out server.key 1024
# 生成服務器端公鑰
openssl rsa -in server.key -pubout -out server.pem


# 生成客戶端私鑰
openssl genrsa -out client.key 1024
# 生成客戶端公鑰
openssl rsa -in client.key -pubout -out client.pem

第二步,生成 CA 證書

# 生成 CA 私鑰
openssl genrsa -out ca.key 1024
# X.509 Certificate Signing Request (CSR) Management.
openssl req -new -key ca.key -out ca.csr
# X.509 Certificate Data Management.
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt

在執行第二步時會出現:

?  keys  openssl req -new -key ca.key -out ca.csr
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]:Zhejiang
Locality Name (eg, city) []:Hangzhou
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My CA
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:

註意,這裏的 Organization Name (eg, company) [Internet Widgits Pty Ltd]: 後面生成客戶端和服務器端證書的時候也需要填寫,不要寫成一樣的!!!可以隨意寫如:My CA, My Server, My Client。

然後 Common Name (e.g. server FQDN or YOUR name) []: 這一項,是最後可以訪問的域名,我這裏為了方便測試,寫成 localhost ,如果是為了給我的網站生成證書,需要寫成 barretlee.com

第三步,生成服務器端證書和客戶端證書

# 服務器端需要向 CA 機構申請簽名證書,在申請簽名證書之前依然是創建自己的 CSR 文件
openssl req -new -key server.key -out server.csr
# 向自己的 CA 機構申請證書,簽名過程需要 CA 的證書和私鑰參與,最終頒發一個帶有 CA 簽名的證書
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt

# client 端
openssl req -new -key client.key -out client.csr
# client 端到 CA 簽名
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in client.csr -out client.crt

此時,我們的 keys 文件夾下已經有如下內容了:

.
├── https-client.js
├── https-server.js
└── keys
    ├── ca.crt
    ├── ca.csr
    ├── ca.key
    ├── ca.pem
    ├── ca.srl
    ├── client.crt
    ├── client.csr
    ├── client.key
    ├── client.pem
    ├── server.crt
    ├── server.csr
    ├── server.key
    └── server.pem

看到上面兩個 js 文件了麽,我們來跑幾個demo。

HTTPS本地測試

服務器代碼:

// file http-server.js
var https = require(‘https‘);
var fs = require(‘fs‘);

var options = {
  key: fs.readFileSync(‘./keys/server.key‘),
  cert: fs.readFileSync(‘./keys/server.crt‘)
};

https.createServer(options, function(req, res) {
  res.writeHead(200);
  res.end(‘hello world‘);
}).listen(8000);

短短幾行代碼就構建了一個簡單的 https 服務器,options 將私鑰和證書帶上。然後利用 curl 測試:

?  https  curl https://localhost:8000
curl: (60) SSL certificate problem: Invalid certificate chain
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn‘t adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you‘d like to turn off curl‘s verification of the certificate, use
 the -k (or --insecure) option.

當我們直接訪問時, curl https://localhost:8000

一堆提示,原因是沒有經過 CA 認證,添加 -k 參數能夠解決這個問題:

?  https  curl -k https://localhost:8000
hello world%

這樣的方式是不安全的,存在我們上面提到的中間人攻擊問題。可以搞一個客戶端帶上 CA 證書試試:

// file http-client.js
var https = require(‘https‘);
var fs = require(‘fs‘);

var options = {
  hostname: "localhost",
  port: 8000,
  path: ‘/‘,
  methed: ‘GET‘,
  key: fs.readFileSync(‘./keys/client.key‘),
  cert: fs.readFileSync(‘./keys/client.crt‘),
  ca: [fs.readFileSync(‘./keys/ca.crt‘)]
};

options.agent = new https.Agent(options);

var req = https.request(options, function(res) {
  res.setEncoding(‘utf-8‘);
  res.on(‘data‘, function(d) {
    console.log(d);
  });
});
req.end();

req.on(‘error‘, function(e) {
  console.log(e);
});

先打開服務器 node http-server.js ,然後執行

?  https  node https-client.js
hello world

如果你的代碼沒有輸出

hello world ,說明證書生成的時候存在問題。也可以通過瀏覽器訪問:

技術分享

提示錯誤:

此服務器無法證明它是localhost;您計算機的操作系統不信任其安全證書。出現此問題的原因可能是配置有誤或您的連接被攔截了。

原因是瀏覽器沒有 CA 證書,只有 CA 證書,服務器才能夠確定,這個用戶就是真實的來自 localhost 的訪問請求(比如不是代理過來的)。

你可以點擊 繼續前往localhost(不安全) 這個鏈接,相當於執行 curl -k https://localhost:8000 。如果我們的證書不是自己頒發,而是去靠譜的機構去申請的,那就不會出現這樣的問題,因為靠譜機構的證書會放到瀏覽器中,瀏覽器會幫我們做很多事情。初次嘗試的同學可以去 startssl.com 申請一個免費的證書。

Nginx 部署

ssh 到你的服務器,對 Nginx 做如下配置:

server_names barretlee.com *.barretlee.com
ssl on;
ssl_certificate /etc/nginx/ssl/barretlee.com.crt;
ssl_certificate_key /etc/nginx/ssl/barretlee.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !MEDIUM";
# Add perfect forward secrecy
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";

會發現,網頁 URL 地址框左邊已經多出了一個小綠鎖。當然,部署好了之後可以去這個網站 看看測評分數,如果分數是 A+,說明你的 HTTPS 的各項配置都還不錯,速度也很快。

openssl https證書