Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
620 views
in Technique[技术] by (71.8m points)

ssl - How do I use a self signed certificate for a HTTPS Node.js server?

I have started writing a wrapper for an API which requires all requests to be over HTTPS. Instead of making requests to the actual API while I am developing and testing it I would like to run my own server locally which mocks the responses.

I am confused about how to generate the certificates I need to create a HTTPS server and send requests to it.

My server looks something like this:

var options = {
  key: fs.readFileSync('./key.pem'),
  cert: fs.readFileSync('./cert.pem')
};

https.createServer(options, function(req, res) {
  res.writeHead(200);
  res.end('OK
');
}).listen(8000);

The pem files were generated with:

openssl genrsa 1024 > key.pem
openssl req -x509 -new -key key.pem > cert.pem

And a request looks something like this:

var options = {
  host: 'localhost',
  port: 8000,
  path: '/api/v1/test'
};

https.request(options, function(res) {
  res.pipe(process.stdout);
}).end();

With this setup I get Error: DEPTH_ZERO_SELF_SIGNED_CERT, so I think I need to add a ca option for the request.

So my question is how should I generate the following:

  1. The server key?
  2. The server cert?
  3. The ca for the request?

I have read a few things about generating self signed certificates with openssl, but can't seem to wrap my head around it and figure out which keys and certificates to use where in my node code.

Update

The API provides a CA certificate to use instead of the defaults. The following code works using their certificate and this is what I want to reproduce locally.

var ca = fs.readFileSync('./certificate.pem');

var options = {
  host: 'example.com',
  path: '/api/v1/test',
  ca: ca
};
options.agent = new https.Agent(options);

https.request(options, function(res) {
  res.pipe(process.stdout);
}).end();
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Update (Nov 2018): Do you need self-signed certs?

Or would real certificates get the job done better? Have you considered any of these?

(Note: Let's Encrypt can also issue certificates to private networks)

ScreenCast

https://coolaj86.com/articles/how-to-create-a-csr-for-https-tls-ssl-rsa-pems/

Full, Working example

  • creates certificates
  • runs node.js server
  • no warnings or errors in node.js client
  • no warnings or errors in cURL

https://github.com/coolaj86/nodejs-self-signed-certificate-example

Using localhost.greenlock.domains as an example (it points to 127.0.0.1):

server.js

'use strict';

var https = require('https')
  , port = process.argv[2] || 8043
  , fs = require('fs')
  , path = require('path')
  , server
  , options
  ;

require('ssl-root-cas')
  .inject()
  .addFile(path.join(__dirname, 'server', 'my-private-root-ca.cert.pem'))
  ;

options = {
  // this is ONLY the PRIVATE KEY
  key: fs.readFileSync(path.join(__dirname, 'server', 'privkey.pem'))
  // You DO NOT specify `ca`, that's only for peer authentication
//, ca: [ fs.readFileSync(path.join(__dirname, 'server', 'my-private-root-ca.cert.pem'))]
  // This should contain both cert.pem AND chain.pem (in that order) 
, cert: fs.readFileSync(path.join(__dirname, 'server', 'fullchain.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://localhost.greenlock.domains:' + port);
});

client.js

'use strict';

var https = require('https')
  , fs = require('fs')
  , path = require('path')
  , ca = fs.readFileSync(path.join(__dirname, 'client', 'my-private-root-ca.cert.pem'))
  , port = process.argv[2] || 8043
  , hostname = process.argv[3] || 'localhost.greenlock.domains'
  ;

var options = {
  host: hostname
, port: port
, path: '/'
, ca: ca
};
options.agent = new https.Agent(options);

https.request(options, function(res) {
  res.pipe(process.stdout);
}).end();

And the script that makes the certificate files:

make-certs.sh

#!/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.privkey.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.privkey.pem 
  -days 1024 
  -out all/my-private-root-ca.cert.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/privkey.pem 
  2048

# Create a request from your Device, which your Root CA will sign
openssl req -new 
  -key all/privkey.pem 
  -out all/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/csr.pem 
  -CA all/my-private-root-ca.cert.pem 
  -CAkey all/my-private-root-ca.privkey.pem 
  -CAcreateserial 
  -out all/cert.pem 
  -days 500

# Put things in their proper place
rsync -a all/{privkey,cert}.pem server/
cat all/cert.pem > server/fullchain.pem         # we have no intermediates in this case
rsync -a all/my-private-root-ca.cert.pem server/
rsync -a all/my-private-root-ca.cert.pem client/

# create DER format crt for iOS Mobile Safari, etc
openssl x509 -outform der -in all/my-private-root-ca.cert.pem -out client/my-private-root-ca.crt

For example:

bash make-certs.sh 'localhost.greenlock.domains'

Hopefully this puts the nail in the coffin on this one.

And some more explanation: https://github.com/coolaj86/node-ssl-root-cas/wiki/Painless-Self-Signed-Certificates-in-node.js

Install private cert on iOS Mobile Safari

You need to create a copy of the root ca certificate a DER format with a .crt extension:

# create DER format crt for iOS Mobile Safari, etc
openssl x509 -outform der -in all/my-private-root-ca.cert.pem -out client/my-private-root-ca.crt

Then you can simply serve that file with your webserver. When you click the link you should be asked if you want to install the certificate.

For an example of how this works you can try installing MIT's Certificate Authority: https://ca.mit.edu/mitca.crt

Related Examples


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...