詳解NodeJS Https HSM雙向認(rèn)證實(shí)現(xiàn)
工作中需要建立一套HSM的HTTPS雙向認(rèn)證通道,即通過(guò)硬件加密機(jī)(Ukey)進(jìn)行本地加密運(yùn)算的HTTPS雙向認(rèn)證,和銀行的UKEY認(rèn)證類似。
NodeJS可以利用openSSL的HSM plugin方式實(shí)現(xiàn),但是需要編譯C++,太麻煩,作者采用了利用Node Socket接口,純JS自行實(shí)現(xiàn)Https/Http協(xié)議的方式實(shí)現(xiàn)
具體實(shí)現(xiàn)可以參考如下 node-https-hsm
TLS規(guī)范自然是參考RFC文檔 The Transport Layer Security (TLS) Protocol Version 1.2
概述
本次TLS雙向認(rèn)證支持以下加密套件(*為建議使用套件):
- TLS_RSA_WITH_AES_128_CBC_SHA256(TLS v1.2) *
- TLS_RSA_WITH_AES_256_CBC_SHA256(TLS v1.2) *
- TLS_RSA_WITH_AES_128_CBC_SHA(TLS v1.1)
- TLS_RSA_WITH_AES_256_CBC_SHA(TLS v1.1)
四種加密套件流程完全一致,只是部分算法細(xì)節(jié)與報(bào)文略有差異,體現(xiàn)在
- AES_128/AES_256的會(huì)話AES密鑰長(zhǎng)度分別為16/32字節(jié)。
- TLS 1.1 在計(jì)算finish報(bào)文數(shù)據(jù)時(shí),進(jìn)行的是MD5 + SHA1的HASH算法,而在TLS v1.2下,HASH算法變成了單次SHA256。
- TLS 1.1 處理finish報(bào)文時(shí)的偽隨機(jī)算法(PRF)需要將種子數(shù)據(jù)為分兩塊,分別用 MD5 / SHA1 取HASH后異或,TLS 1.2 為單次 SHA256。
- TLS 1.2 的 CertificateVerify / ServerKeyExchange 報(bào)文末尾新增2個(gè)字節(jié)的 Signature Hash Algorithm,表示 hash_alg 和 sign_alg。
目前業(yè)界推薦使用TLS v1.2, TLS v1.1不建議使用。
流程圖
以下為 TLS 完整握手流程圖
* =======================FULL HANDSHAKE====================== * Client Server * * ClientHello --------> * ServerHello * Certificate * CertificateRequest * <-------- ServerHelloDone * Certificate * ClientKeyExchange * CertificateVerify * Finished --------> * change_cipher_spec * <-------- Finished * Application Data <-------> Application Data
流程詳解
客戶端發(fā)起握手請(qǐng)求
TLS握手始于客戶端發(fā)起 ClientHello 請(qǐng)求。
struct {
uint32 gmt_unix_time; // UNIX 32-bit format, UTC時(shí)間
opaque random_bytes[28]; // 28位長(zhǎng)度隨機(jī)數(shù)
} Random; //隨機(jī)數(shù)
struct {
ProtocolVersion client_version; // 支持的最高版本的TLS版本
Random random; // 上述隨機(jī)數(shù)
SessionID session_id; // 會(huì)話ID,新會(huì)話為空
CipherSuite cipher_suites<2..2^16-2>; // 客戶端支持的所有加密套件,上述四種
CompressionMethod compression_methods<1..2^8-1>; // 壓縮算法
select (extensions_present) { // 額外插件,為空
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ClientHello; // 客戶端發(fā)送支持的TLS版本、客戶端隨機(jī)數(shù)、支持的加密套件等信息
服務(wù)器端回應(yīng)客戶端握手請(qǐng)求
服務(wù)器端收到 ClientHello 后,如果支持客戶端的TLS版本和算法要求,則返回 ServerHello, Certificate, CertificateRequest, ServerHelloDone 報(bào)文
struct {
ProtocolVersion server_version; // 服務(wù)端最后決定使用的TLS版本
Random random; // 與客戶端隨機(jī)數(shù)算法相同,但是必須是獨(dú)立生成,與客戶端毫無(wú)關(guān)聯(lián)
SessionID session_id; // 確定的會(huì)話ID
CipherSuite cipher_suite; // 最終決定的加密套件
CompressionMethod compression_method; // 最終使用的壓縮算法
select (extensions_present) { // 額外插件,為空
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ServerHello; // 服務(wù)器端返回最終決定的TLS版本,算法,會(huì)話ID和服務(wù)器隨機(jī)數(shù)等信息
struct {
ASN.1Cert certificate_list<0..2^24-1>; // 服務(wù)器證書(shū)信息
} Certificate; // 向客戶端發(fā)送服務(wù)器證書(shū)
struct {
ClientCertificateType certificate_types<1..2^8-1>; // 證書(shū)類型,本次握手為 值固定為rsa_sign
SignatureAndHashAlgorithm supported_signature_algorithms<2^16-1>; // 支持的HASH 簽名算法
DistinguishedName certificate_authorities<0..2^16-1>; // 服務(wù)器能認(rèn)可的CA證書(shū)的Subject列表
} CertificateRequest; // 本次握手為雙向認(rèn)證,此報(bào)文表示請(qǐng)求客戶端發(fā)送客戶端證書(shū)
struct {
} ServerHelloDone // 標(biāo)記服務(wù)器數(shù)據(jù)末尾,無(wú)內(nèi)容
客戶端收到服務(wù)器后響應(yīng)
客戶端應(yīng)校驗(yàn)服務(wù)器端證書(shū),通常用當(dāng)用本地存儲(chǔ)的可信任CA證書(shū)校驗(yàn),如果校驗(yàn)通過(guò),客戶端將返回 Certificate, ClientKeyExchange, CertificateVerify, change_cipher_spec, Finished 報(bào)文。
CertificateVerify 報(bào)文中的簽名為 Ukey硬件簽名 , 此外客戶端證書(shū)也是從Ukey讀取。
struct {
ASN.1Cert certificate_list<0..2^24-1>; // 服務(wù)器證書(shū)信息
} Certificate; // 向服務(wù)器端發(fā)送客戶端證書(shū)
struct {
select (KeyExchangeAlgorithm) {
case rsa:
EncryptedPreMasterSecret; // 服務(wù)器采用RSA算法,用服務(wù)器端證書(shū)的公鑰,加密客戶端生成的46字節(jié)隨機(jī)數(shù)(premaster secret)
case dhe_dss:
case dhe_rsa:
case dh_dss:
case dh_rsa:
case dh_anon:
ClientDiffieHellmanPublic;
} exchange_keys;
} ClientKeyExchange; // 用于返回加密的客戶端生成的隨機(jī)密鑰(premaster secret)
struct {
digitally-signed struct {
opaque handshake_messages[handshake_messages_length]; // 采用客戶端RSA私鑰,對(duì)之前所有的握手報(bào)文數(shù)據(jù),HASH后進(jìn)行RSA簽名
}
} CertificateVerify; // 用于服務(wù)器端校驗(yàn)客戶端對(duì)客戶端證書(shū)的所有權(quán)
struct {
enum { change_cipher_spec(1), (255) } type; // 固定值0x01
} ChangeCipherSpec; // 通知服務(wù)器后續(xù)報(bào)文為密文
struct {
opaque verify_data[verify_data_length]; // 校驗(yàn)密文,算法PRF(master_secret, 'client finished', Hash(handshake_messages))
} Finished; // 密文信息,計(jì)算之前所有收到和發(fā)送的信息(handshake_messages)的摘要,加上`client finished`, 執(zhí)行PRF算法
Finished 報(bào)文生成過(guò)程中,將產(chǎn)生會(huì)話密鑰 master secret,然后生成Finish報(bào)文內(nèi)容。
master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random) verify_data = PRF(master_secret, 'client finished', Hash(handshake_messages))
PRF為T(mén)LS v1.2規(guī)定的偽隨機(jī)算法, 此例子中,HMAC算法為 SHA256
PRF(secret, label, seed) = P_<hash>(secret, label + seed)
P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
HMAC_hash(secret, A(2) + seed) +
HMAC_hash(secret, A(3) + seed) + ...
// A(0) = seed
// A(i) = HMAC_hash(secret, A(i-1))
服務(wù)器完成握手
服務(wù)收到請(qǐng)求后,首先校驗(yàn)客戶端證書(shū)的合法性,并且驗(yàn)證客戶端證書(shū)簽名是否合法。根據(jù)服務(wù)器端證書(shū)私鑰,解密 ClientKeyExchange,獲得pre_master_secret, 用相同的PRF算法即可獲取會(huì)話密鑰,校驗(yàn)客戶端 Finish 信息是否正確。如果正確,則服務(wù)器端與客戶端完成密鑰交換。 返回 change_cipher_spec, Finished 報(bào)文。
struct {
enum { change_cipher_spec(1), (255) } type; // 固定值0x01
} ChangeCipherSpec; // 通知服務(wù)器后續(xù)報(bào)文為密文
struct {
opaque verify_data[verify_data_length]; // 校驗(yàn)密文,算法PRF(master_secret, 'server finished', Hash(handshake_messages))
} Finished; // 密文信息,計(jì)算之前所有收到和發(fā)送的信息(handshake_messages)的摘要,加上`server finished`, 執(zhí)行PRF算法
客戶端會(huì)話開(kāi)始
客戶端校驗(yàn)服務(wù)器的Finished報(bào)文合法后,握手完成,后續(xù)用 master_secret 發(fā)送數(shù)據(jù)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
node.js通過(guò)Sequelize 連接MySQL的方法
這篇文章主要介紹了node.js通過(guò)Sequelize 連接MySQL的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
nodejs 十六進(jìn)制字符串型數(shù)據(jù)與btye型數(shù)據(jù)相互轉(zhuǎn)換
這篇文章主要介紹了nodejs 十六進(jìn)制字符串型數(shù)據(jù)與btye型數(shù)據(jù)相互轉(zhuǎn)換,需要的朋友可以參考下2018-07-07
NodeJs之word文件生成與解析的實(shí)現(xiàn)代碼
這篇文章主要介紹了NodeJs之word文件生成與解析的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
node.js中的fs.lchmodSync方法使用說(shuō)明
這篇文章主要介紹了node.js中的fs.lchmodSync方法使用說(shuō)明,本文介紹了fs.lchmodSync的方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12
Node發(fā)出HTTP POST請(qǐng)求的方法實(shí)例小結(jié)
這篇文章主要介紹了Node發(fā)出HTTP POST請(qǐng)求的方法,結(jié)合實(shí)例形式總結(jié)分析了三種常用的post請(qǐng)求操作方法,以及相關(guān)庫(kù)操作注意事項(xiàng),需要的朋友可以參考下2023-05-05
Nodejs對(duì)postgresql基本操作的封裝方法
今天小編就為大家分享一篇Nodejs對(duì)postgresql基本操作的封裝方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-02-02
node.js中的emitter.on方法使用說(shuō)明
這篇文章主要介紹了node.js中的emitter.on方法使用說(shuō)明,本文介紹了emitter.on的方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12
詳解nodejs微信公眾號(hào)開(kāi)發(fā)——6.自定義菜單
這篇文章主要介紹了詳解nodejs微信公眾號(hào)開(kāi)發(fā)——6.自定義菜單,自定義菜單能夠幫助公眾號(hào)豐富界面,讓用戶更好更快地理解公眾號(hào)的功能。2017-04-04

