@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
package/src/hosts_file_verif.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { createDetailedMessage, createLogger, UNICODE } from "@jsenv/
|
|
2
|
-
|
|
1
|
+
import { createDetailedMessage, createLogger, UNICODE } from "@jsenv/humanize";
|
|
3
2
|
import {
|
|
4
3
|
HOSTS_FILE_PATH,
|
|
5
|
-
readHostsFile,
|
|
6
4
|
parseHosts,
|
|
5
|
+
readHostsFile,
|
|
7
6
|
writeLineInHostsFile,
|
|
8
|
-
} from "./internal/hosts.js"
|
|
7
|
+
} from "./internal/hosts.js";
|
|
9
8
|
|
|
10
9
|
export const verifyHostsFile = async ({
|
|
11
10
|
ipMappings,
|
|
@@ -15,38 +14,38 @@ export const verifyHostsFile = async ({
|
|
|
15
14
|
// for unit test
|
|
16
15
|
hostsFilePath = HOSTS_FILE_PATH,
|
|
17
16
|
}) => {
|
|
18
|
-
logger.info(`Check hosts file content...`)
|
|
19
|
-
const hostsFileContent = await readHostsFile(hostsFilePath)
|
|
20
|
-
const hostnames = parseHosts(hostsFileContent)
|
|
17
|
+
logger.info(`Check hosts file content...`);
|
|
18
|
+
const hostsFileContent = await readHostsFile(hostsFilePath);
|
|
19
|
+
const hostnames = parseHosts(hostsFileContent);
|
|
21
20
|
|
|
22
|
-
const missingMappings = []
|
|
21
|
+
const missingMappings = [];
|
|
23
22
|
Object.keys(ipMappings).forEach((ip) => {
|
|
24
|
-
const ipHostnames = ipMappings[ip]
|
|
23
|
+
const ipHostnames = ipMappings[ip];
|
|
25
24
|
if (!Array.isArray(ipHostnames)) {
|
|
26
25
|
throw new TypeError(
|
|
27
26
|
`ipMappings values must be an array, found ${ipHostnames} for ${ip}`,
|
|
28
|
-
)
|
|
27
|
+
);
|
|
29
28
|
}
|
|
30
|
-
const existingMappings = hostnames.getIpHostnames(ip)
|
|
29
|
+
const existingMappings = hostnames.getIpHostnames(ip);
|
|
31
30
|
const missingHostnames = normalizeHostnames(ipHostnames).filter(
|
|
32
31
|
(hostname) => !existingMappings.includes(hostname),
|
|
33
|
-
)
|
|
32
|
+
);
|
|
34
33
|
if (missingHostnames.length) {
|
|
35
|
-
missingMappings.push({ ip, missingHostnames })
|
|
34
|
+
missingMappings.push({ ip, missingHostnames });
|
|
36
35
|
}
|
|
37
|
-
})
|
|
38
|
-
const missingMappingCount = missingMappings.length
|
|
36
|
+
});
|
|
37
|
+
const missingMappingCount = missingMappings.length;
|
|
39
38
|
if (missingMappingCount === 0) {
|
|
40
|
-
logger.info(`${UNICODE.OK} all ip mappings found in hosts file`)
|
|
41
|
-
return
|
|
39
|
+
logger.info(`${UNICODE.OK} all ip mappings found in hosts file`);
|
|
40
|
+
return;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
const EOL = process.platform === "win32" ? "\r\n" : "\n"
|
|
43
|
+
const EOL = process.platform === "win32" ? "\r\n" : "\n";
|
|
45
44
|
|
|
46
45
|
if (!tryToUpdateHostsFile) {
|
|
47
46
|
const linesToAdd = missingMappings
|
|
48
47
|
.map(({ ip, missingHostnames }) => `${ip} ${missingHostnames.join(" ")}`)
|
|
49
|
-
.join(EOL)
|
|
48
|
+
.join(EOL);
|
|
50
49
|
logger.warn(
|
|
51
50
|
createDetailedMessage(
|
|
52
51
|
`${UNICODE.WARNING} ${formatXMappingMissingMessage(
|
|
@@ -57,36 +56,36 @@ export const verifyHostsFile = async ({
|
|
|
57
56
|
"line(s) to add": linesToAdd,
|
|
58
57
|
},
|
|
59
58
|
),
|
|
60
|
-
)
|
|
61
|
-
return
|
|
59
|
+
);
|
|
60
|
+
return;
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
logger.info(
|
|
65
64
|
`${UNICODE.INFO} ${formatXMappingMissingMessage(missingMappingCount)}`,
|
|
66
|
-
)
|
|
65
|
+
);
|
|
67
66
|
await missingMappings.reduce(async (previous, { ip, missingHostnames }) => {
|
|
68
|
-
await previous
|
|
69
|
-
const mapping = `${ip} ${missingHostnames.join(" ")}
|
|
70
|
-
logger.info(`Append "${mapping}" in host file...`)
|
|
67
|
+
await previous;
|
|
68
|
+
const mapping = `${ip} ${missingHostnames.join(" ")}`;
|
|
69
|
+
logger.info(`Append "${mapping}" in host file...`);
|
|
71
70
|
|
|
72
71
|
await writeLineInHostsFile(mapping, {
|
|
73
72
|
hostsFilePath,
|
|
74
73
|
onBeforeExecCommand: (command) => {
|
|
75
|
-
logger.info(`${UNICODE.COMMAND} ${command}`)
|
|
74
|
+
logger.info(`${UNICODE.COMMAND} ${command}`);
|
|
76
75
|
},
|
|
77
|
-
})
|
|
78
|
-
logger.info(`${UNICODE.OK} mapping added`)
|
|
79
|
-
}, Promise.resolve())
|
|
80
|
-
}
|
|
76
|
+
});
|
|
77
|
+
logger.info(`${UNICODE.OK} mapping added`);
|
|
78
|
+
}, Promise.resolve());
|
|
79
|
+
};
|
|
81
80
|
|
|
82
81
|
const normalizeHostnames = (hostnames) => {
|
|
83
|
-
return hostnames.map((hostname) => hostname.trim().replace(/[\s;]/g, ""))
|
|
84
|
-
}
|
|
82
|
+
return hostnames.map((hostname) => hostname.trim().replace(/[\s;]/g, ""));
|
|
83
|
+
};
|
|
85
84
|
|
|
86
85
|
const formatXMappingMissingMessage = (missingMappingCount) => {
|
|
87
86
|
if (missingMappingCount) {
|
|
88
|
-
return `1 mapping is missing in hosts file
|
|
87
|
+
return `1 mapping is missing in hosts file`;
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
return `${missingMappingCount} mappings are missing in hosts file
|
|
92
|
-
}
|
|
90
|
+
return `${missingMappingCount} mappings are missing in hosts file`;
|
|
91
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
installCertificateAuthority,
|
|
5
|
+
uninstallCertificateAuthority,
|
|
6
|
+
verifyHostsFile,
|
|
7
|
+
} from "@jsenv/https-local";
|
|
8
|
+
import { parseArgs } from "node:util";
|
|
9
|
+
|
|
10
|
+
const options = {
|
|
11
|
+
help: {
|
|
12
|
+
type: "boolean",
|
|
13
|
+
},
|
|
14
|
+
trust: {
|
|
15
|
+
type: "boolean",
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
const { values, positionals } = parseArgs({
|
|
19
|
+
options,
|
|
20
|
+
allowPositionals: true,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (values.help || positionals.length === 0) {
|
|
24
|
+
console.log(`https-local: Generate https certificates to use on your machine.
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
|
|
28
|
+
npx @jsenv/https-local install --trust
|
|
29
|
+
Install root certificate on the filesystem
|
|
30
|
+
- trust: Try to add root certificate to os and browser trusted stores.
|
|
31
|
+
|
|
32
|
+
npx @jsenv/https-local uninstall
|
|
33
|
+
Uninstall root certificate from the filesystem
|
|
34
|
+
|
|
35
|
+
npx @jsenv/https-local localhost-mapping
|
|
36
|
+
Ensure localhost mapping to 127.0.0.1 is set on the filesystem
|
|
37
|
+
|
|
38
|
+
https://github.com/jsenv/core/tree/main/packages/independent/https-local
|
|
39
|
+
|
|
40
|
+
`);
|
|
41
|
+
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const commandHandlers = {
|
|
46
|
+
install: async ({ tryToTrust }) => {
|
|
47
|
+
await installCertificateAuthority({
|
|
48
|
+
tryToTrust,
|
|
49
|
+
NSSDynamicInstall: tryToTrust,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
uninstall: async () => {
|
|
53
|
+
await uninstallCertificateAuthority({
|
|
54
|
+
tryToUntrust: true,
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
["localhost-mapping"]: async () => {
|
|
58
|
+
await verifyHostsFile({
|
|
59
|
+
ipMappings: {
|
|
60
|
+
"127.0.0.1": ["localhost"],
|
|
61
|
+
},
|
|
62
|
+
tryToUpdatesHostsFile: true,
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const [command] = positionals;
|
|
68
|
+
const commandHandler = commandHandlers[command];
|
|
69
|
+
if (!commandHandler) {
|
|
70
|
+
console.error(`Error: unknown command ${command}.`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await commandHandler(values);
|
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import { existsSync } from "node:fs"
|
|
2
|
-
import { fileURLToPath } from "node:url"
|
|
3
|
-
|
|
4
|
-
import { getCertificateAuthorityFileUrls } from "./certificate_authority_file_urls.js"
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { getCertificateAuthorityFileUrls } from "./certificate_authority_file_urls.js";
|
|
5
4
|
|
|
6
5
|
export const getAuthorityFileInfos = () => {
|
|
7
6
|
const {
|
|
8
7
|
certificateAuthorityJsonFileUrl,
|
|
9
8
|
rootCertificateFileUrl,
|
|
10
9
|
rootCertificatePrivateKeyFileUrl,
|
|
11
|
-
} = getCertificateAuthorityFileUrls()
|
|
10
|
+
} = getCertificateAuthorityFileUrls();
|
|
12
11
|
|
|
13
|
-
const authorityJsonFilePath = fileURLToPath(certificateAuthorityJsonFileUrl)
|
|
14
|
-
const authorityJsonFileDetected = existsSync(authorityJsonFilePath)
|
|
12
|
+
const authorityJsonFilePath = fileURLToPath(certificateAuthorityJsonFileUrl);
|
|
13
|
+
const authorityJsonFileDetected = existsSync(authorityJsonFilePath);
|
|
15
14
|
|
|
16
|
-
const rootCertificateFilePath = fileURLToPath(rootCertificateFileUrl)
|
|
17
|
-
const rootCertificateFileDetected = existsSync(rootCertificateFilePath)
|
|
15
|
+
const rootCertificateFilePath = fileURLToPath(rootCertificateFileUrl);
|
|
16
|
+
const rootCertificateFileDetected = existsSync(rootCertificateFilePath);
|
|
18
17
|
|
|
19
18
|
const rootCertificatePrivateKeyFilePath = fileURLToPath(
|
|
20
19
|
rootCertificatePrivateKeyFileUrl,
|
|
21
|
-
)
|
|
20
|
+
);
|
|
22
21
|
const rootCertificatePrivateKeyFileDetected = existsSync(
|
|
23
22
|
rootCertificatePrivateKeyFilePath,
|
|
24
|
-
)
|
|
23
|
+
);
|
|
25
24
|
|
|
26
25
|
return {
|
|
27
26
|
authorityJsonFileInfo: {
|
|
@@ -39,5 +38,5 @@ export const getAuthorityFileInfos = () => {
|
|
|
39
38
|
path: rootCertificatePrivateKeyFilePath,
|
|
40
39
|
exists: rootCertificatePrivateKeyFileDetected,
|
|
41
40
|
},
|
|
42
|
-
}
|
|
43
|
-
}
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync } from "node:fs"
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
2
|
|
|
3
3
|
export const detectBrowser = (pathCandidates) => {
|
|
4
4
|
return pathCandidates.some((pathCandidate) => {
|
|
5
|
-
return existsSync(pathCandidate)
|
|
6
|
-
})
|
|
7
|
-
}
|
|
5
|
+
return existsSync(pathCandidate);
|
|
6
|
+
});
|
|
7
|
+
};
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem";
|
|
2
|
+
import { urlToFilename } from "@jsenv/urls";
|
|
3
3
|
|
|
4
4
|
export const getCertificateAuthorityFileUrls = () => {
|
|
5
5
|
// we need a directory common to every instance of @jsenv/https-local
|
|
6
6
|
// so that even if it's used multiple times, the certificate autority files
|
|
7
7
|
// are reused
|
|
8
|
-
const applicationDirectoryUrl = getJsenvApplicationDirectoryUrl()
|
|
8
|
+
const applicationDirectoryUrl = getJsenvApplicationDirectoryUrl();
|
|
9
9
|
|
|
10
10
|
const certificateAuthorityJsonFileUrl = new URL(
|
|
11
11
|
"./https_local_certificate_authority.json",
|
|
12
12
|
applicationDirectoryUrl,
|
|
13
|
-
)
|
|
13
|
+
);
|
|
14
14
|
|
|
15
15
|
const rootCertificateFileUrl = new URL(
|
|
16
16
|
"./https_local_root_certificate.crt",
|
|
17
17
|
applicationDirectoryUrl,
|
|
18
|
-
)
|
|
18
|
+
);
|
|
19
19
|
|
|
20
20
|
const rootCertificatePrivateKeyFileUrl = new URL(
|
|
21
21
|
"./https_local_root_certificate.key",
|
|
22
22
|
applicationDirectoryUrl,
|
|
23
|
-
).href
|
|
23
|
+
).href;
|
|
24
24
|
|
|
25
25
|
return {
|
|
26
26
|
certificateAuthorityJsonFileUrl,
|
|
27
27
|
rootCertificateFileUrl,
|
|
28
28
|
rootCertificatePrivateKeyFileUrl,
|
|
29
|
-
}
|
|
30
|
-
}
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
31
|
|
|
32
32
|
export const getRootCertificateSymlinkUrls = ({
|
|
33
33
|
rootCertificateFileUrl,
|
|
@@ -35,34 +35,34 @@ export const getRootCertificateSymlinkUrls = ({
|
|
|
35
35
|
serverCertificateFileUrl,
|
|
36
36
|
}) => {
|
|
37
37
|
const serverCertificateDirectory = new URL("./", serverCertificateFileUrl)
|
|
38
|
-
.href
|
|
38
|
+
.href;
|
|
39
39
|
|
|
40
|
-
const rootCertificateFilename = urlToFilename(rootCertificateFileUrl)
|
|
40
|
+
const rootCertificateFilename = urlToFilename(rootCertificateFileUrl);
|
|
41
41
|
const rootCertificateSymlinkUrl = new URL(
|
|
42
42
|
rootCertificateFilename,
|
|
43
43
|
serverCertificateDirectory,
|
|
44
|
-
).href
|
|
45
|
-
const rootPrivateKeyFilename = urlToFilename(rootPrivateKeyFileUrl)
|
|
44
|
+
).href;
|
|
45
|
+
const rootPrivateKeyFilename = urlToFilename(rootPrivateKeyFileUrl);
|
|
46
46
|
const rootPrivateKeySymlinkUrl = new URL(
|
|
47
47
|
rootPrivateKeyFilename,
|
|
48
48
|
serverCertificateDirectory,
|
|
49
|
-
).href
|
|
49
|
+
).href;
|
|
50
50
|
|
|
51
51
|
return {
|
|
52
52
|
rootCertificateSymlinkUrl,
|
|
53
53
|
rootPrivateKeySymlinkUrl,
|
|
54
|
-
}
|
|
55
|
-
}
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
56
|
|
|
57
57
|
// https://github.com/LinusU/node-application-config-path/blob/master/index.js
|
|
58
58
|
const getJsenvApplicationDirectoryUrl = () => {
|
|
59
|
-
const { platform } = process
|
|
59
|
+
const { platform } = process;
|
|
60
60
|
|
|
61
61
|
if (platform === "darwin") {
|
|
62
62
|
return new URL(
|
|
63
63
|
`./Library/Application Support/https_local/`,
|
|
64
64
|
assertAndNormalizeDirectoryUrl(process.env.HOME),
|
|
65
|
-
).href
|
|
65
|
+
).href;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
if (platform === "linux") {
|
|
@@ -70,12 +70,12 @@ const getJsenvApplicationDirectoryUrl = () => {
|
|
|
70
70
|
return new URL(
|
|
71
71
|
`./https_local/`,
|
|
72
72
|
assertAndNormalizeDirectoryUrl(process.env.XDG_CONFIG_HOME),
|
|
73
|
-
).href
|
|
73
|
+
).href;
|
|
74
74
|
}
|
|
75
75
|
return new URL(
|
|
76
76
|
`./.config/https_local/`,
|
|
77
77
|
assertAndNormalizeDirectoryUrl(process.env.HOME),
|
|
78
|
-
).href
|
|
78
|
+
).href;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
if (platform === "win32") {
|
|
@@ -83,14 +83,14 @@ const getJsenvApplicationDirectoryUrl = () => {
|
|
|
83
83
|
return new URL(
|
|
84
84
|
`./https_local/`,
|
|
85
85
|
assertAndNormalizeDirectoryUrl(process.env.LOCALAPPDATA),
|
|
86
|
-
).href
|
|
86
|
+
).href;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
return new URL(
|
|
90
90
|
`./Local Settings/Application Data/https_local/`,
|
|
91
91
|
assertAndNormalizeDirectoryUrl(process.env.USERPROFILE),
|
|
92
|
-
).href
|
|
92
|
+
).href;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
throw new Error(`platform not supported`)
|
|
96
|
-
}
|
|
95
|
+
throw new Error(`platform not supported`);
|
|
96
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isIP } from "node:net"
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
2
|
|
|
3
3
|
export const subjectAltNamesFromAltNames = (altNames) => {
|
|
4
4
|
const altNamesArray = altNames.map((altName) => {
|
|
@@ -6,86 +6,86 @@ export const subjectAltNamesFromAltNames = (altNames) => {
|
|
|
6
6
|
return {
|
|
7
7
|
type: 7,
|
|
8
8
|
ip: altName,
|
|
9
|
-
}
|
|
9
|
+
};
|
|
10
10
|
}
|
|
11
11
|
if (isUrl(altName)) {
|
|
12
12
|
return {
|
|
13
13
|
type: 6,
|
|
14
14
|
value: altName,
|
|
15
|
-
}
|
|
15
|
+
};
|
|
16
16
|
}
|
|
17
17
|
// 2 is DNS (Domain Name Server)
|
|
18
18
|
return {
|
|
19
19
|
type: 2,
|
|
20
20
|
value: altName,
|
|
21
|
-
}
|
|
22
|
-
})
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
23
|
|
|
24
|
-
return altNamesArray
|
|
25
|
-
}
|
|
24
|
+
return altNamesArray;
|
|
25
|
+
};
|
|
26
26
|
|
|
27
27
|
const isUrl = (value) => {
|
|
28
28
|
try {
|
|
29
29
|
// eslint-disable-next-line no-new
|
|
30
|
-
new URL(value)
|
|
31
|
-
return true
|
|
32
|
-
} catch
|
|
33
|
-
return false
|
|
30
|
+
new URL(value);
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
34
|
}
|
|
35
|
-
}
|
|
35
|
+
};
|
|
36
36
|
|
|
37
37
|
export const extensionArrayFromExtensionDescription = (
|
|
38
38
|
extensionDescription,
|
|
39
39
|
) => {
|
|
40
|
-
const extensionArray = []
|
|
40
|
+
const extensionArray = [];
|
|
41
41
|
Object.keys(extensionDescription).forEach((key) => {
|
|
42
|
-
const value = extensionDescription[key]
|
|
42
|
+
const value = extensionDescription[key];
|
|
43
43
|
if (value) {
|
|
44
44
|
extensionArray.push({
|
|
45
45
|
name: key,
|
|
46
46
|
...value,
|
|
47
|
-
})
|
|
47
|
+
});
|
|
48
48
|
}
|
|
49
|
-
})
|
|
50
|
-
return extensionArray
|
|
51
|
-
}
|
|
49
|
+
});
|
|
50
|
+
return extensionArray;
|
|
51
|
+
};
|
|
52
52
|
|
|
53
53
|
export const extensionDescriptionFromExtensionArray = (extensionArray) => {
|
|
54
|
-
const extensionDescription = {}
|
|
54
|
+
const extensionDescription = {};
|
|
55
55
|
extensionArray.forEach((extension) => {
|
|
56
|
-
const { name, ...rest } = extension
|
|
57
|
-
extensionDescription[name] = rest
|
|
58
|
-
})
|
|
59
|
-
return extensionDescription
|
|
60
|
-
}
|
|
56
|
+
const { name, ...rest } = extension;
|
|
57
|
+
extensionDescription[name] = rest;
|
|
58
|
+
});
|
|
59
|
+
return extensionDescription;
|
|
60
|
+
};
|
|
61
61
|
|
|
62
62
|
export const attributeDescriptionFromAttributeArray = (attributeArray) => {
|
|
63
|
-
const attributeObject = {}
|
|
63
|
+
const attributeObject = {};
|
|
64
64
|
attributeArray.forEach((attribute) => {
|
|
65
|
-
attributeObject[attribute.name] = attribute.value
|
|
66
|
-
})
|
|
67
|
-
return attributeObject
|
|
68
|
-
}
|
|
65
|
+
attributeObject[attribute.name] = attribute.value;
|
|
66
|
+
});
|
|
67
|
+
return attributeObject;
|
|
68
|
+
};
|
|
69
69
|
|
|
70
70
|
export const attributeArrayFromAttributeDescription = (
|
|
71
71
|
attributeDescription,
|
|
72
72
|
) => {
|
|
73
|
-
const attributeArray = []
|
|
73
|
+
const attributeArray = [];
|
|
74
74
|
Object.keys(attributeDescription).forEach((key) => {
|
|
75
|
-
const value = attributeDescription[key]
|
|
75
|
+
const value = attributeDescription[key];
|
|
76
76
|
if (typeof value === "undefined") {
|
|
77
|
-
return
|
|
77
|
+
return;
|
|
78
78
|
}
|
|
79
79
|
attributeArray.push({
|
|
80
80
|
name: key,
|
|
81
81
|
value,
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
|
-
return attributeArray
|
|
85
|
-
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
return attributeArray;
|
|
85
|
+
};
|
|
86
86
|
|
|
87
87
|
export const normalizeForgeAltNames = (forgeAltNames) => {
|
|
88
88
|
return forgeAltNames.map((forgeAltName) => {
|
|
89
|
-
return forgeAltName.ip || forgeAltName.value
|
|
90
|
-
})
|
|
91
|
-
}
|
|
89
|
+
return forgeAltName.ip || forgeAltName.value;
|
|
90
|
+
});
|
|
91
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// https://github.com/digitalbazaar/forge/blob/master/examples/create-cert.js
|
|
2
2
|
// https://github.com/digitalbazaar/forge/issues/660#issuecomment-467145103
|
|
3
3
|
|
|
4
|
-
import { forge } from "./forge.js"
|
|
5
4
|
import {
|
|
6
5
|
attributeArrayFromAttributeDescription,
|
|
7
6
|
attributeDescriptionFromAttributeArray,
|
|
8
|
-
subjectAltNamesFromAltNames,
|
|
9
7
|
extensionArrayFromExtensionDescription,
|
|
10
|
-
|
|
8
|
+
subjectAltNamesFromAltNames,
|
|
9
|
+
} from "./certificate_data_converter.js";
|
|
10
|
+
import { forge } from "./forge.js";
|
|
11
11
|
|
|
12
12
|
export const createAuthorityRootCertificate = async ({
|
|
13
13
|
commonName,
|
|
@@ -20,21 +20,21 @@ export const createAuthorityRootCertificate = async ({
|
|
|
20
20
|
serialNumber,
|
|
21
21
|
} = {}) => {
|
|
22
22
|
if (typeof serialNumber !== "number") {
|
|
23
|
-
throw new TypeError(`serial must be a number but received ${serialNumber}`)
|
|
23
|
+
throw new TypeError(`serial must be a number but received ${serialNumber}`);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const { pki } = forge
|
|
27
|
-
const rootCertificateForgeObject = pki.createCertificate()
|
|
28
|
-
const keyPair = pki.rsa.generateKeyPair(2048) // TODO: use async version https://github.com/digitalbazaar/forge#rsa
|
|
29
|
-
const rootCertificatePublicKeyForgeObject = keyPair.publicKey
|
|
30
|
-
const rootCertificatePrivateKeyForgeObject = keyPair.privateKey
|
|
26
|
+
const { pki } = forge;
|
|
27
|
+
const rootCertificateForgeObject = pki.createCertificate();
|
|
28
|
+
const keyPair = pki.rsa.generateKeyPair(2048); // TODO: use async version https://github.com/digitalbazaar/forge#rsa
|
|
29
|
+
const rootCertificatePublicKeyForgeObject = keyPair.publicKey;
|
|
30
|
+
const rootCertificatePrivateKeyForgeObject = keyPair.privateKey;
|
|
31
31
|
|
|
32
|
-
rootCertificateForgeObject.publicKey = rootCertificatePublicKeyForgeObject
|
|
33
|
-
rootCertificateForgeObject.serialNumber = serialNumber.toString(16)
|
|
34
|
-
rootCertificateForgeObject.validity.notBefore = new Date()
|
|
32
|
+
rootCertificateForgeObject.publicKey = rootCertificatePublicKeyForgeObject;
|
|
33
|
+
rootCertificateForgeObject.serialNumber = serialNumber.toString(16);
|
|
34
|
+
rootCertificateForgeObject.validity.notBefore = new Date();
|
|
35
35
|
rootCertificateForgeObject.validity.notAfter = new Date(
|
|
36
36
|
Date.now() + validityDurationInMs,
|
|
37
|
-
)
|
|
37
|
+
);
|
|
38
38
|
rootCertificateForgeObject.setSubject(
|
|
39
39
|
attributeArrayFromAttributeDescription({
|
|
40
40
|
commonName,
|
|
@@ -44,7 +44,7 @@ export const createAuthorityRootCertificate = async ({
|
|
|
44
44
|
organizationName,
|
|
45
45
|
organizationalUnitName,
|
|
46
46
|
}),
|
|
47
|
-
)
|
|
47
|
+
);
|
|
48
48
|
rootCertificateForgeObject.setIssuer(
|
|
49
49
|
attributeArrayFromAttributeDescription({
|
|
50
50
|
commonName,
|
|
@@ -54,7 +54,7 @@ export const createAuthorityRootCertificate = async ({
|
|
|
54
54
|
organizationName,
|
|
55
55
|
organizationalUnitName,
|
|
56
56
|
}),
|
|
57
|
-
)
|
|
57
|
+
);
|
|
58
58
|
rootCertificateForgeObject.setExtensions(
|
|
59
59
|
extensionArrayFromExtensionDescription({
|
|
60
60
|
basicConstraints: {
|
|
@@ -73,17 +73,17 @@ export const createAuthorityRootCertificate = async ({
|
|
|
73
73
|
// },
|
|
74
74
|
// subjectKeyIdentifier: {},
|
|
75
75
|
}),
|
|
76
|
-
)
|
|
76
|
+
);
|
|
77
77
|
|
|
78
78
|
// self-sign certificate
|
|
79
|
-
rootCertificateForgeObject.sign(rootCertificatePrivateKeyForgeObject) // , forge.sha256.create())
|
|
79
|
+
rootCertificateForgeObject.sign(rootCertificatePrivateKeyForgeObject); // , forge.sha256.create())
|
|
80
80
|
|
|
81
81
|
return {
|
|
82
82
|
rootCertificateForgeObject,
|
|
83
83
|
rootCertificatePublicKeyForgeObject,
|
|
84
84
|
rootCertificatePrivateKeyForgeObject,
|
|
85
|
-
}
|
|
86
|
-
}
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
87
|
|
|
88
88
|
export const requestCertificateFromAuthority = ({
|
|
89
89
|
authorityCertificateForgeObject, // could be intermediate or root certificate authority
|
|
@@ -99,7 +99,7 @@ export const requestCertificateFromAuthority = ({
|
|
|
99
99
|
) {
|
|
100
100
|
throw new TypeError(
|
|
101
101
|
`authorityCertificateForgeObject must be an object but received ${authorityCertificateForgeObject}`,
|
|
102
|
-
)
|
|
102
|
+
);
|
|
103
103
|
}
|
|
104
104
|
if (
|
|
105
105
|
typeof auhtorityCertificatePrivateKeyForgeObject !== "object" ||
|
|
@@ -107,26 +107,26 @@ export const requestCertificateFromAuthority = ({
|
|
|
107
107
|
) {
|
|
108
108
|
throw new TypeError(
|
|
109
109
|
`auhtorityCertificatePrivateKeyForgeObject must be an object but received ${auhtorityCertificatePrivateKeyForgeObject}`,
|
|
110
|
-
)
|
|
110
|
+
);
|
|
111
111
|
}
|
|
112
112
|
if (typeof serialNumber !== "number") {
|
|
113
113
|
throw new TypeError(
|
|
114
114
|
`serialNumber must be a number but received ${serialNumber}`,
|
|
115
|
-
)
|
|
115
|
+
);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
const { pki } = forge
|
|
119
|
-
const certificateForgeObject = pki.createCertificate()
|
|
120
|
-
const keyPair = pki.rsa.generateKeyPair(2048) // TODO: use async version https://github.com/digitalbazaar/forge#rsa
|
|
121
|
-
const certificatePublicKeyForgeObject = keyPair.publicKey
|
|
122
|
-
const certificatePrivateKeyForgeObject = keyPair.privateKey
|
|
118
|
+
const { pki } = forge;
|
|
119
|
+
const certificateForgeObject = pki.createCertificate();
|
|
120
|
+
const keyPair = pki.rsa.generateKeyPair(2048); // TODO: use async version https://github.com/digitalbazaar/forge#rsa
|
|
121
|
+
const certificatePublicKeyForgeObject = keyPair.publicKey;
|
|
122
|
+
const certificatePrivateKeyForgeObject = keyPair.privateKey;
|
|
123
123
|
|
|
124
|
-
certificateForgeObject.publicKey = certificatePublicKeyForgeObject
|
|
125
|
-
certificateForgeObject.serialNumber = serialNumber.toString(16)
|
|
126
|
-
certificateForgeObject.validity.notBefore = new Date()
|
|
124
|
+
certificateForgeObject.publicKey = certificatePublicKeyForgeObject;
|
|
125
|
+
certificateForgeObject.serialNumber = serialNumber.toString(16);
|
|
126
|
+
certificateForgeObject.validity.notBefore = new Date();
|
|
127
127
|
certificateForgeObject.validity.notAfter = new Date(
|
|
128
128
|
Date.now() + validityDurationInMs,
|
|
129
|
-
)
|
|
129
|
+
);
|
|
130
130
|
|
|
131
131
|
const attributeDescription = {
|
|
132
132
|
...attributeDescriptionFromAttributeArray(
|
|
@@ -134,13 +134,13 @@ export const requestCertificateFromAuthority = ({
|
|
|
134
134
|
),
|
|
135
135
|
commonName,
|
|
136
136
|
// organizationName: serverCertificateOrganizationName
|
|
137
|
-
}
|
|
137
|
+
};
|
|
138
138
|
const attributeArray =
|
|
139
|
-
attributeArrayFromAttributeDescription(attributeDescription)
|
|
140
|
-
certificateForgeObject.setSubject(attributeArray)
|
|
139
|
+
attributeArrayFromAttributeDescription(attributeDescription);
|
|
140
|
+
certificateForgeObject.setSubject(attributeArray);
|
|
141
141
|
certificateForgeObject.setIssuer(
|
|
142
142
|
authorityCertificateForgeObject.subject.attributes,
|
|
143
|
-
)
|
|
143
|
+
);
|
|
144
144
|
certificateForgeObject.setExtensions(
|
|
145
145
|
extensionArrayFromExtensionDescription({
|
|
146
146
|
basicConstraints: {
|
|
@@ -167,15 +167,15 @@ export const requestCertificateFromAuthority = ({
|
|
|
167
167
|
altNames: subjectAltNamesFromAltNames(altNames),
|
|
168
168
|
},
|
|
169
169
|
}),
|
|
170
|
-
)
|
|
170
|
+
);
|
|
171
171
|
certificateForgeObject.sign(
|
|
172
172
|
auhtorityCertificatePrivateKeyForgeObject,
|
|
173
173
|
forge.sha256.create(),
|
|
174
|
-
)
|
|
174
|
+
);
|
|
175
175
|
|
|
176
176
|
return {
|
|
177
177
|
certificateForgeObject,
|
|
178
178
|
certificatePublicKeyForgeObject,
|
|
179
179
|
certificatePrivateKeyForgeObject,
|
|
180
|
-
}
|
|
181
|
-
}
|
|
180
|
+
};
|
|
181
|
+
};
|