diff --git a/README.md b/README.md index 3584cb2..5514bd6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,83 @@ nodejs-self-signed-certificate-example ====================================== -The end off all your self-sign certificate woes (in node.js at least) +An example that works. + +The end off all your self-signed certificate woes (in node.js at least) + +Test for yourself +--- + +This is an easy-as-git-clone example that will get you on your way without +any `DEPTH_ZERO_SELF_SIGNED_CERT` or `SSL certificate problem: Invalid certificate chain` headaches. + +### Get the repo + +```bash +git clone git@github.com:coolaj86/nodejs-self-signed-certificate-example.git +pushd nodejs-self-signed-certificate-example +npm install +``` + +**For the super impatient**: + +```bash +bash test.sh +``` + +### Create certificates for `local.ldsconnect.org` + +`local.ldsconnect.org` points to `localhost`, so it's ideal for your first test. + +```bash +bash make-root-ca-and-certificates.sh 'local.ldsconnect.org' +``` + +### Run the server + +```bash +node ./serve.js 4443 & +# use `fg` and `ctrl+c` to kill +``` + + +### Test in a client + +Visit in a web browser + + + +Test (warning free) in node.js + +```bash +node ./request-without-warnings.js 4443 +``` + +Test (warning free) with cURL + +```bash +curl -v https://local.ldsconnect.org \ + --cacert client/my-private-root-ca.crt.pem +``` + +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.ldsconnect.org` +with your very own domain name. + +But where's the magic? +==== + +Who's the man behind the curtain you ask? + +Well... I lied. This demo doesn't use self-signed certificates. +It uses a self-signed Root CA and a signed certificate. + +It turns out that self-signed certificates were designed to be +used by the Root Certificate Authorities, not by web servers. + +So instead of trying to work through eleventeen brazillion errors +about self-signed certs, you can just create an authority and then +add the authority to your chain (viola, now it's trusted). diff --git a/make-root-ca-and-certificates.sh b/make-root-ca-and-certificates.sh new file mode 100755 index 0000000..13d2a19 --- /dev/null +++ b/make-root-ca-and-certificates.sh @@ -0,0 +1,48 @@ +#!/bin/bash +FQDN=$1 + +# make directories to work from +mkdir -p server/ client/ all/ + +# Create your very own Root Certificate Authority +openssl genrsa \ + -out all/my-private-root-ca.key.pem \ + 2048 + +# Self-sign your Root Certificate Authority +# Since this is private, the details can be as bogus as you like +openssl req \ + -x509 \ + -new \ + -nodes \ + -key all/my-private-root-ca.key.pem \ + -days 1024 \ + -out all/my-private-root-ca.crt.pem \ + -subj "/C=US/ST=Utah/L=Provo/O=ACME Signing Authority Inc/CN=example.com" + +# Create a Device Certificate for each domain, +# such as example.com, *.example.com, awesome.example.com +# NOTE: You MUST match CN to the domain name or ip address you want to use +openssl genrsa \ + -out all/my-server.key.pem \ + 2048 + +# Create a request from your Device, which your Root CA will sign +openssl req -new \ + -key all/my-server.key.pem \ + -out all/my-server.csr.pem \ + -subj "/C=US/ST=Utah/L=Provo/O=ACME Tech Inc/CN=${FQDN}" + +# Sign the request from Device with your Root CA +openssl x509 \ + -req -in all/my-server.csr.pem \ + -CA all/my-private-root-ca.crt.pem \ + -CAkey all/my-private-root-ca.key.pem \ + -CAcreateserial \ + -out all/my-server.crt.pem \ + -days 500 + +# Put things in their proper place +rsync -a all/my-server.{key,crt}.pem server/ +rsync -a all/my-private-root-ca.crt.pem server/ +rsync -a all/my-private-root-ca.crt.pem client/ diff --git a/package.json b/package.json new file mode 100644 index 0000000..ba6d5c5 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "nodejs-self-signed-certificate-example", + "version": "1.0.0", + "description": "The end off all your self-sign certificate woes (in node.js at least)", + "main": "serve.js", + "scripts": { + "test": "bash test.sh" + }, + "repository": { + "type": "git", + "url": "git://github.com/coolaj86/nodejs-self-signed-certificate-example.git" + }, + "keywords": [ + "nodejs", + "ssl", + "self-signed", + "certificate", + "authority", + "certificates", + "root", + "ca", + "cas" + ], + "author": "AJ ONeal (http://coolaj86.com)", + "license": "Apache-2", + "bugs": { + "url": "https://github.com/coolaj86/nodejs-self-signed-certificate-example/issues" + }, + "homepage": "https://github.com/coolaj86/nodejs-self-signed-certificate-example", + "dependencies": { + "ssl-root-cas": "^1.1.4" + } +} diff --git a/request-without-warnings.js b/request-without-warnings.js new file mode 100755 index 0000000..6fe002f --- /dev/null +++ b/request-without-warnings.js @@ -0,0 +1,21 @@ +#!/usr/bin/env node +'use strict'; + +var https = require('https') + , fs = require('fs') + , path = require('path') + , ca = fs.readFileSync(path.join(__dirname, 'client', 'my-private-root-ca.crt.pem')) + , port = process.argv[2] || 4443 + ; + +var options = { + host: 'local.ldsconnect.org' +, port: port +, path: '/' +, ca: ca +}; +options.agent = new https.Agent(options); + +https.request(options, function(res) { + res.pipe(process.stdout); +}).end(); diff --git a/serve.js b/serve.js new file mode 100755 index 0000000..0892c24 --- /dev/null +++ b/serve.js @@ -0,0 +1,35 @@ +#!/usr/bin/env node +'use strict'; + +var https = require('https') + , port = process.argv[2] || 4443 + , fs = require('fs') + , path = require('path') + , server + , options + ; + +require('ssl-root-cas') + .inject() + .addFile(path.join(__dirname, 'server', 'my-private-root-ca.crt.pem')) + ; + +options = { + key: fs.readFileSync(path.join(__dirname, 'server', 'my-server.key.pem')) +// You don't need to specify `ca`, it's done by `ssl-root-cas` +//, ca: [ fs.readFileSync(path.join(__dirname, 'server', 'my-private-root-ca.crt.pem'))] +, cert: fs.readFileSync(path.join(__dirname, 'server', 'my-server.crt.pem')) +}; + + +function app(req, res) { + res.setHeader('Content-Type', 'text/plain'); + res.end('Hello, encrypted world!'); +} + +server = https.createServer(options, app).listen(port, function () { + port = server.address().port; + console.log('Listening on https://127.0.0.1:' + port); + console.log('Listening on https://' + server.address().address + ':' + port); + console.log('Listening on https://local.ldsconnect.org:' + port); +}); diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..5745268 --- /dev/null +++ b/test.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +bash make-root-ca-and-certificates.sh 'local.ldsconnect.org' + +node ./serve.js & + +sleep 2 + +node ./request-without-warnings.js +echo ""