node.js can i use multiple ssl certificates and keys for same project and how? - node.js

i have my paypal ssl certificate for the paypal ipn added for my code like that and it working without any problems
var httpsOptions = {
key: fs.readFileSync('./app/certsandkeys/my-prvkey.pem'),
cert: fs.readFileSync('./app/certsandkeys/my-pubcert.pem'),
requestCert: true
//pfx: fs.readFileSync('./app/certsandkeys/ssl/crt.pfx'),
//passphrase:"password"
}
https.createServer(httpsOptions, app).listen(443,function (req,res) {
console.log("server listening on port " + 443);
});
but what i need now is to certificating my whole site so i created an ssl cert and key using openssl (server.crt and server.csr and server.key ) but now i don't know how to add it beside the paypal ipn cert and key on httpsOptions
the only thing i found about something like that is this code from github issues
var options = {
key: [key1, key2],
cert: [cert1, cert2],
ca: caCert
};
var server = https.createServer(options);
so what's the right way for doing that ?

Using different keys on the same server is handled by Server Name Indication (SNI) and requires different domain names for the different servers. This question shows how SNI would be used to create a different security context for the second domain name.
Changing that code for a key in the default context should look something like this:
const secondContext = tls.createSecureContext({
key: [key2],
cert: [cert2]
});
const options = {
key: [key1],
cert: [cert1],
SNICallback: function (domain, cb) {
if (domain === 'key2domain.example.com') {
cb(null, secondContext);
} else {
cb();
}
}
}
It's not clear from the paypal docs you refer to whether paypal is having you set up an alternate domain for this IPN service URL. Instead, it looks like their process accepts a CSR to handle self-signed certs for IPN-only use and offers a payed signing of it to use it for user visible buttons, i.e. they offer their own CA service?
You can submit multiple CSRs on the same key, so you could try relying on a single private key and keep a certificate chain from a normal CA. But if they enforce usage of their own certificate chain, then you will probably need to create a separate (sub)domain for this usage to provide different chains with SNI.

Related

Nodejs. Providing different Self-Signed Certificate depending by which IP service was accessed

I'm implementing https server in nodejs which should be accessible as from local IP address as from public IP address over SSL. With help of documentation (and some answers here on SO) I found how I can dynamically provide different Certificate for different hostnames (using SNICallback) but as documentation points that SNICallback works only with hostnames and not with IPs:
Currently, the only server names supported are DNS hostnames Literal
IPv4 and IPv6 addresses are not permitted in "HostName"
Here is some code how the server is created:
var certs = {
'intern': {
key: fs.readFileSync(keyName, 'utf8'),
cert: fs.readFileSync(certName, 'utf8')
},
'extern': {
key: fs.readFileSync(keyNameExternal, 'utf8'),
cert: fs.readFileSync(certNameExternal, 'utf8')
}
}
var httpsOpts = {
SNICallback: function (hostname, cb) {
if (hostname === 'mydomain.com') {
var ctx = tls.createSecureContext(certs['extern'])
cb(null, ctx)
}
},
key: fs.readFileSync(keyName, 'utf8'),
cert: fs.readFileSync(certName, 'utf8')
}
httpsServer = https.createServer(httpsOpts, httpsApp)
httpsServer.listen(443, '0.0.0.0', onlisteningHttps)
Is there a possibility to provide certs['intern'] certificate when server is accessed by local IP '192.168.1.100'
and provide certs['extern'] certificate when server accessed by public IP (not a hostname)?
It is only possible if your server has two separate interface for internal and external network.
The second parameter to httpsServer.listen is actually the bind interface. So for example, assume that you have an "internal" interface at 192.168.1.100, you can do this:
httpsServerInt = https.createServer(certs.intern, httpsApp)
httpsServerExt = https.createServer(certs.extern, httpsApp)
httpsServerInt.listen(443, '192.168.1.100', onlisteningHttps)
httpsServerExt.listen(443, '0.0.0.0', onlisteningHttps)
If the server does not have separate interface, you can look into "linux virtual interface" (or windows if that is the case)

