1. 程式人生 > >NodeJs利用ws建立的基於SSL的Websocket連線(臨時用)

NodeJs利用ws建立的基於SSL的Websocket連線(臨時用)

OpenSSL生成自簽名SSL證書

科普:TLS、SSL、HTTPS以及證書

不少人可能聽過其中的超過3個名詞,但它們究竟有什麼關聯呢?

TLS 是傳輸層安全協議(Transport Layer Security)的縮寫,是一種對基於網路的傳輸的加密協議,可以在受信任的第三方公證基礎上做雙方的身份認證。TLS可以用在TCP上,也可以用在無連線的UDP報文上。協議規定了身份認證、演算法協商、金鑰交換等的實現。 
SSL 是TLS的前身,現在已不再更新。 
HTTPS 是在基於TLS/SSL的安全套接字上的的應用層協議,除了傳輸層進行了加密外,其它與常規HTTP協議基本保持一致 
證書 是TLS協議中用來對身份進行驗證的機制,是一種數字簽名形式的檔案,包含證書擁有者的公鑰及第三方的證書資訊。 
證書分為2類:自簽名證書和CA證書

。一般自簽名證書不能用來進行身份認證,如果一個server端使用自簽名證書,client端要麼被設定為無條件信任任何證書,要麼需要將自簽名證書的公鑰和私鑰加入受信任列表。但這樣一來就增加了server的私鑰洩露風險。 
TLS基於CA的身份認證基本原理是:首先驗證方需要信任CA提供方自己的證書(CAcert),比如證書在作業系統的受信任證書列表中,或者使用者通過“安裝根證書”等方式將CA的公鑰和私鑰加入受信任列表;然後CA對被驗證方的原始證書進行簽名(私鑰加密),生成最終的證書;驗證方得到最終的證書後,利用CAcert中包含的公鑰進行解密,得到被驗證方的原始證書。 
根據RSA的加密原理,如果用CA的公鑰解密成功,說明該證書的確是用CA的私鑰加密的,可以認為被驗證方是可信的。

微信小程式對第三方服務端的網路通訊方式支援https和Websocket,WebSocket是HTML5開始提供的一種在單個 TCP 連線上進行全雙工通訊的協議,解決了HTTP協議效率低下的問題,能更好的節省伺服器資源和頻寬並達到實時通訊。 
這裡選擇使用node.js配置本地伺服器,它有很多websocket庫,ws就是其中之一,號稱最輕量級,最快。ws的用法比較簡單,直接看https://github.com/websockets/ws就可以。 
這裡著重講的是針對微信小程式實現的加入ssl證書的websocket實現。 
生產環境建議你去買一個ssl證書,開發環境你可以給自己生成一個ssl自簽名證書臨時用一下。這裡的開發環境為win7 X64系統

一、安裝OpenSSL

!!!強烈建議下載v1.0.2的版本,千萬不要用v1.1.0及更新的版本,因為會出現已經bug:“problem creating object tsa_policy1=1.2.3.4.1”! 
如果安裝過程出現這個視窗,說明你的電腦沒有安裝Visual C++ 2008 Redistributables, 請自行下載安裝。 
這裡寫圖片描述 
剩下的安裝全部預設即可。我安裝在 D:\OpenSSL-Win32 目錄下。在這裡我想將證書輸出到 D盤根目錄下,首先用管理員許可權開啟cmd(切記!!!)。 
然後進入D盤根目錄。在建立證書之前,你需要設定2個環境變數:

  • set RANDFILE=D:\.rnd
  • set OPENSSL_CONF=D:\OpenSSL-Win32\bin\openssl.cfg

第二句告訴系統去哪找openssl.cfg檔案。 
現在可以執行OpenSSL了,cmd中輸入 D:\OpenSSL-Win32\bin\openssl.exe執行。以上過程如圖: 
這裡寫圖片描述

二、生成伺服器證書

第一步:對於一個網站,首先必須有自己的私鑰。為伺服器建立一個1024位的RSA金鑰,並儲存到ca.key檔案中:

openssl genrsa -out ssl.key 1024

如果想要新增密碼保護,則改成以下命令:

openssl genrsa -des3 -out ssl.key 1024

利用私鑰就可以生成證書了。OpenSSL使用x509命令生成證書。這裡需要區分兩個概念: 證書(certificate) 和 證書請求(certificate sign request) 
證書是自簽名或CA簽名過的憑據,用來進行身份認證。 
證書請求是對簽名的請求,需要使用私鑰進行簽名 
x509命令可以將證書和證書請求相互轉換,不過我們這裡只用到從證書請求到證書的過程。

第二步:從私鑰或已加密的私鑰均可以生成”證書請求”:

openssl req -new -key ssl.key -out ssl.csr

如果私鑰已加密,需要輸入密碼。req命令會通過命令列要求使用者輸入國家、地區、組織等資訊,這些資訊會附加在證書中展示給連線方。

第三步:用私鑰對“證書請求”進行簽名生成證書crt。根據不同的場景,簽名的方式也略有不同:

(一)自簽名,生成私有證書

自簽名的原理是用私鑰對該私鑰生成的“證書請求”進行簽名,生成證書檔案。該證書的簽發者就是自己,所以驗證方必須有該證書的私鑰才能對簽發資訊進行驗證,所以要麼驗證方信任一切證書,面臨冒名頂替的風險,要麼被驗證方的私鑰(或加密過的私鑰)需要傳送到驗證方手中,面臨私鑰洩露的風險。 
當然自簽名也不是一無用處,比如需要內部通訊的兩臺電腦需要使用加密又不想用第三方證書,可以在兩端使用相同的私鑰和證書進行驗證(當然這樣就跟對稱加密沒有區別了)。自簽名的方法為:

