`

Node.Js TLS(SSL) HTTPS双向验证

 
阅读更多

 转载自: http://blog.csdn.net/marujunyy/article/details/8477854

考虑到数据传输的安全及保密,决定采用TLS(SSL)协议,既节省了设计安全协议的时间,也容易与外部系统协同工作。作为工业标准的TLS(SSL)协议已经有许多成熟的解决方案,故不打算自行开发,而是使用开源社区中广泛使用的OpenSSL。

由于TLS(SSL)是基于非对称的加密体系,所以在开发前需要准备用于加密解密及验证的私钥及数字证书。这里分别为CA、服务器、客户端分别准备1套密钥及证书。

CA机构的数字证书是整个TLS(SSL)协议的根证书,用于给服务器及客户端证书签名及验证其真伪。由于不作商业用途,不打算使用权威CA机构的数字证书,而且是使用自签名的CA证书。生成方法如下:

 

    openssl genrsa -out ca-key.pem -des 1024
    openssl req -new -key ca-key.pem -out ca-csr.pem
    openssl x509 -req -days 3650 -in ca-csr.pem -signkey ca-key.pem -out ca-cert.pem

 

 

执行上述3个命令之后,得到3个文件,其中“ca-key.pem”为CA的私钥,“ca-cert.pem”为CA的自签名证书。有了CA证书和私钥之后就可以使用它来签发服务器及客户端的证书了。如果想查看生成证书的详细信息,可以使用以下命令查看:

 

openssl x509 -noout -text -in ca-cert.pem

 

 

在TLS(SSL)协议中,客户端在与服务器连接时,除需要使用CA证书来验证服务器证书的真伪之外,还需要验证服务器证书是否与服务器域名或IP地址相匹配。但由于服务器可能有多个IP地址及多个DNS服务器,所以在生成证书前需要对其进行设置,这里将设置写入一个文件中,方便之后生成证书的工作,配置内容文件如下:

 

[req]
    distinguished_name = req_distinguished_name
    req_extensions = v3_req

    [req_distinguished_name]
    countryName = Country Name (2 letter code)
    countryName_default = CN
    stateOrProvinceName = State or Province Name (full name)
    stateOrProvinceName_default = BeiJing
    localityName = Locality Name (eg, city)
    localityName_default = YaYunCun
    organizationalUnitName	= Organizational Unit Name (eg, section)
    organizationalUnitName_default	= Domain Control Validated
    commonName = Internet Widgits Ltd
    commonName_max	= 64

    [ v3_req ]
    # Extensions to add to a certificate request
    basicConstraints = CA:FALSE
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    subjectAltName = @alt_names

    [alt_names]
    DNS.1 = ns1.dns.com
    DNS.2 = ns2.dns.com
    DNS.3 = ns3.dns.com
    IP.1 = 192.168.1.84
    IP.2 = 127.0.0.1
    IP.3 = 127.0.0.2
    

 

 

 

“req_distinguished_name”部分的内容就是证书的主体内容,包括证书持有者的相关信息。重点是“alt_name”部分的内容,其中的“DNS.1”所指定的是使用该证书的主机所使用的DNS,还有“IP.1”所指定的是使用该证书的主机的IP地址,这里可以指定多个DNS和IP,方便有多DNS和多IP的主机能够使用同一个该证书来完成验证。

 

假设上述配置文件为“openssl.cnf”,则生成服务器私钥及证书的方法如下:

 

 

openssl genrsa -out server-key.pem 1024
openssl req -new -key server-key.pem -config openssl.cnf -out server-csr.pem
openssl x509 -req -days 730 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in server-csr.pem -out server-cert.pem -extensions v3_req -extfile openssl.cnf

 

 

如果查看刚才生成的服务器证书,可以看到设置的DNS及IP信息。还可以将服务器私钥、证书以及CA证书打包成一个单独的.pfx或.p12文件:

 

openssl pkcs12 -export -in server-cert.pem -inkey server-key.pem -certfile ca-cert.pem -out server.pfx
openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -certfile ca-cert.pem -out client.p12    

 

 

这样携带和使用都更加方便。最后就是生成客户端证书:

 

openssl genrsa -out client-key.pem
openssl req -new -key client-key.pem -out client-csr.pem
openssl x509 -req -days 365 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in client-csr.pem -out client-cert.pem

 

 

上面所生成的证书中,CA证书的有效期是10年,服务器证书有效期是2年,客户端证书有效期是1年。



 

 

测试:

 

 

openssl s_client -connect 127.0.0.1:8000 -cert client-cert.pem -key client-key.pem -tls1 -CAfile ca-cert.pem -state -showcerts

 TLS(SSL)方式

 

 

tls服务器程序:

 

var tls = require('tls');
    var fs = require('fs');

    var options = {
        key: fs.readFileSync('../key/server-key.pem'),
        cert: fs.readFileSync('../key/server-cert.pem'),

        // This is necessary only if using the client certificate authentication.
        requestCert: true,

        rejectUnauthorized: true,
//    passphrase:'test',
        // This is necessary only if the client uses the self-signed certificate.
        ca: [ fs.readFileSync('../key/ca-cert.pem') ]
        };

    var server = tls.createServer(options, function(cleartextStream) {
        console.log('server connected',
            cleartextStream.authorized ? 'authorized' : 'unauthorized');
        cleartextStream.write('this message is come from server!');
        cleartextStream.setEncoding('utf8');
        cleartextStream.pipe(cleartextStream);
        cleartextStream.on('data', function(data) {
        console.log(data);
        });
    });
    server.listen(8000, function() {
        console.log('server bound');
        });
    

 

 

tls客户端程序:

 

var tls = require('tls');
    var fs = require('fs');

    var options = {
        // These are necessary only if using the client certificate authentication
        key: fs.readFileSync('../key/client-key.pem'),
        cert: fs.readFileSync('../key/client-cert.pem'),

        rejectUnauthorized: true,
        // This is necessary only if the server uses the self-signed certificate
        ca: [ fs.readFileSync('../key/ca-cert.pem') ]
        };

    var cleartextStream = tls.connect(8000, '127.0.0.1', options, function() {
        console.log('client connected',
            cleartextStream.authorized ? 'authorized' : 'unauthorized');
        cleartextStream.setEncoding('utf8');
        if(!cleartextStream.authorized){
        console.log('cert auth error: ', cleartextStream.authorizationError);
        }
    //    console.log(cleartextStream.getPeerCertificate());
    });
    cleartextStream.setEncoding('utf8');
    cleartextStream.on('data', function(data) {
        console.log(data);
        cleartextStream.write('Hello,this message is come from client!');
        cleartextStream.end();
        });
    cleartextStream.on('end', function() {
        console.log('disconnected');
        });
    cleartextStream.on('error', function(exception) {
        console.log(exception);
        });

 

 

先运行服务器,再运行客户端,就可以看到相互发送的信息对方的控制台上显示出来,表明连接成功。由于服务器和客户端程序中都指定了“rejectUnauthorized: true”,即如果证书验证失败则拒绝连接。所以如果服务器能收到客户端的信息,而客户端又能收到服务器的信息,则表明双向验证成功。

 

使用https方式

 

https服务器端程序(express.js):

 

 

var express = require('express')
    ,fs=require('fs');

    var options = {
        key: fs.readFileSync('../key/server-key.pem'),
        cert: fs.readFileSync('../key/server-cert.pem'),
        ca: [ fs.readFileSync('../key/ca-cert.pem') ],
        requestCert:        true,
        rejectUnauthorized: false
        };
    var app = module.exports = express.createServer(options);

    // Configuration

    app.configure(function(){
        app.use(app.router);
        });

    app.configure('development', function(){
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
    });

    app.configure('production', function(){
        app.use(express.errorHandler());
        });

    // Routes

    app.all('/', function(req, res){
        if (req.client.authorized) {
        res.writeHead(200, {"Content-Type":"application/json"});
    res.end('{"status":"authorized"}');
    //        console.log(req.client);
    console.log("Authorized Client ", req.client.socket.remoteAddress);
    } else {
        res.writeHead(401, {"Content-Type":"application/json"});
    res.end('{"status":"denied"}');
    console.log("Denied Client " , req.client.socket.remoteAddress);
    }
    });
    app.listen(5558);
    console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);


    //curl -v -s -k --key key/client-key.pem --cert key/client-cert.pem https://localhost:5558

 

参考:http://www.hacksparrow.com/express-js-https-server-client-example.html

 


https客户端程序:(client.js)

var https = require('https');
    var fs = require('fs');

    var options = {
        host: '192.168.1.84',
        port: 5558,
        path: '/',
        method: 'GET',
        // These are necessary only if using the client certificate authentication
        key: fs.readFileSync('../key/client-key.pem'),
        cert: fs.readFileSync('../key/client-cert.pem'),

        rejectUnauthorized: true,
        // This is necessary only if the server uses the self-signed certificate
        ca: [ fs.readFileSync('../key/ca-cert.pem') ],
        agent: false
        };
    var req = https.request(options, function(res) {
        console.log('server authorize status: '+res.connection.authorized);
        res.on('data', function(d) {
        console.log('client authorize status: '+ d);
        });
    });
    req.end();
    req.on('error', function(e) {
        console.error(e);
        });

 

注意:agent: false   这个属性一定要加上,不然证书不起作用

 

 

 

 

  • 大小: 86.3 KB
分享到:
评论

相关推荐

    Professional Node.js-Building Javascript Based Scalable Software

    第十五章讲解了如何为 Node.js 中的 TCP 服务器添加 TLS/SSL 支持。这部分内容包括了如何生成证书、如何配置 SSL 选项以及如何处理加密通信中的常见问题。随着网络安全意识的增强,了解如何安全地传输数据变得越来越...

    Node.js-WebSSH2基于Web的SSH2客户端使用xterm.jssocket.io和ssh2实现

    Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它让开发者能够在服务器端使用JavaScript进行编程。在本项目中,"Node.js-WebSSH2基于Web的SSH2客户端使用xterm.js、socket.io和ssh2实现",我们关注的是构建...

    Node.js+ws模块来制作一个简易聊天室

    为了确保聊天室的安全,可以使用 SSL/TLS 加密 WebSocket 连接(`wss`),并实施身份验证机制。性能方面,可以考虑使用负载均衡和集群技术来处理大量并发连接。 综上所述,通过 Node.js 和 ws 模块,我们可以创建一...

    Node.js-Complete-Reference-Guide

    《Node.js 完整参考指南》是一本深入学习 Node.js 的综合教程,旨在帮助开发者全面掌握这一基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 的核心优势在于其非阻塞I/O模型和事件驱动的架构,使得它在构建高...

    验证ssl的证书(pem格式)

    双向验证(也称为mutual TLS)不仅要求服务器验证客户端,还要求客户端验证服务器。这常用于银行、企业内部网络和高安全性的应用场景。在这个过程中,客户端也需要一个PEM文件,包含自己的证书和私钥。验证步骤包括...

    tantan:一个Node.js的轻量级高并发实时聊天组件

    因此,tantan可能会集成如SSL/TLS加密、身份验证机制等安全措施。 ### 8. 性能优化 为了进一步提高性能,tantan可能采用了负载均衡、缓存策略、数据压缩等技术。这些策略有助于减轻服务器压力,提高响应速度,保证...

    breakfast-time:Node.js 中的应用程序,用于选择早餐订单和呼叫酒吧的人

    8. **安全性**:考虑到用户可能输入敏感信息(如支付详情),应用需要确保数据传输和存储的安全,这可能涉及 SSL/TLS 加密,以及使用 JWT(JSON Web Tokens)进行身份验证。 9. **部署和维护**:应用可能部署在云...

    sosialtv:一个在 Node.js 上运行的网站,让用户可以在不同的聊天框中讨论不同的电视节目

    此外,为了保护用户数据,还应该采用 HTTPS 协议,通过 SSL/TLS 加密传输,确保通信安全。 至于部署,sosialtv 可能会使用 Docker 容器化技术,将应用打包成 Docker 映像,然后在 Docker 容器中运行。这种方法便于...

    nodejs_https_server

    在IT行业中,Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它允许开发者使用JavaScript进行服务器端编程。本文将深入探讨如何使用Node.js创建HTTP和HTTPS服务器,特别是针对HTTPS服务器的双向认证设置。 ...

    即时通讯Demo

    - Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它让开发者可以在服务器端使用JavaScript编写代码,提供了一个高效的非阻塞I/O模型。 - 在即时通讯Demo中,Node.js作为服务器平台,负责处理客户端连接、...

    servercat.app:https的源代码

    首先,HTTPS(超文本传输安全协议)是HTTP的安全版本,它通过SSL/TLS协议为网络通信提供了加密处理、数据完整性以及服务器身份验证。在ServerCat.app中,开发者可能使用了Node.js,这是一个基于Chrome V8引擎的...

    servertoserver-nodejs

    Node.js也提供了内置的https模块,支持TLS/SSL加密。 3. **异步I/O**:Node.js采用事件驱动和非阻塞I/O模型,这使得它在处理大量并发请求时非常高效,非常适合服务器到服务器的通信场景。 4. **Websocket**:除了...

    shutdown-my-pc:这个小型快递应用程序可以通过浏览器关闭您的PC

    "npm install" 是Node.js的包管理器命令,用于安装应用程序所需的依赖项,而“node app.js”则是启动一个基于Node.js的应用程序的方式,表明这个程序是用JavaScript语言编写,并且依赖于Node.js环境运行。...

    server

    HTTP(超文本传输协议)是互联网上应用最广泛的数据交换协议,而HTTPS是其安全版本,使用SSL/TLS协议加密通信,确保数据传输的安全性。在Node.js中,可以使用内置的http和https模块创建HTTP和HTTPS服务器。 5. **...

    Forge是一个TLS协议的本地实现

    而且,由于Forge完全用JavaScript编写,因此它在浏览器和Node.js环境中都能运行,这大大增加了其适用范围。 总的来说,Forge是一个全面的JavaScript加密库,它提供了对TLS协议的本地实现,以及其他多种加密和网络...

    webssh-master.zip

    5. `backend` 或 `server` 目录:后端服务代码,通常用Node.js、Python或其他语言编写,处理前端请求,建立与远程SSH服务器的连接。 6. `config` 文件:配置文件,设置WebSSH的行为,如默认端口、认证方式、连接参数...

    websocket-landscape:负责 HTTP 和 WebSocket 管理的 NodeJS 后端

    `websocket-landscape` 可能采取了措施如 HTTPS、WS+SSL/TLS 或使用 WebSocket 中的验证机制来保证连接的安全。 ### 8. 性能优化 对于高并发的 WebSocket 应用,性能优化至关重要。这可能涉及到连接池管理、事件...

    backend-web-socket:测试后端的conexão使用率和conexão的Web socket和nodejs

    使用SSL/TLS可以保护数据传输的安全,同时对服务器进行严格的访问控制和身份验证。 6. 部署与扩展:在生产环境中,WebSocket服务器可能需要水平扩展以应对大量连接。Node.js可以通过集群模块实现多核利用,还可以...

    mgh_node_system:超级节点

    考虑到数据敏感性和用户隐私,mgh_node_system可能采用了加密算法(如SSL/TLS)来保护传输中的数据,使用身份验证和授权机制(如OAuth2.0)来控制访问权限,同时对代码进行安全审计,防止常见的攻击方式。...

Global site tag (gtag.js) - Google Analytics