Using a Microsoft Windows Certificate Store in node.js

I am trying to create a node https server. The idea is that the server will receive the thumbprint of a certificate in the store, pull it out of the store and use it for SSL/TLS connection. I have done examples for self-signed certificates and it has worked fine.
I have the code to look through the certificate store and find the correct certificate based on its thumbprint
The code I have so far is below.
const ca = require('win-ca');
const forge = require('node-forge');
const pki = forge.pki;
const ssh = forge.ssh;
const thumbprint = process.env.THUMBPRINT;
...
for (let cert of ca.all()) {
console.log(forge.pki.certificateToPem(cert));
console.log(' ----------- \r');
const md = forge.md.sha1.create();
md.update(forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes());
const hex = md.digest().toHex();
console.log(hex);
console.log('\r');
if (thumbprint === hex) {
foundCert = cert;
console.log('found\r');
break;
}
}
console.log('**********************');
console.log('\r');
//}
cnt++;
}
if (foundCert) {
console.log(forge.pki.certificateToPem(foundCert));
const pm = forge.pki.certificateToPem(foundCert);
console.log(foundCert);
}
In windows ASP.NET Core, one only needs the the certificate for https. For node.js, all the examples use a private key. I don't have the private key, but I have access to the machine's certificate store. Can I use the certificate from the store without the private key? There seems to be a piece I am missing.
Thanks

Using node.js to verify a X509 certificate with CA cert

I am looking for a node.js way to verify a client certificate in X509 format with a CA certificate which was given to me (none of those are created/managed by me, my software only has to verify what is beeing sent to it).
I have found several modules for this job, however I am having issues with each of them:
X509 is able to do it using x509.verify(cert, CABundlePath, cb), however it needs to read the certificates from FS, and I am having them in memory already. This is cumbersome as it will be done with each web request which reaches my app.
It seems like PKI.js is able to do it, however their examples don't work for me but complain about missing files, so I can't even try it out.
I tried node-forge, but while I am unsure if I use it correctly (they don't have any API documentation) its throwing a forge.pki.BadCertificate error from forge.pki.verifyCertificateChain(caStore, [ cer ], cb).
When trying pem, using a simple pem.verifySigningChain(cer, [ ca ], cb) would throw some error complaining about loading a file from /var/.... Even if it would work, I would avoid using this lib as its relying on the openssl command line tool, which I would like to avoid
Now I feel pretty stupid because I failed to get this simple task done with any of the above modules. Could someone point me to a simple solution which will allow me to verify the signature/validity of a X509 certificate using a given CA certificate? :s
[edit] Basically I would need openssl verify -verbose -CAfile ca-crt.pem client1-crt.pem in Node.js but without dependencies to the openssl command line tool and without temporarily saving the certs to disk.
[edit2] Would it be possible to just use https://nodejs.org/api/crypto.html#crypto_verify_verify_object_signature_signatureformat?
I finally managed to do it using node-forge. Heres a working code example:
let pki = require('node-forge').pki;
let fs = require('fs');
let caCert;
let caStore;
try {
caCert = fs.readFileSync('path/to/ca-cert.pem').toString();
caStore = pki.createCaStore([ caCert ]);
} catch (e) {
log.error('Failed to load CA certificate (' + e + ')');
return....;
}
try {
pki.verifyCertificateChain(caStore, [ cert ]);
} catch (e) {
return handleResponse(new Error('Failed to verify certificate (' + e.message || e + ')'));
}
Both certificates shall be given in base64 encoded PEM format/js string.
verifyCertificateChain checks the certifitate validity (notBefore/notAfter) as well as verifies the given CA chain.
I am not 100% sure if this is the best approach, or if this library is doing a good job, since their source code of verifyCertificateChain is full of #TODOs, so maybe this is not ready for production?
But at least I have a somewhat working solution. Probably it would be better to create a node module which wraps the libssl c calls, but thats just a lot of effort for this small task.
You can also do like this if you want to check the using the client certificates from the http request directly :
// retrieve certificates from the request ( in der format )
clientCert = req.connection.getPeerCertificate(true).raw.toString('base64'))
Method to convert the der certificate to pem and verify against the castore.
const caCert = fs....
const ca = pki.certificateFromPem(caCert)
const caStore = pki.createCaStore([ ca ])
const verify = (clientCert, next) => {
try {
const derKey = forge.util.decode64(clientCert)
const asnObj = forge.asn1.fromDer(derKey)
const asn1Cert = pki.certificateFromAsn1(asnObj)
const pemCert = pki.certificateToPem(asn1Cert)
const client = pki.certificateFromPem(pemCert)
return pki.verifyCertificateChain(caStore, [ client ], cb)
} catch (err) {
next(new Error(err))
}
}
I did not find a better way to verify the client "der" certificate from the request.
fas3r
This works for me:
const fs = require('fs'), pki = require('node-forge').pki
var ca = pki.certificateFromPem(fs.readFileSync('ca.pem', 'ascii'))
var client = pki.certificateFromPem(fs.readFileSync('client.pem', 'ascii'))
try {
if (!ca.verify(client)) throw 'verify failed'
} catch (err) {
console.log(err)
}
try/catch was required, because .verify threw an error (instead of returning false) in my case.

