内容简介:工作中需要建立一套HSM的HTTPS双向认证通道,即通过硬件加密机(Ukey)进行本地加密运算的HTTPS双向认证,和银行的UKEY认证类似。NodeJS可以利用openSSL的HSM plugin方式实现,但是需要编译C++,太麻烦,作者采用了利用Node Socket接口,纯JS自行实现Https/Http协议的方式实现具体实现可以参考如下
工作中需要建立一套HSM的HTTPS双向认证通道,即通过硬件加密机(Ukey)进行本地加密运算的HTTPS双向认证,和银行的UKEY认证类似。
NodeJS可以利用openSSL的HSM plugin方式实现,但是需要编译C++,太麻烦,作者采用了利用Node Socket接口,纯JS自行实现Https/Http协议的方式实现
具体实现可以参考如下 node-https-hsm
TLS规范自然是参考RFC文档 The Transport Layer Security (TLS) Protocol Version 1.2
概述
本次TLS双向认证支持以下加密套件(*为建议使用套件):
- 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)
四种加密套件流程完全一致,只是部分算法细节与报文略有差异,体现在
- AES_128/AES_256的会话AES密钥长度分别为16/32字节。
- TLS 1.1 在计算finish报文数据时,进行的是MD5 + SHA1的HASH算法,而在TLS v1.2下,HASH算法变成了单次SHA256。
- TLS 1.1 处理finish报文时的伪随机算法(PRF)需要将种子数据为分两块,分别用 MD5 / SHA1 取HASH后异或,TLS 1.2 为单次 SHA256。
- TLS 1.2 的 CertificateVerify / ServerKeyExchange 报文末尾新增2个字节的 Signature Hash Algorithm,表示 hash_alg 和 sign_alg。
目前业界推荐使用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 复制代码
流程详解
客户端发起握手请求
TLS握手始于客户端发起 ClientHello 请求。
struct {
uint32 gmt_unix_time; // UNIX 32-bit format, UTC时间
opaque random_bytes[28]; // 28位长度随机数
} Random; //随机数
struct {
ProtocolVersion client_version; // 支持的最高版本的TLS版本
Random random; // 上述随机数
SessionID session_id; // 会话ID,新会话为空
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; // 客户端发送支持的TLS版本、客户端随机数、支持的加密套件等信息
复制代码
服务器端回应客户端握手请求
服务器端收到 ClientHello 后,如果支持客户端的TLS版本和算法要求,则返回 ServerHello, Certificate, CertificateRequest, ServerHelloDone 报文
struct {
ProtocolVersion server_version; // 服务端最后决定使用的TLS版本
Random random; // 与客户端随机数算法相同,但是必须是独立生成,与客户端毫无关联
SessionID session_id; // 确定的会话ID
CipherSuite cipher_suite; // 最终决定的加密套件
CompressionMethod compression_method; // 最终使用的压缩算法
select (extensions_present) { // 额外插件,为空
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ServerHello; // 服务器端返回最终决定的TLS版本,算法,会话ID和服务器随机数等信息
struct {
ASN.1Cert certificate_list<0..2^24-1>; // 服务器证书信息
} Certificate; // 向客户端发送服务器证书
struct {
ClientCertificateType certificate_types<1..2^8-1>; // 证书类型,本次握手为 值固定为rsa_sign
SignatureAndHashAlgorithm supported_signature_algorithms<2^16-1>; // 支持的HASH 签名算法
DistinguishedName certificate_authorities<0..2^16-1>; // 服务器能认可的CA证书的Subject列表
} CertificateRequest; // 本次握手为双向认证,此报文表示请求客户端发送客户端证书
struct {
} ServerHelloDone // 标记服务器数据末尾,无内容
复制代码
客户端收到服务器后响应
客户端应校验服务器端证书,通常用当用本地存储的可信任CA证书校验,如果校验通过,客户端将返回 Certificate, ClientKeyExchange, CertificateVerify, change_cipher_spec, Finished 报文。
CertificateVerify 报文中的签名为 Ukey硬件签名
, 此外客户端证书也是从Ukey读取。
struct {
ASN.1Cert certificate_list<0..2^24-1>; // 服务器证书信息
} Certificate; // 向服务器端发送客户端证书
struct {
select (KeyExchangeAlgorithm) {
case rsa:
EncryptedPreMasterSecret; // 服务器采用RSA算法,用服务器端证书的公钥,加密客户端生成的46字节随机数(premaster secret)
case dhe_dss:
case dhe_rsa:
case dh_dss:
case dh_rsa:
case dh_anon:
ClientDiffieHellmanPublic;
} exchange_keys;
} ClientKeyExchange; // 用于返回加密的客户端生成的随机密钥(premaster secret)
struct {
digitally-signed struct {
opaque handshake_messages[handshake_messages_length]; // 采用客户端RSA私钥,对之前所有的握手报文数据,HASH后进行RSA签名
}
} CertificateVerify; // 用于服务器端校验客户端对客户端证书的所有权
struct {
enum { change_cipher_spec(1), (255) } type; // 固定值0x01
} ChangeCipherSpec; // 通知服务器后续报文为密文
struct {
opaque verify_data[verify_data_length]; // 校验密文,算法PRF(master_secret, 'client finished', Hash(handshake_messages))
} Finished; // 密文信息,计算之前所有收到和发送的信息(handshake_messages)的摘要,加上`client finished`, 执行PRF算法
复制代码
Finished 报文生成过程中,将产生会话密钥 master secret,然后生成Finish报文内容。
master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random) verify_data = PRF(master_secret, 'client finished', Hash(handshake_messages)) 复制代码
PRF为TLS v1.2规定的伪随机算法, 此例子中,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))
复制代码
服务器完成握手
服务收到请求后,首先校验客户端证书的合法性,并且验证客户端证书签名是否合法。根据服务器端证书私钥,解密 ClientKeyExchange,获得pre_master_secret, 用相同的PRF算法即可获取会话密钥,校验客户端 Finish 信息是否正确。如果正确,则服务器端与客户端完成密钥交换。 返回 change_cipher_spec, Finished 报文。
struct {
enum { change_cipher_spec(1), (255) } type; // 固定值0x01
} ChangeCipherSpec; // 通知服务器后续报文为密文
struct {
opaque verify_data[verify_data_length]; // 校验密文,算法PRF(master_secret, 'server finished', Hash(handshake_messages))
} Finished; // 密文信息,计算之前所有收到和发送的信息(handshake_messages)的摘要,加上`server finished`, 执行PRF算法
复制代码
客户端会话开始
客户端校验服务器的Finished报文合法后,握手完成,后续用 master_secret 发送数据。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Golang之双向认证
- ldap之tls 双向认证要我命
- 技术讨论 | 看我如何突破iOS APP双向证书认证防护
- 基于Let’s Encrypt证书实现nginx https双向认证
- Nginx ssl自签名证书双向认证以及Golang https client访问
- Golang TLS双向身份认证DoS漏洞分析(CVE-2018-16875)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Twenty Lectures on Algorithmic Game Theory
Tim Roughgarden / Cambridge University Press / 2016-8-31 / USD 34.99
Computer science and economics have engaged in a lively interaction over the past fifteen years, resulting in the new field of algorithmic game theory. Many problems that are central to modern compute......一起来看看 《Twenty Lectures on Algorithmic Game Theory》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
URL 编码/解码
URL 编码/解码