HAPI SSL error:0906D06C:PEM routines:PEM_read_bio:no start line - node.js

I have a Hapi server which works fine on HTTP. I need to make this work over HTTPS. I have a certificate which we bought from COMODO.
My Key
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDGyXFDz/pSzMxO
...
g7N2PgtU9nhM7eYhQmhjB+4=
-----END PRIVATE KEY-----
My Certificate
-----BEGIN CERTIFICATE-----
MIIFbDCCBFSgAwIBAgIRAK3oQPHzO66FR3iLafOh2JkwDQYJKoZIhvcNAQELBQAw
...
pvWiUJabAat2O+hexjv55O4RkfQ13aIKo1Z7VeWyNQdEPaSCOFtteC4a3WelWcZ7
-----END CERTIFICATE-----
(have also tried this with a combined root certificate bundle with the same problem)
Edit: Both the certificates and the key are in the PEM format and not the DER format. There are also no problems with line endings.
My Server Code
var tls = {
key: fs.readFileSync('privkey.pem'),
cert: fs.readFileSync('certificate.pem')
};
var server = new Hapi.Server();
server.connection({
address: '0.0.0.0',
port: 443,
tls: tls,
routes: { cors: { origin: ['*'] }, validate: { options: { abortEarly: false } } }
});
I end up with the following error when trying to start the server
node server.js
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
at Error (native)
at Object.createSecureContext (_tls_common.js:87:19)
at Server (_tls_wrap.js:754:25)
at new Server (https.js:24:14)
at Object.exports.createServer (https.js:44:10)
at new module.exports.internals.Connection.options (W:\project\node_modules\hapi\lib\connection.js:89:74)
at internals.Server.connection (W:\project\node_modules\hapi\lib\server.js:121:24)
at Object.<anonymous> (W:\project\server.js:98:8)
at Module._compile (module.js:398:26)
at Object.Module._extensions..js (module.js:405:10)
What is going on? how do I fix this?
Any help would be appreciated.
Thanks

I was running into a similar issue, my configuration is different as I'm supplying the cert and key as a string (this is dev only). I was getting this same error because I linearized my key from a text editor which stripped out the new line (\n) characters. As soon as I added those \n characters back into my string, it worked just fine. Which got me thinking about your issue....
I tested your fs.readFileSync('server.key') (yes my server.key is in PEM format) code using my certificate and key and I noticed that without specifying an encoding, the file data was coming back as a byte array.
<Buffer 2d 2d 2d 2d ....
However, specifying an encoding fs.readFileSync('server.key', 'utf-8') did give me back the certificate data as a human readable string.
I'd try updating your readFileSync calls to include an encoding like 'utf-8' - it could be why hapi can't understand your certificate data.
Hope this helps!

Related

SSL certificates with RedBird.js and Node.js with two domains on one server

Bad(?) news "SSL For Free is joining ZeroSSL". Since their news I renewed my certificates and TLS stopped working. Used to work fine.
I believe free certs are now from something called AutoSSL. Hopefully.
With new certificates I get error "You may need to install an Intermediate/chain certificate to link it to a trusted root certificate" from https://www.sslshopper.com/ssl-checker.html and this error "TLS Certificate is not trusted" from https://www.digicert.com/help.
Browsers are smart enough to mask the problem but my Android app uses an API and it stopped working.
Anyone else getting TLS problems since ZeroSSL got involved?
I'm using redbirdjs on nodejs which is awesome since its so simple (two domains, same server), but Zero provides no installation instructions for my setup. (My domains are small in traffic so using the fastest webservers etc. isn't an issue).
Zero took away the 2 domains in one cert option (gee thanks) so my updated script looks like:
const { constants } = require('crypto');
var redbird = new require('redbird')({ port: 8080, ssl: { port: 443 }});
redbird.register('domain1.com', 'http://127.0.0.1:9443', {
ssl: {
key: 'ssl/domain1/private.key',
cert: 'ssl/domain1/certificate.crt',
ca: 'ssl/domain1/ca_bundle.crt',
secureOptions: constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1,
}
});
redbird.register('domain2.com', 'http://127.0.0.1:3003', {
ssl: {
key: 'ssl/domain2/private.key',
cert: 'ssl/domain2/certificate.crt',
ca: 'ssl/domain2/ca_bundle.crt',
secureOptions: constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1,
}
});
Other than separating the domain ssl config, it is the same as what used to work with SSLForFree.
I read somewhere that "free" SSL CA's do not necessarily provide the "full chain".
Anyone know how to get TLS working again with ZeroSSL on redbirdjs and nodejs?
Well, I got it working. I used https://whatsmychaincert.com, which I think just literally joins a couple files together. Either way for redbird fans (like me) here is the script for multiple domains on the same server.
// https://github.com/OptimalBits/redbird
// https://whatsmychaincert.com/
// 9443 is where domain1 server runs locally
// 3003 is where domain2 server runs locally
const { constants } = require('crypto');
var redbird = new require('redbird')({ port: 8080, ssl: { port: 443 }});
redbird.register('domain1.com', 'http://127.0.0.1:9443', {
ssl: {
port: 9443,
key: 'ssl/domain1/private.key',
cert: 'ssl/domain1/domain1.chained.crt', // used whatsmychaincert.com to generate ('enter hostname', no need to include root)
secureOptions: constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1,
}
});
redbird.register('domain2.net', 'http://127.0.0.1:3003', {
ssl: {
port: 3003,
key: 'ssl/domain2/private.key',
cert: 'ssl/domain2/domain2.chained.crt',
secureOptions: constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1,
}
});
Of the 3 files downloaded from ZeroSSL, whatsmychaincert.com put the certificate.crt and the ca_bundle.crt (in that order) into one file called domain.chained.crt (as see in the script above).

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.

