nodejs pem generated openssl self signed certificate generation for intranet CIPHER_MISMATCH - node.js

I am using the module "pem" for nodejs && express to generate openssl self signed certificates for a demo webserver run over a local intranet.
The issue I am having is that when I attempt to load pages off the webserver I am receiving the error: "The client and server don't support a common SSL protocol version or cipher suite."
How would I be able to utilize pem ( or other ) in a way to allow me run my webserver over https via my intranet?
I am running/testing this on a ubtuntu machine and also testing on a windows machine. Both are generating the same error - the accessible machine over the intranet would be from the linux box. I am using nodejs 10 and tested on firefox, chrome, edge and safari
...
pem.createCertificate({ days: 365, selfSigned: true }, this.start);
...
start(err, keys) {
if (err) {
throw err
}
let server = https.createServer(app,
{ key: keys.serviceKey, cert: keys.certificate });
server.listen(port,
() => console.log(`API/NG running on https://localhost:${port}`)
);
}

According to the documentation of the pem module, the order of arguments is in reverse order, like follows:
var serverOptions = {
key: keys.serviceKey,
cert: keys.certificate
};
var app = express();
var server = https.createServer(serverOptions, app);

Related

NodeJS tls.connect() getPeerCertificate() return error (multi) but browser shows fine

I'm building a SSL crawler application where user pass in the domain name and NodeJS use tls library to retrieve the SSL certificate.
First, here is my codes:
server.js
const tls = require('tls');
var rootCas = require('ssl-root-cas/latest').create();
const fs = require('fs');
fs.readdirSync('./keys/intermediate_certs').forEach(file => {
rootCas.addFile('./keys/intermediate_certs/' + file)
});
var secureContext = tls.createSecureContext({
ca: rootCas
});
options = {
host: host, //domain like google.com
port: 443,
secureContext: secureContext,
ca: rootCas,
rejectUnauthorized: true
};
var tlsSocket = tls.connect(options, function () {
let rawCert = tlsSocket.getPeerCertificate()
console.log(rawCert)
})
tlsSocket.on('error', (error) => {
console.log(error)
// [ERR_TLS_CERT_ALTNAME_INVALID] Hostname/IP does not match certificate's altnames: Host: zdns.cn. is not in the cert's altnames: DNS:*.fkw.com, DNS:fkw.com
// unable to verify the first certificate or UNABLE_TO_VERIFY_LEAF_SIGNATURE
});
Problem is the nodejs application throwing error, according to the TLS documentation, the errors were from OpenSSL, however, when browsing the website and view certificate is showing all valid (even the common name matched exactly).
Here are some criteria:
zdns.cn / www.zdns.cn is showing the error: ERR_TLS_CERT_ALTNAME_INVALID; When view cert from browser it show *.zdns.cn
knet.cn / www.knet.cn is showing the error: unable to verify the first certificate; When view cert from browser it show www.knet.cn
Note: I included latest root CA from ssl-root-cas and also downloaded the intermediate certificate manually from CA site.
You are getting that error specifically because of your rejectUnauthorized parameter. The certificate is presenting *.fkw.com as the CN, and it is presenting *.fkw.com and fkw.com as alternate names. None of those match zdns.cn or www.zdns.cn.
If you are just crawling to get the certs, you may want to drop the rejectUnauthorized. Alternatively, the error does seem to display the rest of the certificate information in the error. So you could keep it as is and include in your output information about why the certificate is untrusted/invalid. That seems like valuable information for a crawler pulling certs.

NodeJs - Secure Web Socket and Client Connection

I need to convert an application with websocket in a secure-websocket. (under windows)
Im using nodeJs as websocket server and a simple html page to connect to it.
Searching on google and here, I found this approach:
Create a certificate and a key for server. I've followed this tutorial:
https://www.cloudinsidr.com/content/how-to-install-the-most-recent-version-of-openssl-on-windows-10-in-64-bit/
After creating a .key and a .pem, I'have modified my nodejs websocket server to introduce the certificate:
const httpsOptions = {
key: fs.readFileSync('./api/security/cert.key'),
cert: fs.readFileSync('./api/security/cert.pem')
}
this._http = require('http');
this._server = this._http.createServer(httpsOptions , function(req, res) { this.closeCurrentConnections(req,res)}.bind(this));
var serverConfig = {
server: this._server,
autoAcceptConnections: false
}
this._wsServer = new WebSocketServer(serverConfig);
The Websocket server seems up when I start the nodejs
Now, in the client page I had this code:
var websocket_server = "ws://localhost:8128";
var echo_service = new WebSocket(websocket_server,"echo-protocol");
[...]
I changed it with the following code calling this page over HTTPS instead of simple HTTP:
var websocket_server = "wss://localhost:8128";
var echo_service = new WebSocket(websocket_server,"echo-protocol");
[...]
I got an error on client page:
testing_page.html:283 WebSocket connection to 'wss://localhost:8128/' failed: Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR
I think is due to missing certificate.
I've tried to import my previous create certificate on chrome but I cant import because chrome is especting a .crt and/or other format. I've tried to force .pem but it doens't work.
What Im missing?

