node-opcua-pki 6.22.1 → 7.0.1
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/bin/install_prerequisite.ts +1 -1
- package/bin/pki.ts +1 -1
- package/bin/pki_config.example.js +0 -1
- package/dist/bin/install_prerequisite.d.ts +2 -0
- package/dist/bin/install_prerequisite.js +6 -0
- package/dist/bin/install_prerequisite.js.map +1 -0
- package/dist/bin/pki.d.ts +2 -0
- package/dist/bin/pki.js +4 -0
- package/dist/bin/pki.js.map +1 -0
- package/dist/lib/ca/backends/native_ca_backend.d.ts +49 -0
- package/dist/lib/ca/backends/native_ca_backend.js +365 -0
- package/dist/lib/ca/backends/native_ca_backend.js.map +1 -0
- package/dist/lib/ca/backends/openssl_ca_backend.d.ts +47 -0
- package/dist/lib/ca/backends/openssl_ca_backend.js +398 -0
- package/dist/lib/ca/backends/openssl_ca_backend.js.map +1 -0
- package/dist/lib/ca/certificate_authority.d.ts +62 -0
- package/dist/lib/ca/certificate_authority.js +91 -0
- package/dist/lib/ca/certificate_authority.js.map +1 -0
- package/dist/lib/ca/core/ca_backend.d.ts +89 -0
- package/dist/lib/ca/core/ca_backend.js +24 -0
- package/dist/lib/ca/core/ca_backend.js.map +1 -0
- package/dist/lib/ca/core/ca_database.d.ts +91 -0
- package/dist/lib/ca/core/ca_database.js +267 -0
- package/dist/lib/ca/core/ca_database.js.map +1 -0
- package/dist/{core-BwqSJPHc.d.mts → lib/ca/core/certificate_authority_core.d.ts} +45 -394
- package/dist/lib/ca/core/certificate_authority_core.js +1300 -0
- package/dist/lib/ca/core/certificate_authority_core.js.map +1 -0
- package/dist/lib/ca/core/index.d.ts +17 -0
- package/dist/lib/ca/core/index.js +25 -0
- package/dist/lib/ca/core/index.js.map +1 -0
- package/dist/lib/ca/crypto_create_CA.d.ts +1 -0
- package/dist/lib/ca/crypto_create_CA.js +821 -0
- package/dist/lib/ca/crypto_create_CA.js.map +1 -0
- package/dist/lib/ca/index.d.ts +1 -0
- package/dist/lib/ca/index.js +2 -0
- package/dist/lib/ca/index.js.map +1 -0
- package/dist/lib/ca/templates/ca_config_template.cnf.d.ts +2 -0
- package/dist/lib/ca/templates/ca_config_template.cnf.js +170 -0
- package/dist/lib/ca/templates/ca_config_template.cnf.js.map +1 -0
- package/dist/lib/core.d.ts +33 -0
- package/dist/lib/core.js +56 -0
- package/dist/lib/core.js.map +1 -0
- package/dist/lib/index.d.ts +11 -0
- package/dist/lib/index.js +37 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/misc/applicationurn.d.ts +1 -0
- package/dist/lib/misc/applicationurn.js +40 -0
- package/dist/lib/misc/applicationurn.js.map +1 -0
- package/dist/lib/misc/hostname.d.ts +8 -0
- package/dist/lib/misc/hostname.js +82 -0
- package/dist/lib/misc/hostname.js.map +1 -0
- package/dist/lib/misc/subject.d.ts +2 -0
- package/dist/lib/misc/subject.js +2 -0
- package/dist/lib/misc/subject.js.map +1 -0
- package/dist/{index.d.ts → lib/pki/certificate_manager.d.ts} +24 -248
- package/dist/lib/pki/certificate_manager.js +2302 -0
- package/dist/lib/pki/certificate_manager.js.map +1 -0
- package/dist/lib/pki/templates/simple_config_template.cnf.d.ts +2 -0
- package/dist/lib/pki/templates/simple_config_template.cnf.js +74 -0
- package/dist/lib/pki/templates/simple_config_template.cnf.js.map +1 -0
- package/dist/lib/pki/toolbox_pfx.d.ts +127 -0
- package/dist/lib/pki/toolbox_pfx.js +248 -0
- package/dist/lib/pki/toolbox_pfx.js.map +1 -0
- package/dist/lib/toolbox/common.d.ts +150 -0
- package/dist/lib/toolbox/common.js +82 -0
- package/dist/lib/toolbox/common.js.map +1 -0
- package/dist/lib/toolbox/common2.d.ts +25 -0
- package/dist/lib/toolbox/common2.js +97 -0
- package/dist/lib/toolbox/common2.js.map +1 -0
- package/dist/lib/toolbox/config.d.ts +5 -0
- package/dist/lib/toolbox/config.js +28 -0
- package/dist/lib/toolbox/config.js.map +1 -0
- package/dist/lib/toolbox/debug.d.ts +5 -0
- package/dist/lib/toolbox/debug.js +35 -0
- package/dist/lib/toolbox/debug.js.map +1 -0
- package/dist/lib/toolbox/display.d.ts +4 -0
- package/dist/lib/toolbox/display.js +56 -0
- package/dist/lib/toolbox/display.js.map +1 -0
- package/dist/lib/toolbox/index.d.ts +5 -0
- package/dist/lib/toolbox/index.js +28 -0
- package/dist/lib/toolbox/index.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/_create_random_file.d.ts +4 -0
- package/dist/lib/toolbox/with_openssl/_create_random_file.js +50 -0
- package/dist/lib/toolbox/with_openssl/_create_random_file.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/_env.d.ts +58 -0
- package/dist/lib/toolbox/with_openssl/_env.js +126 -0
- package/dist/lib/toolbox/with_openssl/_env.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.js +72 -0
- package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/create_private_key.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/create_private_key.js +92 -0
- package/dist/lib/toolbox/with_openssl/create_private_key.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.js +135 -0
- package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/execute_openssl.d.ts +67 -0
- package/dist/lib/toolbox/with_openssl/execute_openssl.js +246 -0
- package/dist/lib/toolbox/with_openssl/execute_openssl.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/index.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/index.js +30 -0
- package/dist/lib/toolbox/with_openssl/index.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/install_prerequisite.d.ts +7 -0
- package/dist/lib/toolbox/with_openssl/install_prerequisite.js +358 -0
- package/dist/lib/toolbox/with_openssl/install_prerequisite.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/toolbox.d.ts +53 -0
- package/dist/lib/toolbox/with_openssl/toolbox.js +190 -0
- package/dist/lib/toolbox/with_openssl/toolbox.js.map +1 -0
- package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.d.ts +5 -0
- package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.js +69 -0
- package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.js.map +1 -0
- package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.d.ts +3 -0
- package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.js +78 -0
- package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.js.map +1 -0
- package/dist/lib/toolbox/without_openssl/index.d.ts +2 -0
- package/dist/lib/toolbox/without_openssl/index.js +25 -0
- package/dist/lib/toolbox/without_openssl/index.js.map +1 -0
- package/package.json +12 -31
- package/dist/bin/install_prerequisite.mjs +0 -18
- package/dist/bin/install_prerequisite.mjs.map +0 -1
- package/dist/bin/pki.mjs +0 -5932
- package/dist/bin/pki.mjs.map +0 -1
- package/dist/chunk-EQ4DGI4M.mjs +0 -1917
- package/dist/chunk-EQ4DGI4M.mjs.map +0 -1
- package/dist/chunk-GCHH54PS.mjs +0 -30
- package/dist/chunk-GCHH54PS.mjs.map +0 -1
- package/dist/core-BwqSJPHc.d.ts +0 -1041
- package/dist/core.d.mts +0 -2
- package/dist/core.d.ts +0 -2
- package/dist/core.js +0 -1902
- package/dist/core.js.map +0 -1
- package/dist/core.mjs +0 -21
- package/dist/core.mjs.map +0 -1
- package/dist/index.d.mts +0 -1003
- package/dist/index.js +0 -5009
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -3145
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,821 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// node-opcua
|
|
3
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org
|
|
5
|
+
// Copyright (c) 2022-2026 - Sterfive.com
|
|
6
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
7
|
+
//
|
|
8
|
+
// This project is licensed under the terms of the MIT license.
|
|
9
|
+
//
|
|
10
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
11
|
+
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
|
12
|
+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
13
|
+
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
14
|
+
//
|
|
15
|
+
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
16
|
+
// Software.
|
|
17
|
+
//
|
|
18
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
19
|
+
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
20
|
+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
21
|
+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
23
|
+
// Error.stackTraceLimit = Infinity;
|
|
24
|
+
import assert from "node:assert";
|
|
25
|
+
import fs from "node:fs";
|
|
26
|
+
import { createRequire } from "node:module";
|
|
27
|
+
import os from "node:os";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
import { fileURLToPath } from "node:url";
|
|
30
|
+
// ESM provides no __filename/__dirname; reconstruct them from import.meta.url so
|
|
31
|
+
// the config-template lookups and createRequire below resolve relative to this
|
|
32
|
+
// compiled file (dist/lib/ca/...), matching the pre-ESM behaviour.
|
|
33
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
34
|
+
const __dirname = path.dirname(__filename);
|
|
35
|
+
import chalk from "chalk";
|
|
36
|
+
import { CertificatePurpose, generatePrivateKeyFile, Subject } from "node-opcua-crypto";
|
|
37
|
+
import { makeApplicationUrn } from "../misc/applicationurn.js";
|
|
38
|
+
import { extractFullyQualifiedDomainName, getFullyQualifiedDomainName } from "../misc/hostname.js";
|
|
39
|
+
import { CertificateManager } from "../pki/certificate_manager.js";
|
|
40
|
+
import { debugLog, displayChapter, displaySubtitle, displayTitle, g_config, makePath, mkdirRecursiveSync, warningLog } from "../toolbox/index.js";
|
|
41
|
+
import { createCertificateSigningRequestWithOpenSSL, dumpCertificate, ensure_openssl_installed, fingerprint, getPublicKeyFromPrivateKey, setEnv, toDer } from "../toolbox/with_openssl/index.js";
|
|
42
|
+
import { CertificateAuthority, defaultSubject } from "./certificate_authority.js";
|
|
43
|
+
const epilog = "Copyright (c) sterfive - node-opcua - 2017-2026";
|
|
44
|
+
// ------------------------------------------------- some useful dates
|
|
45
|
+
function get_offset_date(date, nbDays) {
|
|
46
|
+
const d = new Date(date.getTime());
|
|
47
|
+
d.setDate(d.getDate() + nbDays);
|
|
48
|
+
return d;
|
|
49
|
+
}
|
|
50
|
+
const today = new Date();
|
|
51
|
+
const yesterday = get_offset_date(today, -1);
|
|
52
|
+
const two_years_ago = get_offset_date(today, -2 * 365);
|
|
53
|
+
const next_year = get_offset_date(today, 365);
|
|
54
|
+
let gLocalConfig = {};
|
|
55
|
+
let g_certificateAuthority; // the Certificate Authority
|
|
56
|
+
/***
|
|
57
|
+
*
|
|
58
|
+
*
|
|
59
|
+
* prerequisites :
|
|
60
|
+
* g_config.CAFolder : the folder of the CA
|
|
61
|
+
*/
|
|
62
|
+
async function construct_CertificateAuthority(subject) {
|
|
63
|
+
// verify that g_config file has been loaded
|
|
64
|
+
assert(typeof gLocalConfig.CAFolder === "string", "expecting a CAFolder in config");
|
|
65
|
+
assert(typeof gLocalConfig.keySize === "number", "expecting a keySize in config");
|
|
66
|
+
if (!g_certificateAuthority) {
|
|
67
|
+
g_certificateAuthority = new CertificateAuthority({
|
|
68
|
+
keySize: gLocalConfig.keySize,
|
|
69
|
+
location: gLocalConfig.CAFolder,
|
|
70
|
+
subject
|
|
71
|
+
});
|
|
72
|
+
await g_certificateAuthority.initialize();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
let certificateManager; // the Certificate Manager
|
|
76
|
+
/***
|
|
77
|
+
*
|
|
78
|
+
*
|
|
79
|
+
* prerequisites :
|
|
80
|
+
* g_config.PKIFolder : the folder of the PKI
|
|
81
|
+
*/
|
|
82
|
+
async function construct_CertificateManager() {
|
|
83
|
+
assert(typeof gLocalConfig.PKIFolder === "string", "expecting a PKIFolder in config");
|
|
84
|
+
if (!certificateManager) {
|
|
85
|
+
certificateManager = new CertificateManager({
|
|
86
|
+
keySize: gLocalConfig.keySize,
|
|
87
|
+
location: gLocalConfig.PKIFolder
|
|
88
|
+
});
|
|
89
|
+
await certificateManager.initialize();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function _displayConfig(config) {
|
|
93
|
+
function w(str, l) {
|
|
94
|
+
return `${str} `.substring(0, l);
|
|
95
|
+
}
|
|
96
|
+
warningLog(chalk.yellow(" configuration = "));
|
|
97
|
+
for (const [key, value] of Object.entries(config)) {
|
|
98
|
+
warningLog(` ${chalk.yellow(w(key, 30))} : ${chalk.cyan(value.toString())}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function default_template_content() {
|
|
102
|
+
// istanbul ignore next
|
|
103
|
+
if (process.pkg?.entrypoint) {
|
|
104
|
+
// we are using PKG compiled package !
|
|
105
|
+
// warningLog("___filename", __filename);
|
|
106
|
+
// warningLog("__dirname", __dirname);
|
|
107
|
+
// warningLog("process.pkg.entrypoint", (process as unknown as IReadConfigurationOpts).pkg.entrypoint);
|
|
108
|
+
const a = fs.readFileSync(path.join(__dirname, "../../bin/pki_config.example.js"), "utf8");
|
|
109
|
+
return a;
|
|
110
|
+
}
|
|
111
|
+
function find_default_config_template() {
|
|
112
|
+
const rootFolder = find_module_root_folder();
|
|
113
|
+
// Note: we use a hardcoded config filename here because after tsup bundling,
|
|
114
|
+
// __filename points to the bundle file (e.g., "index.mjs") rather than the
|
|
115
|
+
// original source file, making dynamic name construction unreliable.
|
|
116
|
+
const configName = "pki_config.example.js";
|
|
117
|
+
let default_config_template = path.join(rootFolder, "bin", configName);
|
|
118
|
+
if (!fs.existsSync(default_config_template)) {
|
|
119
|
+
default_config_template = path.join(__dirname, "..", configName);
|
|
120
|
+
if (!fs.existsSync(default_config_template)) {
|
|
121
|
+
default_config_template = path.join(__dirname, `../bin/${configName}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return default_config_template;
|
|
125
|
+
}
|
|
126
|
+
const default_config_template = find_default_config_template();
|
|
127
|
+
assert(fs.existsSync(default_config_template));
|
|
128
|
+
const default_config_template_content = fs.readFileSync(default_config_template, "utf8");
|
|
129
|
+
return default_config_template_content;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
*
|
|
133
|
+
*/
|
|
134
|
+
function find_module_root_folder() {
|
|
135
|
+
let rootFolder = path.join(__dirname);
|
|
136
|
+
for (let i = 0; i < 4; i++) {
|
|
137
|
+
if (fs.existsSync(path.join(rootFolder, "package.json"))) {
|
|
138
|
+
return rootFolder;
|
|
139
|
+
}
|
|
140
|
+
rootFolder = path.join(rootFolder, "..");
|
|
141
|
+
}
|
|
142
|
+
assert(fs.existsSync(path.join(rootFolder, "package.json")), "root folder must have a package.json file");
|
|
143
|
+
return rootFolder;
|
|
144
|
+
}
|
|
145
|
+
async function readConfiguration(argv) {
|
|
146
|
+
if (argv.silent) {
|
|
147
|
+
g_config.silent = true;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
g_config.silent = false;
|
|
151
|
+
}
|
|
152
|
+
const fqdn = await extractFullyQualifiedDomainName();
|
|
153
|
+
const hostname = os.hostname();
|
|
154
|
+
let certificateDir;
|
|
155
|
+
function performSubstitution(str) {
|
|
156
|
+
str = str.replace("{CWD}", process.cwd());
|
|
157
|
+
if (certificateDir) {
|
|
158
|
+
str = str.replace("{root}", certificateDir);
|
|
159
|
+
}
|
|
160
|
+
if (gLocalConfig?.PKIFolder) {
|
|
161
|
+
str = str.replace("{PKIFolder}", gLocalConfig.PKIFolder);
|
|
162
|
+
}
|
|
163
|
+
str = str.replace("{hostname}", hostname);
|
|
164
|
+
str = str.replace("%FQDN%", fqdn);
|
|
165
|
+
return str;
|
|
166
|
+
}
|
|
167
|
+
function prepare(file) {
|
|
168
|
+
const tmp = path.resolve(performSubstitution(file));
|
|
169
|
+
return makePath(tmp);
|
|
170
|
+
}
|
|
171
|
+
// ------------------------------------------------------------------------------------------------------------
|
|
172
|
+
certificateDir = argv.root;
|
|
173
|
+
assert(typeof certificateDir === "string");
|
|
174
|
+
certificateDir = prepare(certificateDir);
|
|
175
|
+
mkdirRecursiveSync(certificateDir);
|
|
176
|
+
assert(fs.existsSync(certificateDir));
|
|
177
|
+
// ------------------------------------------------------------------------------------------------------------
|
|
178
|
+
const default_config = path.join(certificateDir, "config.js");
|
|
179
|
+
if (!fs.existsSync(default_config)) {
|
|
180
|
+
// copy
|
|
181
|
+
debugLog(chalk.yellow(" Creating default g_config file "), chalk.cyan(default_config));
|
|
182
|
+
const default_config_template_content = default_template_content();
|
|
183
|
+
fs.writeFileSync(default_config, default_config_template_content);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
debugLog(chalk.yellow(" using g_config file "), chalk.cyan(default_config));
|
|
187
|
+
}
|
|
188
|
+
if (!fs.existsSync(default_config)) {
|
|
189
|
+
debugLog(chalk.redBright(" cannot find config file ", default_config));
|
|
190
|
+
}
|
|
191
|
+
// see http://stackoverflow.com/questions/94445/using-openssl-what-does-unable-to-write-random-state-mean
|
|
192
|
+
// set random file to be random.rnd in the same folder as the g_config file
|
|
193
|
+
const defaultRandomFile = path.join(path.dirname(default_config), "random.rnd");
|
|
194
|
+
setEnv("RANDFILE", defaultRandomFile);
|
|
195
|
+
const _require = createRequire(__filename);
|
|
196
|
+
gLocalConfig = _require(default_config);
|
|
197
|
+
gLocalConfig.subject = new Subject(gLocalConfig.subject || "");
|
|
198
|
+
// if subject is provided on the command line , it has hight priority
|
|
199
|
+
if (argv.subject) {
|
|
200
|
+
gLocalConfig.subject = new Subject(argv.subject);
|
|
201
|
+
}
|
|
202
|
+
// istanbul ignore next
|
|
203
|
+
if (!gLocalConfig.subject.commonName) {
|
|
204
|
+
throw new Error("subject must have a Common Name");
|
|
205
|
+
}
|
|
206
|
+
gLocalConfig.certificateDir = certificateDir;
|
|
207
|
+
// ------------------------------------------------------------------------------------------------------------
|
|
208
|
+
let CAFolder = argv.CAFolder || path.join(certificateDir, "CA");
|
|
209
|
+
CAFolder = prepare(CAFolder);
|
|
210
|
+
gLocalConfig.CAFolder = CAFolder;
|
|
211
|
+
// ------------------------------------------------------------------------------------------------------------
|
|
212
|
+
gLocalConfig.PKIFolder = path.join(gLocalConfig.certificateDir, "PKI");
|
|
213
|
+
if (argv.PKIFolder) {
|
|
214
|
+
gLocalConfig.PKIFolder = prepare(argv.PKIFolder);
|
|
215
|
+
}
|
|
216
|
+
gLocalConfig.PKIFolder = prepare(gLocalConfig.PKIFolder);
|
|
217
|
+
if (argv.privateKey) {
|
|
218
|
+
gLocalConfig.privateKey = prepare(argv.privateKey);
|
|
219
|
+
}
|
|
220
|
+
if (argv.applicationUri) {
|
|
221
|
+
gLocalConfig.applicationUri = performSubstitution(argv.applicationUri);
|
|
222
|
+
}
|
|
223
|
+
if (argv.output) {
|
|
224
|
+
gLocalConfig.outputFile = argv.output;
|
|
225
|
+
}
|
|
226
|
+
gLocalConfig.altNames = [];
|
|
227
|
+
if (argv.altNames) {
|
|
228
|
+
gLocalConfig.altNames = argv.altNames.split(";");
|
|
229
|
+
}
|
|
230
|
+
gLocalConfig.dns = [getFullyQualifiedDomainName()];
|
|
231
|
+
if (argv.dns) {
|
|
232
|
+
gLocalConfig.dns = argv.dns.split(",").map(performSubstitution);
|
|
233
|
+
}
|
|
234
|
+
gLocalConfig.ip = [];
|
|
235
|
+
if (argv.ip) {
|
|
236
|
+
gLocalConfig.ip = argv.ip.split(",");
|
|
237
|
+
}
|
|
238
|
+
if (argv.keySize) {
|
|
239
|
+
const v = argv.keySize;
|
|
240
|
+
if (v !== 1024 && v !== 2048 && v !== 3072 && v !== 4096) {
|
|
241
|
+
throw new Error(`invalid keysize specified ${v} should be 1024,2048,3072 or 4096`);
|
|
242
|
+
}
|
|
243
|
+
gLocalConfig.keySize = argv.keySize;
|
|
244
|
+
}
|
|
245
|
+
if (argv.validity) {
|
|
246
|
+
gLocalConfig.validity = argv.validity;
|
|
247
|
+
}
|
|
248
|
+
// xx displayConfig(g_config);
|
|
249
|
+
// ------------------------------------------------------------------------------------------------------------
|
|
250
|
+
}
|
|
251
|
+
async function createDefaultCertificate(base_name, prefix, key_length, applicationUri, dev) {
|
|
252
|
+
// possible key length in bits
|
|
253
|
+
assert(key_length === 1024 || key_length === 2048 || key_length === 3072 || key_length === 4096);
|
|
254
|
+
const private_key_file = makePath(base_name, `${prefix}key_${key_length}.pem`);
|
|
255
|
+
const public_key_file = makePath(base_name, `${prefix}public_key_${key_length}.pub`);
|
|
256
|
+
const certificate_file = makePath(base_name, `${prefix}cert_${key_length}.pem`);
|
|
257
|
+
const certificate_file_outofdate = makePath(base_name, `${prefix}cert_${key_length}_outofdate.pem`);
|
|
258
|
+
const certificate_file_not_active_yet = makePath(base_name, `${prefix}cert_${key_length}_not_active_yet.pem`);
|
|
259
|
+
const certificate_revoked = makePath(base_name, `${prefix}cert_${key_length}_revoked.pem`);
|
|
260
|
+
const self_signed_certificate_file = makePath(base_name, `${prefix}selfsigned_cert_${key_length}.pem`);
|
|
261
|
+
const fqdn = getFullyQualifiedDomainName();
|
|
262
|
+
const hostname = os.hostname();
|
|
263
|
+
const dns = [
|
|
264
|
+
// for conformance reason, localhost shall not be present in the DNS field of COP
|
|
265
|
+
// ***FORBIDEN** "localhost",
|
|
266
|
+
getFullyQualifiedDomainName()
|
|
267
|
+
];
|
|
268
|
+
if (hostname !== fqdn) {
|
|
269
|
+
dns.push(hostname);
|
|
270
|
+
}
|
|
271
|
+
const ip = [];
|
|
272
|
+
async function createCertificateIfNotExist(certificate, private_key, applicationUri, startDate, validity) {
|
|
273
|
+
// istanbul ignore next
|
|
274
|
+
if (fs.existsSync(certificate)) {
|
|
275
|
+
warningLog(chalk.yellow(" certificate"), chalk.cyan(certificate), chalk.yellow(" already exists => skipping"));
|
|
276
|
+
return "";
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
return await createCertificate(certificate, private_key, applicationUri, startDate, validity);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function createCertificate(certificate, privateKey, applicationUri, startDate, validity) {
|
|
283
|
+
const certificateSigningRequestFile = `${certificate}.csr`;
|
|
284
|
+
const configFile = makePath(base_name, "../certificates/PKI/own/openssl.cnf");
|
|
285
|
+
const dns = [os.hostname()];
|
|
286
|
+
const ip = ["127.0.0.1"];
|
|
287
|
+
const params = {
|
|
288
|
+
applicationUri,
|
|
289
|
+
privateKey,
|
|
290
|
+
rootDir: ".",
|
|
291
|
+
configFile,
|
|
292
|
+
dns,
|
|
293
|
+
ip,
|
|
294
|
+
purpose: CertificatePurpose.ForApplication
|
|
295
|
+
};
|
|
296
|
+
// create CSR
|
|
297
|
+
await createCertificateSigningRequestWithOpenSSL(certificateSigningRequestFile, params);
|
|
298
|
+
return await g_certificateAuthority.signCertificateRequest(certificate, certificateSigningRequestFile, {
|
|
299
|
+
applicationUri,
|
|
300
|
+
dns,
|
|
301
|
+
ip,
|
|
302
|
+
startDate,
|
|
303
|
+
validity
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
async function createSelfSignedCertificate(certificate, private_key, applicationUri, startDate, validity) {
|
|
307
|
+
await g_certificateAuthority.createSelfSignedCertificate(certificate, private_key, {
|
|
308
|
+
applicationUri,
|
|
309
|
+
dns,
|
|
310
|
+
ip,
|
|
311
|
+
startDate,
|
|
312
|
+
validity
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
async function revoke_certificate(certificate) {
|
|
316
|
+
await g_certificateAuthority.revokeCertificate(certificate, {});
|
|
317
|
+
}
|
|
318
|
+
async function createPrivateKeyIfNotExist(privateKey, keyLength) {
|
|
319
|
+
if (fs.existsSync(privateKey)) {
|
|
320
|
+
warningLog(chalk.yellow(" privateKey"), chalk.cyan(privateKey), chalk.yellow(" already exists => skipping"));
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
await generatePrivateKeyFile(privateKey, keyLength);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
displaySubtitle(` create private key :${private_key_file}`);
|
|
328
|
+
await createPrivateKeyIfNotExist(private_key_file, key_length);
|
|
329
|
+
displaySubtitle(` extract public key ${public_key_file} from private key `);
|
|
330
|
+
await getPublicKeyFromPrivateKey(private_key_file, public_key_file);
|
|
331
|
+
displaySubtitle(` create Certificate ${certificate_file}`);
|
|
332
|
+
await createCertificateIfNotExist(certificate_file, private_key_file, applicationUri, yesterday, 365);
|
|
333
|
+
displaySubtitle(` create self signed Certificate ${self_signed_certificate_file}`);
|
|
334
|
+
if (fs.existsSync(self_signed_certificate_file)) {
|
|
335
|
+
// self_signed certificate already exists
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
await createSelfSignedCertificate(self_signed_certificate_file, private_key_file, applicationUri, yesterday, 365);
|
|
339
|
+
if (dev) {
|
|
340
|
+
await createCertificateIfNotExist(certificate_file_outofdate, private_key_file, applicationUri, two_years_ago, 365);
|
|
341
|
+
await createCertificateIfNotExist(certificate_file_not_active_yet, private_key_file, applicationUri, next_year, 365);
|
|
342
|
+
if (!fs.existsSync(certificate_revoked)) {
|
|
343
|
+
// self_signed certificate already exists
|
|
344
|
+
const certificate = await createCertificateIfNotExist(certificate_revoked, private_key_file, `${applicationUri}Revoked`, // make sure we used a uniq URI here
|
|
345
|
+
yesterday, 365);
|
|
346
|
+
warningLog(" certificate to revoke => ", certificate);
|
|
347
|
+
// must be awaited: `openssl ca -revoke` / `-gencrl` rewrite index.txt
|
|
348
|
+
// (index.txt.new -> rename), and a concurrent `openssl ca` signing the
|
|
349
|
+
// next key size's certificate can open index.txt in the window where
|
|
350
|
+
// it does not exist ("Problem with index file ... no such file")
|
|
351
|
+
await revoke_certificate(certificate_revoked);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
async function wrap(func) {
|
|
356
|
+
try {
|
|
357
|
+
await func();
|
|
358
|
+
}
|
|
359
|
+
catch (err) {
|
|
360
|
+
console.log(err.message);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async function create_default_certificates(dev) {
|
|
364
|
+
assert(gLocalConfig);
|
|
365
|
+
const base_name = gLocalConfig.certificateDir || "";
|
|
366
|
+
assert(fs.existsSync(base_name));
|
|
367
|
+
let clientURN;
|
|
368
|
+
let serverURN;
|
|
369
|
+
let discoveryServerURN;
|
|
370
|
+
await wrap(async () => {
|
|
371
|
+
await extractFullyQualifiedDomainName();
|
|
372
|
+
const hostname = os.hostname();
|
|
373
|
+
const fqdn = getFullyQualifiedDomainName();
|
|
374
|
+
warningLog(chalk.yellow(" hostname = "), chalk.cyan(hostname));
|
|
375
|
+
warningLog(chalk.yellow(" fqdn = "), chalk.cyan(fqdn));
|
|
376
|
+
clientURN = makeApplicationUrn(hostname, "NodeOPCUA-Client");
|
|
377
|
+
serverURN = makeApplicationUrn(hostname, "NodeOPCUA-Server");
|
|
378
|
+
discoveryServerURN = makeApplicationUrn(hostname, "NodeOPCUA-DiscoveryServer");
|
|
379
|
+
displayTitle("Create Application Certificate for Server & its private key");
|
|
380
|
+
await createDefaultCertificate(base_name, "client_", 1024, clientURN, dev);
|
|
381
|
+
await createDefaultCertificate(base_name, "client_", 2048, clientURN, dev);
|
|
382
|
+
await createDefaultCertificate(base_name, "client_", 3072, clientURN, dev);
|
|
383
|
+
await createDefaultCertificate(base_name, "client_", 4096, clientURN, dev);
|
|
384
|
+
displayTitle("Create Application Certificate for Client & its private key");
|
|
385
|
+
await createDefaultCertificate(base_name, "server_", 1024, serverURN, dev);
|
|
386
|
+
await createDefaultCertificate(base_name, "server_", 2048, serverURN, dev);
|
|
387
|
+
await createDefaultCertificate(base_name, "server_", 3072, serverURN, dev);
|
|
388
|
+
await createDefaultCertificate(base_name, "server_", 4096, serverURN, dev);
|
|
389
|
+
displayTitle("Create Application Certificate for DiscoveryServer & its private key");
|
|
390
|
+
await createDefaultCertificate(base_name, "discoveryServer_", 1024, discoveryServerURN, dev);
|
|
391
|
+
await createDefaultCertificate(base_name, "discoveryServer_", 2048, discoveryServerURN, dev);
|
|
392
|
+
await createDefaultCertificate(base_name, "discoveryServer_", 3072, discoveryServerURN, dev);
|
|
393
|
+
await createDefaultCertificate(base_name, "discoveryServer_", 4096, discoveryServerURN, dev);
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
async function createDefaultCertificates(dev) {
|
|
397
|
+
await construct_CertificateAuthority("");
|
|
398
|
+
await construct_CertificateManager();
|
|
399
|
+
await create_default_certificates(dev);
|
|
400
|
+
}
|
|
401
|
+
import commandLineArgs from "command-line-args";
|
|
402
|
+
import commandLineUsage from "command-line-usage";
|
|
403
|
+
const commonOptions = [
|
|
404
|
+
{
|
|
405
|
+
name: "root",
|
|
406
|
+
alias: "r",
|
|
407
|
+
type: String,
|
|
408
|
+
defaultValue: "{CWD}/certificates",
|
|
409
|
+
description: "the location of the Certificate folder"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: "CAFolder",
|
|
413
|
+
alias: "c",
|
|
414
|
+
type: String,
|
|
415
|
+
defaultValue: "{root}/CA",
|
|
416
|
+
description: "the location of the Certificate Authority folder"
|
|
417
|
+
},
|
|
418
|
+
{ name: "PKIFolder", type: String, defaultValue: "{root}/PKI", description: "the location of the Public Key Infrastructure" },
|
|
419
|
+
{ name: "silent", type: Boolean, defaultValue: false, description: "minimize output" },
|
|
420
|
+
{
|
|
421
|
+
name: "privateKey",
|
|
422
|
+
alias: "p",
|
|
423
|
+
type: String,
|
|
424
|
+
defaultValue: "{PKIFolder}/own/private_key.pem",
|
|
425
|
+
description: "the private key to use to generate certificate"
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
name: "keySize",
|
|
429
|
+
alias: "k",
|
|
430
|
+
type: Number,
|
|
431
|
+
defaultValue: 2048,
|
|
432
|
+
description: "the private key size in bits (1024|2048|3072|4096)"
|
|
433
|
+
},
|
|
434
|
+
{ name: "help", alias: "h", type: Boolean, description: "display this help" }
|
|
435
|
+
];
|
|
436
|
+
function getOptions(names) {
|
|
437
|
+
return commonOptions.filter((o) => names.includes(o.name) || o.name === "help" || o.name === "silent");
|
|
438
|
+
}
|
|
439
|
+
function showHelp(command, description, options, usage) {
|
|
440
|
+
const sections = [
|
|
441
|
+
{
|
|
442
|
+
header: `Command: ${command}`,
|
|
443
|
+
content: description
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
header: "Usage",
|
|
447
|
+
content: usage || `$0 ${command} [options]`
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
header: "Options",
|
|
451
|
+
optionList: options
|
|
452
|
+
}
|
|
453
|
+
];
|
|
454
|
+
console.log(commandLineUsage(sections));
|
|
455
|
+
}
|
|
456
|
+
export async function main(argumentsList) {
|
|
457
|
+
const mainDefinitions = [{ name: "command", defaultOption: true }];
|
|
458
|
+
let mainOptions;
|
|
459
|
+
try {
|
|
460
|
+
mainOptions = commandLineArgs(mainDefinitions, { argv: argumentsList, stopAtFirstUnknown: true });
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
console.log(err.message);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const argv = mainOptions._unknown || [];
|
|
467
|
+
const command = mainOptions.command;
|
|
468
|
+
if (!command || command === "help") {
|
|
469
|
+
console.log(commandLineUsage([
|
|
470
|
+
{
|
|
471
|
+
header: "node-opcua-pki",
|
|
472
|
+
content: `PKI management for node-opcua\n\n${epilog}`
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
header: "Commands",
|
|
476
|
+
content: [
|
|
477
|
+
{ name: "demo", summary: "create default certificate for node-opcua demos" },
|
|
478
|
+
{ name: "createCA", summary: "create a Certificate Authority" },
|
|
479
|
+
{ name: "createPKI", summary: "create a Public Key Infrastructure" },
|
|
480
|
+
{ name: "certificate", summary: "create a new certificate" },
|
|
481
|
+
{ name: "revoke <certificateFile>", summary: "revoke a existing certificate" },
|
|
482
|
+
{ name: "csr", summary: "create a certificate signing request" },
|
|
483
|
+
{ name: "sign", summary: "validate a certificate signing request and generate a certificate" },
|
|
484
|
+
{ name: "dump <certificateFile>", summary: "display a certificate" },
|
|
485
|
+
{ name: "toder <pemCertificate>", summary: "convert a certificate to a DER format with finger print" },
|
|
486
|
+
{ name: "fingerprint <certificateFile>", summary: "print the certificate fingerprint" },
|
|
487
|
+
{ name: "version", summary: "display the version number" }
|
|
488
|
+
]
|
|
489
|
+
}
|
|
490
|
+
]));
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
if (command === "version") {
|
|
494
|
+
const rootFolder = find_module_root_folder();
|
|
495
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(rootFolder, "package.json"), "utf-8"));
|
|
496
|
+
console.log(pkg.version);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (command === "demo") {
|
|
500
|
+
const optionsDef = [
|
|
501
|
+
...getOptions(["root", "silent"]),
|
|
502
|
+
{ name: "dev", type: Boolean, description: "create all sort of fancy certificates for dev testing purposes" },
|
|
503
|
+
{ name: "clean", type: Boolean, description: "Purge existing directory [use with care!]" }
|
|
504
|
+
];
|
|
505
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
506
|
+
if (local_argv.help)
|
|
507
|
+
return showHelp("demo", "create default certificate for node-opcua demos", optionsDef, "$0 demo [--dev] [--silent] [--clean]");
|
|
508
|
+
await wrap(async () => {
|
|
509
|
+
await ensure_openssl_installed();
|
|
510
|
+
displayChapter("Create Demo certificates");
|
|
511
|
+
displayTitle("reading configuration");
|
|
512
|
+
await readConfiguration(local_argv);
|
|
513
|
+
if (local_argv.clean) {
|
|
514
|
+
displayTitle("Cleaning old certificates");
|
|
515
|
+
assert(gLocalConfig);
|
|
516
|
+
const certificateDir = gLocalConfig.certificateDir || "";
|
|
517
|
+
const files = await fs.promises.readdir(certificateDir);
|
|
518
|
+
for (const file of files) {
|
|
519
|
+
if (file.includes(".pem") || file.includes(".pub")) {
|
|
520
|
+
await fs.promises.unlink(path.join(certificateDir, file));
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
mkdirRecursiveSync(certificateDir);
|
|
524
|
+
}
|
|
525
|
+
displayTitle("create certificates");
|
|
526
|
+
await createDefaultCertificates(local_argv.dev);
|
|
527
|
+
displayChapter("Demo certificates CREATED");
|
|
528
|
+
});
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
if (command === "createCA") {
|
|
532
|
+
const optionsDef = [
|
|
533
|
+
...getOptions(["root", "CAFolder", "keySize", "silent"]),
|
|
534
|
+
{ name: "subject", type: String, defaultValue: defaultSubject, description: "the CA certificate subject" }
|
|
535
|
+
];
|
|
536
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
537
|
+
if (local_argv.help)
|
|
538
|
+
return showHelp("createCA", "create a Certificate Authority", optionsDef);
|
|
539
|
+
await wrap(async () => {
|
|
540
|
+
await ensure_openssl_installed();
|
|
541
|
+
await readConfiguration(local_argv);
|
|
542
|
+
await construct_CertificateAuthority(local_argv.subject);
|
|
543
|
+
});
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (command === "createPKI") {
|
|
547
|
+
const optionsDef = getOptions(["root", "PKIFolder", "keySize", "silent"]);
|
|
548
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
549
|
+
if (local_argv.help)
|
|
550
|
+
return showHelp("createPKI", "create a Public Key Infrastructure", optionsDef);
|
|
551
|
+
await wrap(async () => {
|
|
552
|
+
await readConfiguration(local_argv);
|
|
553
|
+
await construct_CertificateManager();
|
|
554
|
+
});
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (command === "certificate") {
|
|
558
|
+
const optionsDef = [
|
|
559
|
+
...getOptions(["root", "CAFolder", "PKIFolder", "privateKey", "silent"]),
|
|
560
|
+
{
|
|
561
|
+
name: "applicationUri",
|
|
562
|
+
alias: "a",
|
|
563
|
+
type: String,
|
|
564
|
+
defaultValue: "urn:{hostname}:Node-OPCUA-Server",
|
|
565
|
+
description: "the application URI"
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
name: "output",
|
|
569
|
+
alias: "o",
|
|
570
|
+
type: String,
|
|
571
|
+
defaultValue: "my_certificate.pem",
|
|
572
|
+
description: "the name of the generated certificate =>"
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: "selfSigned",
|
|
576
|
+
alias: "s",
|
|
577
|
+
type: Boolean,
|
|
578
|
+
defaultValue: false,
|
|
579
|
+
description: "if true, certificate will be self-signed"
|
|
580
|
+
},
|
|
581
|
+
{ name: "validity", alias: "v", type: Number, description: "the certificate validity in days" },
|
|
582
|
+
{
|
|
583
|
+
name: "dns",
|
|
584
|
+
type: String,
|
|
585
|
+
defaultValue: "{hostname}",
|
|
586
|
+
description: "the list of valid domain name (comma separated)"
|
|
587
|
+
},
|
|
588
|
+
{ name: "ip", type: String, defaultValue: "", description: "the list of valid IPs (comma separated)" },
|
|
589
|
+
{
|
|
590
|
+
name: "subject",
|
|
591
|
+
type: String,
|
|
592
|
+
defaultValue: "",
|
|
593
|
+
description: "the certificate subject ( for instance C=FR/ST=Centre/L=Orleans/O=SomeOrganization/CN=Hello )"
|
|
594
|
+
}
|
|
595
|
+
];
|
|
596
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
597
|
+
if (local_argv.help || !local_argv.applicationUri || !local_argv.output)
|
|
598
|
+
return showHelp("certificate", "create a new certificate", optionsDef);
|
|
599
|
+
async function command_certificate(local_argv) {
|
|
600
|
+
const selfSigned = !!local_argv.selfSigned;
|
|
601
|
+
if (!selfSigned) {
|
|
602
|
+
await command_full_certificate(local_argv);
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
await command_selfsigned_certificate(local_argv);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async function command_selfsigned_certificate(local_argv) {
|
|
609
|
+
const _fqdn = await extractFullyQualifiedDomainName();
|
|
610
|
+
await readConfiguration(local_argv);
|
|
611
|
+
await construct_CertificateManager();
|
|
612
|
+
displaySubtitle(` create self signed Certificate ${gLocalConfig.outputFile}`);
|
|
613
|
+
let subject = local_argv.subject && local_argv.subject.length > 1 ? new Subject(local_argv.subject) : gLocalConfig.subject || "";
|
|
614
|
+
subject = JSON.parse(JSON.stringify(subject));
|
|
615
|
+
const params = {
|
|
616
|
+
applicationUri: gLocalConfig.applicationUri || "",
|
|
617
|
+
dns: gLocalConfig.dns || [],
|
|
618
|
+
ip: gLocalConfig.ip || [],
|
|
619
|
+
outputFile: gLocalConfig.outputFile || "self_signed_certificate.pem",
|
|
620
|
+
startDate: gLocalConfig.startDate || new Date(),
|
|
621
|
+
subject,
|
|
622
|
+
validity: gLocalConfig.validity || 365
|
|
623
|
+
};
|
|
624
|
+
await certificateManager.createSelfSignedCertificate(params);
|
|
625
|
+
}
|
|
626
|
+
async function command_full_certificate(local_argv) {
|
|
627
|
+
await readConfiguration(local_argv);
|
|
628
|
+
await construct_CertificateManager();
|
|
629
|
+
await construct_CertificateAuthority("");
|
|
630
|
+
assert(fs.existsSync(gLocalConfig.CAFolder || ""), " CA folder must exist");
|
|
631
|
+
gLocalConfig.privateKey = undefined; // use PKI private key
|
|
632
|
+
// create a Certificate Request from the certificate Manager
|
|
633
|
+
gLocalConfig.subject = local_argv.subject && local_argv.subject.length > 1 ? local_argv.subject : gLocalConfig.subject;
|
|
634
|
+
const csr_file = await certificateManager.createCertificateRequest(gLocalConfig);
|
|
635
|
+
if (!csr_file) {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
warningLog(" csr_file = ", csr_file);
|
|
639
|
+
const certificate = csr_file.replace(".csr", ".pem");
|
|
640
|
+
if (fs.existsSync(certificate)) {
|
|
641
|
+
throw new Error(` File ${certificate} already exist`);
|
|
642
|
+
}
|
|
643
|
+
await g_certificateAuthority.signCertificateRequest(certificate, csr_file, gLocalConfig);
|
|
644
|
+
assert(typeof gLocalConfig.outputFile === "string");
|
|
645
|
+
fs.writeFileSync(gLocalConfig.outputFile || "", fs.readFileSync(certificate, "ascii"));
|
|
646
|
+
}
|
|
647
|
+
await wrap(async () => await command_certificate(local_argv));
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (command === "revoke") {
|
|
651
|
+
const optionsDef = [{ name: "certificateFile", type: String, defaultOption: true }, ...getOptions(["root", "CAFolder"])];
|
|
652
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
653
|
+
if (local_argv.help || !local_argv.certificateFile)
|
|
654
|
+
return showHelp("revoke <certificateFile>", "revoke a existing certificate", optionsDef, "$0 revoke my_certificate.pem");
|
|
655
|
+
async function revoke_certificate(certificate) {
|
|
656
|
+
await g_certificateAuthority.revokeCertificate(certificate, {});
|
|
657
|
+
}
|
|
658
|
+
await wrap(async () => {
|
|
659
|
+
const certificate = path.resolve(local_argv.certificateFile);
|
|
660
|
+
warningLog(chalk.yellow(" Certificate to revoke : "), chalk.cyan(certificate));
|
|
661
|
+
if (!fs.existsSync(certificate)) {
|
|
662
|
+
throw new Error(`cannot find certificate to revoke ${certificate}`);
|
|
663
|
+
}
|
|
664
|
+
await readConfiguration(local_argv);
|
|
665
|
+
await construct_CertificateAuthority("");
|
|
666
|
+
await revoke_certificate(certificate);
|
|
667
|
+
warningLog("done ... ");
|
|
668
|
+
warningLog(" crl = ", g_certificateAuthority.revocationList);
|
|
669
|
+
warningLog("\nyou should now publish the new Certificate Revocation List");
|
|
670
|
+
});
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (command === "csr") {
|
|
674
|
+
const optionsDef = [
|
|
675
|
+
...getOptions(["root", "PKIFolder", "privateKey", "silent"]),
|
|
676
|
+
{
|
|
677
|
+
name: "applicationUri",
|
|
678
|
+
alias: "a",
|
|
679
|
+
type: String,
|
|
680
|
+
defaultValue: "urn:{hostname}:Node-OPCUA-Server",
|
|
681
|
+
description: "the application URI"
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
name: "output",
|
|
685
|
+
alias: "o",
|
|
686
|
+
type: String,
|
|
687
|
+
defaultValue: "my_certificate_signing_request.csr",
|
|
688
|
+
description: "the name of the generated signing_request"
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
name: "dns",
|
|
692
|
+
type: String,
|
|
693
|
+
defaultValue: "{hostname}",
|
|
694
|
+
description: "the list of valid domain name (comma separated)"
|
|
695
|
+
},
|
|
696
|
+
{ name: "ip", type: String, defaultValue: "", description: "the list of valid IPs (comma separated)" },
|
|
697
|
+
{
|
|
698
|
+
name: "subject",
|
|
699
|
+
type: String,
|
|
700
|
+
defaultValue: "/CN=Certificate",
|
|
701
|
+
description: "the certificate subject ( for instance /C=FR/ST=Centre/L=Orleans/O=SomeOrganization/CN=Hello )"
|
|
702
|
+
}
|
|
703
|
+
];
|
|
704
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
705
|
+
if (local_argv.help)
|
|
706
|
+
return showHelp("csr", "create a certificate signing request", optionsDef);
|
|
707
|
+
await wrap(async () => {
|
|
708
|
+
await readConfiguration(local_argv);
|
|
709
|
+
if (!fs.existsSync(gLocalConfig.PKIFolder || "")) {
|
|
710
|
+
warningLog("PKI folder must exist");
|
|
711
|
+
}
|
|
712
|
+
await construct_CertificateManager();
|
|
713
|
+
if (!gLocalConfig.outputFile || fs.existsSync(gLocalConfig.outputFile)) {
|
|
714
|
+
throw new Error(` File ${gLocalConfig.outputFile} already exist`);
|
|
715
|
+
}
|
|
716
|
+
gLocalConfig.privateKey = undefined; // use PKI private key
|
|
717
|
+
// create a Certificate Request from the certificate Manager
|
|
718
|
+
gLocalConfig.subject = local_argv.subject && local_argv.subject.length > 1 ? local_argv.subject : gLocalConfig.subject;
|
|
719
|
+
const internal_csr_file = await certificateManager.createCertificateRequest(gLocalConfig);
|
|
720
|
+
if (!internal_csr_file) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
if (!gLocalConfig.outputFile) {
|
|
724
|
+
warningLog("please specify a output file");
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const csr = await fs.promises.readFile(internal_csr_file, "utf-8");
|
|
728
|
+
fs.writeFileSync(gLocalConfig.outputFile || "", csr, "utf-8");
|
|
729
|
+
warningLog("Subject = ", gLocalConfig.subject);
|
|
730
|
+
warningLog("applicationUri = ", gLocalConfig.applicationUri);
|
|
731
|
+
warningLog("altNames = ", gLocalConfig.altNames);
|
|
732
|
+
warningLog("dns = ", gLocalConfig.dns);
|
|
733
|
+
warningLog("ip = ", gLocalConfig.ip);
|
|
734
|
+
warningLog("CSR file = ", gLocalConfig.outputFile);
|
|
735
|
+
});
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (command === "sign") {
|
|
739
|
+
const optionsDef = [
|
|
740
|
+
...getOptions(["root", "CAFolder", "silent"]),
|
|
741
|
+
{ name: "csr", alias: "i", type: String, defaultValue: "my_certificate_signing_request.csr", description: "the csr" },
|
|
742
|
+
{
|
|
743
|
+
name: "output",
|
|
744
|
+
alias: "o",
|
|
745
|
+
type: String,
|
|
746
|
+
defaultValue: "my_certificate.pem",
|
|
747
|
+
description: "the name of the generated certificate"
|
|
748
|
+
},
|
|
749
|
+
{ name: "validity", alias: "v", type: Number, defaultValue: 365, description: "the certificate validity in days" }
|
|
750
|
+
];
|
|
751
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
752
|
+
if (local_argv.help || !local_argv.csr || !local_argv.output)
|
|
753
|
+
return showHelp("sign", "validate a certificate signing request and generate a certificate", optionsDef);
|
|
754
|
+
await wrap(async () => {
|
|
755
|
+
await readConfiguration(local_argv);
|
|
756
|
+
if (!fs.existsSync(gLocalConfig.CAFolder || "")) {
|
|
757
|
+
throw new Error(`CA folder must exist:${gLocalConfig.CAFolder}`);
|
|
758
|
+
}
|
|
759
|
+
await construct_CertificateAuthority("");
|
|
760
|
+
const csr_file = path.resolve(local_argv.csr || "");
|
|
761
|
+
if (!fs.existsSync(csr_file)) {
|
|
762
|
+
throw new Error(`Certificate signing request doesn't exist: ${csr_file}`);
|
|
763
|
+
}
|
|
764
|
+
const certificate = path.resolve(local_argv.output || csr_file.replace(".csr", ".pem"));
|
|
765
|
+
if (fs.existsSync(certificate)) {
|
|
766
|
+
throw new Error(` File ${certificate} already exist`);
|
|
767
|
+
}
|
|
768
|
+
await g_certificateAuthority.signCertificateRequest(certificate, csr_file, gLocalConfig);
|
|
769
|
+
assert(typeof gLocalConfig.outputFile === "string");
|
|
770
|
+
fs.writeFileSync(gLocalConfig.outputFile || "", fs.readFileSync(certificate, "ascii"));
|
|
771
|
+
});
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
if (command === "dump") {
|
|
775
|
+
const optionsDef = [
|
|
776
|
+
{ name: "certificateFile", type: String, defaultOption: true },
|
|
777
|
+
{ name: "help", alias: "h", type: Boolean }
|
|
778
|
+
];
|
|
779
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
780
|
+
if (local_argv.help || !local_argv.certificateFile)
|
|
781
|
+
return showHelp("dump <certificateFile>", "display a certificate", optionsDef);
|
|
782
|
+
await wrap(async () => {
|
|
783
|
+
const data = await dumpCertificate(local_argv.certificateFile);
|
|
784
|
+
warningLog(data);
|
|
785
|
+
});
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (command === "toder") {
|
|
789
|
+
const optionsDef = [
|
|
790
|
+
{ name: "pemCertificate", type: String, defaultOption: true },
|
|
791
|
+
{ name: "help", alias: "h", type: Boolean }
|
|
792
|
+
];
|
|
793
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
794
|
+
if (local_argv.help || !local_argv.pemCertificate)
|
|
795
|
+
return showHelp("toder <pemCertificate>", "convert a certificate to a DER format with finger print", optionsDef);
|
|
796
|
+
await wrap(async () => {
|
|
797
|
+
await toDer(local_argv.pemCertificate);
|
|
798
|
+
});
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (command === "fingerprint") {
|
|
802
|
+
const optionsDef = [
|
|
803
|
+
{ name: "certificateFile", type: String, defaultOption: true },
|
|
804
|
+
{ name: "help", alias: "h", type: Boolean }
|
|
805
|
+
];
|
|
806
|
+
const local_argv = commandLineArgs(optionsDef, { argv });
|
|
807
|
+
if (local_argv.help || !local_argv.certificateFile)
|
|
808
|
+
return showHelp("fingerprint <certificateFile>", "print the certificate fingerprint", optionsDef);
|
|
809
|
+
await wrap(async () => {
|
|
810
|
+
const certificate = local_argv.certificateFile;
|
|
811
|
+
const data = await fingerprint(certificate);
|
|
812
|
+
if (!data)
|
|
813
|
+
return;
|
|
814
|
+
const s = data.split("=")[1].split(":").join("").trim();
|
|
815
|
+
warningLog(s);
|
|
816
|
+
});
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
console.log(`Unknown command: ${command}`);
|
|
820
|
+
}
|
|
821
|
+
//# sourceMappingURL=crypto_create_CA.js.map
|