Debug node SSL handshake - node.js

I have a node js server described as this:
const fs = require('fs');
const https = require('https');
const path = require('path');
const log = require('./lib/log');
const server = https.createServer({
key: fs.readFileSync(path.join(__dirname, 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'server.crt')),
ca: [fs.readFileSync(path.join(__dirname, 'ca.pem'))],
requestCert: true,
rejectUnauthorized: true
}, function() { log.debug('ok'); });
server.listen(8080, () => log.info(`Server listening on port 8080`));
My issue is that the certificate presented by the client gets rejected while it has been signed by this CA.
I've tried to use OpenSSL to be sure:
$ openssl s_server -key server.key -cert server.crt -accept 8080 -www -CAfile ca.pem -verify 5
verify depth is 5
depth=0 C = FR, O = MyO, OU = MyOU, CN = MyCN
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = FR, O = MyO, OU = MyOU, CN = MyCN
verify error:num=21:unable to verify the first certificate
verify return:1
Is there a way to have a verbose mode or to get the client's certificate?

Related

HTTPS request, unable to verify first certificate [duplicate]

This question already has answers here:
How to fix missing an Intermediate/chain certificate in nodejs
(1 answer)
Node.js/Express.js Chain Certificate Not working
(2 answers)
Closed last month.
I'm trying to setup an local https server with express. To generate a key and certificate, I am using the openssl package and following this tutorial.
Here is my server file
const express = require("express");
const https = require("https");
const fs = require("fs");
const path = require("path");
const app = express();
const PORT = 1000;
app.post("/test", (req, res) => {
console.log("received request");
res.send("hello");
});
const keyPath = path.join(process.env.CERTS_DIR, "simple.org.key");
const certPath = path.join(process.env.CERTS_DIR, "simple.org.crt");
const options = {
key: fs.readFileSync(keyPath),
cert: fs.readFileSync(certPath)
};
console.log({ options });
const server = https.createServer(options, app);
server.listen(PORT);
server.on("error", function (err) {
console.log("server.error: " + err);
});
server.on("listening", function () {
console.log(
`Secure server listening on port:${PORT}`
);
});
I have no trouble starting my server. But when I make a request with the ssl option rejectUnauthorized: true I get the following error:
"message": "request to https://localhost:1000/test failed, reason: unable to verify the first certificate",
"type": "system",
"errno": "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
"code": "UNABLE_TO_VERIFY_LEAF_SIGNATURE"
}
For my certificate setup, I have:
a root CA (self signed)
a signing CA (signed by root CA)
a server cert (signed by signing CA)
From what research I've done, it looks like my server cert is missing an intermediary CA.
In addition to this, I ran openssl s_client -showcerts -connect localhost:1000 -servername localhost and received the following output:
CONNECTED(00000005)
depth=0 DC = org, DC = simple, O = Simple Inc, CN = www.simple.org
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 DC = org, DC = simple, O = Simple Inc, CN = www.simple.org
verify error:num=21:unable to verify the first certificate
verify return:1
write W BLOCK
Can someone tell me what I'm missing to make this work? I could have sworn the first time I set this up it worked but now it's giving me this error.

error : "ERR_SSL_VERSION_OR_CIPHER_MISMATCH"

I want to use https on my web locally.
Using Nuxt framework, and create a nodejs server.
I follow these command to create key.
openssl genrsa 2048 > server.key
chmod 400 server.key
openssl req -new -x509 -nodes -sha256 -days 365 -key server.key -out server.crt
then have these code on my server.
const app = express()
const path = require('path')
const fs = require('fs')
const option = {
https: {
key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
}
}
const server = require('https').createServer(option, app)
server.listen(port, host)
I have hosts setting 172.0.0.1 local.xxx.com.
when I call my web on chrome https://local.xxx.com
I will get error: ERR_SSL_VERSION_OR_CIPHER_MISMATCH
try others browser, get similar error
fix it!
this error cause my option have excessive 'https',fix it then it works.
const option = {
key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
}

Self sign certificate client authentication with client and server both on localhost