Multiple SSL Certificates and HTTP/2 with Express.js

Scenario:
I have an express.js server which serves variations of the same static landing page based on where req.headers.host says the user is coming from - think sort of like A/B testing.
GET tulip.flower.com serves pages/flower.com/tulip.html
GET rose.flower.com serves pages/flower.com/rose.html
At the same time, this one IP is also responsible for:
GET potato.vegetable.com serving pages/vegetable.com/potato.html
It's important that these pages are served FAST, so they are precompiled and optimized in all sorts of ways.
The server now needs to:
Provide separate certificates for *.vegetables.com, *.fruits.com, *.rocks.net
Optionally provide no certificate for *.flowers.com
Offer HTTP2
The problem is that HTTP2 mandates a certificate, and there's now multiple certificates in play.
It appears that it's possible to use multiple certificates on one Node.js (and presumably by extension Express.js) server, but is it possible to combine it with a module like spdy, and if so, how?
Instead of hacking node, would it be smarter to pawn the task of sorting out http2 and SSL to nginx? Should the caching network like Imperva or Akamai handle this?
You can use also tls.createSecureContext, Nginx is not necassary.
MY example here:
const https = require("https");
const tls = require("tls");
const certs = {
"localhost": {
key: "./certs/localhost.key",
cert: "./certs/localhost.crt",
},
"example.com": {
key: "./certs/example.key",
cert: "./certs/example.cert",
ca: "./certs/example.ca",
},
}
function getSecureContexts(certs) {
if (!certs || Object.keys(certs).length === 0) {
throw new Error("Any certificate wasn't found.");
}
const certsToReturn = {};
for (const serverName of Object.keys(certs)) {
const appCert = certs[serverName];
certsToReturn[serverName] = tls.createSecureContext({
key: fs.readFileSync(appCert.key),
cert: fs.readFileSync(appCert.cert),
// If the 'ca' option is not given, then node.js will use the default
ca: appCert.ca ? sslCADecode(
fs.readFileSync(appCert.ca, "utf8"),
) : null,
});
}
return certsToReturn;
}
// if CA contains more certificates it will be parsed to array
function sslCADecode(source) {
if (!source || typeof (source) !== "string") {
return [];
}
return source.split(/-----END CERTIFICATE-----[\s\n]+-----BEGIN CERTIFICATE-----/)
.map((value, index: number, array) => {
if (index) {
value = "-----BEGIN CERTIFICATE-----" + value;
}
if (index !== array.length - 1) {
value = value + "-----END CERTIFICATE-----";
}
value = value.replace(/^\n+/, "").replace(/\n+$/, "");
return value;
});
}
const secureContexts = getSecureContexts(certs)
const options = {
// A function that will be called if the client supports SNI TLS extension.
SNICallback: (servername, cb) => {
const ctx = secureContexts[servername];
if (!ctx) {
log.debug(`Not found SSL certificate for host: ${servername}`);
} else {
log.debug(`SSL certificate has been found and assigned to ${servername}`);
}
if (cb) {
cb(null, ctx);
} else {
return ctx;
}
},
};
var https = require('https');
var httpsServer = https.createServer(options, (req, res) => { console.log(res, req)});
httpsServer.listen(443, function () {
console.log("Listening https on port: 443")
});
If you want test it:
edit /etc/hosts and add record 127.0.0.1 example.com
open browser with url https://example.com:443
Nginx can handle SSL termination nicely, and this will offload ssl processing power from your application servers.
If you have a secure private network between your nginx and application servers I recommend offloading ssl via nginx reverse proxy. In this practice nginx will listen on ssl, (certificates will be managed on nginx servers) then it will reverse proxy requests to application server on non ssl (so application servers dont require to have certificates on them, no ssl config and no ssl process burden).
If you don't have a secure private network between your nginx and application servers you can still use nginx as reverse proxy via configuring upstreams as ssl, but you will lose offloading benefits.
CDNs can do this too. They are basically reverse proxy + caching so I dont see a problem there.
Good read.
Let's Encrypt w/ Greenlock Express v3
I'm the author if Greenlock Express, which is Let's Encrypt for Node.js, Express, etc, and this use case is exactly what I made it for.
The basic setup looks like this:
require("greenlock-express")
.init(function getConfig() {
return {
package: require("./package.json")
manager: 'greenlock-manager-fs',
cluster: false,
configFile: '~/.config/greenlock/manager.json'
};
})
.serve(httpsWorker);
function httpsWorker(server) {
// Works with any Node app (Express, etc)
var app = require("./my-express-app.js");
// See, all normal stuff here
app.get("/hello", function(req, res) {
res.end("Hello, Encrypted World!");
});
// Serves on 80 and 443
// Get's SSL certificates magically!
server.serveApp(app);
}
It also works with node cluster so that you can take advantage of multiple cores.
It uses SNICallback to dynamically add certificates on the fly.
Site Management
The default manager plugin uses files on the file system, but there's great documentation on how to build your own.
Just to get started, the file-based plugin uses a config file that looks like this:
~/.config/greenlock/manager.json:
{
"subscriberEmail": "letsencrypt-test#therootcompany.com",
"agreeToTerms": true,
"sites": [
{
"subject": "example.com",
"altnames": ["example.com", "www.example.com"]
}
]
}
Very Extensible
I can't post all the possible options here, but it's very small and simple to start with, and very easy to scale out with advanced options as you need them.

