@jsenv/https-local 1.0.0 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -65
- package/package.json +27 -33
- package/src/certificate_authority.js +67 -26
- package/src/certificate_for_localhost.js +27 -11
- package/src/hosts_file_verif.js +13 -6
- package/src/internal/authority_file_infos.js +9 -3
- package/src/internal/certificate_authority_file_urls.js +21 -11
- package/src/internal/certificate_data_converter.js +6 -2
- package/src/internal/certificate_generator.js +21 -7
- package/src/internal/command.js +2 -1
- package/src/internal/exec.js +4 -1
- package/src/internal/hosts/parse_hosts.js +15 -5
- package/src/internal/hosts/write_hosts.js +23 -11
- package/src/internal/hosts/write_line_hosts.js +28 -12
- package/src/internal/linux/chrome_linux.js +8 -2
- package/src/internal/linux/firefox_linux.js +8 -2
- package/src/internal/linux/linux_trust_store.js +45 -20
- package/src/internal/linux/nss_linux.js +8 -2
- package/src/internal/mac/firefox_mac.js +8 -2
- package/src/internal/mac/mac_keychain.js +29 -12
- package/src/internal/mac/nss_mac.js +12 -4
- package/src/internal/nssdb_browser.js +17 -6
- package/src/internal/search_certificate_in_command_output.js +4 -1
- package/src/internal/unsupported_platform/unsupported_platform.js +3 -1
- package/src/internal/validity_formatting.js +3 -1
- package/src/internal/windows/chrome_windows.js +12 -10
- package/src/internal/windows/firefox_windows.js +12 -10
- package/src/internal/windows/windows_certutil.js +38 -16
- package/src/jsenvParameters.js +1 -1
- package/src/validity_duration.js +7 -3
package/README.md
CHANGED
|
@@ -30,7 +30,11 @@ npm install --save-dev @jsenv/https-local
|
|
|
30
30
|
*
|
|
31
31
|
* Read more in https://github.com/jsenv/https-local#installCertificateAuthority
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
installCertificateAuthority,
|
|
36
|
+
verifyHostsFile,
|
|
37
|
+
} from "@jsenv/https-local"
|
|
34
38
|
|
|
35
39
|
await installCertificateAuthority({
|
|
36
40
|
tryToTrust: true,
|
|
@@ -38,13 +42,13 @@ await installCertificateAuthority({
|
|
|
38
42
|
})
|
|
39
43
|
await verifyHostsFile({
|
|
40
44
|
ipMappings: {
|
|
41
|
-
"127.0.0.1": ["localhost"
|
|
45
|
+
"127.0.0.1": ["localhost"],
|
|
42
46
|
},
|
|
43
47
|
tryToUpdatesHostsFile: true,
|
|
44
48
|
})
|
|
45
49
|
```
|
|
46
50
|
|
|
47
|
-
3 -
|
|
51
|
+
3 - Run file to install certificate authority with node
|
|
48
52
|
|
|
49
53
|
```console
|
|
50
54
|
node ./install_certificate_authority.mjs
|
|
@@ -55,7 +59,7 @@ node ./install_certificate_authority.mjs
|
|
|
55
59
|
```js
|
|
56
60
|
/*
|
|
57
61
|
* This file uses "@jsenv/https-local" to obtain a certificate used to start a server in https.
|
|
58
|
-
* The certificate is valid for 396 days and is issued by a certificate authority trusted on this machine.
|
|
62
|
+
* The certificate is valid for 1 year (396 days) and is issued by a certificate authority trusted on this machine.
|
|
59
63
|
* If the certificate authority was not installed before executing this file, an error is thrown
|
|
60
64
|
* explaining that certificate authority must be installed first.
|
|
61
65
|
*
|
|
@@ -65,12 +69,12 @@ node ./install_certificate_authority.mjs
|
|
|
65
69
|
*
|
|
66
70
|
* Read more in https://github.com/jsenv/https-local#requestCertificateForLocalhost
|
|
67
71
|
*/
|
|
72
|
+
|
|
68
73
|
import { createServer } from "node:https"
|
|
69
74
|
import { requestCertificateForLocalhost } from "@jsenv/https-local"
|
|
70
75
|
|
|
71
|
-
const { serverCertificate, serverCertificatePrivateKey } =
|
|
72
|
-
|
|
73
|
-
})
|
|
76
|
+
const { serverCertificate, serverCertificatePrivateKey } =
|
|
77
|
+
await requestCertificateForLocalhost()
|
|
74
78
|
|
|
75
79
|
const server = createServer(
|
|
76
80
|
{
|
|
@@ -91,12 +95,15 @@ server.listen(8080)
|
|
|
91
95
|
console.log(`Server listening at https://local.example:8080`)
|
|
92
96
|
```
|
|
93
97
|
|
|
94
|
-
5 -
|
|
98
|
+
5 - Run file to start server with node
|
|
95
99
|
|
|
96
100
|
```console
|
|
97
101
|
node ./start_dev_server.mjs
|
|
98
102
|
```
|
|
99
103
|
|
|
104
|
+
At this stage of the documentation you have a server running in https.
|
|
105
|
+
The rest of the documentation goes into details.
|
|
106
|
+
|
|
100
107
|
# Certificate expiration
|
|
101
108
|
|
|
102
109
|
The server certificate expires after one year which is the maximum duration allowed by web browsers.
|
|
@@ -105,7 +112,7 @@ In the unlikely scenario where your local server is running for more than a year
|
|
|
105
112
|
|
|
106
113
|
The authority root certificate expires after 20 years which is close to the maximum allowed duration.
|
|
107
114
|
|
|
108
|
-
In the very unlikely scenario where you are using the same machine for more than 20 years, re-execute [installCertificateAuthority](#installCertificateAuthority) to update certificate authority
|
|
115
|
+
In the very unlikely scenario where you are using the same machine for more than 20 years, re-execute [installCertificateAuthority](#installCertificateAuthority) to update certificate authority then restart your server.
|
|
109
116
|
|
|
110
117
|
# installCertificateAuthority
|
|
111
118
|
|
|
@@ -118,6 +125,11 @@ import { installCertificateAuthority } from "@jsenv/https-local"
|
|
|
118
125
|
await installCertificateAuthority()
|
|
119
126
|
```
|
|
120
127
|
|
|
128
|
+
By default, trusting authority root certificate is a manual process.
|
|
129
|
+
This manual process is documented in [BenMorel/dev-certificates#Import the CA in your browser](https://github.com/BenMorel/dev-certificates/tree/c10cd68945da772f31815b7a36721ddf848ff3a3#import-the-ca-in-your-browser).
|
|
130
|
+
|
|
131
|
+
Trusting the root certificate can also be done programmatically as explained in [Auto trust](#Auto-trust).
|
|
132
|
+
|
|
121
133
|
Find below logs written in terminal when this function is executed.
|
|
122
134
|
|
|
123
135
|
<details>
|
|
@@ -128,7 +140,7 @@ Find below logs written in terminal when this function is executed.
|
|
|
128
140
|
|
|
129
141
|
ℹ authority root certificate not found in filesystem
|
|
130
142
|
Generating authority root certificate with a validity of 20 years...
|
|
131
|
-
✔ authority root certificate written at /Users/dmail/
|
|
143
|
+
✔ authority root certificate written at /Users/dmail/https_local/http_local_root_certificate.crt
|
|
132
144
|
ℹ You should add root certificate to mac keychain
|
|
133
145
|
ℹ You should add root certificate to firefox
|
|
134
146
|
```
|
|
@@ -159,7 +171,7 @@ Check if certificate is in firefox...
|
|
|
159
171
|
|
|
160
172
|
ℹ authority root certificate not found in filesystem
|
|
161
173
|
Generating authority root certificate with a validity of 20 years...
|
|
162
|
-
✔ authority root certificate written at /home/dmail/.config/
|
|
174
|
+
✔ authority root certificate written at /home/dmail/.config/https_local/https_local_root_certificate.crt
|
|
163
175
|
ℹ You should add certificate to linux
|
|
164
176
|
ℹ You should add certificate to chrome
|
|
165
177
|
ℹ You should add certificate to firefox
|
|
@@ -193,7 +205,7 @@ Check if certificate is in firefox...
|
|
|
193
205
|
|
|
194
206
|
ℹ authority root certificate not found in filesystem
|
|
195
207
|
Generating authority root certificate with a validity of 20 years...
|
|
196
|
-
✔ authority root certificate written at C:\Users\Dmail\AppData\Local\
|
|
208
|
+
✔ authority root certificate written at C:\Users\Dmail\AppData\Local\https_local\https_local_root_certificate.crt
|
|
197
209
|
ℹ You should add certificate to windows
|
|
198
210
|
ℹ You should add certificate to firefox
|
|
199
211
|
```
|
|
@@ -216,11 +228,6 @@ Check if certificate is trusted by firefox...
|
|
|
216
228
|
|
|
217
229
|
</details>
|
|
218
230
|
|
|
219
|
-
By default, trusting authority root certificate is a manual process.
|
|
220
|
-
This manual process is documented in [BenMorel/dev-certificates#Import the CA in your browser](https://github.com/BenMorel/dev-certificates/tree/c10cd68945da772f31815b7a36721ddf848ff3a3#import-the-ca-in-your-browser).
|
|
221
|
-
|
|
222
|
-
Trusting the root certificate can also be done programmatically as explained the next part.
|
|
223
|
-
|
|
224
231
|
## Auto trust
|
|
225
232
|
|
|
226
233
|
It's possible to trust root certificate programmatically using _tryToTrust_
|
|
@@ -241,9 +248,9 @@ await installCertificateAuthority({
|
|
|
241
248
|
|
|
242
249
|
ℹ authority root certificate not found in filesystem
|
|
243
250
|
Generating authority root certificate with a validity of 20 years...
|
|
244
|
-
✔ authority root certificate written at /Users/dmail/
|
|
251
|
+
✔ authority root certificate written at /Users/dmail/https_local/https_local_root_certificate.crt
|
|
245
252
|
Adding certificate to mac keychain...
|
|
246
|
-
❯ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "/Users/dmail/
|
|
253
|
+
❯ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "/Users/dmail/https_local/https_local_root_certificate.crt"
|
|
247
254
|
Password:
|
|
248
255
|
✔ certificate added to mac keychain
|
|
249
256
|
Adding certificate to firefox...
|
|
@@ -269,7 +276,7 @@ Check if certificate is in Firefox...
|
|
|
269
276
|
</details>
|
|
270
277
|
|
|
271
278
|
<details>
|
|
272
|
-
<summary>
|
|
279
|
+
<summary>linux</summary>
|
|
273
280
|
|
|
274
281
|
```console
|
|
275
282
|
> node ./install_certificate_authority.mjs
|
|
@@ -280,9 +287,9 @@ Checking certificate validity...
|
|
|
280
287
|
Detect if certificate attributes have changed...
|
|
281
288
|
✔ certificate attributes are the same
|
|
282
289
|
Check if certificate is in linux...
|
|
283
|
-
ℹ certificate in linux
|
|
290
|
+
ℹ certificate not in linux
|
|
284
291
|
Adding certificate to linux...
|
|
285
|
-
❯ sudo /bin/cp -f "/home/dmail/.config/
|
|
292
|
+
❯ sudo /bin/cp -f "/home/dmail/.config/https_local/https_local_root_certificate.crt" /usr/local/share/ca-certificates/https_local_root_certificate.crt
|
|
286
293
|
[sudo] Password for dmail :
|
|
287
294
|
❯ sudo update-ca-certificates
|
|
288
295
|
✔ certificate added to linux
|
|
@@ -329,11 +336,11 @@ Detect if certificate attributes have changed...
|
|
|
329
336
|
✔ certificate attributes are the same
|
|
330
337
|
Check if certificate is trusted by windows...
|
|
331
338
|
ℹ certificate not trusted by windows
|
|
332
|
-
Check if certificate is trusted by firefox...
|
|
333
|
-
ℹ unable to detect if certificate is trusted by firefox (not implemented on windows)
|
|
334
339
|
Adding certificate to windows...
|
|
335
|
-
❯ certutil -addstore -user root C:\Users\Dmail\AppData\Local\
|
|
340
|
+
❯ certutil -addstore -user root C:\Users\Dmail\AppData\Local\https_local\https_local_root_certificate.crt
|
|
336
341
|
✔ certificate added to windows
|
|
342
|
+
Check if certificate is trusted by firefox...
|
|
343
|
+
ℹ unable to detect if certificate is trusted by firefox (not implemented on windows)
|
|
337
344
|
```
|
|
338
345
|
|
|
339
346
|
_second execution logs_
|
|
@@ -354,16 +361,33 @@ Check if certificate is trusted by firefox...
|
|
|
354
361
|
|
|
355
362
|
</details>
|
|
356
363
|
|
|
364
|
+
# requestCertificateForLocalhost
|
|
365
|
+
|
|
366
|
+
_requestCertificateForLocalhost_ function returns a certificate and private key that can be used to start a server in HTTPS.
|
|
367
|
+
|
|
368
|
+
```js
|
|
369
|
+
import { createServer } from "node:https"
|
|
370
|
+
import { requestCertificateForLocalhost } from "@jsenv/https-local"
|
|
371
|
+
|
|
372
|
+
const { serverCertificate, serverCertificatePrivateKey } =
|
|
373
|
+
await requestCertificateForLocalhost({
|
|
374
|
+
serverCertificateAltNames: ["localhost", "local.example"],
|
|
375
|
+
})
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
[installCertificateAuthority](#installCertificateAuthority) must be called before this function.
|
|
379
|
+
|
|
357
380
|
# verifyHostsFile
|
|
358
381
|
|
|
359
|
-
|
|
382
|
+
This function is not mandatory to obtain the https certificates.
|
|
383
|
+
But it is useful to programmatically verify ip mappings that are important for your local server are present in hosts file.
|
|
360
384
|
|
|
361
385
|
```js
|
|
362
386
|
import { verifyHostsFile } from "@jsenv/https-local"
|
|
363
387
|
|
|
364
388
|
await verifyHostsFile({
|
|
365
389
|
ipMappings: {
|
|
366
|
-
"127.0.0.1": ["localhost", "local.example
|
|
390
|
+
"127.0.0.1": ["localhost", "local.example"],
|
|
367
391
|
},
|
|
368
392
|
})
|
|
369
393
|
```
|
|
@@ -381,7 +405,7 @@ Check hosts file content...
|
|
|
381
405
|
--- hosts file path ---
|
|
382
406
|
/etc/hosts
|
|
383
407
|
--- line(s) to add ---
|
|
384
|
-
127.0.0.1 localhost local.example
|
|
408
|
+
127.0.0.1 localhost local.example
|
|
385
409
|
```
|
|
386
410
|
|
|
387
411
|
</details>
|
|
@@ -397,7 +421,7 @@ Check hosts file content...
|
|
|
397
421
|
--- hosts file path ---
|
|
398
422
|
C:\\Windows\\System32\\Drivers\\etc\\hosts
|
|
399
423
|
--- line(s) to add ---
|
|
400
|
-
127.0.0.1 localhost local.example
|
|
424
|
+
127.0.0.1 localhost local.example
|
|
401
425
|
```
|
|
402
426
|
|
|
403
427
|
</details>
|
|
@@ -411,7 +435,7 @@ import { verifyHostsFile } from "@jsenv/https-local"
|
|
|
411
435
|
|
|
412
436
|
await verifyHostsFile({
|
|
413
437
|
ipMappings: {
|
|
414
|
-
"127.0.0.1": ["localhost", "local.example
|
|
438
|
+
"127.0.0.1": ["localhost", "local.example"],
|
|
415
439
|
},
|
|
416
440
|
tryToUpdateHostsFile: true,
|
|
417
441
|
})
|
|
@@ -424,7 +448,7 @@ await verifyHostsFile({
|
|
|
424
448
|
Check hosts file content...
|
|
425
449
|
ℹ 1 mapping is missing in hosts file
|
|
426
450
|
Adding 1 mapping(s) in hosts file...
|
|
427
|
-
❯ echo "127.0.0.1 local.example
|
|
451
|
+
❯ echo "127.0.0.1 local.example" | sudo tee -a /etc/hosts
|
|
428
452
|
Password:
|
|
429
453
|
✔ mappings added to hosts file
|
|
430
454
|
```
|
|
@@ -447,7 +471,7 @@ Check hosts file content...
|
|
|
447
471
|
Check hosts file content...
|
|
448
472
|
ℹ 1 mapping is missing in hosts file
|
|
449
473
|
Adding 1 mapping(s) in hosts file...
|
|
450
|
-
❯ (echo 127.0.0.1 local.example
|
|
474
|
+
❯ (echo 127.0.0.1 local.example) >> C:\\Windows\\System32\\Drivers\\etc\\hosts
|
|
451
475
|
Password:
|
|
452
476
|
✔ mappings added to hosts file
|
|
453
477
|
```
|
|
@@ -462,36 +486,3 @@ Check hosts file content...
|
|
|
462
486
|
```
|
|
463
487
|
|
|
464
488
|
</details>
|
|
465
|
-
|
|
466
|
-
# requestCertificateForLocalhost
|
|
467
|
-
|
|
468
|
-
_requestCertificateForLocalhost_ function returns a certificate and private key that can be used to start a server in HTTPS.
|
|
469
|
-
|
|
470
|
-
```js
|
|
471
|
-
import { createServer } from "node:https"
|
|
472
|
-
import { requestCertificateForLocalhost } from "@jsenv/https-local"
|
|
473
|
-
|
|
474
|
-
const { serverCertificate, serverCertificatePrivateKey } = await requestCertificateForLocalhost({
|
|
475
|
-
serverCertificateAltNames: ["localhost", "local.example.com"],
|
|
476
|
-
})
|
|
477
|
-
|
|
478
|
-
const server = createServer(
|
|
479
|
-
{
|
|
480
|
-
cert: serverCertificate,
|
|
481
|
-
key: serverCertificatePrivateKey,
|
|
482
|
-
},
|
|
483
|
-
(request, response) => {
|
|
484
|
-
const body = "Hello world"
|
|
485
|
-
response.writeHead(200, {
|
|
486
|
-
"content-type": "text/plain",
|
|
487
|
-
"content-length": Buffer.byteLength(body),
|
|
488
|
-
})
|
|
489
|
-
response.write(body)
|
|
490
|
-
response.end()
|
|
491
|
-
},
|
|
492
|
-
)
|
|
493
|
-
server.listen(8080)
|
|
494
|
-
console.log(`Server listening at https://localhost:8080`)
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
[installCertificateAuthority](#installCertificateAuthority) must be called before this function.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/https-local",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "A programmatic way to generate locally trusted certificates",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -13,11 +13,10 @@
|
|
|
13
13
|
"url": "https://github.com/jsenv/https-local"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
16
|
+
"node": ">=16.13.0"
|
|
17
17
|
},
|
|
18
18
|
"publishConfig": {
|
|
19
|
-
"access": "public"
|
|
20
|
-
"registry": "https://registry.npmjs.org"
|
|
19
|
+
"access": "public"
|
|
21
20
|
},
|
|
22
21
|
"type": "module",
|
|
23
22
|
"exports": {
|
|
@@ -26,14 +25,17 @@
|
|
|
26
25
|
},
|
|
27
26
|
"./*": "./*"
|
|
28
27
|
},
|
|
28
|
+
"main": "./main.js",
|
|
29
29
|
"files": [
|
|
30
30
|
"/src/",
|
|
31
31
|
"/main.js"
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
|
-
"eslint
|
|
35
|
-
"
|
|
36
|
-
"
|
|
34
|
+
"eslint": "node ./node_modules/eslint/bin/eslint.js . --ext=.js,.mjs",
|
|
35
|
+
"importmap": "node ./script/importmap/importmap.mjs",
|
|
36
|
+
"performances": "node --expose-gc ./script/performance/generate_performance_report.mjs --log",
|
|
37
|
+
"test": "node ./script/test/test.mjs",
|
|
38
|
+
"test-with-coverage": "npm run test -- --coverage",
|
|
37
39
|
"start-node-server": "node ./script/certificate/start_node_server.mjs",
|
|
38
40
|
"log-root-certificate-trust": "node ./script/certificate/log_root_certificate_trust.mjs",
|
|
39
41
|
"trust-root-certificate": "node ./script/certificate/trust_root_certificate.mjs",
|
|
@@ -43,39 +45,31 @@
|
|
|
43
45
|
"remove-localhost-mappings": "node ./script/hosts/remove_localhost_mappings.mjs",
|
|
44
46
|
"verify-localhost-mappings": "node ./script/hosts/verify_localhost_mappings.mjs",
|
|
45
47
|
"ensure-localhost-mappings": "node ./script/hosts/ensure_localhost_mappings.mjs",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"prettier-format": "node ./script/prettier/prettier_format.mjs",
|
|
49
|
-
"prettier-format-stage": "npm run prettier-format -- --staged",
|
|
50
|
-
"prettier-check": "npm run prettier-format -- --dry-run",
|
|
51
|
-
"install-playwright": "npx playwright install-deps && npx playwright install",
|
|
52
|
-
"prepublishOnly": "node ./script/publish/remove_postinstall.mjs",
|
|
53
|
-
"postpublish": "node ./script/publish/restore_postinstall.mjs"
|
|
48
|
+
"prettier": "prettier --write .",
|
|
49
|
+
"playwright-install": "npx playwright install-deps && npx playwright install"
|
|
54
50
|
},
|
|
55
51
|
"dependencies": {
|
|
56
|
-
"@jsenv/filesystem": "2.
|
|
52
|
+
"@jsenv/filesystem": "2.7.1",
|
|
57
53
|
"@jsenv/logger": "4.0.1",
|
|
58
54
|
"command-exists": "1.2.9",
|
|
59
55
|
"is-unicode-supported": "1.1.0",
|
|
60
|
-
"node-forge": "
|
|
56
|
+
"node-forge": "1.2.1",
|
|
61
57
|
"sudo-prompt": "9.2.1",
|
|
62
|
-
"supports-color": "9.
|
|
58
|
+
"supports-color": "9.2.1",
|
|
63
59
|
"which": "2.0.2"
|
|
64
60
|
},
|
|
65
61
|
"devDependencies": {
|
|
66
|
-
"@jsenv/assert": "2.
|
|
67
|
-
"@jsenv/
|
|
68
|
-
"@jsenv/
|
|
69
|
-
"@jsenv/
|
|
70
|
-
"@jsenv/
|
|
71
|
-
"@jsenv/importmap-
|
|
72
|
-
"@jsenv/
|
|
73
|
-
"@jsenv/
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"playwright": "1.14.0",
|
|
79
|
-
"prettier": "2.3.2"
|
|
62
|
+
"@jsenv/assert": "2.4.1",
|
|
63
|
+
"@jsenv/core": "25.3.0",
|
|
64
|
+
"@jsenv/eslint-config": "16.0.9",
|
|
65
|
+
"@jsenv/github-release-package": "1.3.2",
|
|
66
|
+
"@jsenv/importmap-eslint-resolver": "5.2.5",
|
|
67
|
+
"@jsenv/importmap-node-module": "5.1.3",
|
|
68
|
+
"@jsenv/package-publish": "1.7.1",
|
|
69
|
+
"@jsenv/performance-impact": "2.2.4",
|
|
70
|
+
"eslint": "8.7.0",
|
|
71
|
+
"eslint-plugin-import": "2.25.4",
|
|
72
|
+
"playwright": "1.18.0",
|
|
73
|
+
"prettier": "2.5.1"
|
|
80
74
|
}
|
|
81
|
-
}
|
|
75
|
+
}
|
|
@@ -6,7 +6,10 @@ import { readFile, writeFile, removeFileSystemNode } from "@jsenv/filesystem"
|
|
|
6
6
|
import { infoSign, okSign } from "./internal/logs.js"
|
|
7
7
|
import { getAuthorityFileInfos } from "./internal/authority_file_infos.js"
|
|
8
8
|
import { attributeDescriptionFromAttributeArray } from "./internal/certificate_data_converter.js"
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
formatTimeDelta,
|
|
11
|
+
formatDuration,
|
|
12
|
+
} from "./internal/validity_formatting.js"
|
|
10
13
|
import { importNodeForge } from "./internal/forge.js"
|
|
11
14
|
import { createAuthorityRootCertificate } from "./internal/certificate_generator.js"
|
|
12
15
|
import { importPlatformMethods } from "./internal/platform.js"
|
|
@@ -54,8 +57,11 @@ export const installCertificateAuthority = async ({
|
|
|
54
57
|
)
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
const {
|
|
58
|
-
|
|
60
|
+
const {
|
|
61
|
+
authorityJsonFileInfo,
|
|
62
|
+
rootCertificateFileInfo,
|
|
63
|
+
rootCertificatePrivateKeyFileInfo,
|
|
64
|
+
} = getAuthorityFileInfos()
|
|
59
65
|
const authorityJsonFileUrl = authorityJsonFileInfo.url
|
|
60
66
|
const rootCertificateFileUrl = rootCertificateFileInfo.url
|
|
61
67
|
const rootPrivateKeyFileUrl = rootCertificatePrivateKeyFileInfo.url
|
|
@@ -76,16 +82,23 @@ export const installCertificateAuthority = async ({
|
|
|
76
82
|
})
|
|
77
83
|
|
|
78
84
|
const { pki } = await importNodeForge()
|
|
79
|
-
const rootCertificate = pemAsFileContent(
|
|
85
|
+
const rootCertificate = pemAsFileContent(
|
|
86
|
+
pki.certificateToPem(rootCertificateForgeObject),
|
|
87
|
+
)
|
|
80
88
|
const rootCertificatePrivateKey = pemAsFileContent(
|
|
81
89
|
pki.privateKeyToPem(rootCertificatePrivateKeyForgeObject),
|
|
82
90
|
)
|
|
83
91
|
|
|
84
92
|
await writeFile(rootCertificateFileUrl, rootCertificate)
|
|
85
93
|
await writeFile(rootPrivateKeyFileUrl, rootCertificatePrivateKey)
|
|
86
|
-
await writeFile(
|
|
94
|
+
await writeFile(
|
|
95
|
+
authorityJsonFileUrl,
|
|
96
|
+
JSON.stringify({ serialNumber: 0 }, null, " "),
|
|
97
|
+
)
|
|
87
98
|
|
|
88
|
-
logger.info(
|
|
99
|
+
logger.info(
|
|
100
|
+
`${okSign} authority root certificate written at ${rootCertificateFileInfo.path}`,
|
|
101
|
+
)
|
|
89
102
|
return {
|
|
90
103
|
rootCertificateForgeObject,
|
|
91
104
|
rootCertificatePrivateKeyForgeObject,
|
|
@@ -140,14 +153,18 @@ export const installCertificateAuthority = async ({
|
|
|
140
153
|
logger.debug(
|
|
141
154
|
`Authority root certificate is not on filesystem at ${rootCertificateFileInfo.path}`,
|
|
142
155
|
)
|
|
143
|
-
logger.info(
|
|
156
|
+
logger.info(
|
|
157
|
+
`${infoSign} authority root certificate not found in filesystem`,
|
|
158
|
+
)
|
|
144
159
|
return generate()
|
|
145
160
|
}
|
|
146
161
|
if (!rootCertificatePrivateKeyFileInfo.exists) {
|
|
147
162
|
logger.debug(
|
|
148
163
|
`Authority root certificate private key is not on filesystem at ${rootCertificatePrivateKeyFileInfo.path}`,
|
|
149
164
|
)
|
|
150
|
-
logger.info(
|
|
165
|
+
logger.info(
|
|
166
|
+
`${infoSign} authority root certificate not found in filesystem`,
|
|
167
|
+
)
|
|
151
168
|
return generate()
|
|
152
169
|
}
|
|
153
170
|
logger.debug(
|
|
@@ -155,18 +172,23 @@ export const installCertificateAuthority = async ({
|
|
|
155
172
|
)
|
|
156
173
|
logger.info(`${okSign} authority root certificate found in filesystem`)
|
|
157
174
|
|
|
158
|
-
const rootCertificate = await readFile(rootCertificateFileInfo.path, {
|
|
175
|
+
const rootCertificate = await readFile(rootCertificateFileInfo.path, {
|
|
176
|
+
as: "string",
|
|
177
|
+
})
|
|
159
178
|
const { pki } = await importNodeForge()
|
|
160
179
|
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate)
|
|
161
180
|
|
|
162
181
|
logger.info(`Checking certificate validity...`)
|
|
163
|
-
const rootCertificateValidityDurationInMs =
|
|
182
|
+
const rootCertificateValidityDurationInMs =
|
|
183
|
+
getCertificateValidityDurationInMs(rootCertificateForgeObject)
|
|
184
|
+
const rootCertificateValidityRemainingMs = getCertificateRemainingMs(
|
|
164
185
|
rootCertificateForgeObject,
|
|
165
186
|
)
|
|
166
|
-
const rootCertificateValidityRemainingMs = getCertificateRemainingMs(rootCertificateForgeObject)
|
|
167
187
|
if (rootCertificateValidityRemainingMs < 0) {
|
|
168
188
|
logger.info(
|
|
169
|
-
`${infoSign} certificate expired ${formatTimeDelta(
|
|
189
|
+
`${infoSign} certificate expired ${formatTimeDelta(
|
|
190
|
+
rootCertificateValidityRemainingMs,
|
|
191
|
+
)}`,
|
|
170
192
|
)
|
|
171
193
|
return regenerate()
|
|
172
194
|
}
|
|
@@ -174,30 +196,44 @@ export const installCertificateAuthority = async ({
|
|
|
174
196
|
rootCertificateValidityRemainingMs / rootCertificateValidityDurationInMs
|
|
175
197
|
if (rootCertificateValidityRemainingRatio < aboutToExpireRatio) {
|
|
176
198
|
logger.info(
|
|
177
|
-
`${infoSign} certificate will expire ${formatTimeDelta(
|
|
199
|
+
`${infoSign} certificate will expire ${formatTimeDelta(
|
|
200
|
+
rootCertificateValidityRemainingMs,
|
|
201
|
+
)}`,
|
|
178
202
|
)
|
|
179
203
|
return regenerate()
|
|
180
204
|
}
|
|
181
205
|
logger.info(
|
|
182
|
-
`${okSign} certificate still valid for ${formatDuration(
|
|
206
|
+
`${okSign} certificate still valid for ${formatDuration(
|
|
207
|
+
rootCertificateValidityRemainingMs,
|
|
208
|
+
)}`,
|
|
183
209
|
)
|
|
184
210
|
|
|
185
211
|
logger.info(`Detect if certificate attributes have changed...`)
|
|
186
|
-
const rootCertificateDifferences = compareRootCertificateAttributes(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
212
|
+
const rootCertificateDifferences = compareRootCertificateAttributes(
|
|
213
|
+
rootCertificateForgeObject,
|
|
214
|
+
{
|
|
215
|
+
certificateCommonName,
|
|
216
|
+
certificateValidityDurationInMs,
|
|
217
|
+
},
|
|
218
|
+
)
|
|
190
219
|
if (rootCertificateDifferences.length) {
|
|
191
220
|
const paramNames = Object.keys(rootCertificateDifferences)
|
|
192
|
-
logger.info(
|
|
221
|
+
logger.info(
|
|
222
|
+
`${infoSign} certificate attributes are outdated: ${paramNames}`,
|
|
223
|
+
)
|
|
193
224
|
return regenerate()
|
|
194
225
|
}
|
|
195
226
|
logger.info(`${okSign} certificate attributes are the same`)
|
|
196
227
|
|
|
197
|
-
const rootCertificatePrivateKey = await readFile(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
228
|
+
const rootCertificatePrivateKey = await readFile(
|
|
229
|
+
rootCertificatePrivateKeyFileInfo.path,
|
|
230
|
+
{
|
|
231
|
+
as: "string",
|
|
232
|
+
},
|
|
233
|
+
)
|
|
234
|
+
const rootCertificatePrivateKeyForgeObject = pki.privateKeyFromPem(
|
|
235
|
+
rootCertificatePrivateKey,
|
|
236
|
+
)
|
|
201
237
|
|
|
202
238
|
const trustInfo = await platformMethods.executeTrustQuery({
|
|
203
239
|
logger,
|
|
@@ -272,8 +308,11 @@ export const uninstallCertificateAuthority = async ({
|
|
|
272
308
|
logger = createLogger({ logLevel }),
|
|
273
309
|
tryToUntrust = false,
|
|
274
310
|
} = {}) => {
|
|
275
|
-
const {
|
|
276
|
-
|
|
311
|
+
const {
|
|
312
|
+
authorityJsonFileInfo,
|
|
313
|
+
rootCertificateFileInfo,
|
|
314
|
+
rootCertificatePrivateKeyFileInfo,
|
|
315
|
+
} = getAuthorityFileInfos()
|
|
277
316
|
|
|
278
317
|
const filesToRemove = []
|
|
279
318
|
|
|
@@ -283,7 +322,9 @@ export const uninstallCertificateAuthority = async ({
|
|
|
283
322
|
if (rootCertificateFileInfo.exists) {
|
|
284
323
|
// first untrust the root cert file
|
|
285
324
|
if (tryToUntrust) {
|
|
286
|
-
const rootCertificate = await readFile(rootCertificateFileInfo.url, {
|
|
325
|
+
const rootCertificate = await readFile(rootCertificateFileInfo.url, {
|
|
326
|
+
as: "string",
|
|
327
|
+
})
|
|
287
328
|
const { pki } = await importNodeForge()
|
|
288
329
|
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate)
|
|
289
330
|
const rootCertificateCommonName = attributeDescriptionFromAttributeArray(
|
|
@@ -15,8 +15,8 @@ export const requestCertificateForLocalhost = async ({
|
|
|
15
15
|
logLevel,
|
|
16
16
|
logger = createLogger({ logLevel }), // to be able to catch logs during unit tests
|
|
17
17
|
|
|
18
|
-
serverCertificateAltNames = [],
|
|
19
|
-
serverCertificateCommonName = "
|
|
18
|
+
serverCertificateAltNames = ["localhost"],
|
|
19
|
+
serverCertificateCommonName = "https local server certificate",
|
|
20
20
|
serverCertificateValidityDurationInMs = createValidityDurationOfXDays(396),
|
|
21
21
|
} = {}) => {
|
|
22
22
|
if (typeof serverCertificateValidityDurationInMs !== "number") {
|
|
@@ -41,8 +41,11 @@ export const requestCertificateForLocalhost = async ({
|
|
|
41
41
|
)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const {
|
|
45
|
-
|
|
44
|
+
const {
|
|
45
|
+
authorityJsonFileInfo,
|
|
46
|
+
rootCertificateFileInfo,
|
|
47
|
+
rootCertificatePrivateKeyFileInfo,
|
|
48
|
+
} = getAuthorityFileInfos()
|
|
46
49
|
if (!rootCertificateFileInfo.exists) {
|
|
47
50
|
throw new Error(
|
|
48
51
|
`Certificate authority not found, "installCertificateAuthority" must be called before "requestCertificateForLocalhost"`,
|
|
@@ -57,16 +60,26 @@ export const requestCertificateForLocalhost = async ({
|
|
|
57
60
|
|
|
58
61
|
logger.debug(`Restoring certificate authority from filesystem...`)
|
|
59
62
|
const { pki } = await importNodeForge()
|
|
60
|
-
const rootCertificate = await readFile(rootCertificateFileInfo.url, {
|
|
61
|
-
const rootCertificatePrivateKey = await readFile(rootCertificatePrivateKeyFileInfo.url, {
|
|
63
|
+
const rootCertificate = await readFile(rootCertificateFileInfo.url, {
|
|
62
64
|
as: "string",
|
|
63
65
|
})
|
|
64
|
-
const
|
|
66
|
+
const rootCertificatePrivateKey = await readFile(
|
|
67
|
+
rootCertificatePrivateKeyFileInfo.url,
|
|
68
|
+
{
|
|
69
|
+
as: "string",
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
const certificateAuthorityData = await readFile(authorityJsonFileInfo.url, {
|
|
73
|
+
as: "json",
|
|
74
|
+
})
|
|
65
75
|
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate)
|
|
66
|
-
const rootCertificatePrivateKeyForgeObject = pki.privateKeyFromPem(
|
|
76
|
+
const rootCertificatePrivateKeyForgeObject = pki.privateKeyFromPem(
|
|
77
|
+
rootCertificatePrivateKey,
|
|
78
|
+
)
|
|
67
79
|
logger.debug(`${okSign} certificate authority restored from filesystem`)
|
|
68
80
|
|
|
69
|
-
const serverCertificateSerialNumber =
|
|
81
|
+
const serverCertificateSerialNumber =
|
|
82
|
+
certificateAuthorityData.serialNumber + 1
|
|
70
83
|
await writeFile(
|
|
71
84
|
authorityJsonFileInfo.url,
|
|
72
85
|
JSON.stringify({ serialNumber: serverCertificateSerialNumber }, null, " "),
|
|
@@ -81,14 +94,17 @@ export const requestCertificateForLocalhost = async ({
|
|
|
81
94
|
await requestCertificateFromAuthority({
|
|
82
95
|
logger,
|
|
83
96
|
authorityCertificateForgeObject: rootCertificateForgeObject,
|
|
84
|
-
auhtorityCertificatePrivateKeyForgeObject:
|
|
97
|
+
auhtorityCertificatePrivateKeyForgeObject:
|
|
98
|
+
rootCertificatePrivateKeyForgeObject,
|
|
85
99
|
serialNumber: serverCertificateSerialNumber,
|
|
86
100
|
altNames: serverCertificateAltNames,
|
|
87
101
|
commonName: serverCertificateCommonName,
|
|
88
102
|
validityDurationInMs: serverCertificateValidityDurationInMs,
|
|
89
103
|
})
|
|
90
104
|
const serverCertificate = pki.certificateToPem(certificateForgeObject)
|
|
91
|
-
const serverCertificatePrivateKey = pki.privateKeyToPem(
|
|
105
|
+
const serverCertificatePrivateKey = pki.privateKeyToPem(
|
|
106
|
+
certificatePrivateKeyForgeObject,
|
|
107
|
+
)
|
|
92
108
|
logger.debug(
|
|
93
109
|
`${okSign} server certificate generated, it will be valid for ${formatDuration(
|
|
94
110
|
serverCertificateValidityDurationInMs,
|