Setting up an StartSSL certificate on an Express app

I'm learning how to use HTTPS on Express, using as example, a certificate that I generated at StartSSL. I tried taking a look at other solutions over there, but nothing seems to work for me.
Ok, so I generated a .pem file in StartSSL, with a key I saved here. I've put the .pem file in the Express app folder, and I'm trying to set up a working server with this code:
const app = require('express')();
const fs = require('fs');
const https = require('https');
// Setup HTTPS
const httpsPort = 3443;
const options = {
key: "my key in plain text",
cert: fs.readFileSync("./cert.pem")
};
var secureServer = https.createServer(options, app).listen(httpsPort, () => {
console.log(">> App listening at port "+httpsPort);
});
I also tried with .crt certificate and so, but nothing seems to work. It throws the following error:
C:\some\path\test>node index.js
_tls_common.js:85
c.context.setKey(options.key, options.passphrase);
^
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
at Error (native)
at Object.createSecureContext (_tls_common.js:85:17)
at Server (_tls_wrap.js:776:25)
at new Server (https.js:26:14)
at Object.exports.createServer (https.js:47:10)
at Object.<anonymous> (C:\some\path\test\index.js:18:26)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
What am I doing wrong, guys? Thank you!
EDIT:
Also tried putting the password in a .pem file with the following content:
-----BEGIN PRIVATE KEY-----
(my key)
-----END PRIVATE KEY-----
Which throws another error, maybe more fs-related:
Error: error:0906D064:PEM routines:PEM_read_bio:bad base64 decode

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!');
});

Node.js - Using https.request() with an internal CA

Who do I get https.request() to trust my internally-signed server certificate. Here is a quick example of the code I'm running in v0.10.25:
var options = {
hostname: 'encrypted.mydomain.local',
port: 443,
path: '/',
method: 'GET'
};
var https = require('https')
https.request(options)
I'm running this on a Windows system which has my internal root CA trusted at the system level, but whenever I make a request like this I get the exception
events.js:72
throw er; // Unhandled 'error' event
^
Error: CERT_UNTRUSTED
at SecurePair.<anonymous> (tls.js:1370:32)
at SecurePair.EventEmitter.emit (events.js:92:17)
at SecurePair.maybeInitFinished (tls.js:982:10)
at CleartextStream.read [as _read] (tls.js:469:13)
at CleartextStream.Readable.read (_stream_readable.js:320:10)
at EncryptedStream.write [as _write] (tls.js:366:25)
at doWrite (_stream_writable.js:223:10)
at writeOrBuffer (_stream_writable.js:213:5)
at EncryptedStream.Writable.write (_stream_writable.js:180:11)
at write (_stream_readable.js:583:24)
For a little more detail, this is all happening inside of the node-atlassian-crowd module I'm attempting to use for authentication
You need to add a ca: cafile.pem line to your options. See http://nodejs.org/api/https.html#https_https_request_options_callback for more details.
The relevant portion:
The following options from tls.connect() can also be specified. However, a globalAgent silently ignores these.
pfx: Certificate, Private key and CA certificates to use for SSL. Default null.
key: Private key to use for SSL. Default null.
passphrase: A string of passphrase for the private key or pfx. Default null.
cert: Public x509 certificate to use. Default null.
ca: An authority certificate or array of authority certificates to check the remote host against.
During application startup, read in the CA's certificate file with something like var casigningcert = fs.readFileSync('keys/ca-certsigning-cert.pem') and then consume it later in your options, which should then look something like:
var options = {
hostname: 'encrypted.mydomain.local',
port: 443,
path: '/',
method: 'GET',
ca: casigningcert
};

Resources