1 // Copyright 2016 Joyent, Inc.
10 var assert = require('assert-plus');
11 var asn1 = require('asn1');
12 var algs = require('../algs');
13 var utils = require('../utils');
14 var Key = require('../key');
15 var PrivateKey = require('../private-key');
16 var pem = require('./pem');
17 var Identity = require('../identity');
18 var Signature = require('../signature');
19 var Certificate = require('../certificate');
20 var pkcs8 = require('./pkcs8');
23 * This file is based on RFC5280 (X.509).
26 /* Helper to read in a single mpint */
27 function readMPInt(der, nm) {
28 assert.strictEqual(der.peek(), asn1.Ber.Integer,
29 nm + ' is not an Integer');
30 return (utils.mpNormalize(der.readString(asn1.Ber.Integer, true)));
33 function verify(cert, key) {
34 var sig = cert.signatures.x509;
35 assert.object(sig, 'x509 signature');
37 var algParts = sig.algo.split('-');
38 if (algParts[0] !== key.type)
42 if (blob === undefined) {
43 var der = new asn1.BerWriter();
44 writeTBSCert(cert, der);
48 var verifier = key.createVerify(algParts[1]);
50 return (verifier.verify(sig.signature));
54 return (asn1.Ber.Context | asn1.Ber.Constructor | i);
58 return (asn1.Ber.Context | i);
62 'rsa-md5': '1.2.840.113549.1.1.4',
63 'rsa-sha1': '1.2.840.113549.1.1.5',
64 'rsa-sha256': '1.2.840.113549.1.1.11',
65 'rsa-sha384': '1.2.840.113549.1.1.12',
66 'rsa-sha512': '1.2.840.113549.1.1.13',
67 'dsa-sha1': '1.2.840.10040.4.3',
68 'dsa-sha256': '2.16.840.1.101.3.4.3.2',
69 'ecdsa-sha1': '1.2.840.10045.4.1',
70 'ecdsa-sha256': '1.2.840.10045.4.3.2',
71 'ecdsa-sha384': '1.2.840.10045.4.3.3',
72 'ecdsa-sha512': '1.2.840.10045.4.3.4'
74 Object.keys(SIGN_ALGS).forEach(function (k) {
75 SIGN_ALGS[SIGN_ALGS[k]] = k;
77 SIGN_ALGS['1.3.14.3.2.3'] = 'rsa-md5';
78 SIGN_ALGS['1.3.14.3.2.29'] = 'rsa-sha1';
81 'issuerKeyId': '2.5.29.35',
82 'altName': '2.5.29.17'
85 function read(buf, options) {
86 if (typeof (buf) === 'string') {
87 buf = new Buffer(buf, 'binary');
89 assert.buffer(buf, 'buf');
91 var der = new asn1.BerReader(buf);
94 if (Math.abs(der.length - der.remain) > 1) {
95 throw (new Error('DER sequence does not contain whole byte ' +
99 var tbsStart = der.offset;
101 var sigOffset = der.offset + der.length;
102 var tbsEnd = sigOffset;
104 if (der.peek() === Local(0)) {
105 der.readSequence(Local(0));
106 var version = der.readInt();
107 assert.ok(version <= 3,
108 'only x.509 versions up to v3 supported');
112 cert.signatures = {};
113 var sig = (cert.signatures.x509 = {});
116 cert.serial = readMPInt(der, 'serial');
119 var after = der.offset + der.length;
120 var certAlgOid = der.readOID();
121 var certAlg = SIGN_ALGS[certAlgOid];
122 if (certAlg === undefined)
123 throw (new Error('unknown signature algorithm ' + certAlgOid));
126 cert.issuer = Identity.parseAsn1(der);
129 cert.validFrom = readDate(der);
130 cert.validUntil = readDate(der);
132 cert.subjects = [Identity.parseAsn1(der)];
135 after = der.offset + der.length;
136 cert.subjectKey = pkcs8.readPkcs8(undefined, 'public', der);
140 if (der.peek() === Local(1)) {
141 der.readSequence(Local(1));
142 sig.extras.issuerUniqueID =
143 buf.slice(der.offset, der.offset + der.length);
144 der._offset += der.length;
147 /* subjectUniqueID */
148 if (der.peek() === Local(2)) {
149 der.readSequence(Local(2));
150 sig.extras.subjectUniqueID =
151 buf.slice(der.offset, der.offset + der.length);
152 der._offset += der.length;
156 if (der.peek() === Local(3)) {
157 der.readSequence(Local(3));
158 var extEnd = der.offset + der.length;
161 while (der.offset < extEnd)
162 readExtension(cert, buf, der);
164 assert.strictEqual(der.offset, extEnd);
167 assert.strictEqual(der.offset, sigOffset);
170 after = der.offset + der.length;
171 var sigAlgOid = der.readOID();
172 var sigAlg = SIGN_ALGS[sigAlgOid];
173 if (sigAlg === undefined)
174 throw (new Error('unknown signature algorithm ' + sigAlgOid));
177 var sigData = der.readString(asn1.Ber.BitString, true);
178 if (sigData[0] === 0)
179 sigData = sigData.slice(1);
180 var algParts = sigAlg.split('-');
182 sig.signature = Signature.parse(sigData, algParts[0], 'asn1');
183 sig.signature.hashAlgorithm = algParts[1];
185 sig.cache = buf.slice(tbsStart, tbsEnd);
187 return (new Certificate(cert));
190 function readDate(der) {
191 if (der.peek() === asn1.Ber.UTCTime) {
192 return (utcTimeToDate(der.readString(asn1.Ber.UTCTime)));
193 } else if (der.peek() === asn1.Ber.GeneralizedTime) {
194 return (gTimeToDate(der.readString(asn1.Ber.GeneralizedTime)));
196 throw (new Error('Unsupported date format'));
200 /* RFC5280, section 4.2.1.6 (GeneralName type) */
203 RFC822Name: Context(1),
205 X400Address: Local(3),
206 DirectoryName: Local(4),
207 EDIPartyName: Local(5),
209 IPAddress: Context(7),
213 function readExtension(cert, buf, der) {
215 var after = der.offset + der.length;
216 var extId = der.readOID();
218 var sig = cert.signatures.x509;
219 sig.extras.exts = [];
222 if (der.peek() === asn1.Ber.Boolean)
223 critical = der.readBoolean();
227 der.readSequence(asn1.Ber.OctetString);
229 var aeEnd = der.offset + der.length;
230 while (der.offset < aeEnd) {
231 switch (der.peek()) {
232 case ALTNAME.OtherName:
233 case ALTNAME.EDIPartyName:
235 der._offset += der.length;
238 der.readOID(ALTNAME.OID);
240 case ALTNAME.RFC822Name:
241 /* RFC822 specifies email addresses */
242 var email = der.readString(ALTNAME.RFC822Name);
243 id = Identity.forEmail(email);
244 if (!cert.subjects[0].equals(id))
245 cert.subjects.push(id);
247 case ALTNAME.DirectoryName:
248 der.readSequence(ALTNAME.DirectoryName);
249 id = Identity.parseAsn1(der);
250 if (!cert.subjects[0].equals(id))
251 cert.subjects.push(id);
253 case ALTNAME.DNSName:
254 var host = der.readString(
256 id = Identity.forHost(host);
257 if (!cert.subjects[0].equals(id))
258 cert.subjects.push(id);
261 der.readString(der.peek());
265 sig.extras.exts.push({ oid: extId, critical: critical });
268 sig.extras.exts.push({
271 data: der.readString(asn1.Ber.OctetString, true)
280 /^([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/;
281 function utcTimeToDate(t) {
282 var m = t.match(UTCTIME_RE);
283 assert.ok(m, 'timestamps must be in UTC');
286 var thisYear = d.getUTCFullYear();
287 var century = Math.floor(thisYear / 100) * 100;
289 var year = parseInt(m[1], 10);
290 if (thisYear % 100 < 50 && year >= 60)
291 year += (century - 1);
294 d.setUTCFullYear(year, parseInt(m[2], 10) - 1, parseInt(m[3], 10));
295 d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10));
296 if (m[6] && m[6].length > 0)
297 d.setUTCSeconds(parseInt(m[6], 10));
302 /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/;
303 function gTimeToDate(t) {
304 var m = t.match(GTIME_RE);
308 d.setUTCFullYear(parseInt(m[1], 10), parseInt(m[2], 10) - 1,
310 d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10));
311 if (m[6] && m[6].length > 0)
312 d.setUTCSeconds(parseInt(m[6], 10));
316 function zeroPad(n) {
323 function dateToUTCTime(d) {
325 s += zeroPad(d.getUTCFullYear() % 100);
326 s += zeroPad(d.getUTCMonth() + 1);
327 s += zeroPad(d.getUTCDate());
328 s += zeroPad(d.getUTCHours());
329 s += zeroPad(d.getUTCMinutes());
330 s += zeroPad(d.getUTCSeconds());
335 function sign(cert, key) {
336 if (cert.signatures.x509 === undefined)
337 cert.signatures.x509 = {};
338 var sig = cert.signatures.x509;
340 sig.algo = key.type + '-' + key.defaultHashAlgorithm();
341 if (SIGN_ALGS[sig.algo] === undefined)
344 var der = new asn1.BerWriter();
345 writeTBSCert(cert, der);
346 var blob = der.buffer;
349 var signer = key.createSign();
351 cert.signatures.x509.signature = signer.sign();
356 function write(cert, options) {
357 var sig = cert.signatures.x509;
358 assert.object(sig, 'x509 signature');
360 var der = new asn1.BerWriter();
363 der._ensure(sig.cache.length);
364 sig.cache.copy(der._buf, der._offset);
365 der._offset += sig.cache.length;
367 writeTBSCert(cert, der);
371 der.writeOID(SIGN_ALGS[sig.algo]);
372 if (sig.algo.match(/^rsa-/))
376 var sigData = sig.signature.toBuffer('asn1');
377 var data = new Buffer(sigData.length + 1);
379 sigData.copy(data, 1);
380 der.writeBuffer(data, asn1.Ber.BitString);
386 function writeTBSCert(cert, der) {
387 var sig = cert.signatures.x509;
388 assert.object(sig, 'x509 signature');
392 der.startSequence(Local(0));
396 der.writeBuffer(utils.mpNormalize(cert.serial), asn1.Ber.Integer);
399 der.writeOID(SIGN_ALGS[sig.algo]);
402 cert.issuer.toAsn1(der);
405 der.writeString(dateToUTCTime(cert.validFrom), asn1.Ber.UTCTime);
406 der.writeString(dateToUTCTime(cert.validUntil), asn1.Ber.UTCTime);
409 var subject = cert.subjects[0];
410 var altNames = cert.subjects.slice(1);
413 pkcs8.writePkcs8(der, cert.subjectKey);
415 if (sig.extras && sig.extras.issuerUniqueID) {
416 der.writeBuffer(sig.extras.issuerUniqueID, Local(1));
419 if (sig.extras && sig.extras.subjectUniqueID) {
420 der.writeBuffer(sig.extras.subjectUniqueID, Local(2));
423 if (altNames.length > 0 || subject.type === 'host' ||
424 (sig.extras && sig.extras.exts)) {
425 der.startSequence(Local(3));
429 { oid: EXTS.altName }
431 if (sig.extras && sig.extras.exts)
432 exts = sig.extras.exts;
434 for (var i = 0; i < exts.length; ++i) {
436 der.writeOID(exts[i].oid);
438 if (exts[i].critical !== undefined)
439 der.writeBoolean(exts[i].critical);
441 if (exts[i].oid === EXTS.altName) {
442 der.startSequence(asn1.Ber.OctetString);
444 if (subject.type === 'host') {
445 der.writeString(subject.hostname,
448 for (var j = 0; j < altNames.length; ++j) {
449 if (altNames[j].type === 'host') {
451 altNames[j].hostname,
453 } else if (altNames[j].type ===
460 * Encode anything else as a
461 * DN style name for now.
464 ALTNAME.DirectoryName);
465 altNames[j].toAsn1(der);
472 der.writeBuffer(exts[i].data,
473 asn1.Ber.OctetString);