I have a node/express server running https on localhost:9002, and I want to use client certificate for a react app, running on localhost:8080 (webpack dev server). The react app is using ajax request with superagent to the https server, and I have a passport middleware to check certificate automatically.
Environment
Windows 10, Chrome Version 71.0.3578.98
Setup
Using openssl, I created a root CA. Then I generated my server certificate, and a client certificate. This is the script used (I run it with git bash, so it's UNIX style but I'm on windows):
## CREATE CERTIFICATES FOR AUTHENTICATION
#########################################
## 1. Create Root Certificate Authority #
#########################################
# Root CA private key
openssl genrsa -out ./rootCA.key 4096
# Root CA certificate to register in RootCA store on OS
openssl req -x509 -new -nodes -key ./rootCA.key -sha256 -days 3650 -out ./rootCA.pem
#################################
## 2. Create Server certificate #
#################################
# Create private key for server
openssl genrsa -out ./server.key 4096
# Create server certificate sign request (CSR) based on the private key
openssl req -new -sha256 -nodes -out ./server.csr -key ./server.key -config ./server.csr.conf
# Create server certificate linked to the previoulsy created Root CA
openssl x509 -req -in ./server.csr -CA ./rootCA.pem -CAkey ./rootCA.key -CAcreateserial -out ./server.crt -days 3650 -sha256 -extfile ./v3.ext
#################################
## 3. Create Client certificate #
#################################
# Create private key for client
openssl genrsa -out ./client.key 4096
# Create the Certificate Sign Request (CSR) file from the client private key
openssl req -new -config ./client.csr.conf -key ./client.key -out ./client.csr
# Self sign the certificate for 10 years
openssl x509 -req -days 3650 -in ./client.csr -CA ./server.crt -CAkey ./server.key -CAcreateserial -out ./client.crt
# Display the fingerprint of the newly generated fingerprint
openssl x509 -noout -fingerprint -inform pem -in ./client.crt
# Generate a PFX file for integration in browser
openssl pkcs12 -export -out ./client.pfx -inkey ./client.key -in ./client.crt -passout pass:
Here are the different configurations used:
server.csr.conf
[ req ]
default_bits = 4096
default_md = sha512
prompt = no
encrypt_key = no
distinguished_name = req_distinguished_name
# distinguished_name
[ req_distinguished_name ]
countryName = "FR"
localityName = "Lille"
organizationName = "Sopra Steria"
organizationalUnitName = "Webskillz"
commonName = "localhost"
v3.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = #alt_names
[alt_names]
DNS.1 = localhost
client.csr.conf
[ req ]
default_bits = 4096
default_md = sha512
default_keyfile = server.key
prompt = no
encrypt_key = no
distinguished_name = req_distinguished_name
# distinguished_name
[ req_distinguished_name ]
countryName = "FR"
localityName = "Lille"
organizationName = "Sopra Steria"
organizationalUnitName = "Webskillz"
commonName = "localhost"
Finally, I addedd rootCA.pem to the Trusted Root Certification Authorities using certmgr.msc, and I added the client.pfx and server.crt certificate to the Personnal store.
Issue 1
Chrome is annoyingly redirecting http://localhost:8080 to https://localhost:8080, and I don't want to systematically open chrome://net-internals/#hsts to delete the localhost key...
Issue 2
When I finally access to http://localhost:8080, I'm asked to choose the certificate I want to authenticate to https://localhost:9002 (yeay!), but I still get a 401, which is not caused by the passport cert-auth middleware (there is no log in my middleware).
Additional information
1. Almost working setup
I managed to make this client/server setup work without a root certificate, but the issue was that I got a NET::ERR_CERT_AUTHORITY_INVALID from Chrome... That's why I added a root CA, following some advice on the World Wide Web... And indeed it corrected the problem, but then I was not able to authenticate, and Chrome began to redirect automatically http to https ಠ෴ಠ
Oh by the way, CORS is allowed server side so no problems from CORS.
2. Server code
Passport auth strategy: we just check for the fingerprint in the database.
cert-auth.js
import { Strategy } from 'passport-client-cert';
export default new Strategy(async (clientCert, done) => {
console.log(clientCert); // NO LOG HERE!!
if (clientCert.fingerprint) {
try {
const user = await findByFingerprintInMyAwesomeDb({ fingerprint: clientCert.fingerprint });
return done(null, user);
} catch (err) {
return done(new Error(err));
}
}
return done(null, false);
});
bootstrap-express.js
import passport from 'passport';
import certificateStrategy from 'cert-auth';
export default (app) => {
// CORS setup, bodyparser stuff & all...
// ... //
// Using authentication based on certificate
passport.use(certificateStrategy);
app.use(passport.initialize());
app.use(passport.authenticate('client-cert', { session: false }));
// Api routes.
app.get('/api/stream',
passport.authenticate('client-cert', { session: false }),
(req, res) => {
// Some router stuff
});
};
index.js
import https from 'https';
import express from 'express';
import fs from 'fs';
import path from 'path';
import bootstrapExpress from 'bootstrap-express';
const certDir = path.join(__dirname, '..', 'cert');
const listenPromise = server => port => new Promise((resolve, reject) => {
const listener = server.listen(port, err => (err ? reject(err) : resolve(listener)));
});
const options = {
key: fs.readFileSync(path.join(certDir, 'server.key')),
cert: fs.readFileSync(path.join(certDir, 'server.crt')),
ca: fs.readFileSync(path.join(certDir, 'server.crt')),
requestCert: true,
rejectUnauthorized: false,
};
(async function main() {
try {
logger.info('Initializing server');
const app = express();
bootstrapExpress(app);
const httpsServer = https.createServer(options, app);
const httpsListener = await listenPromise(httpsServer)(9002);
logger.info(`HTTPS listening on port ${httpsListener.address().port} in ${app.get('env')} environment`);
} catch (err) {
logger.error(err);
process.exit(1);
}
}());
Conclusion
Any help is welcome :)
Regards
Okay, I did many changes so that the chain of certificate could be clearer, but the reason I was still having 401 after all my efforts was because of this configuration in my express server:
const options = {
key: fs.readFileSync(path.join(certDir, 'server.key')),
cert: fs.readFileSync(path.join(certDir, 'server.crt')),
ca: fs.readFileSync(path.join(certDir, 'server.crt')),
requestCert: true,
rejectUnauthorized: false,
};
The working configuration is the following (replacing ca by the rootCA):
const options = {
key: fs.readFileSync(path.join(certDir, 'server.key')),
cert: fs.readFileSync(path.join(certDir, 'server.crt')),
ca: fs.readFileSync(path.join(certDir, 'rootCA.pem')),
requestCert: true,
rejectUnauthorized: false,
};
This issue helped me by the way, but I only find it few minutes ago: https://github.com/nodejs/help/issues/253^
Additional Info: in order to avoid the redirection from http to https because my server was on the localhost DNS, I simply added a new DNS in C:\Windows\System32\drivers\etc\host
127.0.0.1 mysuperdns
Therefore, the common name for the server certificate must be mysuperdns.