Broken HTTPS SSL in express-js server (net::ERR_CERT_COMMON_NAME_INVALID)

I have an express js application that I want to listen on HTTPS.
I had a .key file and a .crt file that were already in PEM format (they contained readable text, as this answer says to check), so I used OpenSSL with these commands (taken from the answer linked above, and before finding that answer I had tried using the .key and .crt files I already had and using .pem files created by just renaming those two files into .pem, with no success):
openssl x509 -in public.crt -out public.pem -outform PEM
openssl rsa -in private.key -out private.pem -outform PEM
When I try to access the website at https://localhost, though, this is the error I get:
How can I make it work as intended?
Note that the certificate and key are VALID since I'm already using them on an existing website, it's not a self-signed test certificate.
Also, the client page tries to get the resource "/hey" but in addition to the HTTPS error in the certificate, instead of the resource the page gets a response that says "Cannot GET/"
Here is the code to the node.js app:
var express = require('C:/Users/f.fiore/AppData/Roaming/npm/node_modules/express');
var fs = require('fs');
var http = require('http');
var https = require('https');
var key = fs.readFileSync('./private.key');
var cert = fs.readFileSync('./public.crt')
var options = {
key: key,
cert: cert
};
var PORT = 8000;
var HOST = 'localhost';
var app = express();
var httpServer = https.createServer(app);
var httpsServer = https.createServer(options, app);
httpServer.listen(PORT);
httpsServer.listen(443);
// routes
app.get('/hey', function(req, res) {
sendToClient("HO!", res, 200, "text/plain");
});
function getHeader(type){
return {"Content-Type": type};
}
function sendToClient(data, res, code, type){
res.writeHead(code, getHeader(type));
(type === "text/html" || type === "text") ? res.end(data, "utf-8") : res.end(data);
}
Your certificate is valid, however the provider of the certificate is not the original issuer of this certificate.
So you need to provide the whole chain certificate at your localhost to make it work.
https://certificatechain.io/ seems like they are providing a service for this, but haven't tried. Better way is to check with your certificate provider.
Self signed certificates also bring such an error.
EDIT
Seems like the problem was more basics. Updating the solution
Try to play with your etc/hosts file to show the real domain name at your localhost. Right now it is looking for a domain called localhost and I don't think that you get a certificate for your localhost :) \Windows\System32\drivers\etc\hosts at windows environment
For your basic request of /hey please insert this codeblock
app.get('/hey', function(req, res){
res.send('HO!');
});

How to set connect server (node) to work on HTTPS? Error: error:0906D06C:PEM routines:PEM_read_bio:no start line

I am using connect server, and I need to create my localhost under https (even if certificates are not valid).
At the moment I am using the following script which createa a server listing at http://127.0.0.1:8080/
I need to set it up as: https://127.0.0.1:8080/
How to configure connect server?
gulp.task('dev:connect', function () {
// runs connect server for rapid development
connect.server({
root: ''
});
});
I am also trying this but with not success:
var https = require('https');
var options = {
key: fs.readFileSync('b.key'),
cert: fs.readFileSync('a.crt')
};
var app = connect(); // error here object is not a function
https.createServer(options, app).listen(8080);
Try to run the following, I think the key is protected:
$ openssl rsa -in b.key -out b-unprotected.key
$ cat b-unprotected.key a.crt > a.pem
Let me know if this works

Resources