openssl x509 -req -in ssl.csr -signkey ssl.key -out ssl.crt

(二)自簽名,生成CA證書

CA證書是一種特殊的自簽名證書,可以用來對其它證書進行簽名。這樣當驗證方選擇信任了CA證書,被簽名的其它證書就被信任了。在驗證方進行驗證時,CA證書來自作業系統的信任證書庫,或者指定的證書列表。

openssl x509 -req -in sign.csr -extensions v3_ca -signkey sign.key -out sign.crt

(三)利用CA證書對其它證書進行簽名

openssl x509 -req -in ssl.csr -extensions v3_usr -CA sign.crt -CAkey sign.key -CAcreateserial -out ssl.crt

四、拓展知識

如果想將下屬CA金鑰用於授權碼簽名(通過Microsoft’s signtool工具),你必須把金鑰和證書打包進一個PKCS12檔案。

pkcs12 -export -out ia.p12 -inkey ia.key -in ia.crt -chain -CAfile ca.crt

這裡寫圖片描述

如果在上一節中,你將根CA和下屬CA的Common Name設定成一樣了,那麼這一步你會得到這樣的報錯:

Error self signed certificate getting chain. 
error in pkcs12

在Window上用signtool給可執行檔案簽名的方法: 
“受信仰的根證書頒發機構”儲存區中安裝 ia.p12 檔案(如:雙擊它),然後用signtool /wizard為它簽名。

在上面你建立的證書(.crt檔案)也可以雙擊來檢視和安裝它: 
這裡寫圖片描述

篇章二 NodeJs使用SSL證書,egret的連結使用connectByUrl("wss://127.0.0.1:8888/")

一、準備工作

首先找一個資料夾建立今天的程式碼檔案:wss.js,並將上一篇生成的伺服器私鑰ssl.key和證書ssl.crt放在同目錄下。 
由於這裡需要用到node.js的ws模組,所以通過npm命令進行下載。 
這裡需要注意,npm安裝模組分為全域性安裝和區域性安裝,為了不汙染全域性環境,這裡就把ws模組安裝到server.js同目錄下。 
在 wss.js 目錄下開啟cmd,執行以下程式碼下載ws:

npm install ws

如果安裝成功,會在目錄下看到一個 node_modules 資料夾,裡面有ws資料夾,表示ws模組已經安裝成功。

二、伺服器程式碼編寫

// wss.js

const fs = require('fs');

// 一些配置資訊
const cfg = {
    port: 8888,
    ssl_key: 'ssl.key',
    ssl_cert: 'ssl.crt'
};

const httpServ = require('https');
const WebSocketServer = require('ws').Server; // 引用Server類

// 建立request請求監聽器
const processRequest = (req, res) => {
    res.writeHead(200);
    res.end('厲害了,我的WebSockets!\n');
};

const app = httpServ.createServer({
    // 向server傳遞key和cert引數
    key: fs.readFileSync(cfg.ssl_key),
    cert: fs.readFileSync(cfg.ssl_cert)
}, processRequest).listen(cfg.port);

// 例項化WebSocket伺服器
const wss = new WebSocketServer({
    server: app
});
// 如果有WebSocket請求接入,wss物件可以響應connection事件來處理
wss.on('connection', (wsConnect) => {
    console.log('伺服器已啟動,監聽中~');
    wsConnect.on('message', (message) => {
        console.log(`伺服器接收到:${message}`);
        wsConnect.send(`伺服器回覆: ${message}`, (err) => {
            if (err) {
                console.log(`伺服器錯誤:${err}`);
            }
        });
    });
});

提示:這裡的 ${ }是ES6標準中的佔位符,外層不是普通的 單引號,而是反引號,即左上角 ESC 鍵下面那個。

到此伺服器程式碼已經寫完,具體作用不解釋了,註釋應該能看懂。

三、執行伺服器程式碼,並驗證連線

在wss.js 目錄下執行cmd,執行以下命令執行 wss.js :

node wss.js

這裡寫圖片描述 
此時伺服器處於等待連線狀態,此時在瀏覽器地址中開啟網址:https://127.0.0.1/8888,如果看到瀏覽器中顯示:厲害了,我的WebSockets!,則表示執行成功。

下面在支援javascript的瀏覽器的console中依次執行下面的客戶端程式碼進行驗證,檢視伺服器返回資料是否正確。 
我使用的是QQ瀏覽器,在https://127.0.0.1/8888中按F12開啟開發者視窗,並選擇Console標籤,依次輸入以下程式碼:

const socket = new WebSocket('wss://127.0.0.1:8888/');
socket.onmessage = function (e) {
  console.log('伺服器: ' + e.data);
};
socket.send('你好,伺服器');

!!!注意:以上程式碼要一個個執行,執行完上一條無誤後,再執行下一條。 
你會在Console中看到這些:

這裡寫圖片描述 
在cmd中也會得到相應的反饋: 
這裡寫圖片描述

篇章三 實際上,在使用時證書只是生成了私有證書,然後egret連線NodeJs的Websocket要再瀏覽器裡開啟才能連得上