/nodejs-ssl-trusted-peer-example

This is a working example of a trusted-peer setup using SSL.

Primary LanguageShellApache License 2.0Apache-2.0

nodejs-ssl-trusted-peer-example

This is a working example of a trusted-peer setup using HTTPS / SSL.

  • You are your own Root Certificate Authority
  • Your primary server has a certificate signed by your CA
  • Your clients also have certificates signed by your CA
  • Client/Peer certificates which have not been signed by a trusted authority (your CA) are rejected.
  • The built-in CA list is replaced rather than appended to (see ssl-root-cas for appending).

This is a kind of "part 2" to coolaj86/nodejs-self-signed-certificate-example.

See also:

Test for yourself

An example that works.

example
├── make-root-ca-and-certificates.sh
├── package.json
├── serve.js
├── request.sh
└── request.js

Get the repo

git clone git@github.com:coolaj86/nodejs-ssl-trusted-peer-example.git
pushd nodejs-ssl-trusted-peer-example
npm install

For the super impatient:

bash test.sh

This will

  • create all of the keys
  • ask you to create a file to protect your p12 file
  • (needed to allow Chrome, Safari, and other apps to use your key + crt in OS X Keychain Access)
  • run the tests and print happy messages

Create certificates for your FQDN

local.foobar3000.com points to localhost, so it's ideal for your first test.

bash make-root-ca-and-certificates.sh local.foobar3000.com 8043

This will ask you to create a passphrase to protect your p12 file. If you forget the passphrase, the file can easily be recreated later (see source in the file).

For the purposes of this tutorial, I recommend choosing 'secret'

certs/
├── ca
│   ├── my-root-ca.crt.pem
│   ├── my-root-ca.key.pem
│   └── my-root-ca.srl
├── client
│   ├── my-app-client.crt.pem
│   ├── my-app-client.key.pem
│   ├── my-app-client.p12
│   └── my-root-ca.crt.pem
├── server
│   ├── my-root-ca.crt.pem
│   ├── my-server.crt.pem
│   └── my-server.key.pem
└── tmp
    ├── my-app-client.csr.pem
    └── my-server.csr.pem

Run the server

node ./serve.js 8043 &
# use `fg` and `ctrl+c` to kill

Test in a client

Test in node.js

node ./request.js local.foobar3000.com 8043

Test with cURL

curl -v -s "https://local.foobar3000.com:8043" \
  --key certs/client/my-app-client.key.pem \
  --cert certs/client/my-app-client.crt.pem \
  --cacert certs/client/my-root-ca.crt.pem

Visit in a web browser

https://local.foobar3000.com:8043

To get rid of the browser errors and warnings, simply add the certificate from the client folder to your list of certificates by alt-clicking "Open With => Keychain Access" on my-root-ca.crt.pem and also on my-app-client.p12 (you will need to use the passphrase you created earlier)

You do have to set Always Trust a few times as explained by Rob Peck.

Now season to taste

You can poke around in the files for generating the certificates, but all you really have to do is replace local.foobar3000.com with your very own domain name.

Show me the Magic!

You have 3 different certificates with private / public pairs:

  • The Root CA (CN=example.com), which is used to sign the other certs
  • The Server Cert (CN=local.foobar3000.com), which is signed by the Root CA
  • The Peer Cert (CN=client.example.net), which is signed by the Root CA

Since the server's CA list is overwritten with only the self-signed Root CA only clients which have a certificate signed by that CA will be trusted (or potentially something else in the same certificate chain).

On the server

https.createServer({
  key: fs.readFileSync(path.join(__dirname, 'certs', 'server', 'my-server.key.pem'))
, ca: [ fs.readFileSync(path.join(__dirname, 'certs', 'server', 'my-root-ca.crt.pem'))]
, cert: fs.readFileSync(path.join(__dirname, 'certs', 'server', 'my-server.crt.pem'))
, requestCert: true
, rejectUnauthorized: true
})
  • requestCert asks the client to identify itself.
  • rejectUnauthorized prevents clients that aren't in the CA chain from being accepted.

If you need to selectively trust certain servers and clients when serving and making requests, or also serve to public clients you may need to have multiple servers and clients. ssl-root-cas will probably help.

In the client

var options
  ;

options = {
  host: 'local.foobar3000.com'
, port: 8043
, path: '/'
, ca: [ fs.readFileSync(path.join(__dirname, 'certs', 'client', 'my-root-ca.crt.pem')) ]
, key: fs.readFileSync(path.join(__dirname, 'certs', 'client', 'my-app-client.key.pem'))
, cert: fs.readFileSync(path.join(__dirname, 'certs', 'client', 'my-app-client.crt.pem'))
};
options.agent = new https.Agent(options);

https.request(options, handler);

The node example for this (http://nodejs.org/api/https.html) is a bit confusing. I'll have to read the source to better understand why the options are laid out as such.

What About WebSockets?

Viola: https://groups.google.com/forum/#!topic/nodejs/KJPk8aibQHY

Other SSL Resources

Zero-Config clone 'n' run (tm) Repos:

Articles