Skip to content

Commit

Permalink
#25 want support for extKeyUsage in X.509
Browse files Browse the repository at this point in the history
Reviewed by: Robert Mustacchi <rm@joyent.com>
  • Loading branch information
arekinath committed Mar 2, 2017
1 parent 3ae5356 commit 3bd4c38
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 12 deletions.
27 changes: 23 additions & 4 deletions README.md
Expand Up @@ -408,10 +408,10 @@ and the OpenSSH certificate format. This feature is intended to be used mainly
to access basic metadata about certificates, extract public keys from them, and
also to generate simple self-signed certificates from an existing key.

Notably, there is no implementation of CA chain-of-trust verification, and no
support for key usage restrictions (or other kinds of restrictions). Please do
the security world a favour, and DO NOT use this code for certificate
verification in the traditional X.509 CA chain style.
Notably, there is no implementation of CA chain-of-trust verification, and only
very minimal support for key usage restrictions. Please do the security world
a favour, and DO NOT use this code for certificate verification in the
traditional X.509 CA chain style.

### `parseCertificate(data, format)`

Expand All @@ -436,6 +436,7 @@ Parameters
certificate validity period. If given
`lifetime` will be ignored
- `serial` -- optional Buffer, the serial number of the certificate
- `purposes` -- optional Array of String, X.509 key usage restrictions

### `createCertificate(subject, key, issuer, issuerKey[, options])`

Expand All @@ -452,6 +453,7 @@ Parameters
certificate validity period. If given
`lifetime` will be ignored
- `serial` -- optional Buffer, the serial number of the certificate
- `purposes` -- optional Array of String, X.509 key usage restrictions

### `Certificate#subjects`

Expand All @@ -475,6 +477,23 @@ May be `undefined` if the issuer's key is unknown (e.g. on an X509 certificate).
The serial number of the certificate. As this is normally a 64-bit or wider
integer, it is returned as a Buffer.

### `Certificate#purposes`

Array of Strings indicating the X.509 key usage purposes that this certificate
is valid for. The possible strings at the moment are:

* `'signature'` -- key can be used for digital signatures
* `'identity'` -- key can be used to attest about the identity of the signer
(X.509 calls this `nonRepudiation`)
* `'codeSigning'` -- key can be used to sign executable code
* `'keyEncryption'` -- key can be used to encrypt other keys
* `'encryption'` -- key can be used to encrypt data (only applies for RSA)
* `'keyAgreement'` -- key can be used for key exchange protocols such as
Diffie-Hellman
* `'ca'` -- key can be used to sign other certificates (is a Certificate
Authority)
* `'crl'` -- key can be used to sign Certificate Revocation Lists (CRLs)

### `Certificate#isExpired([when])`

Tests whether the Certificate is currently expired (i.e. the `validFrom` and
Expand Down
90 changes: 88 additions & 2 deletions lib/certificate.js
Expand Up @@ -39,6 +39,8 @@ function Certificate(opts) {
assert.date(opts.validFrom, 'options.validFrom');
assert.date(opts.validUntil, 'optons.validUntil');

assert.optionalArrayOfString(opts.purposes, 'options.purposes');

this._hashCache = {};

this.subjects = opts.subjects;
Expand All @@ -49,6 +51,7 @@ function Certificate(opts) {
this.serial = opts.serial;
this.validFrom = opts.validFrom;
this.validUntil = opts.validUntil;
this.purposes = opts.purposes;
}

Certificate.formats = formats;
Expand Down Expand Up @@ -108,6 +111,10 @@ Certificate.prototype.isSignedBy = function (issuerCert) {

if (!this.issuer.equals(issuerCert.subjects[0]))
return (false);
if (this.issuer.purposes && this.issuer.purposes.length > 0 &&
this.issuer.purposes.indexOf('ca') === -1) {
return (false);
}

return (this.isSignedByKey(issuerCert.subjectKey));
};
Expand Down Expand Up @@ -180,6 +187,47 @@ Certificate.createSelfSigned = function (subjectOrSubjects, key, options) {
if (serial === undefined)
serial = new Buffer('0000000000000001', 'hex');

var purposes = options.purposes;
if (purposes === undefined)
purposes = [];

if (purposes.indexOf('signature') === -1)
purposes.push('signature');

/* Self-signed certs are always CAs. */
if (purposes.indexOf('ca') === -1)
purposes.push('ca');
if (purposes.indexOf('crl') === -1)
purposes.push('crl');

/*
* If we weren't explicitly given any other purposes, do the sensible
* thing and add some basic ones depending on the subject type.
*/
if (purposes.length <= 3) {
var hostSubjects = subjects.filter(function (subject) {
return (subject.type === 'host');
});
var userSubjects = subjects.filter(function (subject) {
return (subject.type === 'user');
});
if (hostSubjects.length > 0) {
if (purposes.indexOf('serverAuth') === -1)
purposes.push('serverAuth');
}
if (userSubjects.length > 0) {
if (purposes.indexOf('clientAuth') === -1)
purposes.push('clientAuth');
}
if (userSubjects.length > 0 || hostSubjects.length > 0) {
if (purposes.indexOf('keyAgreement') === -1)
purposes.push('keyAgreement');
if (key.type === 'rsa' &&
purposes.indexOf('encryption') === -1)
purposes.push('encryption');
}
}

var cert = new Certificate({
subjects: subjects,
issuer: subjects[0],
Expand All @@ -188,7 +236,8 @@ Certificate.createSelfSigned = function (subjectOrSubjects, key, options) {
signatures: {},
serial: serial,
validFrom: validFrom,
validUntil: validUntil
validUntil: validUntil,
purposes: purposes
});
cert.signWith(key);

Expand Down Expand Up @@ -236,6 +285,42 @@ Certificate.create =
if (serial === undefined)
serial = new Buffer('0000000000000001', 'hex');

var purposes = options.purposes;
if (purposes === undefined)
purposes = [];

if (purposes.indexOf('signature') === -1)
purposes.push('signature');

if (options.ca === true) {
if (purposes.indexOf('ca') === -1)
purposes.push('ca');
if (purposes.indexOf('crl') === -1)
purposes.push('crl');
}

var hostSubjects = subjects.filter(function (subject) {
return (subject.type === 'host');
});
var userSubjects = subjects.filter(function (subject) {
return (subject.type === 'user');
});
if (hostSubjects.length > 0) {
if (purposes.indexOf('serverAuth') === -1)
purposes.push('serverAuth');
}
if (userSubjects.length > 0) {
if (purposes.indexOf('clientAuth') === -1)
purposes.push('clientAuth');
}
if (userSubjects.length > 0 || hostSubjects.length > 0) {
if (purposes.indexOf('keyAgreement') === -1)
purposes.push('keyAgreement');
if (key.type === 'rsa' &&
purposes.indexOf('encryption') === -1)
purposes.push('encryption');
}

var cert = new Certificate({
subjects: subjects,
issuer: issuer,
Expand All @@ -244,7 +329,8 @@ Certificate.create =
signatures: {},
serial: serial,
validFrom: validFrom,
validUntil: validUntil
validUntil: validUntil,
purposes: purposes
});
cert.signWith(issuerKey);

Expand Down

0 comments on commit 3bd4c38

Please sign in to comment.