Nodejs Https Certification

Recently I bought a ssl certification
Now I have 5 files:
1) COMODORSADomainValidationSecureServerCA.crt
2) COMODORSAAddTrustCA.crt
3) AddTrustExternalCARoot.crt
4) www_photoshooter_gr.crt
5) key.key (which is the private key)
I know that I have to create an https nodejs server like this sample
var https = require('https');
var fs = require('fs');
var opts = {key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')};
https.createServer(opts, function (req, res) {
res.end('secured!');
}).listen(4443);
but I don't have .pem files!!! How can I create them?
I do it like so :
var server = https.createServer({
key: fs.readFileSync('secret/server.key'),
cert: fs.readFileSync('secret/server.crt'),
ca: fs.readFileSync('secret/ca.crt'),
requestCert: true, rejectUnauthorized: false
}, app);
Where app is an Express app.
You can replace server.key with key.key, server.crt with www_photoshooter_gr.crt and ca.crt with COMODORSADomainValidationSecureServerCA.crt

How can you get yourself a key and certificate pair for using in https?

I am trying to use the https module in Node.js.
Here's the code:
var options = {
key : <key comes here>,
cert : <key comes here>
};
https.createServer(options, app).listen(app.get('port'));
You can use OpenSSL,
A private key is created like this
openssl genrsa -out ryans-key.pem 1024
The first step to getting a certificate is to create a "Certificate Signing Request" (CSR) file. This is done with:
openssl req -new -key ryans-key.pem -out ryans-csr.pem
To create a self-signed certificate with the CSR, do this:
openssl x509 -req -in ryans-csr.pem -signkey ryans-key.pem -out ryans-cert.pem
To create .pfx or .p12, do this:
openssl pkcs12 -export -in agent5-cert.pem -inkey agent5-key.pem \
-certfile ca-cert.pem -out agent5.pfx
Here is a simple example echo server
var tls = require('tls');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
// This is necessary only if using the client certificate authentication.
requestCert: true,
// This is necessary only if the client uses the self-signed certificate.
ca: [ fs.readFileSync('client-cert.pem') ]
};
var server = tls.createServer(options, function(cleartextStream) {
console.log('server connected',
cleartextStream.authorized ? 'authorized' : 'unauthorized');
cleartextStream.write("welcome!\n");
cleartextStream.setEncoding('utf8');
cleartextStream.pipe(cleartextStream);
});
server.listen(8000, function() {
console.log('server bound');
});
you can refer to http://nodejs.org/api/tls.html for more info,
Regards
Follow the openssl instructions found at the Node.js website.

Resources