How to setup an EV Certificate a node.js server

I've received four files from Comodo:
AddTrustExternalCARoot.crt
COMODORSAAddTrustCA.crt
COMODORSAExtendedValidationSecureServerCA.crt
mydomain.crt
This is my first time setting up a https server.
I know that I have to put on parameters that is passed to https.createServer but my problem is I don't know which one is the correct property.
The server certificate is set as cert, whereas your CA certificates are set under ca:
var fs = require('fs'),
https = require('https');
var cfg = {
key: fs.readFileSync('/path/to/privatekey.pem'),
cert: fs.readFileSync('/path/to/mydomain.crt'), // PEM format
ca: [
fs.readFileSync('/path/to/AddTrustExternalCARoot.crt'), // PEM format
fs.readFileSync('/path/to/COMODORSAAddTrustCA.crt'), // PEM format
fs.readFileSync('/path/to/COMODORSAExtendedValidationSecureServerCA.crt') // PEM format
]
};
https.createServer(cfg, function(req, res) {
// ...
}).listen(443);
Or you can use just pfx if you have your key, cert, and ca files all bundled into a single PFX/PKCS12-formatted file.

Https two-way authentication with server using a public signed cert, but client using a private CA

I'm a node-js guy but I think this is about Certificate/CA only.
I want to set up an https server using a certificate which is signed by a public CA, so that all the browsers can visit my website without certificate error. At the same time, I want my server to provide two-way https authentication, so that the server can recognize my clients if my clients is using a certificate. Client certificate is signed by CA created by myself.
When I let the client connect to the server, it gets an error called Error: CERT_UNTRUSTED. But I have set up the "ca" & "agent" option for both the server and the client, so I can't figure out my mistake.
I have installed my self-signed CA in my windows 8 Root Certificates, altough I don't think it's really needed.
My Code:
Server
var options = {
key:keyForCertificate,
cert:certFromPublicCA,
ca:[PublicCA, self-signedCA],
requestCert: true,
rejectUnauthorized: false
};
var server = require('https').Server(options, require('express')());
server.listen(443);
Client
require('https').request({ host: "www.publicWebsite.com"
, method: "GET"
, port: 443
, headers: { host: "www.publicWebsite.com" }
, ca:[PublicCA, self-signedCA],
, path: "/" }, function (res) {
if (res.client.authorized) {
console.log("node test: OK")
} else {
throw new Error(res.client.authorizationError)
}
}).end()

Resources