Skip to content

Commit

Permalink
Merge pull request #60 from achingbrain/add-verify-signing-chain
Browse files Browse the repository at this point in the history
Adds method to verify a certificate's signing chain
  • Loading branch information
Dexus committed Aug 26, 2015
2 parents 5bc4716 + a34fda8 commit 3bf922b
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,18 @@ Where
* **options** is an optional options object with `cipher` and `clientKeyPassword` (ciphers:["aes128", "aes192", "aes256", "camellia128", "camellia192", "camellia256", "des", "des3", "idea"])
* **callback** is a callback function with an error object and `{pkcs12}` (binary)

## Verify a certificate signing chain

Use `verifySigningChain` to assert that a given certificate has a valid signing chain.

pem.verifySigningChain(certificate, ca, callback)

Where

* **certificate** is a PEM encoded certificate string
* **ca** is a PEM encoded CA certificate string or an array of certificate strings
* **callback** is a callback function with an error object and a boolean as arguments

### Setting openssl location

In some systems the `openssl` executable might not be available by the default name or it is not included in $PATH. In this case you can define the location of the executable yourself as a one time action after you have loaded the pem module:
Expand Down
32 changes: 32 additions & 0 deletions lib/pem.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module.exports.getFingerprint = getFingerprint;
module.exports.getModulus = getModulus;
module.exports.getModulusFromProtected = getModulusFromProtected;
module.exports.createPkcs12 = createPkcs12;
module.exports.verifySigningChain = verifySigningChain;
module.exports.config = config;

// PUBLIC API
Expand Down Expand Up @@ -564,6 +565,37 @@ function createPkcs12(key, certificate, password, options, callback) {
});
}

/**
* Verifies the signing chain of the passed certificate
*
* @param {String} PEM encoded certificate
* @param {Array} List of CA certificates
* @param {Function} callback Callback function with an error object and a boolean valid
*/
function verifySigningChain(certificate, ca, callback) {
if (!Array.isArray(ca)) {
ca = [ca];
}

var files = [
ca.join('\n'),
certificate
];

var params = ['verify',
'-CAfile',
'--TMPFILE--',
'--TMPFILE--'
];

spawnWrapper(params, files, function(err, code, stdout) {
if (err) {
return callback(err);
}

callback(null, stdout.trim().slice(-4) === ': OK');
});
}

// HELPER FUNCTIONS

Expand Down
138 changes: 138 additions & 0 deletions test/pem.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,5 +497,143 @@ exports['General Tests'] = {
});
});
});
},
'Verify sigining chain': function(test) {
pem.createCertificate({
commonName: 'CA Certificate'
}, function (error, ca) {
test.ifError(error);

pem.createCertificate({
serviceKey: ca.serviceKey,
serviceCertificate: ca.certificate,
serial: Date.now(),
}, function (error, cert) {
test.ifError(error);

pem.verifySigningChain(cert.certificate, ca.certificate, function (error, valid) {
test.ifError(error);
test.ok(valid === true);

test.done();
});
});
});
},
'Verify deep sigining chain': function(test) {
pem.createCertificate({
commonName: 'CA Certificate'
}, function (error, ca) {
test.ifError(error);

pem.createCertificate({
commonName: 'Intermediate CA Certificate',
serviceKey: ca.serviceKey,
serviceCertificate: ca.certificate,
serial: Date.now(),
}, function (error, intermediate) {
test.ifError(error);

pem.createCertificate({
serviceKey: intermediate.clientKey,
serviceCertificate: intermediate.certificate,
serial: Date.now(),
}, function (error, cert) {
test.ifError(error);

pem.verifySigningChain(cert.certificate, [ca.certificate, intermediate.certificate], function (error, valid) {
test.ifError(error);
test.ok(valid === true);

test.done();
});
});
});
});
},
'Fail to verify invalid sigining chain': function(test) {
pem.createCertificate({
commonName: 'CA Certificate'
}, function (error, ca) {
test.ifError(error);

pem.createCertificate({
serviceKey: ca.serviceKey,
serviceCertificate: ca.certificate,
serial: Date.now(),
}, function (error, cert) {
test.ifError(error);

pem.verifySigningChain(cert.certificate, cert.certificate, function (error, valid) {
test.ifError(error);
test.ok(valid === false);

test.done();
});
});
});
},
'Fail to verify deep sigining chain with missing CA certificate': function(test) {
pem.createCertificate({
commonName: 'CA Certificate'
}, function (error, ca) {
test.ifError(error);

pem.createCertificate({
commonName: 'Intermediate CA Certificate',
serviceKey: ca.serviceKey,
serviceCertificate: ca.certificate,
serial: Date.now(),
}, function (error, intermediate) {
test.ifError(error);

pem.createCertificate({
serviceKey: intermediate.clientKey,
serviceCertificate: intermediate.certificate,
serial: Date.now(),
}, function (error, cert) {
test.ifError(error);

pem.verifySigningChain(cert.certificate, [intermediate.certificate], function (error, valid) {
test.ifError(error);
test.ok(valid === false);

test.done();
});
});
});
});
},
'Fail to verify deep sigining chain with missing intermediate certificate': function(test) {
pem.createCertificate({
commonName: 'CA Certificate'
}, function (error, ca) {
test.ifError(error);

pem.createCertificate({
commonName: 'Intermediate CA Certificate',
serviceKey: ca.serviceKey,
serviceCertificate: ca.certificate,
serial: Date.now(),
}, function (error, intermediate) {
test.ifError(error);

pem.createCertificate({
serviceKey: intermediate.clientKey,
serviceCertificate: intermediate.certificate,
serial: Date.now(),
days: 1024
}, function (error, cert) {
test.ifError(error);

pem.verifySigningChain(cert.certificate, [ca.certificate], function (error, valid) {
test.ifError(error);
test.ok(valid === false);

test.done();
});
});
});
});
}
};

0 comments on commit 3bf922b

Please sign in to comment.