https證書的驗證過程與生成方法
1.簡潔的解釋:
1.服務器 用RSA生成公鑰和私鑰
2.把公鑰放在證書裏發送給客戶端,私鑰自己保存
3.客戶端首先向一個權威的服務器檢查證書的合法性,如果證書合法,客戶端產生一段隨機數,這個隨機數就作為通信的密鑰,我們稱之為對稱密鑰,用公鑰加密這段隨機數,然後發送到服務器
4.服務器用密鑰解密獲取對稱密鑰,然後,雙方就已對稱密鑰進行加密解密通信了
PS:非對稱的RSA加密性能是非常低的,原因在於尋找大素數、大數計算、數據分割需要耗費很多的CPU周期,所以一般的HTTPS連接只在第一次握手時使用非對稱加密,通過握手交換對稱加密密鑰,在之後的通信走對稱加密。
2.詳細的:
1.瀏覽器將自己支持的一套加密規則發送給網站。
3.瀏覽器獲得網站證書之後瀏覽器要做以下工作:
a) 驗證證書的合法性(頒發證書的機構是否合法,證書中包含的網站地址是否與正在訪問的地址一致等),如果證書受信任,則瀏覽器欄裏面會顯示一個小鎖頭,否則會給出證書不受信的提示。
b) 如果證書受信任,或者是用戶接受了不受信的證書,瀏覽器會生成一串隨機數的密碼,並用證書中提供的公鑰加密。
c) 使用約定好的HASH算法計算握手消息,並使用生成的隨機數對消息進行加密,最後將之前生成的所有信息發送給網站。
a) 使用自己的私鑰將信息解密取出密碼,使用密碼解密瀏覽器發來的握手消息,並驗證HASH是否與瀏覽器發來的一致。
b) 使用密碼加密一段握手消息,發送給瀏覽器。
5.瀏覽器解密並計算握手消息的HASH,如果與服務端發來的HASH一致,此時握手過程結束,之後所有的通信數據將由之前瀏覽器生成的隨機密碼並利用對稱加密算法進行加密。
3.實現:
生成密鑰、證書
第一步,為服務器端和客戶端準備公鑰、私鑰
[java] view plain copy- # 生成服務器端私鑰
- 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 證書
[java] view plain copy- # 生成 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
在執行第二步時會出現:
[java] view plain copy- ? 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
。
第三步,生成服務器端證書和客戶端證書
[java] view plain copy- # 服務器端需要向 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 文件夾下已經有如下內容了:
[java] view plain copy- .
- ├── 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本地測試
服務器代碼:
[java] view plain copy- // 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 測試:
[java] view plain copy- ? 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
參數能夠解決這個問題:
[java] view plain copy
- ? https curl -k https://localhost:8000
- hello world%
這樣的方式是不安全的,存在我們上面提到的中間人攻擊問題。可以搞一個客戶端帶上 CA 證書試試:
[java] view plain copy- // 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 申請一個免費的證書。
參考鏈接:https://blog.csdn.net/abcd1101/article/details/71512809
https證書的驗證過程與生成方法