source: trip-planner-front/node_modules/sshpk/lib/certificate.js@ 188ee53

Last change on this file since 188ee53 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 11.3 KB
RevLine 
[6a3a178]1// Copyright 2016 Joyent, Inc.
2
3module.exports = Certificate;
4
5var assert = require('assert-plus');
6var Buffer = require('safer-buffer').Buffer;
7var algs = require('./algs');
8var crypto = require('crypto');
9var Fingerprint = require('./fingerprint');
10var Signature = require('./signature');
11var errs = require('./errors');
12var util = require('util');
13var utils = require('./utils');
14var Key = require('./key');
15var PrivateKey = require('./private-key');
16var Identity = require('./identity');
17
18var formats = {};
19formats['openssh'] = require('./formats/openssh-cert');
20formats['x509'] = require('./formats/x509');
21formats['pem'] = require('./formats/x509-pem');
22
23var CertificateParseError = errs.CertificateParseError;
24var InvalidAlgorithmError = errs.InvalidAlgorithmError;
25
26function Certificate(opts) {
27 assert.object(opts, 'options');
28 assert.arrayOfObject(opts.subjects, 'options.subjects');
29 utils.assertCompatible(opts.subjects[0], Identity, [1, 0],
30 'options.subjects');
31 utils.assertCompatible(opts.subjectKey, Key, [1, 0],
32 'options.subjectKey');
33 utils.assertCompatible(opts.issuer, Identity, [1, 0], 'options.issuer');
34 if (opts.issuerKey !== undefined) {
35 utils.assertCompatible(opts.issuerKey, Key, [1, 0],
36 'options.issuerKey');
37 }
38 assert.object(opts.signatures, 'options.signatures');
39 assert.buffer(opts.serial, 'options.serial');
40 assert.date(opts.validFrom, 'options.validFrom');
41 assert.date(opts.validUntil, 'optons.validUntil');
42
43 assert.optionalArrayOfString(opts.purposes, 'options.purposes');
44
45 this._hashCache = {};
46
47 this.subjects = opts.subjects;
48 this.issuer = opts.issuer;
49 this.subjectKey = opts.subjectKey;
50 this.issuerKey = opts.issuerKey;
51 this.signatures = opts.signatures;
52 this.serial = opts.serial;
53 this.validFrom = opts.validFrom;
54 this.validUntil = opts.validUntil;
55 this.purposes = opts.purposes;
56}
57
58Certificate.formats = formats;
59
60Certificate.prototype.toBuffer = function (format, options) {
61 if (format === undefined)
62 format = 'x509';
63 assert.string(format, 'format');
64 assert.object(formats[format], 'formats[format]');
65 assert.optionalObject(options, 'options');
66
67 return (formats[format].write(this, options));
68};
69
70Certificate.prototype.toString = function (format, options) {
71 if (format === undefined)
72 format = 'pem';
73 return (this.toBuffer(format, options).toString());
74};
75
76Certificate.prototype.fingerprint = function (algo) {
77 if (algo === undefined)
78 algo = 'sha256';
79 assert.string(algo, 'algorithm');
80 var opts = {
81 type: 'certificate',
82 hash: this.hash(algo),
83 algorithm: algo
84 };
85 return (new Fingerprint(opts));
86};
87
88Certificate.prototype.hash = function (algo) {
89 assert.string(algo, 'algorithm');
90 algo = algo.toLowerCase();
91 if (algs.hashAlgs[algo] === undefined)
92 throw (new InvalidAlgorithmError(algo));
93
94 if (this._hashCache[algo])
95 return (this._hashCache[algo]);
96
97 var hash = crypto.createHash(algo).
98 update(this.toBuffer('x509')).digest();
99 this._hashCache[algo] = hash;
100 return (hash);
101};
102
103Certificate.prototype.isExpired = function (when) {
104 if (when === undefined)
105 when = new Date();
106 return (!((when.getTime() >= this.validFrom.getTime()) &&
107 (when.getTime() < this.validUntil.getTime())));
108};
109
110Certificate.prototype.isSignedBy = function (issuerCert) {
111 utils.assertCompatible(issuerCert, Certificate, [1, 0], 'issuer');
112
113 if (!this.issuer.equals(issuerCert.subjects[0]))
114 return (false);
115 if (this.issuer.purposes && this.issuer.purposes.length > 0 &&
116 this.issuer.purposes.indexOf('ca') === -1) {
117 return (false);
118 }
119
120 return (this.isSignedByKey(issuerCert.subjectKey));
121};
122
123Certificate.prototype.getExtension = function (keyOrOid) {
124 assert.string(keyOrOid, 'keyOrOid');
125 var ext = this.getExtensions().filter(function (maybeExt) {
126 if (maybeExt.format === 'x509')
127 return (maybeExt.oid === keyOrOid);
128 if (maybeExt.format === 'openssh')
129 return (maybeExt.name === keyOrOid);
130 return (false);
131 })[0];
132 return (ext);
133};
134
135Certificate.prototype.getExtensions = function () {
136 var exts = [];
137 var x509 = this.signatures.x509;
138 if (x509 && x509.extras && x509.extras.exts) {
139 x509.extras.exts.forEach(function (ext) {
140 ext.format = 'x509';
141 exts.push(ext);
142 });
143 }
144 var openssh = this.signatures.openssh;
145 if (openssh && openssh.exts) {
146 openssh.exts.forEach(function (ext) {
147 ext.format = 'openssh';
148 exts.push(ext);
149 });
150 }
151 return (exts);
152};
153
154Certificate.prototype.isSignedByKey = function (issuerKey) {
155 utils.assertCompatible(issuerKey, Key, [1, 2], 'issuerKey');
156
157 if (this.issuerKey !== undefined) {
158 return (this.issuerKey.
159 fingerprint('sha512').matches(issuerKey));
160 }
161
162 var fmt = Object.keys(this.signatures)[0];
163 var valid = formats[fmt].verify(this, issuerKey);
164 if (valid)
165 this.issuerKey = issuerKey;
166 return (valid);
167};
168
169Certificate.prototype.signWith = function (key) {
170 utils.assertCompatible(key, PrivateKey, [1, 2], 'key');
171 var fmts = Object.keys(formats);
172 var didOne = false;
173 for (var i = 0; i < fmts.length; ++i) {
174 if (fmts[i] !== 'pem') {
175 var ret = formats[fmts[i]].sign(this, key);
176 if (ret === true)
177 didOne = true;
178 }
179 }
180 if (!didOne) {
181 throw (new Error('Failed to sign the certificate for any ' +
182 'available certificate formats'));
183 }
184};
185
186Certificate.createSelfSigned = function (subjectOrSubjects, key, options) {
187 var subjects;
188 if (Array.isArray(subjectOrSubjects))
189 subjects = subjectOrSubjects;
190 else
191 subjects = [subjectOrSubjects];
192
193 assert.arrayOfObject(subjects);
194 subjects.forEach(function (subject) {
195 utils.assertCompatible(subject, Identity, [1, 0], 'subject');
196 });
197
198 utils.assertCompatible(key, PrivateKey, [1, 2], 'private key');
199
200 assert.optionalObject(options, 'options');
201 if (options === undefined)
202 options = {};
203 assert.optionalObject(options.validFrom, 'options.validFrom');
204 assert.optionalObject(options.validUntil, 'options.validUntil');
205 var validFrom = options.validFrom;
206 var validUntil = options.validUntil;
207 if (validFrom === undefined)
208 validFrom = new Date();
209 if (validUntil === undefined) {
210 assert.optionalNumber(options.lifetime, 'options.lifetime');
211 var lifetime = options.lifetime;
212 if (lifetime === undefined)
213 lifetime = 10*365*24*3600;
214 validUntil = new Date();
215 validUntil.setTime(validUntil.getTime() + lifetime*1000);
216 }
217 assert.optionalBuffer(options.serial, 'options.serial');
218 var serial = options.serial;
219 if (serial === undefined)
220 serial = Buffer.from('0000000000000001', 'hex');
221
222 var purposes = options.purposes;
223 if (purposes === undefined)
224 purposes = [];
225
226 if (purposes.indexOf('signature') === -1)
227 purposes.push('signature');
228
229 /* Self-signed certs are always CAs. */
230 if (purposes.indexOf('ca') === -1)
231 purposes.push('ca');
232 if (purposes.indexOf('crl') === -1)
233 purposes.push('crl');
234
235 /*
236 * If we weren't explicitly given any other purposes, do the sensible
237 * thing and add some basic ones depending on the subject type.
238 */
239 if (purposes.length <= 3) {
240 var hostSubjects = subjects.filter(function (subject) {
241 return (subject.type === 'host');
242 });
243 var userSubjects = subjects.filter(function (subject) {
244 return (subject.type === 'user');
245 });
246 if (hostSubjects.length > 0) {
247 if (purposes.indexOf('serverAuth') === -1)
248 purposes.push('serverAuth');
249 }
250 if (userSubjects.length > 0) {
251 if (purposes.indexOf('clientAuth') === -1)
252 purposes.push('clientAuth');
253 }
254 if (userSubjects.length > 0 || hostSubjects.length > 0) {
255 if (purposes.indexOf('keyAgreement') === -1)
256 purposes.push('keyAgreement');
257 if (key.type === 'rsa' &&
258 purposes.indexOf('encryption') === -1)
259 purposes.push('encryption');
260 }
261 }
262
263 var cert = new Certificate({
264 subjects: subjects,
265 issuer: subjects[0],
266 subjectKey: key.toPublic(),
267 issuerKey: key.toPublic(),
268 signatures: {},
269 serial: serial,
270 validFrom: validFrom,
271 validUntil: validUntil,
272 purposes: purposes
273 });
274 cert.signWith(key);
275
276 return (cert);
277};
278
279Certificate.create =
280 function (subjectOrSubjects, key, issuer, issuerKey, options) {
281 var subjects;
282 if (Array.isArray(subjectOrSubjects))
283 subjects = subjectOrSubjects;
284 else
285 subjects = [subjectOrSubjects];
286
287 assert.arrayOfObject(subjects);
288 subjects.forEach(function (subject) {
289 utils.assertCompatible(subject, Identity, [1, 0], 'subject');
290 });
291
292 utils.assertCompatible(key, Key, [1, 0], 'key');
293 if (PrivateKey.isPrivateKey(key))
294 key = key.toPublic();
295 utils.assertCompatible(issuer, Identity, [1, 0], 'issuer');
296 utils.assertCompatible(issuerKey, PrivateKey, [1, 2], 'issuer key');
297
298 assert.optionalObject(options, 'options');
299 if (options === undefined)
300 options = {};
301 assert.optionalObject(options.validFrom, 'options.validFrom');
302 assert.optionalObject(options.validUntil, 'options.validUntil');
303 var validFrom = options.validFrom;
304 var validUntil = options.validUntil;
305 if (validFrom === undefined)
306 validFrom = new Date();
307 if (validUntil === undefined) {
308 assert.optionalNumber(options.lifetime, 'options.lifetime');
309 var lifetime = options.lifetime;
310 if (lifetime === undefined)
311 lifetime = 10*365*24*3600;
312 validUntil = new Date();
313 validUntil.setTime(validUntil.getTime() + lifetime*1000);
314 }
315 assert.optionalBuffer(options.serial, 'options.serial');
316 var serial = options.serial;
317 if (serial === undefined)
318 serial = Buffer.from('0000000000000001', 'hex');
319
320 var purposes = options.purposes;
321 if (purposes === undefined)
322 purposes = [];
323
324 if (purposes.indexOf('signature') === -1)
325 purposes.push('signature');
326
327 if (options.ca === true) {
328 if (purposes.indexOf('ca') === -1)
329 purposes.push('ca');
330 if (purposes.indexOf('crl') === -1)
331 purposes.push('crl');
332 }
333
334 var hostSubjects = subjects.filter(function (subject) {
335 return (subject.type === 'host');
336 });
337 var userSubjects = subjects.filter(function (subject) {
338 return (subject.type === 'user');
339 });
340 if (hostSubjects.length > 0) {
341 if (purposes.indexOf('serverAuth') === -1)
342 purposes.push('serverAuth');
343 }
344 if (userSubjects.length > 0) {
345 if (purposes.indexOf('clientAuth') === -1)
346 purposes.push('clientAuth');
347 }
348 if (userSubjects.length > 0 || hostSubjects.length > 0) {
349 if (purposes.indexOf('keyAgreement') === -1)
350 purposes.push('keyAgreement');
351 if (key.type === 'rsa' &&
352 purposes.indexOf('encryption') === -1)
353 purposes.push('encryption');
354 }
355
356 var cert = new Certificate({
357 subjects: subjects,
358 issuer: issuer,
359 subjectKey: key,
360 issuerKey: issuerKey.toPublic(),
361 signatures: {},
362 serial: serial,
363 validFrom: validFrom,
364 validUntil: validUntil,
365 purposes: purposes
366 });
367 cert.signWith(issuerKey);
368
369 return (cert);
370};
371
372Certificate.parse = function (data, format, options) {
373 if (typeof (data) !== 'string')
374 assert.buffer(data, 'data');
375 if (format === undefined)
376 format = 'auto';
377 assert.string(format, 'format');
378 if (typeof (options) === 'string')
379 options = { filename: options };
380 assert.optionalObject(options, 'options');
381 if (options === undefined)
382 options = {};
383 assert.optionalString(options.filename, 'options.filename');
384 if (options.filename === undefined)
385 options.filename = '(unnamed)';
386
387 assert.object(formats[format], 'formats[format]');
388
389 try {
390 var k = formats[format].read(data, options);
391 return (k);
392 } catch (e) {
393 throw (new CertificateParseError(options.filename, format, e));
394 }
395};
396
397Certificate.isCertificate = function (obj, ver) {
398 return (utils.isCompatible(obj, Certificate, ver));
399};
400
401/*
402 * API versions for Certificate:
403 * [1,0] -- initial ver
404 * [1,1] -- openssh format now unpacks extensions
405 */
406Certificate.prototype._sshpkApiVersion = [1, 1];
407
408Certificate._oldVersionDetect = function (obj) {
409 return ([1, 0]);
410};
Note: See TracBrowser for help on using the repository browser.