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

Add CertificationSigningRequest Handling #23

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
186 changes: 186 additions & 0 deletions lib/certificate-signing-request.js
@@ -0,0 +1,186 @@
// Copyright 2016 Joyent, Inc.

module.exports = CertificateSigningRequest;

var assert = require('assert-plus');
var algs = require('./algs');
var crypto = require('crypto');
var Fingerprint = require('./fingerprint');
var Signature = require('./signature');
var errs = require('./errors');
var util = require('util');
var utils = require('./utils');
var Key = require('./key');
var PrivateKey = require('./private-key');
var Identity = require('./identity');

var formats = {};
formats['der'] = require('./formats/csr');
formats['pem'] = require('./formats/csr-pem');

var CertificateParseError = errs.CertificateParseError;
var InvalidAlgorithmError = errs.InvalidAlgorithmError;

function CertificateSigningRequest(opts) {
// console.log(opts)
assert.object(opts, 'options');
assert.arrayOfObject(opts.subjects, 'options.subjects');
utils.assertCompatible(opts.subjects[0], Identity, [1, 0],
'options.subjects');
utils.assertCompatible(opts.subjectKey, Key, [1, 0],
'options.subjectKey');

assert.object(opts.signatures, 'options.signatures');

if (opts.extentions) {
utils.assertCompatible(opts.extentions[0], Identity, [1, 0],
'options.extentions');
}

this._hashCache = {};

this.type = "certificate-signing-request"

this.version = opts.version || 0;

this.subjects = opts.subjects;
this.subjectKey = opts.subjectKey;
this.signatures = opts.signatures;
this.extentions = opts.extentions;
}

CertificateSigningRequest.formats = formats;

CertificateSigningRequest.prototype.toBuffer = function (format, options) {
if (format === undefined)
format = 'pem';
assert.string(format, 'format');
assert.object(formats[format], 'formats[format]');
assert.optionalObject(options, 'options');

return (formats[format].write(this, options));
};

CertificateSigningRequest.prototype.toString = function (format, options) {
if (format === undefined)
format = 'pem';
return (this.toBuffer(format, options).toString());
};

CertificateSigningRequest.prototype.fingerprint = function (algo) {
if (algo === undefined)
algo = 'sha256';
assert.string(algo, 'algorithm');
var opts = {
type: 'certificate-signing-request',
hash: this.hash(algo),
algorithm: algo
};
return (new Fingerprint(opts));
};

CertificateSigningRequest.prototype.hash = function (algo) {
assert.string(algo, 'algorithm');
algo = algo.toLowerCase();
if (algs.hashAlgs[algo] === undefined)
throw (new InvalidAlgorithmError(algo));

if (this._hashCache[algo])
return (this._hashCache[algo]);

var hash = crypto.createHash(algo).
update(this.toBuffer('der')).digest();
this._hashCache[algo] = hash;
return (hash);
};

CertificateSigningRequest.create = function (subjectOrSubjects, key, options) {
var subjects;
if (Array.isArray(subjectOrSubjects))
subjects = subjectOrSubjects;
else
subjects = [subjectOrSubjects];

assert.arrayOfObject(subjects);
subjects.forEach(function (subject) {
utils.assertCompatible(subject, Identity, [1, 0], 'subject');
});

utils.assertCompatible(key, Key, [1, 0], 'key');
if (PrivateKey.isPrivateKey(key))
key = key.toPublic();
utils.assertCompatible(issuer, Identity, [1, 0], 'issuer');
utils.assertCompatible(issuerKey, PrivateKey, [1, 2], 'issuer key');

assert.optionalObject(options, 'options');
if (options === undefined)
options = {};
assert.optionalObject(options.validFrom, 'options.validFrom');
assert.optionalObject(options.validUntil, 'options.validUntil');
var validFrom = options.validFrom;
var validUntil = options.validUntil;
if (validFrom === undefined)
validFrom = new Date();
if (validUntil === undefined) {
assert.optionalNumber(options.lifetime, 'options.lifetime');
var lifetime = options.lifetime;
if (lifetime === undefined)
lifetime = 10*365*24*3600;
validUntil = new Date();
validUntil.setTime(validUntil.getTime() + lifetime*1000);
}
assert.optionalBuffer(options.serial, 'options.serial');
var serial = options.serial;
if (serial === undefined)
serial = new Buffer('0000000000000001', 'hex');

var cert = new Certificate({
subjects: subjects,
issuer: issuer,
subjectKey: key,
issuerKey: issuerKey.toPublic(),
signatures: {},
serial: serial,
validFrom: validFrom,
validUntil: validUntil
});
cert.signWith(issuerKey);

return (cert);
};

