Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doc and test cert apis #10389

Merged
merged 6 commits into from
Jan 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -583,13 +583,16 @@ For Example: `{ type: 'ECDH', name: 'prime256v1', size: 256 }`
added: v0.11.4
-->

* `detailed` {boolean} Specify `true` to request that the full certificate
chain with the `issuer` property be returned; `false` to return only the
top certificate without the `issuer` property.
* `detailed` {boolean} Include the full certificate chain if `true`, otherwise
include just the peer's certificate.

Returns an object representing the peer's certificate. The returned object has
some properties corresponding to the fields of the certificate.

If the full certificate chain was requested, each certificate will include a
`issuerCertificate` property containing an object representing its issuer's
certificate.

For example:

```text
Expand All @@ -600,24 +603,23 @@ For example:
O: 'node.js',
OU: 'Test TLS Certificate',
CN: 'localhost' },
issuerInfo:
issuer:
{ C: 'UK',
ST: 'Acknack Ltd',
L: 'Rhys Jones',
O: 'node.js',
OU: 'Test TLS Certificate',
CN: 'localhost' },
issuer:
{ ... another certificate ... },
issuerCertificate:
{ ... another certificate, possibly with a .issuerCertificate ... },
raw: < RAW DER buffer >,
valid_from: 'Nov 11 09:52:22 2009 GMT',
valid_to: 'Nov 6 09:52:22 2029 GMT',
fingerprint: '2A:7A:C2:DD:E5:F9:CC:53:72:35:99:7A:02:5A:71:38:52:EC:8A:DF',
serialNumber: 'B9B0D332A1AA5635' }
```

If the peer does not provide a certificate, `null` or an empty object will be
returned.
If the peer does not provide a certificate, an empty object will be returned.

### tlsSocket.getProtocol()
<!-- YAML
Expand Down Expand Up @@ -907,10 +909,21 @@ added: v0.11.13
the same order as their private keys in `key`. If the intermediate
certificates are not provided, the peer will not be able to validate the
certificate, and the handshake will fail.
* `ca`{string|string[]|Buffer|Buffer[]} Optional CA certificates to trust.
Default is the well-known CAs from Mozilla. When connecting to peers that
use certificates issued privately, or self-signed, the private root CA or
self-signed certificate must be provided to verify the peer.
* `ca` {string|string[]|Buffer|Buffer[]} Optionally override the trusted CA
certificates. Default is to trust the well-known CAs curated by Mozilla.
Mozilla's CAs are completely replaced when CAs are explicitly specified
using this option. The value can be a string or Buffer, or an Array of
strings and/or Buffers. Any string or Buffer can contain multiple PEM CAs
concatenated together. The peer's certificate must be chainable to a CA
trusted by the server for the connection to be authenticated. When using
certificates that are not chainable to a well-known CA, the certificate's CA
must be explicitly specified as a trusted or the connection will fail to
authenticate.
If the peer uses a certificate that doesn't match or chain to one of the
default CAs, use the `ca` option to provide a CA certificate that the peer's
certificate can match or chain to.
For self-signed certificates, the certificate is its own CA, and must be
provided.
* `crl` {string|string[]|Buffer|Buffer[]} Optional PEM formatted
CRLs (Certificate Revocation Lists).
* `ciphers` {string} Optional cipher suite specification, replacing the
Expand Down
101 changes: 101 additions & 0 deletions test/fixtures/tls-connect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// One shot call to connect a TLS client and server based on options to
// tls.createServer() and tls.connect(), so assertions can be made on both ends
// of the connection.
'use strict';

const common = require('../common');
const fs = require('fs');
const join = require('path').join;
const tls = require('tls');
const util = require('util');

module.exports = exports = checkCrypto;

function checkCrypto() {
if (!common.hasCrypto) {
common.skip('missing crypto');
process.exit(0);
}
return exports;
}

exports.assert = require('assert');
exports.debug = util.debuglog('test');
exports.tls = tls;

