@jsenv/https-local 3.0.6 → 3.1.0
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 +160 -192
- package/package.json +19 -38
- package/src/certificate_authority.js +111 -110
- package/src/certificate_request.js +34 -35
- package/src/hosts_file_verif.js +34 -35
- package/src/https_local_cli.mjs +74 -0
- package/src/internal/authority_file_infos.js +12 -13
- package/src/internal/browser_detection.js +4 -4
- package/src/internal/certificate_authority_file_urls.js +23 -23
- package/src/internal/certificate_data_converter.js +39 -39
- package/src/internal/certificate_generator.js +39 -39
- package/src/internal/command.js +6 -6
- package/src/internal/exec.js +10 -10
- package/src/internal/forge.js +3 -3
- package/src/internal/hosts/hosts_utils.js +2 -2
- package/src/internal/hosts/parse_hosts.js +67 -66
- package/src/internal/hosts/read_hosts.js +5 -6
- package/src/internal/hosts/write_hosts.js +29 -31
- package/src/internal/hosts/write_line_hosts.js +30 -32
- package/src/internal/hosts.js +5 -5
- package/src/internal/linux/chrome_linux.js +21 -20
- package/src/internal/linux/firefox_linux.js +23 -20
- package/src/internal/linux/linux.js +8 -8
- package/src/internal/linux/linux_trust_store.js +58 -59
- package/src/internal/linux/nss_linux.js +21 -20
- package/src/internal/mac/chrome_mac.js +15 -16
- package/src/internal/mac/firefox_mac.js +20 -21
- package/src/internal/mac/mac.js +10 -10
- package/src/internal/mac/mac_keychain.js +46 -47
- package/src/internal/mac/nss_mac.js +29 -30
- package/src/internal/mac/safari.js +2 -2
- package/src/internal/memoize.js +14 -14
- package/src/internal/nssdb_browser.js +150 -145
- package/src/internal/platform.js +6 -6
- package/src/internal/search_certificate_in_command_output.js +4 -4
- package/src/internal/trust_query.js +4 -4
- package/src/internal/unsupported_platform/unsupported_platform.js +5 -5
- package/src/internal/validity_formatting.js +32 -32
- package/src/internal/windows/chrome_windows.js +26 -27
- package/src/internal/windows/edge.js +2 -2
- package/src/internal/windows/firefox_windows.js +31 -32
- package/src/internal/windows/windows.js +10 -10
- package/src/internal/windows/windows_certutil.js +41 -42
- package/src/jsenvParameters.js +2 -2
- package/src/main.js +5 -8
- package/src/validity_duration.js +12 -12
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { readFile,
|
|
2
|
-
import { UNICODE,
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { readFile, removeEntry, writeFile } from "@jsenv/filesystem";
|
|
2
|
+
import { UNICODE, createDetailedMessage, createLogger } from "@jsenv/humanize";
|
|
3
|
+
import { getAuthorityFileInfos } from "./internal/authority_file_infos.js";
|
|
4
|
+
import { attributeDescriptionFromAttributeArray } from "./internal/certificate_data_converter.js";
|
|
5
|
+
import { createAuthorityRootCertificate } from "./internal/certificate_generator.js";
|
|
6
|
+
import { forge } from "./internal/forge.js";
|
|
7
|
+
import { importPlatformMethods } from "./internal/platform.js";
|
|
7
8
|
import {
|
|
8
|
-
formatTimeDelta,
|
|
9
9
|
formatDuration,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { verifyRootCertificateValidityDuration } from "./validity_duration.js"
|
|
10
|
+
formatTimeDelta,
|
|
11
|
+
} from "./internal/validity_formatting.js";
|
|
12
|
+
import { jsenvParameters } from "./jsenvParameters.js";
|
|
13
|
+
import { verifyRootCertificateValidityDuration } from "./validity_duration.js";
|
|
15
14
|
|
|
16
15
|
export const installCertificateAuthority = async ({
|
|
17
16
|
logLevel,
|
|
@@ -29,80 +28,80 @@ export const installCertificateAuthority = async ({
|
|
|
29
28
|
if (typeof certificateCommonName !== "string") {
|
|
30
29
|
throw new TypeError(
|
|
31
30
|
`certificateCommonName must be a string but received ${certificateCommonName}`,
|
|
32
|
-
)
|
|
31
|
+
);
|
|
33
32
|
}
|
|
34
33
|
if (typeof certificateValidityDurationInMs !== "number") {
|
|
35
34
|
throw new TypeError(
|
|
36
35
|
`certificateValidityDurationInMs must be a number but received ${certificateValidityDurationInMs}`,
|
|
37
|
-
)
|
|
36
|
+
);
|
|
38
37
|
}
|
|
39
38
|
if (certificateValidityDurationInMs < 1) {
|
|
40
39
|
throw new TypeError(
|
|
41
40
|
`certificateValidityDurationInMs must be > 0 but received ${certificateValidityDurationInMs}`,
|
|
42
|
-
)
|
|
41
|
+
);
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
const validityDurationInfo = verifyRootCertificateValidityDuration(
|
|
46
45
|
certificateValidityDurationInMs,
|
|
47
|
-
)
|
|
46
|
+
);
|
|
48
47
|
if (!validityDurationInfo.ok) {
|
|
49
|
-
certificateValidityDurationInMs = validityDurationInfo.maxAllowedValue
|
|
48
|
+
certificateValidityDurationInMs = validityDurationInfo.maxAllowedValue;
|
|
50
49
|
logger.warn(
|
|
51
50
|
createDetailedMessage(validityDurationInfo.message, {
|
|
52
51
|
details: validityDurationInfo.details,
|
|
53
52
|
}),
|
|
54
|
-
)
|
|
53
|
+
);
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
const {
|
|
58
57
|
authorityJsonFileInfo,
|
|
59
58
|
rootCertificateFileInfo,
|
|
60
59
|
rootCertificatePrivateKeyFileInfo,
|
|
61
|
-
} = getAuthorityFileInfos()
|
|
62
|
-
const authorityJsonFileUrl = authorityJsonFileInfo.url
|
|
63
|
-
const rootCertificateFileUrl = rootCertificateFileInfo.url
|
|
64
|
-
const rootPrivateKeyFileUrl = rootCertificatePrivateKeyFileInfo.url
|
|
65
|
-
const platformMethods = await importPlatformMethods()
|
|
60
|
+
} = getAuthorityFileInfos();
|
|
61
|
+
const authorityJsonFileUrl = authorityJsonFileInfo.url;
|
|
62
|
+
const rootCertificateFileUrl = rootCertificateFileInfo.url;
|
|
63
|
+
const rootPrivateKeyFileUrl = rootCertificatePrivateKeyFileInfo.url;
|
|
64
|
+
const platformMethods = await importPlatformMethods();
|
|
66
65
|
|
|
67
66
|
const generateRootCertificate = async () => {
|
|
68
67
|
logger.info(
|
|
69
68
|
`Generating authority root certificate with a validity of ${formatDuration(
|
|
70
69
|
certificateValidityDurationInMs,
|
|
71
70
|
)}...`,
|
|
72
|
-
)
|
|
71
|
+
);
|
|
73
72
|
const { rootCertificateForgeObject, rootCertificatePrivateKeyForgeObject } =
|
|
74
73
|
await createAuthorityRootCertificate({
|
|
75
74
|
logger,
|
|
76
75
|
commonName: certificateCommonName,
|
|
77
76
|
validityDurationInMs: certificateValidityDurationInMs,
|
|
78
77
|
serialNumber: 0,
|
|
79
|
-
})
|
|
78
|
+
});
|
|
80
79
|
|
|
81
|
-
const { pki } = forge
|
|
80
|
+
const { pki } = forge;
|
|
82
81
|
const rootCertificate = pemAsFileContent(
|
|
83
82
|
pki.certificateToPem(rootCertificateForgeObject),
|
|
84
|
-
)
|
|
83
|
+
);
|
|
85
84
|
const rootCertificatePrivateKey = pemAsFileContent(
|
|
86
85
|
pki.privateKeyToPem(rootCertificatePrivateKeyForgeObject),
|
|
87
|
-
)
|
|
86
|
+
);
|
|
88
87
|
|
|
89
|
-
await writeFile(rootCertificateFileUrl, rootCertificate)
|
|
90
|
-
await writeFile(rootPrivateKeyFileUrl, rootCertificatePrivateKey)
|
|
88
|
+
await writeFile(rootCertificateFileUrl, rootCertificate);
|
|
89
|
+
await writeFile(rootPrivateKeyFileUrl, rootCertificatePrivateKey);
|
|
91
90
|
await writeFile(
|
|
92
91
|
authorityJsonFileUrl,
|
|
93
92
|
JSON.stringify({ serialNumber: 0 }, null, " "),
|
|
94
|
-
)
|
|
93
|
+
);
|
|
95
94
|
|
|
96
95
|
logger.info(
|
|
97
96
|
`${UNICODE.OK} authority root certificate written at ${rootCertificateFileInfo.path}`,
|
|
98
|
-
)
|
|
97
|
+
);
|
|
99
98
|
return {
|
|
100
99
|
rootCertificateForgeObject,
|
|
101
100
|
rootCertificatePrivateKeyForgeObject,
|
|
102
101
|
rootCertificate,
|
|
103
102
|
rootCertificatePrivateKey,
|
|
104
|
-
}
|
|
105
|
-
}
|
|
103
|
+
};
|
|
104
|
+
};
|
|
106
105
|
|
|
107
106
|
const generate = async () => {
|
|
108
107
|
const {
|
|
@@ -110,7 +109,7 @@ export const installCertificateAuthority = async ({
|
|
|
110
109
|
rootCertificatePrivateKeyForgeObject,
|
|
111
110
|
rootCertificate,
|
|
112
111
|
rootCertificatePrivateKey,
|
|
113
|
-
} = await generateRootCertificate()
|
|
112
|
+
} = await generateRootCertificate();
|
|
114
113
|
|
|
115
114
|
const trustInfo = await platformMethods.executeTrustQuery({
|
|
116
115
|
logger,
|
|
@@ -120,7 +119,7 @@ export const installCertificateAuthority = async ({
|
|
|
120
119
|
certificate: rootCertificate,
|
|
121
120
|
verb: tryToTrust ? "ADD_TRUST" : "CHECK_TRUST",
|
|
122
121
|
NSSDynamicInstall,
|
|
123
|
-
})
|
|
122
|
+
});
|
|
124
123
|
|
|
125
124
|
return {
|
|
126
125
|
rootCertificateForgeObject,
|
|
@@ -129,8 +128,8 @@ export const installCertificateAuthority = async ({
|
|
|
129
128
|
rootCertificatePrivateKey,
|
|
130
129
|
rootCertificateFilePath: rootCertificateFileInfo.path,
|
|
131
130
|
trustInfo,
|
|
132
|
-
}
|
|
133
|
-
}
|
|
131
|
+
};
|
|
132
|
+
};
|
|
134
133
|
|
|
135
134
|
const regenerate = async () => {
|
|
136
135
|
if (tryToTrust) {
|
|
@@ -140,97 +139,97 @@ export const installCertificateAuthority = async ({
|
|
|
140
139
|
certificateFileUrl: rootCertificateFileUrl,
|
|
141
140
|
certificate: rootCertificate,
|
|
142
141
|
verb: "REMOVE_TRUST",
|
|
143
|
-
})
|
|
142
|
+
});
|
|
144
143
|
}
|
|
145
|
-
return generate()
|
|
146
|
-
}
|
|
144
|
+
return generate();
|
|
145
|
+
};
|
|
147
146
|
|
|
148
|
-
logger.debug(`Search existing certificate authority on filesystem...`)
|
|
147
|
+
logger.debug(`Search existing certificate authority on filesystem...`);
|
|
149
148
|
if (!rootCertificateFileInfo.exists) {
|
|
150
149
|
logger.debug(
|
|
151
150
|
`Authority root certificate is not on filesystem at ${rootCertificateFileInfo.path}`,
|
|
152
|
-
)
|
|
151
|
+
);
|
|
153
152
|
logger.info(
|
|
154
153
|
`${UNICODE.INFO} authority root certificate not found in filesystem`,
|
|
155
|
-
)
|
|
156
|
-
return generate()
|
|
154
|
+
);
|
|
155
|
+
return generate();
|
|
157
156
|
}
|
|
158
157
|
if (!rootCertificatePrivateKeyFileInfo.exists) {
|
|
159
158
|
logger.debug(
|
|
160
159
|
`Authority root certificate private key is not on filesystem at ${rootCertificatePrivateKeyFileInfo.path}`,
|
|
161
|
-
)
|
|
160
|
+
);
|
|
162
161
|
logger.info(
|
|
163
162
|
`${UNICODE.INFO} authority root certificate not found in filesystem`,
|
|
164
|
-
)
|
|
165
|
-
return generate()
|
|
163
|
+
);
|
|
164
|
+
return generate();
|
|
166
165
|
}
|
|
167
166
|
logger.debug(
|
|
168
167
|
`found authority root certificate files at ${rootCertificateFileInfo.path} and ${rootCertificatePrivateKeyFileInfo.path}`,
|
|
169
|
-
)
|
|
170
|
-
logger.info(`${UNICODE.OK} authority root certificate found in filesystem`)
|
|
168
|
+
);
|
|
169
|
+
logger.info(`${UNICODE.OK} authority root certificate found in filesystem`);
|
|
171
170
|
|
|
172
171
|
const rootCertificate = await readFile(rootCertificateFileInfo.path, {
|
|
173
172
|
as: "string",
|
|
174
|
-
})
|
|
175
|
-
const { pki } = forge
|
|
176
|
-
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate)
|
|
173
|
+
});
|
|
174
|
+
const { pki } = forge;
|
|
175
|
+
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate);
|
|
177
176
|
|
|
178
|
-
logger.info(`Checking certificate validity...`)
|
|
177
|
+
logger.info(`Checking certificate validity...`);
|
|
179
178
|
const rootCertificateValidityDurationInMs =
|
|
180
|
-
getCertificateValidityDurationInMs(rootCertificateForgeObject)
|
|
179
|
+
getCertificateValidityDurationInMs(rootCertificateForgeObject);
|
|
181
180
|
const rootCertificateValidityRemainingMs = getCertificateRemainingMs(
|
|
182
181
|
rootCertificateForgeObject,
|
|
183
|
-
)
|
|
182
|
+
);
|
|
184
183
|
if (rootCertificateValidityRemainingMs < 0) {
|
|
185
184
|
logger.info(
|
|
186
185
|
`${UNICODE.INFO} certificate expired ${formatTimeDelta(
|
|
187
186
|
rootCertificateValidityRemainingMs,
|
|
188
187
|
)}`,
|
|
189
|
-
)
|
|
190
|
-
return regenerate()
|
|
188
|
+
);
|
|
189
|
+
return regenerate();
|
|
191
190
|
}
|
|
192
191
|
const rootCertificateValidityRemainingRatio =
|
|
193
|
-
rootCertificateValidityRemainingMs / rootCertificateValidityDurationInMs
|
|
192
|
+
rootCertificateValidityRemainingMs / rootCertificateValidityDurationInMs;
|
|
194
193
|
if (rootCertificateValidityRemainingRatio < aboutToExpireRatio) {
|
|
195
194
|
logger.info(
|
|
196
195
|
`${UNICODE.INFO} certificate will expire ${formatTimeDelta(
|
|
197
196
|
rootCertificateValidityRemainingMs,
|
|
198
197
|
)}`,
|
|
199
|
-
)
|
|
200
|
-
return regenerate()
|
|
198
|
+
);
|
|
199
|
+
return regenerate();
|
|
201
200
|
}
|
|
202
201
|
logger.info(
|
|
203
202
|
`${UNICODE.OK} certificate still valid for ${formatDuration(
|
|
204
203
|
rootCertificateValidityRemainingMs,
|
|
205
204
|
)}`,
|
|
206
|
-
)
|
|
205
|
+
);
|
|
207
206
|
|
|
208
|
-
logger.info(`Detect if certificate attributes have changed...`)
|
|
207
|
+
logger.info(`Detect if certificate attributes have changed...`);
|
|
209
208
|
const rootCertificateDifferences = compareRootCertificateAttributes(
|
|
210
209
|
rootCertificateForgeObject,
|
|
211
210
|
{
|
|
212
211
|
certificateCommonName,
|
|
213
212
|
certificateValidityDurationInMs,
|
|
214
213
|
},
|
|
215
|
-
)
|
|
214
|
+
);
|
|
216
215
|
if (rootCertificateDifferences.length) {
|
|
217
|
-
const paramNames = Object.keys(rootCertificateDifferences)
|
|
216
|
+
const paramNames = Object.keys(rootCertificateDifferences);
|
|
218
217
|
logger.info(
|
|
219
218
|
`${UNICODE.INFO} certificate attributes are outdated: ${paramNames}`,
|
|
220
|
-
)
|
|
221
|
-
return regenerate()
|
|
219
|
+
);
|
|
220
|
+
return regenerate();
|
|
222
221
|
}
|
|
223
|
-
logger.info(`${UNICODE.OK} certificate attributes are the same`)
|
|
222
|
+
logger.info(`${UNICODE.OK} certificate attributes are the same`);
|
|
224
223
|
|
|
225
224
|
const rootCertificatePrivateKey = await readFile(
|
|
226
225
|
rootCertificatePrivateKeyFileInfo.path,
|
|
227
226
|
{
|
|
228
227
|
as: "string",
|
|
229
228
|
},
|
|
230
|
-
)
|
|
229
|
+
);
|
|
231
230
|
const rootCertificatePrivateKeyForgeObject = pki.privateKeyFromPem(
|
|
232
231
|
rootCertificatePrivateKey,
|
|
233
|
-
)
|
|
232
|
+
);
|
|
234
233
|
const trustInfo = await platformMethods.executeTrustQuery({
|
|
235
234
|
logger,
|
|
236
235
|
certificateCommonName,
|
|
@@ -238,7 +237,7 @@ export const installCertificateAuthority = async ({
|
|
|
238
237
|
certificate: rootCertificate,
|
|
239
238
|
verb: tryToTrust ? "ENSURE_TRUST" : "CHECK_TRUST",
|
|
240
239
|
NSSDynamicInstall,
|
|
241
|
-
})
|
|
240
|
+
});
|
|
242
241
|
|
|
243
242
|
return {
|
|
244
243
|
rootCertificateForgeObject,
|
|
@@ -247,8 +246,8 @@ export const installCertificateAuthority = async ({
|
|
|
247
246
|
rootCertificatePrivateKey,
|
|
248
247
|
rootCertificateFilePath: rootCertificateFileInfo.path,
|
|
249
248
|
trustInfo,
|
|
250
|
-
}
|
|
251
|
-
}
|
|
249
|
+
};
|
|
250
|
+
};
|
|
252
251
|
|
|
253
252
|
// const getCertificateValidSinceInMs = (forgeCertificate) => {
|
|
254
253
|
// const { notBefore } = forgeCertificate.validity
|
|
@@ -258,17 +257,17 @@ export const installCertificateAuthority = async ({
|
|
|
258
257
|
// }
|
|
259
258
|
|
|
260
259
|
const getCertificateRemainingMs = (certificateForgeObject) => {
|
|
261
|
-
const { notAfter } = certificateForgeObject.validity
|
|
262
|
-
const nowDate = Date.now()
|
|
263
|
-
const remainingMs = notAfter - nowDate
|
|
264
|
-
return remainingMs
|
|
265
|
-
}
|
|
260
|
+
const { notAfter } = certificateForgeObject.validity;
|
|
261
|
+
const nowDate = Date.now();
|
|
262
|
+
const remainingMs = notAfter - nowDate;
|
|
263
|
+
return remainingMs;
|
|
264
|
+
};
|
|
266
265
|
|
|
267
266
|
const getCertificateValidityDurationInMs = (certificateForgeObject) => {
|
|
268
|
-
const { notBefore, notAfter } = certificateForgeObject.validity
|
|
269
|
-
const validityDurationInMs = notAfter - notBefore
|
|
270
|
-
return validityDurationInMs
|
|
271
|
-
}
|
|
267
|
+
const { notBefore, notAfter } = certificateForgeObject.validity;
|
|
268
|
+
const validityDurationInMs = notAfter - notBefore;
|
|
269
|
+
return validityDurationInMs;
|
|
270
|
+
};
|
|
272
271
|
|
|
273
272
|
const compareRootCertificateAttributes = (
|
|
274
273
|
rootCertificateForgeObject,
|
|
@@ -276,28 +275,28 @@ const compareRootCertificateAttributes = (
|
|
|
276
275
|
) => {
|
|
277
276
|
const attributeDescription = attributeDescriptionFromAttributeArray(
|
|
278
277
|
rootCertificateForgeObject.subject.attributes,
|
|
279
|
-
)
|
|
280
|
-
const differences = {}
|
|
278
|
+
);
|
|
279
|
+
const differences = {};
|
|
281
280
|
|
|
282
|
-
const { commonName } = attributeDescription
|
|
281
|
+
const { commonName } = attributeDescription;
|
|
283
282
|
if (commonName !== certificateCommonName) {
|
|
284
283
|
differences.certificateCommonName = {
|
|
285
284
|
valueFromCertificate: commonName,
|
|
286
285
|
valueFromParam: certificateCommonName,
|
|
287
|
-
}
|
|
286
|
+
};
|
|
288
287
|
}
|
|
289
288
|
|
|
290
|
-
const { notBefore, notAfter } = rootCertificateForgeObject.validity
|
|
291
|
-
const rootCertificateValidityDurationInMs = notAfter - notBefore
|
|
289
|
+
const { notBefore, notAfter } = rootCertificateForgeObject.validity;
|
|
290
|
+
const rootCertificateValidityDurationInMs = notAfter - notBefore;
|
|
292
291
|
if (rootCertificateValidityDurationInMs !== certificateValidityDurationInMs) {
|
|
293
292
|
differences.rootCertificateValidityDurationInMs = {
|
|
294
293
|
valueFromCertificate: rootCertificateValidityDurationInMs,
|
|
295
294
|
valueFromParam: certificateValidityDurationInMs,
|
|
296
|
-
}
|
|
295
|
+
};
|
|
297
296
|
}
|
|
298
297
|
|
|
299
|
-
return differences
|
|
300
|
-
}
|
|
298
|
+
return differences;
|
|
299
|
+
};
|
|
301
300
|
|
|
302
301
|
export const uninstallCertificateAuthority = async ({
|
|
303
302
|
logLevel,
|
|
@@ -308,58 +307,60 @@ export const uninstallCertificateAuthority = async ({
|
|
|
308
307
|
authorityJsonFileInfo,
|
|
309
308
|
rootCertificateFileInfo,
|
|
310
309
|
rootCertificatePrivateKeyFileInfo,
|
|
311
|
-
} = getAuthorityFileInfos()
|
|
310
|
+
} = getAuthorityFileInfos();
|
|
312
311
|
|
|
313
|
-
const filesToRemove = []
|
|
312
|
+
const filesToRemove = [];
|
|
314
313
|
|
|
315
314
|
if (authorityJsonFileInfo.exists) {
|
|
316
|
-
filesToRemove.push(authorityJsonFileInfo.url)
|
|
315
|
+
filesToRemove.push(authorityJsonFileInfo.url);
|
|
317
316
|
}
|
|
318
317
|
if (rootCertificateFileInfo.exists) {
|
|
319
318
|
// first untrust the root cert file
|
|
320
319
|
if (tryToUntrust) {
|
|
321
320
|
const rootCertificate = await readFile(rootCertificateFileInfo.url, {
|
|
322
321
|
as: "string",
|
|
323
|
-
})
|
|
324
|
-
const { pki } = forge
|
|
325
|
-
const rootCertificateForgeObject =
|
|
322
|
+
});
|
|
323
|
+
const { pki } = forge;
|
|
324
|
+
const rootCertificateForgeObject =
|
|
325
|
+
pki.certificateFromPem(rootCertificate);
|
|
326
326
|
const rootCertificateCommonName = attributeDescriptionFromAttributeArray(
|
|
327
327
|
rootCertificateForgeObject.subject.attributes,
|
|
328
|
-
).commonName
|
|
329
|
-
const { removeCertificateFromTrustStores } =
|
|
328
|
+
).commonName;
|
|
329
|
+
const { removeCertificateFromTrustStores } =
|
|
330
|
+
await importPlatformMethods();
|
|
330
331
|
await removeCertificateFromTrustStores({
|
|
331
332
|
logger,
|
|
332
333
|
certificate: rootCertificate,
|
|
333
334
|
certificateFileUrl: rootCertificateFileInfo.url,
|
|
334
335
|
certificateCommonName: rootCertificateCommonName,
|
|
335
|
-
})
|
|
336
|
+
});
|
|
336
337
|
}
|
|
337
|
-
filesToRemove.push(rootCertificateFileInfo.url)
|
|
338
|
+
filesToRemove.push(rootCertificateFileInfo.url);
|
|
338
339
|
}
|
|
339
340
|
if (rootCertificatePrivateKeyFileInfo.exists) {
|
|
340
|
-
filesToRemove.push(rootCertificatePrivateKeyFileInfo.url)
|
|
341
|
+
filesToRemove.push(rootCertificatePrivateKeyFileInfo.url);
|
|
341
342
|
}
|
|
342
343
|
|
|
343
344
|
if (filesToRemove.length) {
|
|
344
|
-
logger.info(`Removing certificate authority files...`)
|
|
345
|
+
logger.info(`Removing certificate authority files...`);
|
|
345
346
|
await Promise.all(
|
|
346
347
|
filesToRemove.map(async (file) => {
|
|
347
|
-
await removeEntry(file)
|
|
348
|
+
await removeEntry(file);
|
|
348
349
|
}),
|
|
349
|
-
)
|
|
350
|
+
);
|
|
350
351
|
logger.info(
|
|
351
352
|
`${UNICODE.OK} certificate authority files removed from filesystem`,
|
|
352
|
-
)
|
|
353
|
+
);
|
|
353
354
|
}
|
|
354
|
-
}
|
|
355
|
+
};
|
|
355
356
|
|
|
356
357
|
const pemAsFileContent = (pem) => {
|
|
357
358
|
if (process.platform === "win32") {
|
|
358
|
-
return pem
|
|
359
|
+
return pem;
|
|
359
360
|
}
|
|
360
361
|
// prefer \n when writing pem into files
|
|
361
|
-
return pem.replace(/\r\n/g, "\n")
|
|
362
|
-
}
|
|
362
|
+
return pem.replace(/\r\n/g, "\n");
|
|
363
|
+
};
|
|
363
364
|
|
|
364
365
|
/*
|
|
365
366
|
* The root certificate files can be "hard" to find because
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
1
|
+
import { writeFileSync } from "@jsenv/filesystem";
|
|
2
|
+
import { UNICODE, createDetailedMessage, createLogger } from "@jsenv/humanize";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { getAuthorityFileInfos } from "./internal/authority_file_infos.js";
|
|
5
|
+
import { requestCertificateFromAuthority } from "./internal/certificate_generator.js";
|
|
6
|
+
import { forge } from "./internal/forge.js";
|
|
7
|
+
import { formatDuration } from "./internal/validity_formatting.js";
|
|
6
8
|
import {
|
|
7
9
|
createValidityDurationOfXDays,
|
|
8
10
|
verifyServerCertificateValidityDuration,
|
|
9
|
-
} from "./validity_duration.js"
|
|
10
|
-
import { getAuthorityFileInfos } from "./internal/authority_file_infos.js"
|
|
11
|
-
import { requestCertificateFromAuthority } from "./internal/certificate_generator.js"
|
|
12
|
-
import { formatDuration } from "./internal/validity_formatting.js"
|
|
11
|
+
} from "./validity_duration.js";
|
|
13
12
|
|
|
14
13
|
export const requestCertificate = ({
|
|
15
14
|
logLevel,
|
|
@@ -22,66 +21,66 @@ export const requestCertificate = ({
|
|
|
22
21
|
if (typeof validityDurationInMs !== "number") {
|
|
23
22
|
throw new TypeError(
|
|
24
23
|
`validityDurationInMs must be a number but received ${validityDurationInMs}`,
|
|
25
|
-
)
|
|
24
|
+
);
|
|
26
25
|
}
|
|
27
26
|
if (validityDurationInMs < 1) {
|
|
28
27
|
throw new TypeError(
|
|
29
28
|
`validityDurationInMs must be > 0 but received ${validityDurationInMs}`,
|
|
30
|
-
)
|
|
29
|
+
);
|
|
31
30
|
}
|
|
32
31
|
const validityDurationInfo =
|
|
33
|
-
verifyServerCertificateValidityDuration(validityDurationInMs)
|
|
32
|
+
verifyServerCertificateValidityDuration(validityDurationInMs);
|
|
34
33
|
if (!validityDurationInfo.ok) {
|
|
35
|
-
validityDurationInMs = validityDurationInfo.maxAllowedValue
|
|
34
|
+
validityDurationInMs = validityDurationInfo.maxAllowedValue;
|
|
36
35
|
logger.warn(
|
|
37
36
|
createDetailedMessage(validityDurationInfo.message, {
|
|
38
37
|
details: validityDurationInfo.details,
|
|
39
38
|
}),
|
|
40
|
-
)
|
|
39
|
+
);
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
const {
|
|
44
43
|
authorityJsonFileInfo,
|
|
45
44
|
rootCertificateFileInfo,
|
|
46
45
|
rootCertificatePrivateKeyFileInfo,
|
|
47
|
-
} = getAuthorityFileInfos()
|
|
46
|
+
} = getAuthorityFileInfos();
|
|
48
47
|
if (!rootCertificateFileInfo.exists) {
|
|
49
48
|
throw new Error(
|
|
50
49
|
`Certificate authority not found, "installCertificateAuthority" must be called before "requestServerCertificate"`,
|
|
51
|
-
)
|
|
50
|
+
);
|
|
52
51
|
}
|
|
53
52
|
if (!rootCertificatePrivateKeyFileInfo.exists) {
|
|
54
|
-
throw new Error(`Cannot find authority root certificate private key`)
|
|
53
|
+
throw new Error(`Cannot find authority root certificate private key`);
|
|
55
54
|
}
|
|
56
55
|
if (!authorityJsonFileInfo.exists) {
|
|
57
|
-
throw new Error(`Cannot find authority json file`)
|
|
56
|
+
throw new Error(`Cannot find authority json file`);
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
logger.debug(`Restoring certificate authority from filesystem...`)
|
|
61
|
-
const { pki } = forge
|
|
59
|
+
logger.debug(`Restoring certificate authority from filesystem...`);
|
|
60
|
+
const { pki } = forge;
|
|
62
61
|
const rootCertificate = String(
|
|
63
62
|
readFileSync(new URL(rootCertificateFileInfo.url)),
|
|
64
|
-
)
|
|
63
|
+
);
|
|
65
64
|
const rootCertificatePrivateKey = String(
|
|
66
65
|
readFileSync(new URL(rootCertificatePrivateKeyFileInfo.url)),
|
|
67
|
-
)
|
|
66
|
+
);
|
|
68
67
|
const certificateAuthorityData = JSON.parse(
|
|
69
68
|
String(readFileSync(new URL(authorityJsonFileInfo.url))),
|
|
70
|
-
)
|
|
71
|
-
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate)
|
|
69
|
+
);
|
|
70
|
+
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate);
|
|
72
71
|
const rootCertificatePrivateKeyForgeObject = pki.privateKeyFromPem(
|
|
73
72
|
rootCertificatePrivateKey,
|
|
74
|
-
)
|
|
75
|
-
logger.debug(`${UNICODE.OK} certificate authority restored from filesystem`)
|
|
73
|
+
);
|
|
74
|
+
logger.debug(`${UNICODE.OK} certificate authority restored from filesystem`);
|
|
76
75
|
|
|
77
76
|
const serverCertificateSerialNumber =
|
|
78
|
-
certificateAuthorityData.serialNumber + 1
|
|
77
|
+
certificateAuthorityData.serialNumber + 1;
|
|
79
78
|
writeFileSync(
|
|
80
79
|
authorityJsonFileInfo.url,
|
|
81
80
|
JSON.stringify({ serialNumber: serverCertificateSerialNumber }, null, " "),
|
|
82
|
-
)
|
|
81
|
+
);
|
|
83
82
|
|
|
84
|
-
logger.debug(`Generating server certificate...`)
|
|
83
|
+
logger.debug(`Generating server certificate...`);
|
|
85
84
|
const { certificateForgeObject, certificatePrivateKeyForgeObject } =
|
|
86
85
|
requestCertificateFromAuthority({
|
|
87
86
|
logger,
|
|
@@ -92,22 +91,22 @@ export const requestCertificate = ({
|
|
|
92
91
|
altNames,
|
|
93
92
|
commonName,
|
|
94
93
|
validityDurationInMs,
|
|
95
|
-
})
|
|
96
|
-
const serverCertificate = pki.certificateToPem(certificateForgeObject)
|
|
94
|
+
});
|
|
95
|
+
const serverCertificate = pki.certificateToPem(certificateForgeObject);
|
|
97
96
|
const serverCertificatePrivateKey = pki.privateKeyToPem(
|
|
98
97
|
certificatePrivateKeyForgeObject,
|
|
99
|
-
)
|
|
98
|
+
);
|
|
100
99
|
logger.debug(
|
|
101
100
|
`${
|
|
102
101
|
UNICODE.OK
|
|
103
102
|
} server certificate generated, it will be valid for ${formatDuration(
|
|
104
103
|
validityDurationInMs,
|
|
105
104
|
)}`,
|
|
106
|
-
)
|
|
105
|
+
);
|
|
107
106
|
|
|
108
107
|
return {
|
|
109
108
|
certificate: serverCertificate,
|
|
110
109
|
privateKey: serverCertificatePrivateKey,
|
|
111
110
|
rootCertificateFilePath: rootCertificateFileInfo.path,
|
|
112
|
-
}
|
|
113
|
-
}
|
|
111
|
+
};
|
|
112
|
+
};
|