@@ -243,14 +243,30 @@ added: v0.3.4 |
243 | 243 | * `requestListener` {Function} A listener to be added to the `'request'` event. |
244 | 244 | * Returns: {https.Server} |
245 | 245 |
|
246 | | -```js |
| 246 | +```mjs |
| 247 | +// curl -k https://localhost:8000/ |
| 248 | +import { createServer } from 'node:https'; |
| 249 | +import { readFileSync } from 'node:fs'; |
| 250 | + |
| 251 | +const options = { |
| 252 | + key: readFileSync('private-key.pem'), |
| 253 | + cert: readFileSync('certificate.pem'), |
| 254 | +}; |
| 255 | + |
| 256 | +createServer(options, (req, res) => { |
| 257 | + res.writeHead(200); |
| 258 | + res.end('hello world\n'); |
| 259 | +}).listen(8000); |
| 260 | +``` |
| 261 | + |
| 262 | +```cjs |
247 | 263 | // curl -k https://localhost:8000/ |
248 | 264 | const https = require('node:https'); |
249 | 265 | const fs = require('node:fs'); |
250 | 266 |
|
251 | 267 | const options = { |
252 | | - key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), |
253 | | - cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'), |
| 268 | + key: fs.readFileSync('private-key.pem'), |
| 269 | + cert: fs.readFileSync('certificate.pem'), |
254 | 270 | }; |
255 | 271 |
|
256 | 272 | https.createServer(options, (req, res) => { |
@@ -261,12 +277,27 @@ https.createServer(options, (req, res) => { |
261 | 277 |
|
262 | 278 | Or |
263 | 279 |
|
264 | | -```js |
| 280 | +```mjs |
| 281 | +import { createServer } from 'node:https'; |
| 282 | +import { readFileSync } from 'node:fs'; |
| 283 | + |
| 284 | +const options = { |
| 285 | + pfx: readFileSync('test_cert.pfx'), |
| 286 | + passphrase: 'sample', |
| 287 | +}; |
| 288 | + |
| 289 | +createServer(options, (req, res) => { |
| 290 | + res.writeHead(200); |
| 291 | + res.end('hello world\n'); |
| 292 | +}).listen(8000); |
| 293 | +``` |
| 294 | + |
| 295 | +```cjs |
265 | 296 | const https = require('node:https'); |
266 | 297 | const fs = require('node:fs'); |
267 | 298 |
|
268 | 299 | const options = { |
269 | | - pfx: fs.readFileSync('test/fixtures/test_cert.pfx'), |
| 300 | + pfx: fs.readFileSync('test_cert.pfx'), |
270 | 301 | passphrase: 'sample', |
271 | 302 | }; |
272 | 303 |
|
@@ -276,6 +307,20 @@ https.createServer(options, (req, res) => { |
276 | 307 | }).listen(8000); |
277 | 308 | ``` |
278 | 309 |
|
| 310 | +To generate the certificate and key for this example, run: |
| 311 | + |
| 312 | +```bash |
| 313 | +openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ |
| 314 | + -keyout private-key.pem -out certificate.pem |
| 315 | +``` |
| 316 | + |
| 317 | +Then, to generate the `pfx` certificate for this example, run: |
| 318 | + |
| 319 | +```bash |
| 320 | +openssl pkcs12 -certpbe AES-256-CBC -export -out test_cert.pfx \ |
| 321 | + -inkey private-key.pem -in certificate.pem -passout pass:sample |
| 322 | +``` |
| 323 | + |
279 | 324 | ## `https.get(options[, callback])` |
280 | 325 |
|
281 | 326 | ## `https.get(url[, options][, callback])` |
@@ -303,7 +348,24 @@ Like [`http.get()`][] but for HTTPS. |
303 | 348 | string, it is automatically parsed with [`new URL()`][]. If it is a [`URL`][] |
304 | 349 | object, it will be automatically converted to an ordinary `options` object. |
305 | 350 |
|
306 | | -```js |
| 351 | +```mjs |
| 352 | +import { get } from 'node:https'; |
| 353 | +import process from 'node:process'; |
| 354 | + |
| 355 | +get('https://encrypted.google.com/', (res) => { |
| 356 | + console.log('statusCode:', res.statusCode); |
| 357 | + console.log('headers:', res.headers); |
| 358 | + |
| 359 | + res.on('data', (d) => { |
| 360 | + process.stdout.write(d); |
| 361 | + }); |
| 362 | + |
| 363 | +}).on('error', (e) => { |
| 364 | + console.error(e); |
| 365 | +}); |
| 366 | +``` |
| 367 | + |
| 368 | +```cjs |
307 | 369 | const https = require('node:https'); |
308 | 370 |
|
309 | 371 | https.get('https://encrypted.google.com/', (res) => { |
@@ -394,7 +456,33 @@ object, it will be automatically converted to an ordinary `options` object. |
394 | 456 | class. The `ClientRequest` instance is a writable stream. If one needs to |
395 | 457 | upload a file with a POST request, then write to the `ClientRequest` object. |
396 | 458 |
|
397 | | -```js |
| 459 | +```mjs |
| 460 | +import { request } from 'node:https'; |
| 461 | +import process from 'node:process'; |
| 462 | + |
| 463 | +const options = { |
| 464 | + hostname: 'encrypted.google.com', |
| 465 | + port: 443, |
| 466 | + path: '/', |
| 467 | + method: 'GET', |
| 468 | +}; |
| 469 | + |
| 470 | +const req = request(options, (res) => { |
| 471 | + console.log('statusCode:', res.statusCode); |
| 472 | + console.log('headers:', res.headers); |
| 473 | + |
| 474 | + res.on('data', (d) => { |
| 475 | + process.stdout.write(d); |
| 476 | + }); |
| 477 | +}); |
| 478 | + |
| 479 | +req.on('error', (e) => { |
| 480 | + console.error(e); |
| 481 | +}); |
| 482 | +req.end(); |
| 483 | +``` |
| 484 | + |
| 485 | +```cjs |
398 | 486 | const https = require('node:https'); |
399 | 487 |
|
400 | 488 | const options = { |
@@ -427,8 +515,8 @@ const options = { |
427 | 515 | port: 443, |
428 | 516 | path: '/', |
429 | 517 | method: 'GET', |
430 | | - key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), |
431 | | - cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'), |
| 518 | + key: fs.readFileSync('private-key.pem'), |
| 519 | + cert: fs.readFileSync('certificate.pem'), |
432 | 520 | }; |
433 | 521 | options.agent = new https.Agent(options); |
434 | 522 |
|
@@ -445,8 +533,8 @@ const options = { |
445 | 533 | port: 443, |
446 | 534 | path: '/', |
447 | 535 | method: 'GET', |
448 | | - key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), |
449 | | - cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'), |
| 536 | + key: fs.readFileSync('private-key.pem'), |
| 537 | + cert: fs.readFileSync('certificate.pem'), |
450 | 538 | agent: false, |
451 | 539 | }; |
452 | 540 |
|
@@ -468,7 +556,80 @@ const req = https.request(options, (res) => { |
468 | 556 | Example pinning on certificate fingerprint, or the public key (similar to |
469 | 557 | `pin-sha256`): |
470 | 558 |
|
471 | | -```js |
| 559 | +```mjs |
| 560 | +import { checkServerIdentity } from 'node:tls'; |
| 561 | +import { Agent, request } from 'node:https'; |
| 562 | +import { createHash } from 'node:crypto'; |
| 563 | + |
| 564 | +function sha256(s) { |
| 565 | + return createHash('sha256').update(s).digest('base64'); |
| 566 | +} |
| 567 | +const options = { |
| 568 | + hostname: 'github.com', |
| 569 | + port: 443, |
| 570 | + path: '/', |
| 571 | + method: 'GET', |
| 572 | + checkServerIdentity: function(host, cert) { |
| 573 | + // Make sure the certificate is issued to the host we are connected to |
| 574 | + const err = checkServerIdentity(host, cert); |
| 575 | + if (err) { |
| 576 | + return err; |
| 577 | + } |
| 578 | + |
| 579 | + // Pin the public key, similar to HPKP pin-sha256 pinning |
| 580 | + const pubkey256 = 'SIXvRyDmBJSgatgTQRGbInBaAK+hZOQ18UmrSwnDlK8='; |
| 581 | + if (sha256(cert.pubkey) !== pubkey256) { |
| 582 | + const msg = 'Certificate verification error: ' + |
| 583 | + `The public key of '${cert.subject.CN}' ` + |
| 584 | + 'does not match our pinned fingerprint'; |
| 585 | + return new Error(msg); |
| 586 | + } |
| 587 | + |
| 588 | + // Pin the exact certificate, rather than the pub key |
| 589 | + const cert256 = 'FD:6E:9B:0E:F3:98:BC:D9:04:C3:B2:EC:16:7A:7B:' + |
| 590 | + '0F:DA:72:01:C9:03:C5:3A:6A:6A:E5:D0:41:43:63:EF:65'; |
| 591 | + if (cert.fingerprint256 !== cert256) { |
| 592 | + const msg = 'Certificate verification error: ' + |
| 593 | + `The certificate of '${cert.subject.CN}' ` + |
| 594 | + 'does not match our pinned fingerprint'; |
| 595 | + return new Error(msg); |
| 596 | + } |
| 597 | + |
| 598 | + // This loop is informational only. |
| 599 | + // Print the certificate and public key fingerprints of all certs in the |
| 600 | + // chain. Its common to pin the public key of the issuer on the public |
| 601 | + // internet, while pinning the public key of the service in sensitive |
| 602 | + // environments. |
| 603 | + let lastprint256; |
| 604 | + do { |
| 605 | + console.log('Subject Common Name:', cert.subject.CN); |
| 606 | + console.log(' Certificate SHA256 fingerprint:', cert.fingerprint256); |
| 607 | + |
| 608 | + const hash = createHash('sha256'); |
| 609 | + console.log(' Public key ping-sha256:', sha256(cert.pubkey)); |
| 610 | + |
| 611 | + lastprint256 = cert.fingerprint256; |
| 612 | + cert = cert.issuerCertificate; |
| 613 | + } while (cert.fingerprint256 !== lastprint256); |
| 614 | + |
| 615 | + }, |
| 616 | +}; |
| 617 | + |
| 618 | +options.agent = new Agent(options); |
| 619 | +const req = request(options, (res) => { |
| 620 | + console.log('All OK. Server matched our pinned cert or public key'); |
| 621 | + console.log('statusCode:', res.statusCode); |
| 622 | + |
| 623 | + res.on('data', (d) => {}); |
| 624 | +}); |
| 625 | + |
| 626 | +req.on('error', (e) => { |
| 627 | + console.error(e.message); |
| 628 | +}); |
| 629 | +req.end(); |
| 630 | +``` |
| 631 | + |
| 632 | +```cjs |
472 | 633 | const tls = require('node:tls'); |
473 | 634 | const https = require('node:https'); |
474 | 635 | const crypto = require('node:crypto'); |
@@ -489,7 +650,7 @@ const options = { |
489 | 650 | } |
490 | 651 |
|
491 | 652 | // Pin the public key, similar to HPKP pin-sha256 pinning |
492 | | - const pubkey256 = 'pL1+qb9HTMRZJmuC/bB/ZI9d302BYrrqiVuRyW+DGrU='; |
| 653 | + const pubkey256 = 'SIXvRyDmBJSgatgTQRGbInBaAK+hZOQ18UmrSwnDlK8='; |
493 | 654 | if (sha256(cert.pubkey) !== pubkey256) { |
494 | 655 | const msg = 'Certificate verification error: ' + |
495 | 656 | `The public key of '${cert.subject.CN}' ` + |
@@ -498,8 +659,8 @@ const options = { |
498 | 659 | } |
499 | 660 |
|
500 | 661 | // Pin the exact certificate, rather than the pub key |
501 | | - const cert256 = '25:FE:39:32:D9:63:8C:8A:FC:A1:9A:29:87:' + |
502 | | - 'D8:3E:4C:1D:98:DB:71:E4:1A:48:03:98:EA:22:6A:BD:8B:93:16'; |
| 662 | + const cert256 = 'FD:6E:9B:0E:F3:98:BC:D9:04:C3:B2:EC:16:7A:7B:' + |
| 663 | + '0F:DA:72:01:C9:03:C5:3A:6A:6A:E5:D0:41:43:63:EF:65'; |
503 | 664 | if (cert.fingerprint256 !== cert256) { |
504 | 665 | const msg = 'Certificate verification error: ' + |
505 | 666 | `The certificate of '${cert.subject.CN}' ` + |
@@ -530,8 +691,6 @@ options.agent = new https.Agent(options); |
530 | 691 | const req = https.request(options, (res) => { |
531 | 692 | console.log('All OK. Server matched our pinned cert or public key'); |
532 | 693 | console.log('statusCode:', res.statusCode); |
533 | | - // Print the HPKP values |
534 | | - console.log('headers:', res.headers['public-key-pins']); |
535 | 694 |
|
536 | 695 | res.on('data', (d) => {}); |
537 | 696 | }); |
@@ -546,17 +705,19 @@ Outputs for example: |
546 | 705 |
|
547 | 706 | ```text |
548 | 707 | Subject Common Name: github.com |
549 | | - Certificate SHA256 fingerprint: 25:FE:39:32:D9:63:8C:8A:FC:A1:9A:29:87:D8:3E:4C:1D:98:DB:71:E4:1A:48:03:98:EA:22:6A:BD:8B:93:16 |
550 | | - Public key ping-sha256: pL1+qb9HTMRZJmuC/bB/ZI9d302BYrrqiVuRyW+DGrU= |
551 | | -Subject Common Name: DigiCert SHA2 Extended Validation Server CA |
552 | | - Certificate SHA256 fingerprint: 40:3E:06:2A:26:53:05:91:13:28:5B:AF:80:A0:D4:AE:42:2C:84:8C:9F:78:FA:D0:1F:C9:4B:C5:B8:7F:EF:1A |
553 | | - Public key ping-sha256: RRM1dGqnDFsCJXBTHky16vi1obOlCgFFn/yOhI/y+ho= |
554 | | -Subject Common Name: DigiCert High Assurance EV Root CA |
555 | | - Certificate SHA256 fingerprint: 74:31:E5:F4:C3:C1:CE:46:90:77:4F:0B:61:E0:54:40:88:3B:A9:A0:1E:D0:0B:A6:AB:D7:80:6E:D3:B1:18:CF |
556 | | - Public key ping-sha256: WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18= |
| 708 | + Certificate SHA256 fingerprint: FD:6E:9B:0E:F3:98:BC:D9:04:C3:B2:EC:16:7A:7B:0F:DA:72:01:C9:03:C5:3A:6A:6A:E5:D0:41:43:63:EF:65 |
| 709 | + Public key ping-sha256: SIXvRyDmBJSgatgTQRGbInBaAK+hZOQ18UmrSwnDlK8= |
| 710 | +Subject Common Name: Sectigo ECC Domain Validation Secure Server CA |
| 711 | + Certificate SHA256 fingerprint: 61:E9:73:75:E9:F6:DA:98:2F:F5:C1:9E:2F:94:E6:6C:4E:35:B6:83:7C:E3:B9:14:D2:24:5C:7F:5F:65:82:5F |
| 712 | + Public key ping-sha256: Eep0p/AsSa9lFUH6KT2UY+9s1Z8v7voAPkQ4fGknZ2g= |
| 713 | +Subject Common Name: USERTrust ECC Certification Authority |
| 714 | + Certificate SHA256 fingerprint: A6:CF:64:DB:B4:C8:D5:FD:19:CE:48:89:60:68:DB:03:B5:33:A8:D1:33:6C:62:56:A8:7D:00:CB:B3:DE:F3:EA |
| 715 | + Public key ping-sha256: UJM2FOhG9aTNY0Pg4hgqjNzZ/lQBiMGRxPD5Y2/e0bw= |
| 716 | +Subject Common Name: AAA Certificate Services |
| 717 | + Certificate SHA256 fingerprint: D7:A7:A0:FB:5D:7E:27:31:D7:71:E9:48:4E:BC:DE:F7:1D:5F:0C:3E:0A:29:48:78:2B:C8:3E:E0:EA:69:9E:F4 |
| 718 | + Public key ping-sha256: vRU+17BDT2iGsXvOi76E7TQMcTLXAqj0+jGPdW7L1vM= |
557 | 719 | All OK. Server matched our pinned cert or public key |
558 | 720 | statusCode: 200 |
559 | | -headers: max-age=0; pin-sha256="WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="; pin-sha256="RRM1dGqnDFsCJXBTHky16vi1obOlCgFFn/yOhI/y+ho="; pin-sha256="k2v657xBsOVe1PQRwOsHsw3bsGT2VzIqz5K+59sNQws="; pin-sha256="K87oWBWM9UZfyddvDfoxL+8lpNyoUB2ptGtn0fv6G2Q="; pin-sha256="IQBnNBEiFuhj+8x6X8XLgh01V9Ic5/V3IRQLNFFc7v4="; pin-sha256="iie1VXtL7HzAMF+/PVPR9xzT80kQxdZeJ+zduCB3uj0="; pin-sha256="LvRiGEjRqfzurezaWuj8Wie2gyHMrW5Q06LspMnox7A="; includeSubDomains |
560 | 721 | ``` |
561 | 722 |
|
562 | 723 | [`Agent`]: #class-httpsagent |
|