// Pre-load keys from common fixtures for ease of use by tests.
const keys = exports.keys = {
agent1: load('agent1', 'ca1'),
agent2: load('agent2', 'agent2'),
agent3: load('agent3', 'ca2'),
agent4: load('agent4', 'ca2'),
agent5: load('agent5', 'ca2'),
agent6: load('agent6', 'ca1'),
agent7: load('agent7', 'fake-cnnic-root'),
ec: load('ec', 'ec'),
};

function load(cert, issuer) {
issuer = issuer || cert; // Assume self-signed if no issuer
const id = {
key: read(cert + '-key.pem'),
cert: read(cert + '-cert.pem'),
ca: read(issuer + '-cert.pem'),
};
return id;
}

function read(file) {
return fs.readFileSync(join(common.fixturesDir, 'keys', file), 'binary');
}

exports.connect = function connect(options, callback) {
callback = common.mustCall(callback);

const server = {};
const client = {};
const pair = {
server: server,
client: client,
};

tls.createServer(options.server, function(conn) {
server.conn = conn;
conn.pipe(conn);
maybeCallback()
}).listen(0, function() {
server.server = this;

const optClient = util._extend({
port: this.address().port,
}, options.client);

tls.connect(optClient)
.on('secureConnect', function() {
client.conn = this;
maybeCallback();
})
.on('error', function(err) {
client.err = err;
client.conn = this;
maybeCallback();
});
});

function maybeCallback() {
if (!callback)
return;
if (server.conn && (client.conn || client.err)) {
const err = pair.client.err || pair.server.err;
callback(err, pair, cleanup);
callback = null;

function cleanup() {
if (server.server)
server.server.close();
if (client.conn)
client.conn.end();
}
}
}
}
55 changes: 55 additions & 0 deletions test/internet/test-tls-add-ca-cert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';
const common = require('../common');

if (!common.hasCrypto) {
common.skip('missing crypto');
return;
}

// Test interaction of compiled-in CAs with user-provided CAs.

const assert = require('assert');
const fs = require('fs');
const tls = require('tls');

function filenamePEM(n) {
return require('path').join(common.fixturesDir, 'keys', n + '.pem');
}

function loadPEM(n) {
return fs.readFileSync(filenamePEM(n));
}

const caCert = loadPEM('ca1-cert');

var opts = {
host: 'www.nodejs.org',
port: 443,
rejectUnauthorized: true
};

// Success relies on the compiled in well-known root CAs
tls.connect(opts, common.mustCall(end));

// The .ca option replaces the well-known roots, so connection fails.
opts.ca = caCert;
tls.connect(opts, fail).on('error', common.mustCall((err) => {
assert.strictEqual(err.message, 'unable to get local issuer certificate');
}));

function fail() {
assert(false, 'should fail to connect');
}

// New secure contexts have the well-known root CAs.
opts.secureContext = tls.createSecureContext();
tls.connect(opts, common.mustCall(end));

// Explicit calls to addCACert() add to the default well-known roots, instead
// of replacing, so connection still succeeds.
opts.secureContext.context.addCACert(caCert);
tls.connect(opts, common.mustCall(end));

function end() {
this.end();
}
16 changes: 11 additions & 5 deletions test/parallel/test-process-getgroups.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const exec = require('child_process').exec;

// Check `id -G` and `process.getgroups()` return same groups.

if (common.isOSX) {
common.skip('Output of `id -G` is unreliable on Darwin.');
return;
}
const assert = require('assert');
const exec = require('child_process').exec;

