• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const asn1 = require('asn1.js');
4const crypto = require('crypto');
5const { writeFileSync } = require('fs');
6const rfc5280 = require('asn1.js-rfc5280');
7const BN = asn1.bignum;
8
9const oid = {
10  commonName: [2, 5, 4, 3],
11  countryName: [2, 5, 4, 6],
12  localityName: [2, 5, 4, 7],
13  rsaEncryption: [1, 2, 840, 113549, 1, 1, 1],
14  sha256WithRSAEncryption: [1, 2, 840, 113549, 1, 1, 11],
15  xmppAddr: [1, 3, 6, 1, 5, 5, 7, 8, 5],
16  srvName: [1, 3, 6, 1, 5, 5, 7, 8, 7],
17  ocsp: [1, 3, 6, 1, 5, 5, 7, 48, 1],
18  caIssuers: [1, 3, 6, 1, 5, 5, 7, 48, 2],
19  privateUnrecognized: [1, 3, 9999, 12, 34]
20};
21
22const digest = 'SHA256';
23
24const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
25  modulusLength: 4096,
26  publicKeyEncoding: {
27    type: 'pkcs1',
28    format: 'der'
29  }
30});
31
32writeFileSync('server-key.pem', privateKey.export({
33  type: 'pkcs8',
34  format: 'pem'
35}));
36
37const now = Date.now();
38const days = 3650;
39
40function utilType(name, fn) {
41  return asn1.define(name, function() {
42    this[fn]();
43  });
44}
45
46const Null_ = utilType('Null_', 'null_');
47const null_ = Null_.encode('der');
48
49const IA5String = utilType('IA5String', 'ia5str');
50const PrintableString = utilType('PrintableString', 'printstr');
51const UTF8String = utilType('UTF8String', 'utf8str');
52
53const subjectCommonName = PrintableString.encode('evil.example.com', 'der');
54
55const sans = [
56  { type: 'dNSName', value: 'good.example.com, DNS:evil.example.com' },
57  { type: 'uniformResourceIdentifier', value: 'http://example.com/' },
58  { type: 'uniformResourceIdentifier', value: 'http://example.com/?a=b&c=d' },
59  { type: 'uniformResourceIdentifier', value: 'http://example.com/a,b' },
60  { type: 'uniformResourceIdentifier', value: 'http://example.com/a%2Cb' },
61  {
62    type: 'uniformResourceIdentifier',
63    value: 'http://example.com/a, DNS:good.example.com'
64  },
65  { type: 'dNSName', value: Buffer.from('exämple.com', 'latin1') },
66  { type: 'dNSName', value: '"evil.example.com"' },
67  { type: 'iPAddress', value: Buffer.from('08080808', 'hex') },
68  { type: 'iPAddress', value: Buffer.from('08080404', 'hex') },
69  { type: 'iPAddress', value: Buffer.from('0008080404', 'hex') },
70  { type: 'iPAddress', value: Buffer.from('000102030405', 'hex') },
71  {
72    type: 'iPAddress',
73    value: Buffer.from('0a0b0c0d0e0f0000000000007a7b7c7d', 'hex')
74  },
75  { type: 'rfc822Name', value: 'foo@example.com' },
76  { type: 'rfc822Name', value: 'foo@example.com, DNS:good.example.com' },
77  {
78    type: 'directoryName',
79    value: {
80      type: 'rdnSequence',
81      value: [
82        [
83          {
84            type: oid.countryName,
85            value: PrintableString.encode('DE', 'der')
86          }
87        ],
88        [
89          {
90            type: oid.localityName,
91            value: UTF8String.encode('Hannover', 'der')
92          }
93        ]
94      ]
95    }
96  },
97  {
98    type: 'directoryName',
99    value: {
100      type: 'rdnSequence',
101      value: [
102        [
103          {
104            type: oid.countryName,
105            value: PrintableString.encode('DE', 'der')
106          }
107        ],
108        [
109          {
110            type: oid.localityName,
111            value: UTF8String.encode('München', 'der')
112          }
113        ]
114      ]
115    }
116  },
117  {
118    type: 'directoryName',
119    value: {
120      type: 'rdnSequence',
121      value: [
122        [
123          {
124            type: oid.countryName,
125            value: PrintableString.encode('DE', 'der')
126          }
127        ],
128        [
129          {
130            type: oid.localityName,
131            value: UTF8String.encode('Berlin, DNS:good.example.com', 'der')
132          }
133        ]
134      ]
135    }
136  },
137  {
138    type: 'directoryName',
139    value: {
140      type: 'rdnSequence',
141      value: [
142        [
143          {
144            type: oid.countryName,
145            value: PrintableString.encode('DE', 'der')
146          }
147        ],
148        [
149          {
150            type: oid.localityName,
151            value: UTF8String.encode('Berlin, DNS:good.example.com\0evil.example.com', 'der')
152          }
153        ]
154      ]
155    }
156  },
157  {
158    type: 'directoryName',
159    value: {
160      type: 'rdnSequence',
161      value: [
162        [
163          {
164            type: oid.countryName,
165            value: PrintableString.encode('DE', 'der')
166          }
167        ],
168        [
169          {
170            type: oid.localityName,
171            value: UTF8String.encode(
172              'Berlin, DNS:good.example.com\\\0evil.example.com', 'der')
173          }
174        ]
175      ]
176    }
177  },
178  {
179    type: 'directoryName',
180    value: {
181      type: 'rdnSequence',
182      value: [
183        [
184          {
185            type: oid.countryName,
186            value: PrintableString.encode('DE', 'der')
187          }
188        ],
189        [
190          {
191            type: oid.localityName,
192            value: UTF8String.encode('Berlin\r\n', 'der')
193          }
194        ]
195      ]
196    }
197  },
198  {
199    type: 'directoryName',
200    value: {
201      type: 'rdnSequence',
202      value: [
203        [
204          {
205            type: oid.countryName,
206            value: PrintableString.encode('DE', 'der')
207          }
208        ],
209        [
210          {
211            type: oid.localityName,
212            value: UTF8String.encode('Berlin/CN=good.example.com', 'der')
213          }
214        ]
215      ]
216    }
217  },
218  {
219    type: 'registeredID',
220    value: oid.sha256WithRSAEncryption
221  },
222  {
223    type: 'registeredID',
224    value: oid.privateUnrecognized
225  },
226  {
227    type: 'otherName',
228    value: {
229      'type-id': oid.xmppAddr,
230      value: UTF8String.encode('abc123', 'der')
231    }
232  },
233  {
234    type: 'otherName',
235    value: {
236      'type-id': oid.xmppAddr,
237      value: UTF8String.encode('abc123, DNS:good.example.com', 'der')
238    }
239  },
240  {
241    type: 'otherName',
242    value: {
243      'type-id': oid.xmppAddr,
244      value: UTF8String.encode('good.example.com\0abc123', 'der')
245    }
246  },
247  {
248    type: 'otherName',
249    value: {
250      'type-id': oid.privateUnrecognized,
251      value: UTF8String.encode('abc123', 'der')
252    }
253  },
254  {
255    type: 'otherName',
256    value: {
257      'type-id': oid.srvName,
258      value: IA5String.encode('abc123', 'der')
259    }
260  },
261  {
262    type: 'otherName',
263    value: {
264      'type-id': oid.srvName,
265      value: UTF8String.encode('abc123', 'der')
266    }
267  },
268  {
269    type: 'otherName',
270    value: {
271      'type-id': oid.srvName,
272      value: IA5String.encode('abc\0def', 'der')
273    }
274  }
275];
276
277for (let i = 0; i < sans.length; i++) {
278  const san = sans[i];
279
280  const tbs = {
281    version: 'v3',
282    serialNumber: new BN('01', 16),
283    signature: {
284      algorithm: oid.sha256WithRSAEncryption,
285      parameters: null_
286    },
287    issuer: {
288      type: 'rdnSequence',
289      value: [
290        [
291          { type: oid.commonName, value: subjectCommonName }
292        ]
293      ]
294    },
295    validity: {
296      notBefore: { type: 'utcTime', value: now },
297      notAfter: { type: 'utcTime', value: now + days * 86400000 }
298    },
299    subject: {
300      type: 'rdnSequence',
301      value: [
302        [
303          { type: oid.commonName, value: subjectCommonName }
304        ]
305      ]
306    },
307    subjectPublicKeyInfo: {
308      algorithm: {
309        algorithm: oid.rsaEncryption,
310        parameters: null_
311      },
312      subjectPublicKey: {
313        unused: 0,
314        data: publicKey
315      }
316    },
317    extensions: [
318      {
319        extnID: 'subjectAlternativeName',
320        critical: false,
321        extnValue: [san]
322      }
323    ]
324  };
325
326  // Self-sign the certificate.
327  const tbsDer = rfc5280.TBSCertificate.encode(tbs, 'der');
328  const signature = crypto.createSign(digest).update(tbsDer).sign(privateKey);
329
330  // Construct the signed certificate.
331  const cert = {
332    tbsCertificate: tbs,
333    signatureAlgorithm: {
334      algorithm: oid.sha256WithRSAEncryption,
335      parameters: null_
336    },
337    signature: {
338      unused: 0,
339      data: signature
340    }
341  };
342
343  // Store the signed certificate.
344  const pem = rfc5280.Certificate.encode(cert, 'pem', {
345    label: 'CERTIFICATE'
346  });
347  writeFileSync(`./alt-${i}-cert.pem`, `${pem}\n`);
348}
349
350const infoAccessExtensions = [
351  [
352    {
353      accessMethod: oid.ocsp,
354      accessLocation: {
355        type: 'uniformResourceIdentifier',
356        value: 'http://good.example.com/\nOCSP - URI:http://evil.example.com/',
357      },
358    },
359  ],
360  [
361    {
362      accessMethod: oid.caIssuers,
363      accessLocation: {
364        type: 'uniformResourceIdentifier',
365        value: 'http://ca.example.com/\nOCSP - URI:http://evil.example.com',
366      },
367    },
368    {
369      accessMethod: oid.ocsp,
370      accessLocation: {
371        type: 'dNSName',
372        value: 'good.example.com\nOCSP - URI:http://ca.nodejs.org/ca.cert',
373      },
374    },
375  ],
376  [
377    {
378      accessMethod: oid.privateUnrecognized,
379      accessLocation: {
380        type: 'uniformResourceIdentifier',
381        value: 'http://ca.example.com/',
382      },
383    },
384  ],
385  [
386    {
387      accessMethod: oid.ocsp,
388      accessLocation: {
389        type: 'otherName',
390        value: {
391          'type-id': oid.xmppAddr,
392          value: UTF8String.encode('good.example.com', 'der'),
393        },
394      },
395    },
396    {
397      accessMethod: oid.ocsp,
398      accessLocation: {
399        type: 'otherName',
400        value: {
401          'type-id': oid.privateUnrecognized,
402          value: UTF8String.encode('abc123', 'der')
403        },
404      },
405    },
406    {
407      accessMethod: oid.ocsp,
408      accessLocation: {
409        type: 'otherName',
410        value: {
411          'type-id': oid.srvName,
412          value: IA5String.encode('abc123', 'der')
413        }
414      }
415    },
416  ],
417  [
418    {
419      accessMethod: oid.ocsp,
420      accessLocation: {
421        type: 'otherName',
422        value: {
423          'type-id': oid.xmppAddr,
424          value: UTF8String.encode('good.example.com\0abc123', 'der'),
425        },
426      },
427    },
428  ],
429];
430
431for (let i = 0; i < infoAccessExtensions.length; i++) {
432  const infoAccess = infoAccessExtensions[i];
433
434  const tbs = {
435    version: 'v3',
436    serialNumber: new BN('01', 16),
437    signature: {
438      algorithm: oid.sha256WithRSAEncryption,
439      parameters: null_
440    },
441    issuer: {
442      type: 'rdnSequence',
443      value: [
444        [
445          { type: oid.commonName, value: subjectCommonName }
446        ]
447      ]
448    },
449    validity: {
450      notBefore: { type: 'utcTime', value: now },
451      notAfter: { type: 'utcTime', value: now + days * 86400000 }
452    },
453    subject: {
454      type: 'rdnSequence',
455      value: [
456        [
457          { type: oid.commonName, value: subjectCommonName }
458        ]
459      ]
460    },
461    subjectPublicKeyInfo: {
462      algorithm: {
463        algorithm: oid.rsaEncryption,
464        parameters: null_
465      },
466      subjectPublicKey: {
467        unused: 0,
468        data: publicKey
469      }
470    },
471    extensions: [
472      {
473        extnID: 'authorityInformationAccess',
474        critical: false,
475        extnValue: infoAccess
476      }
477    ]
478  };
479
480  // Self-sign the certificate.
481  const tbsDer = rfc5280.TBSCertificate.encode(tbs, 'der');
482  const signature = crypto.createSign(digest).update(tbsDer).sign(privateKey);
483
484  // Construct the signed certificate.
485  const cert = {
486    tbsCertificate: tbs,
487    signatureAlgorithm: {
488      algorithm: oid.sha256WithRSAEncryption,
489      parameters: null_
490    },
491    signature: {
492      unused: 0,
493      data: signature
494    }
495  };
496
497  // Store the signed certificate.
498  const pem = rfc5280.Certificate.encode(cert, 'pem', {
499    label: 'CERTIFICATE'
500  });
501  writeFileSync(`./info-${i}-cert.pem`, `${pem}\n`);
502}
503
504const subjects = [
505  [
506    [
507      { type: oid.localityName, value: UTF8String.encode('Somewhere') }
508    ],
509    [
510      { type: oid.commonName, value: UTF8String.encode('evil.example.com') }
511    ]
512  ],
513  [
514    [
515      {
516        type: oid.localityName,
517        value: UTF8String.encode('Somewhere\0evil.example.com'),
518      }
519    ]
520  ],
521  [
522    [
523      {
524        type: oid.localityName,
525        value: UTF8String.encode('Somewhere\nCN=evil.example.com')
526      }
527    ]
528  ],
529  [
530    [
531      {
532        type: oid.localityName,
533        value: UTF8String.encode('Somewhere, CN = evil.example.com')
534      }
535    ]
536  ],
537  [
538    [
539      {
540        type: oid.localityName,
541        value: UTF8String.encode('Somewhere/CN=evil.example.com')
542      }
543    ]
544  ],
545  [
546    [
547      {
548        type: oid.localityName,
549        value: UTF8String.encode('M\u00fcnchen\\\nCN=evil.example.com')
550      }
551    ]
552  ],
553  [
554    [
555      { type: oid.localityName, value: UTF8String.encode('Somewhere') },
556      { type: oid.commonName, value: UTF8String.encode('evil.example.com') },
557    ]
558  ],
559  [
560    [
561      {
562        type: oid.localityName,
563        value: UTF8String.encode('Somewhere + CN=evil.example.com'),
564      }
565    ]
566  ],
567  [
568    [
569      { type: oid.localityName, value: UTF8String.encode('L1') },
570      { type: oid.localityName, value: UTF8String.encode('L2') },
571    ],
572    [
573      { type: oid.localityName, value: UTF8String.encode('L3') },
574    ]
575  ],
576  [
577    [
578      { type: oid.localityName, value: UTF8String.encode('L1') },
579    ],
580    [
581      { type: oid.localityName, value: UTF8String.encode('L2') },
582    ],
583    [
584      { type: oid.localityName, value: UTF8String.encode('L3') },
585    ],
586  ],
587];
588
589for (let i = 0; i < subjects.length; i++) {
590  const tbs = {
591    version: 'v3',
592    serialNumber: new BN('01', 16),
593    signature: {
594      algorithm: oid.sha256WithRSAEncryption,
595      parameters: null_
596    },
597    issuer: {
598      type: 'rdnSequence',
599      value: subjects[i]
600    },
601    validity: {
602      notBefore: { type: 'utcTime', value: now },
603      notAfter: { type: 'utcTime', value: now + days * 86400000 }
604    },
605    subject: {
606      type: 'rdnSequence',
607      value: subjects[i]
608    },
609    subjectPublicKeyInfo: {
610      algorithm: {
611        algorithm: oid.rsaEncryption,
612        parameters: null_
613      },
614      subjectPublicKey: {
615        unused: 0,
616        data: publicKey
617      }
618    }
619  };
620
621  // Self-sign the certificate.
622  const tbsDer = rfc5280.TBSCertificate.encode(tbs, 'der');
623  const signature = crypto.createSign(digest).update(tbsDer).sign(privateKey);
624
625  // Construct the signed certificate.
626  const cert = {
627    tbsCertificate: tbs,
628    signatureAlgorithm: {
629      algorithm: oid.sha256WithRSAEncryption,
630      parameters: null_
631    },
632    signature: {
633      unused: 0,
634      data: signature
635    }
636  };
637
638  // Store the signed certificate.
639  const pem = rfc5280.Certificate.encode(cert, 'pem', {
640    label: 'CERTIFICATE'
641  });
642  writeFileSync(`./subj-${i}-cert.pem`, `${pem}\n`);
643}
644