CertificateSigningRequest.parse = function (data, format, options) {
if (typeof (data) !== 'string')
assert.buffer(data, 'data');
if (format === undefined)
format = 'auto';
assert.string(format, 'format');
if (typeof (options) === 'string')
options = { filename: options };
assert.optionalObject(options, 'options');
if (options === undefined)
options = {};
assert.optionalString(options.filename, 'options.filename');
if (options.filename === undefined)
options.filename = '(unnamed)';

assert.object(formats[format], 'formats[format]');

try {
var k = formats[format].read(data, options);
return (k);
} catch (e) {
console.log(e)
throw (new CertificateParseError(options.filename, format, e));
}
};

/*
* API versions for CertificateSigningRequest:
* [1,0] -- initial ver
*/
CertificateSigningRequest.prototype._sshpkApiVersion = [1, 0];

CertificateSigningRequest._oldVersionDetect = function (obj) {
return ([1, 0]);
};
8 changes: 7 additions & 1 deletion lib/fingerprint.js
Expand Up @@ -8,6 +8,7 @@ var crypto = require('crypto');
var errs = require('./errors');
var Key = require('./key');
var Certificate = require('./certificate');
var CertificateSigningRequest = require('./certificate-signing-request');
var utils = require('./utils');

var FingerprintFormatError = errs.FingerprintFormatError;
Expand Down Expand Up @@ -51,9 +52,14 @@ Fingerprint.prototype.matches = function (other) {
assert.object(other, 'key or certificate');
if (this.type === 'key') {
utils.assertCompatible(other, Key, [1, 0], 'key');
} else {
} else if (this.type == "Certificate") {
utils.assertCompatible(other, Certificate, [1, 0],
'certificate');
} else if (this.type == "CertificateSigningRequest") {
utils.assertCompatible(other, CertificateSigningRequest, [1, 0],
'certificate-signing-request');
} else {
assert.string("", 'unknown type');
}

var theirHash = other.hash(this.algorithm);
Expand Down
75 changes: 75 additions & 0 deletions lib/formats/csr-pem.js
@@ -0,0 +1,75 @@
// Copyright 2016 Joyent, Inc.

var csr = require('./csr');

module.exports = {
read: read,
write: write
};

var assert = require('assert-plus');
// var asn1 = require('asn1');
// var algs = require('../algs');
// var utils = require('../utils');
// var Key = require('../key');
// var PrivateKey = require('../private-key');
// var pem = require('./pem');
// var Identity = require('../identity');
// var Signature = require('../signature');
// var Certificate = require('../certificate');

function read(buf, options) {
if (typeof (buf) !== 'string') {
assert.buffer(buf, 'buf');
buf = buf.toString('ascii');
}

var lines = buf.trim().split(/[\r\n]+/g);

var m = lines[0].match(/*JSSTYLED*/
/[-]+[ ]*BEGIN CERTIFICATE REQUEST[ ]*[-]+/);
assert.ok(m, 'invalid PEM header');

var m2 = lines[lines.length - 1].match(/*JSSTYLED*/
/[-]+[ ]*END CERTIFICATE REQUEST[ ]*[-]+/);
assert.ok(m2, 'invalid PEM footer');

var headers = {};
while (true) {
lines = lines.slice(1);
m = lines[0].match(/*JSSTYLED*/
/^([A-Za-z0-9-]+): (.+)$/);
if (!m)
break;
headers[m[1].toLowerCase()] = m[2];
}

/* Chop off the first and last lines */
lines = lines.slice(0, -1).join('');
buf = new Buffer(lines, 'base64');

return (csr.read(buf, options));
}

function write(cert, options) {
var dbuf = csr.write(cert, options);

var header = 'CERTIFICATE REQUEST';
var tmp = dbuf.toString('base64');
var len = tmp.length + (tmp.length / 64) +
18 + 16 + header.length*2 + 10;
var buf = new Buffer(len);
var o = 0;
o += buf.write('-----BEGIN ' + header + '-----\n', o);
for (var i = 0; i < tmp.length; ) {
var limit = i + 64;
if (limit > tmp.length)
limit = tmp.length;
o += buf.write(tmp.slice(i, limit), o);
buf[o++] = 10;
i = limit;
}
o += buf.write('-----END ' + header + '-----\n', o);

return (buf.slice(0, o));
}