if (typeof process.getgroups === 'function') {
const groups = process.getgroups();
const groups = unique(process.getgroups());
assert(Array.isArray(groups));
assert(groups.length > 0);
exec('id -G', function(err, stdout) {
assert.ifError(err);
const real_groups = stdout.match(/\d+/g).map(Number);
assert.strictEqual(groups.length, real_groups.length);
const real_groups = unique(stdout.match(/\d+/g).map(Number));
assert.deepStrictEqual(groups, real_groups);
check(groups, real_groups);
check(real_groups, groups);
});
Expand All @@ -24,3 +26,7 @@ if (typeof process.getgroups === 'function') {
function check(a, b) {
for (let i = 0; i < a.length; ++i) assert.notStrictEqual(b.indexOf(a[i]), -1);
}

function unique(groups) {
return [...new Set(groups)].sort();
}
78 changes: 33 additions & 45 deletions test/parallel/test-tls-addca.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,50 @@
'use strict';
const common = require('../common');
const fs = require('fs');

if (!common.hasCrypto) {
common.skip('missing crypto');
return;
}
const tls = require('tls');

function filenamePEM(n) {
return require('path').join(common.fixturesDir, 'keys', n + '.pem');
}
// Adding a CA certificate to contextWithCert should not also add it to
// contextWithoutCert. This is tested by trying to connect to a server that
// depends on that CA using contextWithoutCert.

function loadPEM(n) {
return fs.readFileSync(filenamePEM(n));
}
const join = require('path').join;
const {
assert, connect, keys, tls
} = require(join(common.fixturesDir, 'tls-connect'))();

const caCert = loadPEM('ca1-cert');
const contextWithoutCert = tls.createSecureContext({});
const contextWithCert = tls.createSecureContext({});
// Adding a CA certificate to contextWithCert should not also add it to
// contextWithoutCert. This is tested by trying to connect to a server that
// depends on that CA using contextWithoutCert.
contextWithCert.context.addCACert(caCert);
contextWithCert.context.addCACert(keys.agent1.ca);

const serverOptions = {
key: loadPEM('agent1-key'),
cert: loadPEM('agent1-cert'),
key: keys.agent1.key,
cert: keys.agent1.cert,
};
const server = tls.createServer(serverOptions, function() {});

const clientOptions = {
port: undefined,
ca: [caCert],
ca: [keys.agent1.ca],
servername: 'agent1',
rejectUnauthorized: true,
};

function startTest() {
// This client should fail to connect because it doesn't trust the CA
// This client should fail to connect because it doesn't trust the CA
// certificate.
clientOptions.secureContext = contextWithoutCert;

connect({
client: clientOptions,
server: serverOptions,
}, function(err, pair, cleanup) {
assert(err);
assert.strictEqual(err.message, 'unable to verify the first certificate');
cleanup();

// This time it should connect because contextWithCert includes the needed CA
// certificate.
clientOptions.secureContext = contextWithoutCert;
clientOptions.port = server.address().port;
const client = tls.connect(clientOptions, common.fail);
client.on('error', common.mustCall(() => {
client.destroy();

// This time it should connect because contextWithCert includes the needed
// CA certificate.
clientOptions.secureContext = contextWithCert;
const client2 = tls.connect(clientOptions, common.mustCall(() => {
client2.destroy();
server.close();
}));
client2.on('error', (e) => {
console.log(e);
});
}));
}

server.listen(0, startTest);
clientOptions.secureContext = contextWithCert;
connect({
client: clientOptions,
server: serverOptions,
}, function(err, pair, cleanup) {
assert.ifError(err);
cleanup();
});
});
24 changes: 24 additions & 0 deletions test/parallel/test-tls-ca-concat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';
const common = require('../common');

// Check ca option can contain concatenated certs by prepending an unrelated
// non-CA cert and showing that agent6's CA root is still found.

const join = require('path').join;
const {
assert, connect, keys
} = require(join(common.fixturesDir, 'tls-connect'))();

connect({
client: {
checkServerIdentity: (servername, cert) => { },
ca: keys.agent1.cert + '\n' + keys.agent6.ca,
},
server: {
cert: keys.agent6.cert,
key: keys.agent6.key,
},
}, function(err, pair, cleanup) {
assert.ifError(err);
return cleanup();
});
Loading