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