在Node.js中,tls
模块使用OpenSSL来提供TLS/SSL,实现加密过的流通讯。TLS/SSL会在传输层上对网络连接进行加密,防止传输数据被窃听和篡改。tls
模块创建的TLS服务器和客户端与net
模块相似,但对方法进行了扩展,如:对公钥、私钥和证书的设置等。
tls.Server
继承自net.Server
,二者在功能上比较相似。但tls.Server
创建服务器时,使用的是安全连接。
1. 初始化服务器
初始化服务器可以使用构造函数tls.Server
或工厂方法tls.createServer
。与创始TCP
服务器不同,创建TLS
服务器时,需要传服务器私钥和证书等文件。
var tls = require('tls'); var fs = require('fs'); //使用服务端私钥和证书创建服务器 var options = { key: fs.readFileSync('./ssl/itbilu-key.pem'), cert: fs.readFileSync('./ssl/itbilu-cert.pem'), requestCert: true, // 可接收的客户端自签名证书认证 ca: [ fs.readFileSync('./ssl/client-cert.pem') ] }; //使用pfx或p12文件创建 /* var options = { pfx: fs.readFileSync('./ssl/itbilu.pfx') }; */ var server = tls.createServer(options);
在以上示例中,分别从PEM私钥
和pfx
证书创建了TLS服务端,创建TLS服务端详细参数如下:
tls.createServer(options[, secureConnectionListener])
创建一个新的tls.Server
。参数connectionListener
会自动设置为secureConnection
事件的监听器,也可以创建服务器后单独添加对该事件的监听。
pfx
:包含私钥、证书和服务器的CA
证书(PFX 或 PKCS12 格式)字符串或缓存Buffer
。(key
,cert
和ca
互斥)。key
:包含服务器私钥(PEM 格式)字符串或缓存Buffer
。(可以是keys的数组)(必传)。passphrase
:私钥或pfx
的密码字符串cert
:包含服务器证书key(PEM 格式)字符串或缓存Buffer
。(可以是certs的数组)(必传)。ca
:信任的证书(PEM 格式)的字符串/缓存数组,用来授权连接。如果忽略这个参数,将会使用"root" CAs,如 :VeriSign。crl
:不是PEM编码CRLs(Certificate Revocation List,证书撤销列表)的字符串就是字符串列表ciphers
:要使用或排除的密码(cipher)字符串ecdhCurve
:是否包含ECDH
秘钥,或false
禁用ECDH
。 默认prime256v1
,更多可参考 RFC 4492 。dhparam
:DH
参数文件,用于DHE
秘钥。使用openssl dhparam
命令来创建。如果加载文件失败,该参数会被抛弃。handshakeTimeout
: 握手超时时长,如果SSL/TLS
握手事件超过这个参数,会断开连接。超时后,tls.Server
对象会触发'clientError
' 事件。默认是 120 秒。honorCipherOrder
:当选择一个密码(cipher)时,使用服务器配置。该参数默认不可用,配合
ciphers
参数连接使用,可减轻 BEAST 攻击。requestCert
:如果设为true
,服务器会要求连接的客户端发送证书,并尝试验证证书。默认:false
。rejectUnauthorized
:如果为true
,服务器将会拒绝不在 CAs 授权列表内的连接。仅requestCert
参数为true
时这个参数才有效。默认:false
。checkServerIdentity(servername, cert)
:提供一个重写的方法来检查证书对应的主机名。如果验证失败,返回error
;如果验证通过,返回undefined
。NPNProtocols
:一个包含NPN
协议的Buffer
数组(协议需按优先级排序)。SNICallback(servername, cb)
:如果客户端支持SNI TLS
扩展会调用这个方法。该方法接受2个参数:servername
和cb
。SNICallback
回调函数格式为cb(null, ctx)
,其中ctx
是SecureContext
实例(可以用tls.createSecureContext(...)
来获取相应的 SecureContext上下文)。如果SNICallback
没有提供,将会使用高级的 API(参见下文).sessionTimeout
:整数,设定了服务器创建TLS会话标示符(TLS session identifiers)和TLS会话凭证(TLS session tickets)后的超时时间(单位:秒)。更多请参考:SSL_CTX_set_timeout。ticketKeys
:一个 48 字节的Buffer
实例。由 16 字节的前缀,16 字节的hmac key
,16 字节的AES key
组成。可用来接受 tls 服务器实例上的 tls会话凭证(tls session tickets)。注: 自动在集群
cluster
进程间共享。sessionIdContext
:会话恢复(session resumption)的标示符字符串。如果requestCert
为true
,则默认值为命令行生成的 MD5 哈希值,否则不提供该参数默认值。secureProtocol
:SSL 使用的方法。如:SSLv3_method
强制 SSL 版本为3。可传入的值取决于你所安装的 OpenSSL 中的常量,参考:SSL_METHODS。secureOptions
:服务器配置项。例如设置SSL_OP_NO_SSLv3
可用禁用 SSLv3 协议。所有可用参数请参考:SSL_CTX_set_options
2. 监听连接
TLS
服务器与TCP
服务器一样,也需要将其绑定到TCP端口或socket
套接字上。下面我们使用端口绑定,更多绑定选项可参考net.Server。
在上面创建TLS
服务器时,我们没有传入默认的监听方法,因此需要添加对客户端连接事件的监听。当有TLS客户端连接进入时,tls.Server
会发射一个'secureConnection'
事件,我们可以通过监听此事件来处理客户端请求。
//添加'secureConnection'事件监听 server.on('secureConnection', function (clientStream){ console.log('收到了客户端的连接') }); //将TLS服务器绑定到3333端口上 server.listen(3333);
3. 与客户端交互数据
在'secureConnection'
事件的回调函数中,会传入一个tls.TLSSocket
对象实例,该实例与net.Socket
实例类似。该实例是一个可读写的Stream流,从客户端读取数据或是向客户端发送数据都,是基于对这个Stream
的操作。
server.on('secureConnection', function (tlsSocket){ console.log('收到了客户端的连接') //tlsSocket是一个Stream,监听'data'事件可查看客户端数据 tlsSocket.on('data', function(data){ console.log('收到客户端数据:%s', data); }); //向客户端写入数据 tlsSocket.write('Hello client -- from itbilu.com') });
4. 断开连接
调用tls.Server
对象的end
方法可断开TLS
服务器与客户端的连接。与TCP
服务器一样,该方法也可以接收一个参数,参数为字符串或缓冲区Buffer
,这些数据将在发送完毕后断开连接。
server.on('secureConnection', function (tlsSocket){ console.log('收到了客户端的连接') //tlsSocket是一个Stream,监听'data'事件可查看客户端数据 tlsSocket.on('data', function(data){ //客户端发来exit时,将断开服务器与客户端的连接 if(data.toString().trim().toLowerCase() === 'exit'){ server.end('bye ~ '); } else { console.log('收到客户端数据:%s', data); } }); //向客户端写入数据 tlsSocket.write('Hello client -- from itbilu.com') });
5. 运行服务端
完整代码整理如下:
var tls = require('tls'); var fs = require('fs'); //使用服务端私钥和证书创建服务器 var options = { key: fs.readFileSync('./ssl/itbilu-key.pem'), cert: fs.readFileSync('./ssl/itbilu-cert.pem'), requestCert: true, // 可接收的客户端自签名证书认证 ca: [ fs.readFileSync('./ssl/client-cert.pem') ] }; //使用pfx或p12文件创建 /* var options = { pfx: fs.readFileSync('./ssl/itbilu.pfx') }; */ var server = tls.createServer(options); //添加'secureConnection'事件监听 server.on('secureConnection', function (tlsSocket){ console.log('收到了客户端的连接,该连接:', tlsSocket.authorized ? '已认证' : '未认证'); //tlsSocket是一个Stream,监听'data'事件可查看客户端数据 tlsSocket.on('data', function(data){ //客户端发来exit时,将断开服务器与客户端的连接 if(data.toString().trim().toLowerCase() === 'exit'){ tlsSocket.end('bye ~ '); } else { console.log('收到客户端数据:%s', data); } }); //向客户端写入数据 tlsSocket.write('Hello client -- from itbilu.com') }); //tls.Server继承自net.Server,所在'connection'事件依然可用 server.on('connection', function (socket){ console.log('收到非安全连接') }); //将TLS服务器绑定到3333端口上 server.listen(3333, function() { console.log('TLS 服务器已绑定'); });
将以上代码保存运行后,可以通过 openssl s_client
来连接服务器测试:
openssl s_client -connect 127.0.0.1:3333
你打算打赏多少钱呢?
(微信扫一扫)