node-opcua-pki 3.0.2 → 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/.ignore +6 -6
- package/.prettierrc +5 -5
- package/LICENSE +22 -22
- package/bin/crypto_create_CA.js +0 -0
- package/bin/crypto_create_CA_config.example.js +18 -18
- package/bin/install_prerequisite.js +9 -9
- package/dist/crypto_create_CA.d.ts +2 -2
- package/dist/crypto_create_CA.js +897 -897
- package/dist/index.d.ts +6 -6
- package/dist/index.js +44 -44
- package/dist/misc/applicationurn.d.ts +1 -1
- package/dist/misc/applicationurn.js +46 -46
- package/dist/misc/hostname.d.ts +8 -8
- package/dist/misc/hostname.js +102 -102
- package/dist/misc/install_prerequisite.d.ts +9 -9
- package/dist/misc/install_prerequisite.js +363 -360
- package/dist/misc/install_prerequisite.js.map +1 -1
- package/dist/misc/subject.d.ts +26 -26
- package/dist/misc/subject.js +121 -121
- package/dist/pki/certificate_authority.d.ts +61 -61
- package/dist/pki/certificate_authority.js +481 -481
- package/dist/pki/certificate_manager.d.ts +144 -144
- package/dist/pki/certificate_manager.js +883 -883
- package/dist/pki/certificate_manager.js.map +1 -1
- package/dist/pki/common.d.ts +5 -5
- package/dist/pki/common.js +2 -2
- package/dist/pki/templates/ca_config_template.cnf.d.ts +2 -2
- package/dist/pki/templates/ca_config_template.cnf.js +129 -129
- package/dist/pki/templates/simple_config_template.cnf.d.ts +2 -2
- package/dist/pki/templates/simple_config_template.cnf.js +75 -75
- package/dist/pki/toolbox.d.ts +160 -160
- package/dist/pki/toolbox.js +699 -699
- package/dist/pki/toolbox_pfx.js +18 -18
- package/lib/crypto_create_CA.ts +1135 -1135
- package/lib/index.ts +28 -28
- package/lib/misc/applicationurn.ts +45 -45
- package/lib/misc/hostname.ts +89 -89
- package/lib/misc/install_prerequisite.ts +454 -454
- package/lib/misc/subject.ts +141 -141
- package/lib/pki/certificate_manager.ts +1 -1
- package/lib/pki/common.ts +5 -5
- package/lib/pki/templates/ca_config_template.cnf.ts +129 -129
- package/lib/pki/templates/simple_config_template.cnf.ts +75 -75
- package/lib/pki/toolbox_pfx.ts +19 -19
- package/package.json +89 -89
- package/readme.md +214 -214
- package/tsconfig.json +20 -20
- package/dist/misc/fs.d.ts +0 -24
- package/dist/misc/fs.js +0 -21
- package/dist/misc/fs.js.map +0 -1
- package/dist/misc/get_default_filesystem.d.ts +0 -2
- package/dist/misc/get_default_filesystem.js +0 -9
- package/dist/misc/get_default_filesystem.js.map +0 -1
package/dist/pki/toolbox.js
CHANGED
|
@@ -1,700 +1,700 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
-
// ---------------------------------------------------------------------------------------------------------------------
|
|
4
|
-
// node-opcua-pki
|
|
5
|
-
// ---------------------------------------------------------------------------------------------------------------------
|
|
6
|
-
// Copyright (c) 2014-2022 - Etienne Rossignon - etienne.rossignon (at) gadz.org
|
|
7
|
-
// Copyright (c) 2022 - Sterfive.com
|
|
8
|
-
// ---------------------------------------------------------------------------------------------------------------------
|
|
9
|
-
//
|
|
10
|
-
// This project is licensed under the terms of the MIT license.
|
|
11
|
-
//
|
|
12
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
13
|
-
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
|
14
|
-
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
15
|
-
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
16
|
-
//
|
|
17
|
-
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
18
|
-
// Software.
|
|
19
|
-
//
|
|
20
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
21
|
-
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
22
|
-
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
23
|
-
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
-
// ---------------------------------------------------------------------------------------------------------------------
|
|
25
|
-
// tslint:disable:no-console
|
|
26
|
-
// tslint:disable:no-shadowed-variable
|
|
27
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.fingerprint = exports.toDer = exports.dumpCertificate = exports.configurationFileSimpleTemplate = exports.configurationFileTemplate = exports.createSelfSignCertificate = exports.processAltNames = exports.certificateFileExist = exports.adjustApplicationUri = exports.adjustDate = exports.CertificatePurpose = exports.x509Date = exports.createCertificateSigningRequest = exports.createRandomFileIfNotExist = exports.createRandomFile = exports.createPrivateKey = exports.getPublicKeyFromCertificate = exports.getPublicKeyFromPrivateKey = exports.make_path = exports.generateStaticConfig = exports.getEnvironmentVarNames = exports.displaySubtitle = exports.displayTitle = exports.displayChapter = exports.execute_openssl_no_failure = exports.executeOpensslAsync = exports.execute_openssl = exports.ensure_openssl_installed = exports.useRandFile = exports.execute = exports.hasEnv = exports.setEnv = exports.mkdir = exports.find_openssl = exports.debugLog = exports.g_config = exports.quote = void 0;
|
|
29
|
-
const assert = require("assert");
|
|
30
|
-
const async = require("async");
|
|
31
|
-
const byline = require("byline");
|
|
32
|
-
const chalk = require("chalk");
|
|
33
|
-
const child_process = require("child_process");
|
|
34
|
-
const fs = require("fs");
|
|
35
|
-
const os = require("os");
|
|
36
|
-
const path = require("path");
|
|
37
|
-
const install_prerequisite_1 = require("../misc/install_prerequisite");
|
|
38
|
-
const subject_1 = require("../misc/subject");
|
|
39
|
-
const ca_config_template_cnf_1 = require("./templates/ca_config_template.cnf");
|
|
40
|
-
const simple_config_template_cnf_1 = require("./templates/simple_config_template.cnf");
|
|
41
|
-
const exportedEnvVars = {};
|
|
42
|
-
function quote(str) {
|
|
43
|
-
return "\"" + (str || "") + "\"";
|
|
44
|
-
}
|
|
45
|
-
exports.quote = quote;
|
|
46
|
-
// tslint:disable-next-line:variable-name
|
|
47
|
-
exports.g_config = {
|
|
48
|
-
opensslVersion: "unset",
|
|
49
|
-
silent: process.env.VERBOSE ? !process.env.VERBOSE : true,
|
|
50
|
-
force: false,
|
|
51
|
-
};
|
|
52
|
-
const doDebug = process.env.NODEOPCUAPKIDEBUG || false;
|
|
53
|
-
const displayError = true;
|
|
54
|
-
const displayDebug = !!process.env.NODEOPCUAPKIDEBUG || false;
|
|
55
|
-
// tslint:disable-next-line:no-empty
|
|
56
|
-
function debugLog(...args) {
|
|
57
|
-
// istanbul ignore next
|
|
58
|
-
if (displayDebug) {
|
|
59
|
-
console.log.apply(null, args);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
exports.debugLog = debugLog;
|
|
63
|
-
let opensslPath; // not initialized
|
|
64
|
-
function find_openssl(callback) {
|
|
65
|
-
(0, install_prerequisite_1.get_openssl_exec_path)((err, _opensslPath) => {
|
|
66
|
-
opensslPath = _opensslPath;
|
|
67
|
-
callback(err, opensslPath);
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
exports.find_openssl = find_openssl;
|
|
71
|
-
function mkdir(folder) {
|
|
72
|
-
if (!fs.existsSync(folder)) {
|
|
73
|
-
// istanbul ignore next
|
|
74
|
-
if (!exports.g_config.silent) {
|
|
75
|
-
console.log(chalk.white(" .. constructing "), folder);
|
|
76
|
-
}
|
|
77
|
-
fs.mkdirSync(folder);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
exports.mkdir = mkdir;
|
|
81
|
-
function setEnv(varName, value) {
|
|
82
|
-
// istanbul ignore next
|
|
83
|
-
if (!exports.g_config.silent) {
|
|
84
|
-
console.log(" set " + varName + "=" + value);
|
|
85
|
-
}
|
|
86
|
-
exportedEnvVars[varName] = value;
|
|
87
|
-
if (["OPENSSL_CONF"].indexOf(varName) >= 0) {
|
|
88
|
-
process.env[varName] = value;
|
|
89
|
-
}
|
|
90
|
-
if (["RANDFILE"].indexOf(varName) >= 0) {
|
|
91
|
-
process.env[varName] = value;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
exports.setEnv = setEnv;
|
|
95
|
-
function hasEnv(varName) {
|
|
96
|
-
return Object.prototype.hasOwnProperty.call(exportedEnvVars, varName);
|
|
97
|
-
}
|
|
98
|
-
exports.hasEnv = hasEnv;
|
|
99
|
-
function execute(cmd, options, callback) {
|
|
100
|
-
assert(typeof callback === "function");
|
|
101
|
-
/// assert(g_config.CARootDir && fs.existsSync(option.CARootDir));
|
|
102
|
-
options.cwd = options.cwd || process.cwd();
|
|
103
|
-
// istanbul ignore next
|
|
104
|
-
if (!exports.g_config.silent) {
|
|
105
|
-
console.log(chalk.cyan(" CWD "), options.cwd);
|
|
106
|
-
}
|
|
107
|
-
const outputs = [];
|
|
108
|
-
const child = child_process.exec(cmd, {
|
|
109
|
-
cwd: options.cwd,
|
|
110
|
-
windowsHide: true,
|
|
111
|
-
}, (err) => {
|
|
112
|
-
// istanbul ignore next
|
|
113
|
-
if (err) {
|
|
114
|
-
if (!options.hideErrorMessage) {
|
|
115
|
-
const fence = "###########################################";
|
|
116
|
-
console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
|
|
117
|
-
console.error(chalk.bgWhiteBright.redBright("CWD = " + options.cwd));
|
|
118
|
-
console.error(chalk.bgWhiteBright.redBright(err.message));
|
|
119
|
-
console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
|
|
120
|
-
}
|
|
121
|
-
// console.log(" ERR = ".bgWhite.red, err);
|
|
122
|
-
callback(new Error(err.message));
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
callback(null, outputs.join(""));
|
|
126
|
-
});
|
|
127
|
-
if (child.stdout) {
|
|
128
|
-
const stream2 = byline(child.stdout);
|
|
129
|
-
stream2.on("data", (line) => {
|
|
130
|
-
outputs.push(line + "\n");
|
|
131
|
-
});
|
|
132
|
-
if (!exports.g_config.silent) {
|
|
133
|
-
stream2.on("data", (line) => {
|
|
134
|
-
line = line.toString();
|
|
135
|
-
if (doDebug) {
|
|
136
|
-
process.stdout.write(chalk.white(" stdout ") + chalk.whiteBright(line) + "\n");
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// istanbul ignore next
|
|
142
|
-
if (!exports.g_config.silent) {
|
|
143
|
-
if (child.stderr) {
|
|
144
|
-
const stream1 = byline(child.stderr);
|
|
145
|
-
stream1.on("data", (line) => {
|
|
146
|
-
line = line.toString();
|
|
147
|
-
if (displayError) {
|
|
148
|
-
process.stdout.write(chalk.white(" stderr ") + chalk.red(line) + "\n");
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
exports.execute = execute;
|
|
155
|
-
function useRandFile() {
|
|
156
|
-
// istanbul ignore next
|
|
157
|
-
if (exports.g_config.opensslVersion && exports.g_config.opensslVersion.toLowerCase().indexOf("libressl") > -1) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
exports.useRandFile = useRandFile;
|
|
163
|
-
function openssl_require2DigitYearInDate() {
|
|
164
|
-
// istanbul ignore next
|
|
165
|
-
if (!exports.g_config.opensslVersion) {
|
|
166
|
-
throw new Error("openssl_require2DigitYearInDate : openssl version is not known:" + " please call ensure_openssl_installed(callback)");
|
|
167
|
-
}
|
|
168
|
-
return exports.g_config.opensslVersion.match(/OpenSSL 0\.9/);
|
|
169
|
-
}
|
|
170
|
-
exports.g_config.opensslVersion = "";
|
|
171
|
-
function ensure_openssl_installed(callback) {
|
|
172
|
-
assert(typeof callback === "function");
|
|
173
|
-
if (!opensslPath) {
|
|
174
|
-
return find_openssl((err) => {
|
|
175
|
-
// istanbul ignore next
|
|
176
|
-
if (err) {
|
|
177
|
-
return callback(err);
|
|
178
|
-
}
|
|
179
|
-
execute_openssl("version", { cwd: "." }, (err, outputs) => {
|
|
180
|
-
// istanbul ignore next
|
|
181
|
-
if (err || !outputs) {
|
|
182
|
-
return callback(err || new Error("no outputs"));
|
|
183
|
-
}
|
|
184
|
-
exports.g_config.opensslVersion = outputs.trim();
|
|
185
|
-
if (doDebug) {
|
|
186
|
-
console.log("OpenSSL version : ", exports.g_config.opensslVersion);
|
|
187
|
-
}
|
|
188
|
-
callback(err ? err : undefined);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
return callback();
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
exports.ensure_openssl_installed = ensure_openssl_installed;
|
|
197
|
-
function getTempFolder() {
|
|
198
|
-
return os.tmpdir();
|
|
199
|
-
}
|
|
200
|
-
function execute_openssl(cmd, options, callback) {
|
|
201
|
-
// tslint:disable-next-line:variable-name
|
|
202
|
-
const empty_config_file = n(getTempFolder(), "empty_config.cnf");
|
|
203
|
-
if (!fs.existsSync(empty_config_file)) {
|
|
204
|
-
fs.writeFileSync(empty_config_file, "# empty config file");
|
|
205
|
-
}
|
|
206
|
-
assert(typeof callback === "function");
|
|
207
|
-
options = options || {};
|
|
208
|
-
options.openssl_conf = options.openssl_conf || empty_config_file; // "!! OPEN SLL CONF NOT DEFINED BAD FILE !!";
|
|
209
|
-
assert(options.openssl_conf);
|
|
210
|
-
setEnv("OPENSSL_CONF", options.openssl_conf);
|
|
211
|
-
// istanbul ignore next
|
|
212
|
-
if (!exports.g_config.silent) {
|
|
213
|
-
console.log(chalk.cyan(" OPENSSL_CONF"), process.env.OPENSSL_CONF);
|
|
214
|
-
console.log(chalk.cyan(" RANDFILE "), process.env.RANDFILE);
|
|
215
|
-
console.log(chalk.cyan(" CMD openssl "), chalk.cyanBright(cmd));
|
|
216
|
-
}
|
|
217
|
-
ensure_openssl_installed((err) => {
|
|
218
|
-
// istanbul ignore next
|
|
219
|
-
if (err) {
|
|
220
|
-
return callback(err);
|
|
221
|
-
}
|
|
222
|
-
execute(quote(opensslPath) + " " + cmd, options, callback);
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
exports.execute_openssl = execute_openssl;
|
|
226
|
-
function executeOpensslAsync(cmd, options) {
|
|
227
|
-
return new Promise((resolve, reject) => {
|
|
228
|
-
execute_openssl(cmd, options, (err, output) => {
|
|
229
|
-
// istanbul ignore next
|
|
230
|
-
if (err) {
|
|
231
|
-
reject(err);
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
resolve(output || "");
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
exports.executeOpensslAsync = executeOpensslAsync;
|
|
240
|
-
function execute_openssl_no_failure(cmd, options, callback) {
|
|
241
|
-
options = options || {};
|
|
242
|
-
options.hideErrorMessage = true;
|
|
243
|
-
execute_openssl(cmd, options, (err, output) => {
|
|
244
|
-
// istanbul ignore next
|
|
245
|
-
if (err) {
|
|
246
|
-
debugLog(" (ignored error = ERROR : )", err.message);
|
|
247
|
-
}
|
|
248
|
-
callback(null, output);
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
exports.execute_openssl_no_failure = execute_openssl_no_failure;
|
|
252
|
-
// istanbul ignore next
|
|
253
|
-
function displayChapter(str, callback) {
|
|
254
|
-
const l = " ";
|
|
255
|
-
console.log(chalk.bgWhite(l) + " ");
|
|
256
|
-
str = (" " + str + l).substring(0, l.length);
|
|
257
|
-
console.log(chalk.bgWhite.cyan(str));
|
|
258
|
-
console.log(chalk.bgWhite(l) + " ");
|
|
259
|
-
if (callback) {
|
|
260
|
-
callback();
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
exports.displayChapter = displayChapter;
|
|
264
|
-
function displayTitle(str, callback) {
|
|
265
|
-
// istanbul ignore next
|
|
266
|
-
if (!exports.g_config.silent) {
|
|
267
|
-
console.log("");
|
|
268
|
-
console.log(chalk.yellowBright(str));
|
|
269
|
-
console.log(chalk.yellow(new Array(str.length + 1).join("=")), "\n");
|
|
270
|
-
}
|
|
271
|
-
if (callback) {
|
|
272
|
-
callback();
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
exports.displayTitle = displayTitle;
|
|
276
|
-
function displaySubtitle(str, callback) {
|
|
277
|
-
// istanbul ignore next
|
|
278
|
-
if (!exports.g_config.silent) {
|
|
279
|
-
console.log("");
|
|
280
|
-
console.log(" " + chalk.yellowBright(str));
|
|
281
|
-
console.log(" " + chalk.white(new Array(str.length + 1).join("-")), "\n");
|
|
282
|
-
}
|
|
283
|
-
if (callback) {
|
|
284
|
-
callback();
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
exports.displaySubtitle = displaySubtitle;
|
|
288
|
-
function getEnvironmentVarNames() {
|
|
289
|
-
return Object.keys(exportedEnvVars).map((varName) => {
|
|
290
|
-
return { key: varName, pattern: "\\$ENV\\:\\:" + varName };
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
exports.getEnvironmentVarNames = getEnvironmentVarNames;
|
|
294
|
-
function generateStaticConfig(configPath, options) {
|
|
295
|
-
const prePath = (options && options.cwd) || "";
|
|
296
|
-
const staticConfigPath = configPath + ".tmp";
|
|
297
|
-
let staticConfig = fs.readFileSync(path.join(prePath, configPath), { encoding: "utf8" });
|
|
298
|
-
for (const envVar of getEnvironmentVarNames()) {
|
|
299
|
-
staticConfig = staticConfig.replace(new RegExp(envVar.pattern, "gi"), exportedEnvVars[envVar.key]);
|
|
300
|
-
}
|
|
301
|
-
fs.writeFileSync(path.join(prePath, staticConfigPath), staticConfig);
|
|
302
|
-
return staticConfigPath;
|
|
303
|
-
}
|
|
304
|
-
exports.generateStaticConfig = generateStaticConfig;
|
|
305
|
-
const q = quote;
|
|
306
|
-
function make_path(folderName, filename) {
|
|
307
|
-
let s;
|
|
308
|
-
if (filename) {
|
|
309
|
-
s = path.join(path.normalize(folderName), filename);
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
assert(folderName);
|
|
313
|
-
s = folderName;
|
|
314
|
-
}
|
|
315
|
-
s = s.replace(/\\/g, "/");
|
|
316
|
-
return s;
|
|
317
|
-
}
|
|
318
|
-
exports.make_path = make_path;
|
|
319
|
-
const n = make_path;
|
|
320
|
-
/**
|
|
321
|
-
* calculate the public key from private key
|
|
322
|
-
* openssl rsa -pubout -in private_key.pem
|
|
323
|
-
*
|
|
324
|
-
* @method getPublicKeyFromPrivateKey
|
|
325
|
-
* @param privateKeyFilename
|
|
326
|
-
* @param publicKeyFilename
|
|
327
|
-
* @param callback
|
|
328
|
-
*/
|
|
329
|
-
function getPublicKeyFromPrivateKey(privateKeyFilename, publicKeyFilename, callback) {
|
|
330
|
-
assert(fs.existsSync(privateKeyFilename));
|
|
331
|
-
execute_openssl("rsa -pubout -in " + q(n(privateKeyFilename)) + " -out " + q(n(publicKeyFilename)), {}, callback);
|
|
332
|
-
}
|
|
333
|
-
exports.getPublicKeyFromPrivateKey = getPublicKeyFromPrivateKey;
|
|
334
|
-
/**
|
|
335
|
-
* extract public key from a certificate
|
|
336
|
-
* openssl x509 -pubkey -in certificate.pem -nottext
|
|
337
|
-
*
|
|
338
|
-
* @method getPublicKeyFromCertificate
|
|
339
|
-
* @param certificateFilename
|
|
340
|
-
* @param publicKeyFilename
|
|
341
|
-
* @param callback
|
|
342
|
-
*/
|
|
343
|
-
function getPublicKeyFromCertificate(certificateFilename, publicKeyFilename, callback) {
|
|
344
|
-
assert(fs.existsSync(certificateFilename));
|
|
345
|
-
execute_openssl("x509 -pubkey -in " + q(n(certificateFilename)) + " > " + q(n(publicKeyFilename)), {}, callback);
|
|
346
|
-
}
|
|
347
|
-
exports.getPublicKeyFromCertificate = getPublicKeyFromCertificate;
|
|
348
|
-
/**
|
|
349
|
-
* create a RSA PRIVATE KEY
|
|
350
|
-
*
|
|
351
|
-
* @method createPrivateKey
|
|
352
|
-
*
|
|
353
|
-
* @param privateKeyFilename
|
|
354
|
-
* @param keyLength
|
|
355
|
-
* @param callback {Function}
|
|
356
|
-
*/
|
|
357
|
-
function createPrivateKey(privateKeyFilename, keyLength, callback) {
|
|
358
|
-
// istanbul ignore next
|
|
359
|
-
if (useRandFile()) {
|
|
360
|
-
assert(hasEnv("RANDFILE"));
|
|
361
|
-
}
|
|
362
|
-
assert([1024, 2048, 3072, 4096].indexOf(keyLength) >= 0);
|
|
363
|
-
const randomFile = exportedEnvVars.RANDFILE ? n(exportedEnvVars.RANDFILE) : "random.rnd";
|
|
364
|
-
const tasks = [
|
|
365
|
-
(callback) => createRandomFileIfNotExist(randomFile, {}, callback),
|
|
366
|
-
// Note OpenSSL1 generates a -----BEGIN RSA PRIVATE KEY---- whereas
|
|
367
|
-
// OpenSSL3 generates a -----BEGIN PRIVATE KEY----- unless the new -traditional option is used
|
|
368
|
-
//
|
|
369
|
-
// a BEGIN PRIVATE KEY structure is
|
|
370
|
-
//
|
|
371
|
-
// SEQUENCE (3 elem)
|
|
372
|
-
// INTEGER 0
|
|
373
|
-
// SEQUENCE (2 elem)
|
|
374
|
-
// OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
|
|
375
|
-
// NULL
|
|
376
|
-
// OCTET STRING (609 byte) 3082025D02010002818100C5B53231183906122A5E3778736B05C095C75F1BB80D48B
|
|
377
|
-
// SEQUENCE (9 elem)
|
|
378
|
-
//
|
|
379
|
-
// a BEGIN RSA PRIVATE KEY structure is just
|
|
380
|
-
// SEQUENCE (9 elem)
|
|
381
|
-
//
|
|
382
|
-
(callback) => {
|
|
383
|
-
execute_openssl("genrsa " +
|
|
384
|
-
" -out " +
|
|
385
|
-
q(n(privateKeyFilename)) +
|
|
386
|
-
(useRandFile() ? " -rand " + q(randomFile) : "") +
|
|
387
|
-
" " +
|
|
388
|
-
keyLength, {}, (err) => {
|
|
389
|
-
callback(err ? err : undefined);
|
|
390
|
-
});
|
|
391
|
-
},
|
|
392
|
-
];
|
|
393
|
-
async.series(tasks, callback);
|
|
394
|
-
}
|
|
395
|
-
exports.createPrivateKey = createPrivateKey;
|
|
396
|
-
function createRandomFile(randomFile, options, callback) {
|
|
397
|
-
// istanbul ignore next
|
|
398
|
-
if (!useRandFile()) {
|
|
399
|
-
return callback();
|
|
400
|
-
}
|
|
401
|
-
execute_openssl("rand " + " -out " + q(randomFile) + " -hex 256", options, (err) => {
|
|
402
|
-
callback(err ? err : undefined);
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
exports.createRandomFile = createRandomFile;
|
|
406
|
-
function createRandomFileIfNotExist(randomFile, options, callback) {
|
|
407
|
-
const randomFilePath = options.cwd ? path.join(options.cwd, randomFile) : randomFile;
|
|
408
|
-
if (fs.existsSync(randomFilePath)) {
|
|
409
|
-
if (doDebug) {
|
|
410
|
-
console.log(chalk.yellow(" randomFile"), chalk.cyan(randomFile), chalk.yellow(" already exists => skipping"));
|
|
411
|
-
}
|
|
412
|
-
return callback();
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
createRandomFile(randomFile, options, callback);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
exports.createRandomFileIfNotExist = createRandomFileIfNotExist;
|
|
419
|
-
/**
|
|
420
|
-
* create a certificate signing request
|
|
421
|
-
*
|
|
422
|
-
* @param certificateSigningRequestFilename
|
|
423
|
-
* @param params
|
|
424
|
-
* @param callback
|
|
425
|
-
*/
|
|
426
|
-
function createCertificateSigningRequest(certificateSigningRequestFilename, params, callback) {
|
|
427
|
-
assert(params);
|
|
428
|
-
assert(params.rootDir);
|
|
429
|
-
assert(params.configFile);
|
|
430
|
-
assert(params.privateKey);
|
|
431
|
-
assert(typeof params.privateKey === "string");
|
|
432
|
-
assert(fs.existsSync(params.configFile), "config file must exist " + params.configFile);
|
|
433
|
-
assert(fs.existsSync(params.privateKey), "Private key must exist" + params.privateKey);
|
|
434
|
-
assert(fs.existsSync(params.rootDir), "RootDir key must exist");
|
|
435
|
-
assert(typeof certificateSigningRequestFilename === "string");
|
|
436
|
-
// note : this openssl command requires a config file
|
|
437
|
-
processAltNames(params);
|
|
438
|
-
const configFile = generateStaticConfig(params.configFile);
|
|
439
|
-
const options = { cwd: params.rootDir, openssl_conf: configFile };
|
|
440
|
-
const configOption = " -config " + q(n(configFile));
|
|
441
|
-
const subject = params.subject ? new subject_1.Subject(params.subject).toString() : undefined;
|
|
442
|
-
// process.env.OPENSSL_CONF ="";
|
|
443
|
-
const subjectOptions = subject ? " -subj \"" + subject + "\"" : "";
|
|
444
|
-
async.series([
|
|
445
|
-
(callback) => {
|
|
446
|
-
displaySubtitle("- Creating a Certificate Signing Request", callback);
|
|
447
|
-
},
|
|
448
|
-
(callback) => {
|
|
449
|
-
execute_openssl("req -new" +
|
|
450
|
-
" -sha256 " +
|
|
451
|
-
" -batch " +
|
|
452
|
-
" -text " +
|
|
453
|
-
configOption +
|
|
454
|
-
" -key " +
|
|
455
|
-
q(n(params.privateKey)) +
|
|
456
|
-
subjectOptions +
|
|
457
|
-
" -out " +
|
|
458
|
-
q(n(certificateSigningRequestFilename)), options, (err) => {
|
|
459
|
-
callback(err ? err : undefined);
|
|
460
|
-
});
|
|
461
|
-
},
|
|
462
|
-
], (err) => callback(err));
|
|
463
|
-
}
|
|
464
|
-
exports.createCertificateSigningRequest = createCertificateSigningRequest;
|
|
465
|
-
function x509Date(date) {
|
|
466
|
-
date = date || new Date();
|
|
467
|
-
const Y = date.getUTCFullYear();
|
|
468
|
-
const M = date.getUTCMonth() + 1;
|
|
469
|
-
const D = date.getUTCDate();
|
|
470
|
-
const h = date.getUTCHours();
|
|
471
|
-
const m = date.getUTCMinutes();
|
|
472
|
-
const s = date.getUTCSeconds();
|
|
473
|
-
function w(s, l) {
|
|
474
|
-
return ("" + s).padStart(l, "0");
|
|
475
|
-
}
|
|
476
|
-
if (openssl_require2DigitYearInDate()) {
|
|
477
|
-
// for example: on MacOS , where openssl 0.98 is installed by default
|
|
478
|
-
return w(Y, 2) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2) + "Z";
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
// for instance when openssl version is greater than 1.0.0
|
|
482
|
-
return w(Y, 4) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2) + "Z";
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
exports.x509Date = x509Date;
|
|
486
|
-
// purpose of self-signed certificate
|
|
487
|
-
var CertificatePurpose;
|
|
488
|
-
(function (CertificatePurpose) {
|
|
489
|
-
CertificatePurpose[CertificatePurpose["NotSpecified"] = 0] = "NotSpecified";
|
|
490
|
-
CertificatePurpose[CertificatePurpose["ForCertificateAuthority"] = 1] = "ForCertificateAuthority";
|
|
491
|
-
CertificatePurpose[CertificatePurpose["ForApplication"] = 2] = "ForApplication";
|
|
492
|
-
CertificatePurpose[CertificatePurpose["ForUserAuthentication"] = 3] = "ForUserAuthentication";
|
|
493
|
-
})(CertificatePurpose = exports.CertificatePurpose || (exports.CertificatePurpose = {}));
|
|
494
|
-
function adjustDate(params) {
|
|
495
|
-
assert(params instanceof Object);
|
|
496
|
-
params.startDate = params.startDate || new Date();
|
|
497
|
-
assert(params.startDate instanceof Date);
|
|
498
|
-
params.validity = params.validity || 365; // one year
|
|
499
|
-
params.endDate = new Date(params.startDate.getTime());
|
|
500
|
-
params.endDate.setDate(params.startDate.getDate() + params.validity);
|
|
501
|
-
// xx params.endDate = x509Date(endDate);
|
|
502
|
-
// xx params.startDate = x509Date(startDate);
|
|
503
|
-
assert(params.endDate instanceof Date);
|
|
504
|
-
assert(params.startDate instanceof Date);
|
|
505
|
-
// istanbul ignore next
|
|
506
|
-
if (!exports.g_config.silent) {
|
|
507
|
-
console.log(" start Date ", params.startDate.toUTCString(), x509Date(params.startDate));
|
|
508
|
-
console.log(" end Date ", params.endDate.toUTCString(), x509Date(params.endDate));
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
exports.adjustDate = adjustDate;
|
|
512
|
-
function adjustApplicationUri(params) {
|
|
513
|
-
const applicationUri = params.applicationUri || "";
|
|
514
|
-
if (applicationUri.length > 200) {
|
|
515
|
-
throw new Error("Openssl doesn't support urn with length greater than 200" + applicationUri);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
exports.adjustApplicationUri = adjustApplicationUri;
|
|
519
|
-
function certificateFileExist(certificateFile) {
|
|
520
|
-
assert(typeof certificateFile === "string");
|
|
521
|
-
// istanbul ignore next
|
|
522
|
-
if (fs.existsSync(certificateFile) && !exports.g_config.force) {
|
|
523
|
-
console.log(chalk.yellow(" certificate ") + chalk.cyan(certificateFile) + chalk.yellow(" already exists => do not overwrite"));
|
|
524
|
-
return false;
|
|
525
|
-
}
|
|
526
|
-
return true;
|
|
527
|
-
}
|
|
528
|
-
exports.certificateFileExist = certificateFileExist;
|
|
529
|
-
/**
|
|
530
|
-
*
|
|
531
|
-
* @param params
|
|
532
|
-
* @param params.applicationUri
|
|
533
|
-
* @param params.dns
|
|
534
|
-
* @param params.ip
|
|
535
|
-
* @private
|
|
536
|
-
*/
|
|
537
|
-
function processAltNames(params) {
|
|
538
|
-
params.dns = params.dns || [];
|
|
539
|
-
params.ip = params.ip || [];
|
|
540
|
-
// construct subjectAtlName
|
|
541
|
-
let subjectAltName = [];
|
|
542
|
-
subjectAltName.push("URI:" + params.applicationUri);
|
|
543
|
-
subjectAltName = [].concat(subjectAltName, params.dns.map((d) => "DNS:" + d));
|
|
544
|
-
subjectAltName = [].concat(subjectAltName, params.ip.map((d) => "IP:" + d));
|
|
545
|
-
const subjectAltNameString = subjectAltName.join(", ");
|
|
546
|
-
setEnv("ALTNAME", subjectAltNameString);
|
|
547
|
-
}
|
|
548
|
-
exports.processAltNames = processAltNames;
|
|
549
|
-
/**
|
|
550
|
-
*
|
|
551
|
-
* @param certificate
|
|
552
|
-
* @param params
|
|
553
|
-
* @param params.configFile
|
|
554
|
-
* @param params.rootDir
|
|
555
|
-
* @param params.privateKey
|
|
556
|
-
* @param params.applicationUri
|
|
557
|
-
* @param params.dns
|
|
558
|
-
* @param params.ip
|
|
559
|
-
* @param params.validity certificate duration in days
|
|
560
|
-
* @param params.purpose
|
|
561
|
-
* @param [params.subject= "C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=ZZNodeOPCUA"]
|
|
562
|
-
* @param callback
|
|
563
|
-
*/
|
|
564
|
-
function createSelfSignCertificate(certificate, params, callback) {
|
|
565
|
-
ensure_openssl_installed((err) => {
|
|
566
|
-
if (err) {
|
|
567
|
-
return callback(err);
|
|
568
|
-
}
|
|
569
|
-
try {
|
|
570
|
-
params.purpose = params.purpose || CertificatePurpose.ForApplication;
|
|
571
|
-
assert(params.purpose, "Please provide a Certificate Purpose");
|
|
572
|
-
/**
|
|
573
|
-
* note: due to a limitation of openssl ,
|
|
574
|
-
* it is not possible to control the startDate of the certificate validity
|
|
575
|
-
* to achieve this the certificateAuthority tool shall be used.
|
|
576
|
-
*/
|
|
577
|
-
assert(fs.existsSync(params.configFile));
|
|
578
|
-
assert(fs.existsSync(params.rootDir));
|
|
579
|
-
assert(fs.existsSync(params.privateKey));
|
|
580
|
-
if (!params.subject) {
|
|
581
|
-
return callback(new Error("Missing subject"));
|
|
582
|
-
}
|
|
583
|
-
assert(typeof params.applicationUri === "string");
|
|
584
|
-
assert(params.dns instanceof Array);
|
|
585
|
-
// xx no key size in self-signed assert(params.keySize == 2048 || params.keySize == 4096);
|
|
586
|
-
processAltNames(params);
|
|
587
|
-
adjustDate(params);
|
|
588
|
-
assert(Object.prototype.hasOwnProperty.call(params, "validity"));
|
|
589
|
-
let subject = new subject_1.Subject(params.subject);
|
|
590
|
-
subject = subject.toString();
|
|
591
|
-
const certificateRequestFilename = certificate + ".csr";
|
|
592
|
-
const configFile = generateStaticConfig(params.configFile);
|
|
593
|
-
const configOption = " -config " + q(n(configFile));
|
|
594
|
-
let extension;
|
|
595
|
-
switch (params.purpose) {
|
|
596
|
-
case CertificatePurpose.ForApplication:
|
|
597
|
-
extension = "v3_selfsigned";
|
|
598
|
-
break;
|
|
599
|
-
case CertificatePurpose.ForCertificateAuthority:
|
|
600
|
-
extension = "v3_ca";
|
|
601
|
-
break;
|
|
602
|
-
case CertificatePurpose.ForUserAuthentication:
|
|
603
|
-
default:
|
|
604
|
-
extension = "v3_selfsigned";
|
|
605
|
-
}
|
|
606
|
-
const tasks = [
|
|
607
|
-
(callback) => {
|
|
608
|
-
displayTitle("Generate a certificate request", callback);
|
|
609
|
-
},
|
|
610
|
-
// Once the private key is generated a Certificate Signing Request can be generated.
|
|
611
|
-
// The CSR is then used in one of two ways. Ideally, the CSR will be sent to a Certificate Authority, such as
|
|
612
|
-
// Thawte or Verisign who will verify the identity of the requestor and issue a signed certificate.
|
|
613
|
-
// The second option is to self-sign the CSR, which will be demonstrated in the next section
|
|
614
|
-
(callback) => {
|
|
615
|
-
execute_openssl("req -new" +
|
|
616
|
-
" -sha256 " +
|
|
617
|
-
" -text " +
|
|
618
|
-
" -extensions " +
|
|
619
|
-
extension +
|
|
620
|
-
" " +
|
|
621
|
-
configOption +
|
|
622
|
-
" -key " +
|
|
623
|
-
q(n(params.privateKey)) +
|
|
624
|
-
" -out " +
|
|
625
|
-
q(n(certificateRequestFilename)) +
|
|
626
|
-
" -subj \"" +
|
|
627
|
-
subject +
|
|
628
|
-
"\"", {}, callback);
|
|
629
|
-
},
|
|
630
|
-
// Xx // Step 3: Remove Passphrase from Key
|
|
631
|
-
// Xx execute("cp private/cakey.pem private/cakey.pem.org");
|
|
632
|
-
// Xx execute(openssl_path + " rsa -in private/cakey.pem.org
|
|
633
|
-
// Xx -out private/cakey.pem -passin pass:"+paraphrase);
|
|
634
|
-
(callback) => {
|
|
635
|
-
displayTitle("Generate Certificate (self-signed)", callback);
|
|
636
|
-
},
|
|
637
|
-
(callback) => {
|
|
638
|
-
execute_openssl(" x509 -req " +
|
|
639
|
-
" -days " +
|
|
640
|
-
params.validity +
|
|
641
|
-
" -extensions " +
|
|
642
|
-
extension +
|
|
643
|
-
" " +
|
|
644
|
-
" -extfile " +
|
|
645
|
-
q(n(configFile)) +
|
|
646
|
-
" -in " +
|
|
647
|
-
q(n(certificateRequestFilename)) +
|
|
648
|
-
" -signkey " +
|
|
649
|
-
q(n(params.privateKey)) +
|
|
650
|
-
" -text " +
|
|
651
|
-
" -out " +
|
|
652
|
-
q(certificate) +
|
|
653
|
-
" -text ", {}, callback);
|
|
654
|
-
},
|
|
655
|
-
// remove unnecessary certificate request file
|
|
656
|
-
(callback) => {
|
|
657
|
-
fs.unlink(certificateRequestFilename, callback);
|
|
658
|
-
},
|
|
659
|
-
];
|
|
660
|
-
async.series(tasks, callback);
|
|
661
|
-
}
|
|
662
|
-
catch (err) {
|
|
663
|
-
callback(err);
|
|
664
|
-
}
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
exports.createSelfSignCertificate = createSelfSignCertificate;
|
|
668
|
-
// tslint:disable-next-line:variable-name
|
|
669
|
-
exports.configurationFileTemplate = ca_config_template_cnf_1.default;
|
|
670
|
-
/**
|
|
671
|
-
*
|
|
672
|
-
* a minimalist config file for openssl that allows
|
|
673
|
-
* self-signed certificate to be generated.
|
|
674
|
-
*
|
|
675
|
-
*/
|
|
676
|
-
// tslint:disable-next-line:variable-name
|
|
677
|
-
exports.configurationFileSimpleTemplate = simple_config_template_cnf_1.default;
|
|
678
|
-
/**
|
|
679
|
-
* @param certificate - the certificate file in PEM format, file must exist
|
|
680
|
-
* @param callback
|
|
681
|
-
*/
|
|
682
|
-
function dumpCertificate(certificate, callback) {
|
|
683
|
-
assert(fs.existsSync(certificate));
|
|
684
|
-
assert(typeof callback === "function");
|
|
685
|
-
execute_openssl("x509 " + " -in " + q(n(certificate)) + " -text " + " -noout", {}, callback);
|
|
686
|
-
}
|
|
687
|
-
exports.dumpCertificate = dumpCertificate;
|
|
688
|
-
function toDer(certificatePem, callback) {
|
|
689
|
-
assert(fs.existsSync(certificatePem));
|
|
690
|
-
const certificateDer = certificatePem.replace(".pem", ".der");
|
|
691
|
-
execute_openssl("x509 " + " -outform der " + " -in " + certificatePem + " -out " + certificateDer, {}, callback);
|
|
692
|
-
}
|
|
693
|
-
exports.toDer = toDer;
|
|
694
|
-
function fingerprint(certificatePem, callback) {
|
|
695
|
-
// openssl x509 -in my_certificate.pem -hash -dates -noout -fingerprint
|
|
696
|
-
assert(fs.existsSync(certificatePem));
|
|
697
|
-
execute_openssl("x509 " + " -fingerprint " + " -noout " + " -in " + certificatePem, {}, callback);
|
|
698
|
-
}
|
|
699
|
-
exports.fingerprint = fingerprint;
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
// node-opcua-pki
|
|
5
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
6
|
+
// Copyright (c) 2014-2022 - Etienne Rossignon - etienne.rossignon (at) gadz.org
|
|
7
|
+
// Copyright (c) 2022 - Sterfive.com
|
|
8
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
9
|
+
//
|
|
10
|
+
// This project is licensed under the terms of the MIT license.
|
|
11
|
+
//
|
|
12
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
13
|
+
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
|
14
|
+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
15
|
+
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
16
|
+
//
|
|
17
|
+
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
18
|
+
// Software.
|
|
19
|
+
//
|
|
20
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
21
|
+
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
22
|
+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
23
|
+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
25
|
+
// tslint:disable:no-console
|
|
26
|
+
// tslint:disable:no-shadowed-variable
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.fingerprint = exports.toDer = exports.dumpCertificate = exports.configurationFileSimpleTemplate = exports.configurationFileTemplate = exports.createSelfSignCertificate = exports.processAltNames = exports.certificateFileExist = exports.adjustApplicationUri = exports.adjustDate = exports.CertificatePurpose = exports.x509Date = exports.createCertificateSigningRequest = exports.createRandomFileIfNotExist = exports.createRandomFile = exports.createPrivateKey = exports.getPublicKeyFromCertificate = exports.getPublicKeyFromPrivateKey = exports.make_path = exports.generateStaticConfig = exports.getEnvironmentVarNames = exports.displaySubtitle = exports.displayTitle = exports.displayChapter = exports.execute_openssl_no_failure = exports.executeOpensslAsync = exports.execute_openssl = exports.ensure_openssl_installed = exports.useRandFile = exports.execute = exports.hasEnv = exports.setEnv = exports.mkdir = exports.find_openssl = exports.debugLog = exports.g_config = exports.quote = void 0;
|
|
29
|
+
const assert = require("assert");
|
|
30
|
+
const async = require("async");
|
|
31
|
+
const byline = require("byline");
|
|
32
|
+
const chalk = require("chalk");
|
|
33
|
+
const child_process = require("child_process");
|
|
34
|
+
const fs = require("fs");
|
|
35
|
+
const os = require("os");
|
|
36
|
+
const path = require("path");
|
|
37
|
+
const install_prerequisite_1 = require("../misc/install_prerequisite");
|
|
38
|
+
const subject_1 = require("../misc/subject");
|
|
39
|
+
const ca_config_template_cnf_1 = require("./templates/ca_config_template.cnf");
|
|
40
|
+
const simple_config_template_cnf_1 = require("./templates/simple_config_template.cnf");
|
|
41
|
+
const exportedEnvVars = {};
|
|
42
|
+
function quote(str) {
|
|
43
|
+
return "\"" + (str || "") + "\"";
|
|
44
|
+
}
|
|
45
|
+
exports.quote = quote;
|
|
46
|
+
// tslint:disable-next-line:variable-name
|
|
47
|
+
exports.g_config = {
|
|
48
|
+
opensslVersion: "unset",
|
|
49
|
+
silent: process.env.VERBOSE ? !process.env.VERBOSE : true,
|
|
50
|
+
force: false,
|
|
51
|
+
};
|
|
52
|
+
const doDebug = process.env.NODEOPCUAPKIDEBUG || false;
|
|
53
|
+
const displayError = true;
|
|
54
|
+
const displayDebug = !!process.env.NODEOPCUAPKIDEBUG || false;
|
|
55
|
+
// tslint:disable-next-line:no-empty
|
|
56
|
+
function debugLog(...args) {
|
|
57
|
+
// istanbul ignore next
|
|
58
|
+
if (displayDebug) {
|
|
59
|
+
console.log.apply(null, args);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.debugLog = debugLog;
|
|
63
|
+
let opensslPath; // not initialized
|
|
64
|
+
function find_openssl(callback) {
|
|
65
|
+
(0, install_prerequisite_1.get_openssl_exec_path)((err, _opensslPath) => {
|
|
66
|
+
opensslPath = _opensslPath;
|
|
67
|
+
callback(err, opensslPath);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
exports.find_openssl = find_openssl;
|
|
71
|
+
function mkdir(folder) {
|
|
72
|
+
if (!fs.existsSync(folder)) {
|
|
73
|
+
// istanbul ignore next
|
|
74
|
+
if (!exports.g_config.silent) {
|
|
75
|
+
console.log(chalk.white(" .. constructing "), folder);
|
|
76
|
+
}
|
|
77
|
+
fs.mkdirSync(folder);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.mkdir = mkdir;
|
|
81
|
+
function setEnv(varName, value) {
|
|
82
|
+
// istanbul ignore next
|
|
83
|
+
if (!exports.g_config.silent) {
|
|
84
|
+
console.log(" set " + varName + "=" + value);
|
|
85
|
+
}
|
|
86
|
+
exportedEnvVars[varName] = value;
|
|
87
|
+
if (["OPENSSL_CONF"].indexOf(varName) >= 0) {
|
|
88
|
+
process.env[varName] = value;
|
|
89
|
+
}
|
|
90
|
+
if (["RANDFILE"].indexOf(varName) >= 0) {
|
|
91
|
+
process.env[varName] = value;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.setEnv = setEnv;
|
|
95
|
+
function hasEnv(varName) {
|
|
96
|
+
return Object.prototype.hasOwnProperty.call(exportedEnvVars, varName);
|
|
97
|
+
}
|
|
98
|
+
exports.hasEnv = hasEnv;
|
|
99
|
+
function execute(cmd, options, callback) {
|
|
100
|
+
assert(typeof callback === "function");
|
|
101
|
+
/// assert(g_config.CARootDir && fs.existsSync(option.CARootDir));
|
|
102
|
+
options.cwd = options.cwd || process.cwd();
|
|
103
|
+
// istanbul ignore next
|
|
104
|
+
if (!exports.g_config.silent) {
|
|
105
|
+
console.log(chalk.cyan(" CWD "), options.cwd);
|
|
106
|
+
}
|
|
107
|
+
const outputs = [];
|
|
108
|
+
const child = child_process.exec(cmd, {
|
|
109
|
+
cwd: options.cwd,
|
|
110
|
+
windowsHide: true,
|
|
111
|
+
}, (err) => {
|
|
112
|
+
// istanbul ignore next
|
|
113
|
+
if (err) {
|
|
114
|
+
if (!options.hideErrorMessage) {
|
|
115
|
+
const fence = "###########################################";
|
|
116
|
+
console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
|
|
117
|
+
console.error(chalk.bgWhiteBright.redBright("CWD = " + options.cwd));
|
|
118
|
+
console.error(chalk.bgWhiteBright.redBright(err.message));
|
|
119
|
+
console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
|
|
120
|
+
}
|
|
121
|
+
// console.log(" ERR = ".bgWhite.red, err);
|
|
122
|
+
callback(new Error(err.message));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
callback(null, outputs.join(""));
|
|
126
|
+
});
|
|
127
|
+
if (child.stdout) {
|
|
128
|
+
const stream2 = byline(child.stdout);
|
|
129
|
+
stream2.on("data", (line) => {
|
|
130
|
+
outputs.push(line + "\n");
|
|
131
|
+
});
|
|
132
|
+
if (!exports.g_config.silent) {
|
|
133
|
+
stream2.on("data", (line) => {
|
|
134
|
+
line = line.toString();
|
|
135
|
+
if (doDebug) {
|
|
136
|
+
process.stdout.write(chalk.white(" stdout ") + chalk.whiteBright(line) + "\n");
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// istanbul ignore next
|
|
142
|
+
if (!exports.g_config.silent) {
|
|
143
|
+
if (child.stderr) {
|
|
144
|
+
const stream1 = byline(child.stderr);
|
|
145
|
+
stream1.on("data", (line) => {
|
|
146
|
+
line = line.toString();
|
|
147
|
+
if (displayError) {
|
|
148
|
+
process.stdout.write(chalk.white(" stderr ") + chalk.red(line) + "\n");
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.execute = execute;
|
|
155
|
+
function useRandFile() {
|
|
156
|
+
// istanbul ignore next
|
|
157
|
+
if (exports.g_config.opensslVersion && exports.g_config.opensslVersion.toLowerCase().indexOf("libressl") > -1) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
exports.useRandFile = useRandFile;
|
|
163
|
+
function openssl_require2DigitYearInDate() {
|
|
164
|
+
// istanbul ignore next
|
|
165
|
+
if (!exports.g_config.opensslVersion) {
|
|
166
|
+
throw new Error("openssl_require2DigitYearInDate : openssl version is not known:" + " please call ensure_openssl_installed(callback)");
|
|
167
|
+
}
|
|
168
|
+
return exports.g_config.opensslVersion.match(/OpenSSL 0\.9/);
|
|
169
|
+
}
|
|
170
|
+
exports.g_config.opensslVersion = "";
|
|
171
|
+
function ensure_openssl_installed(callback) {
|
|
172
|
+
assert(typeof callback === "function");
|
|
173
|
+
if (!opensslPath) {
|
|
174
|
+
return find_openssl((err) => {
|
|
175
|
+
// istanbul ignore next
|
|
176
|
+
if (err) {
|
|
177
|
+
return callback(err);
|
|
178
|
+
}
|
|
179
|
+
execute_openssl("version", { cwd: "." }, (err, outputs) => {
|
|
180
|
+
// istanbul ignore next
|
|
181
|
+
if (err || !outputs) {
|
|
182
|
+
return callback(err || new Error("no outputs"));
|
|
183
|
+
}
|
|
184
|
+
exports.g_config.opensslVersion = outputs.trim();
|
|
185
|
+
if (doDebug) {
|
|
186
|
+
console.log("OpenSSL version : ", exports.g_config.opensslVersion);
|
|
187
|
+
}
|
|
188
|
+
callback(err ? err : undefined);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
return callback();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
exports.ensure_openssl_installed = ensure_openssl_installed;
|
|
197
|
+
function getTempFolder() {
|
|
198
|
+
return os.tmpdir();
|
|
199
|
+
}
|
|
200
|
+
function execute_openssl(cmd, options, callback) {
|
|
201
|
+
// tslint:disable-next-line:variable-name
|
|
202
|
+
const empty_config_file = n(getTempFolder(), "empty_config.cnf");
|
|
203
|
+
if (!fs.existsSync(empty_config_file)) {
|
|
204
|
+
fs.writeFileSync(empty_config_file, "# empty config file");
|
|
205
|
+
}
|
|
206
|
+
assert(typeof callback === "function");
|
|
207
|
+
options = options || {};
|
|
208
|
+
options.openssl_conf = options.openssl_conf || empty_config_file; // "!! OPEN SLL CONF NOT DEFINED BAD FILE !!";
|
|
209
|
+
assert(options.openssl_conf);
|
|
210
|
+
setEnv("OPENSSL_CONF", options.openssl_conf);
|
|
211
|
+
// istanbul ignore next
|
|
212
|
+
if (!exports.g_config.silent) {
|
|
213
|
+
console.log(chalk.cyan(" OPENSSL_CONF"), process.env.OPENSSL_CONF);
|
|
214
|
+
console.log(chalk.cyan(" RANDFILE "), process.env.RANDFILE);
|
|
215
|
+
console.log(chalk.cyan(" CMD openssl "), chalk.cyanBright(cmd));
|
|
216
|
+
}
|
|
217
|
+
ensure_openssl_installed((err) => {
|
|
218
|
+
// istanbul ignore next
|
|
219
|
+
if (err) {
|
|
220
|
+
return callback(err);
|
|
221
|
+
}
|
|
222
|
+
execute(quote(opensslPath) + " " + cmd, options, callback);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
exports.execute_openssl = execute_openssl;
|
|
226
|
+
function executeOpensslAsync(cmd, options) {
|
|
227
|
+
return new Promise((resolve, reject) => {
|
|
228
|
+
execute_openssl(cmd, options, (err, output) => {
|
|
229
|
+
// istanbul ignore next
|
|
230
|
+
if (err) {
|
|
231
|
+
reject(err);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
resolve(output || "");
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
exports.executeOpensslAsync = executeOpensslAsync;
|
|
240
|
+
function execute_openssl_no_failure(cmd, options, callback) {
|
|
241
|
+
options = options || {};
|
|
242
|
+
options.hideErrorMessage = true;
|
|
243
|
+
execute_openssl(cmd, options, (err, output) => {
|
|
244
|
+
// istanbul ignore next
|
|
245
|
+
if (err) {
|
|
246
|
+
debugLog(" (ignored error = ERROR : )", err.message);
|
|
247
|
+
}
|
|
248
|
+
callback(null, output);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
exports.execute_openssl_no_failure = execute_openssl_no_failure;
|
|
252
|
+
// istanbul ignore next
|
|
253
|
+
function displayChapter(str, callback) {
|
|
254
|
+
const l = " ";
|
|
255
|
+
console.log(chalk.bgWhite(l) + " ");
|
|
256
|
+
str = (" " + str + l).substring(0, l.length);
|
|
257
|
+
console.log(chalk.bgWhite.cyan(str));
|
|
258
|
+
console.log(chalk.bgWhite(l) + " ");
|
|
259
|
+
if (callback) {
|
|
260
|
+
callback();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
exports.displayChapter = displayChapter;
|
|
264
|
+
function displayTitle(str, callback) {
|
|
265
|
+
// istanbul ignore next
|
|
266
|
+
if (!exports.g_config.silent) {
|
|
267
|
+
console.log("");
|
|
268
|
+
console.log(chalk.yellowBright(str));
|
|
269
|
+
console.log(chalk.yellow(new Array(str.length + 1).join("=")), "\n");
|
|
270
|
+
}
|
|
271
|
+
if (callback) {
|
|
272
|
+
callback();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
exports.displayTitle = displayTitle;
|
|
276
|
+
function displaySubtitle(str, callback) {
|
|
277
|
+
// istanbul ignore next
|
|
278
|
+
if (!exports.g_config.silent) {
|
|
279
|
+
console.log("");
|
|
280
|
+
console.log(" " + chalk.yellowBright(str));
|
|
281
|
+
console.log(" " + chalk.white(new Array(str.length + 1).join("-")), "\n");
|
|
282
|
+
}
|
|
283
|
+
if (callback) {
|
|
284
|
+
callback();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
exports.displaySubtitle = displaySubtitle;
|
|
288
|
+
function getEnvironmentVarNames() {
|
|
289
|
+
return Object.keys(exportedEnvVars).map((varName) => {
|
|
290
|
+
return { key: varName, pattern: "\\$ENV\\:\\:" + varName };
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
exports.getEnvironmentVarNames = getEnvironmentVarNames;
|
|
294
|
+
function generateStaticConfig(configPath, options) {
|
|
295
|
+
const prePath = (options && options.cwd) || "";
|
|
296
|
+
const staticConfigPath = configPath + ".tmp";
|
|
297
|
+
let staticConfig = fs.readFileSync(path.join(prePath, configPath), { encoding: "utf8" });
|
|
298
|
+
for (const envVar of getEnvironmentVarNames()) {
|
|
299
|
+
staticConfig = staticConfig.replace(new RegExp(envVar.pattern, "gi"), exportedEnvVars[envVar.key]);
|
|
300
|
+
}
|
|
301
|
+
fs.writeFileSync(path.join(prePath, staticConfigPath), staticConfig);
|
|
302
|
+
return staticConfigPath;
|
|
303
|
+
}
|
|
304
|
+
exports.generateStaticConfig = generateStaticConfig;
|
|
305
|
+
const q = quote;
|
|
306
|
+
function make_path(folderName, filename) {
|
|
307
|
+
let s;
|
|
308
|
+
if (filename) {
|
|
309
|
+
s = path.join(path.normalize(folderName), filename);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
assert(folderName);
|
|
313
|
+
s = folderName;
|
|
314
|
+
}
|
|
315
|
+
s = s.replace(/\\/g, "/");
|
|
316
|
+
return s;
|
|
317
|
+
}
|
|
318
|
+
exports.make_path = make_path;
|
|
319
|
+
const n = make_path;
|
|
320
|
+
/**
|
|
321
|
+
* calculate the public key from private key
|
|
322
|
+
* openssl rsa -pubout -in private_key.pem
|
|
323
|
+
*
|
|
324
|
+
* @method getPublicKeyFromPrivateKey
|
|
325
|
+
* @param privateKeyFilename
|
|
326
|
+
* @param publicKeyFilename
|
|
327
|
+
* @param callback
|
|
328
|
+
*/
|
|
329
|
+
function getPublicKeyFromPrivateKey(privateKeyFilename, publicKeyFilename, callback) {
|
|
330
|
+
assert(fs.existsSync(privateKeyFilename));
|
|
331
|
+
execute_openssl("rsa -pubout -in " + q(n(privateKeyFilename)) + " -out " + q(n(publicKeyFilename)), {}, callback);
|
|
332
|
+
}
|
|
333
|
+
exports.getPublicKeyFromPrivateKey = getPublicKeyFromPrivateKey;
|
|
334
|
+
/**
|
|
335
|
+
* extract public key from a certificate
|
|
336
|
+
* openssl x509 -pubkey -in certificate.pem -nottext
|
|
337
|
+
*
|
|
338
|
+
* @method getPublicKeyFromCertificate
|
|
339
|
+
* @param certificateFilename
|
|
340
|
+
* @param publicKeyFilename
|
|
341
|
+
* @param callback
|
|
342
|
+
*/
|
|
343
|
+
function getPublicKeyFromCertificate(certificateFilename, publicKeyFilename, callback) {
|
|
344
|
+
assert(fs.existsSync(certificateFilename));
|
|
345
|
+
execute_openssl("x509 -pubkey -in " + q(n(certificateFilename)) + " > " + q(n(publicKeyFilename)), {}, callback);
|
|
346
|
+
}
|
|
347
|
+
exports.getPublicKeyFromCertificate = getPublicKeyFromCertificate;
|
|
348
|
+
/**
|
|
349
|
+
* create a RSA PRIVATE KEY
|
|
350
|
+
*
|
|
351
|
+
* @method createPrivateKey
|
|
352
|
+
*
|
|
353
|
+
* @param privateKeyFilename
|
|
354
|
+
* @param keyLength
|
|
355
|
+
* @param callback {Function}
|
|
356
|
+
*/
|
|
357
|
+
function createPrivateKey(privateKeyFilename, keyLength, callback) {
|
|
358
|
+
// istanbul ignore next
|
|
359
|
+
if (useRandFile()) {
|
|
360
|
+
assert(hasEnv("RANDFILE"));
|
|
361
|
+
}
|
|
362
|
+
assert([1024, 2048, 3072, 4096].indexOf(keyLength) >= 0);
|
|
363
|
+
const randomFile = exportedEnvVars.RANDFILE ? n(exportedEnvVars.RANDFILE) : "random.rnd";
|
|
364
|
+
const tasks = [
|
|
365
|
+
(callback) => createRandomFileIfNotExist(randomFile, {}, callback),
|
|
366
|
+
// Note OpenSSL1 generates a -----BEGIN RSA PRIVATE KEY---- whereas
|
|
367
|
+
// OpenSSL3 generates a -----BEGIN PRIVATE KEY----- unless the new -traditional option is used
|
|
368
|
+
//
|
|
369
|
+
// a BEGIN PRIVATE KEY structure is
|
|
370
|
+
//
|
|
371
|
+
// SEQUENCE (3 elem)
|
|
372
|
+
// INTEGER 0
|
|
373
|
+
// SEQUENCE (2 elem)
|
|
374
|
+
// OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
|
|
375
|
+
// NULL
|
|
376
|
+
// OCTET STRING (609 byte) 3082025D02010002818100C5B53231183906122A5E3778736B05C095C75F1BB80D48B
|
|
377
|
+
// SEQUENCE (9 elem)
|
|
378
|
+
//
|
|
379
|
+
// a BEGIN RSA PRIVATE KEY structure is just
|
|
380
|
+
// SEQUENCE (9 elem)
|
|
381
|
+
//
|
|
382
|
+
(callback) => {
|
|
383
|
+
execute_openssl("genrsa " +
|
|
384
|
+
" -out " +
|
|
385
|
+
q(n(privateKeyFilename)) +
|
|
386
|
+
(useRandFile() ? " -rand " + q(randomFile) : "") +
|
|
387
|
+
" " +
|
|
388
|
+
keyLength, {}, (err) => {
|
|
389
|
+
callback(err ? err : undefined);
|
|
390
|
+
});
|
|
391
|
+
},
|
|
392
|
+
];
|
|
393
|
+
async.series(tasks, callback);
|
|
394
|
+
}
|
|
395
|
+
exports.createPrivateKey = createPrivateKey;
|
|
396
|
+
function createRandomFile(randomFile, options, callback) {
|
|
397
|
+
// istanbul ignore next
|
|
398
|
+
if (!useRandFile()) {
|
|
399
|
+
return callback();
|
|
400
|
+
}
|
|
401
|
+
execute_openssl("rand " + " -out " + q(randomFile) + " -hex 256", options, (err) => {
|
|
402
|
+
callback(err ? err : undefined);
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
exports.createRandomFile = createRandomFile;
|
|
406
|
+
function createRandomFileIfNotExist(randomFile, options, callback) {
|
|
407
|
+
const randomFilePath = options.cwd ? path.join(options.cwd, randomFile) : randomFile;
|
|
408
|
+
if (fs.existsSync(randomFilePath)) {
|
|
409
|
+
if (doDebug) {
|
|
410
|
+
console.log(chalk.yellow(" randomFile"), chalk.cyan(randomFile), chalk.yellow(" already exists => skipping"));
|
|
411
|
+
}
|
|
412
|
+
return callback();
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
createRandomFile(randomFile, options, callback);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
exports.createRandomFileIfNotExist = createRandomFileIfNotExist;
|
|
419
|
+
/**
|
|
420
|
+
* create a certificate signing request
|
|
421
|
+
*
|
|
422
|
+
* @param certificateSigningRequestFilename
|
|
423
|
+
* @param params
|
|
424
|
+
* @param callback
|
|
425
|
+
*/
|
|
426
|
+
function createCertificateSigningRequest(certificateSigningRequestFilename, params, callback) {
|
|
427
|
+
assert(params);
|
|
428
|
+
assert(params.rootDir);
|
|
429
|
+
assert(params.configFile);
|
|
430
|
+
assert(params.privateKey);
|
|
431
|
+
assert(typeof params.privateKey === "string");
|
|
432
|
+
assert(fs.existsSync(params.configFile), "config file must exist " + params.configFile);
|
|
433
|
+
assert(fs.existsSync(params.privateKey), "Private key must exist" + params.privateKey);
|
|
434
|
+
assert(fs.existsSync(params.rootDir), "RootDir key must exist");
|
|
435
|
+
assert(typeof certificateSigningRequestFilename === "string");
|
|
436
|
+
// note : this openssl command requires a config file
|
|
437
|
+
processAltNames(params);
|
|
438
|
+
const configFile = generateStaticConfig(params.configFile);
|
|
439
|
+
const options = { cwd: params.rootDir, openssl_conf: configFile };
|
|
440
|
+
const configOption = " -config " + q(n(configFile));
|
|
441
|
+
const subject = params.subject ? new subject_1.Subject(params.subject).toString() : undefined;
|
|
442
|
+
// process.env.OPENSSL_CONF ="";
|
|
443
|
+
const subjectOptions = subject ? " -subj \"" + subject + "\"" : "";
|
|
444
|
+
async.series([
|
|
445
|
+
(callback) => {
|
|
446
|
+
displaySubtitle("- Creating a Certificate Signing Request", callback);
|
|
447
|
+
},
|
|
448
|
+
(callback) => {
|
|
449
|
+
execute_openssl("req -new" +
|
|
450
|
+
" -sha256 " +
|
|
451
|
+
" -batch " +
|
|
452
|
+
" -text " +
|
|
453
|
+
configOption +
|
|
454
|
+
" -key " +
|
|
455
|
+
q(n(params.privateKey)) +
|
|
456
|
+
subjectOptions +
|
|
457
|
+
" -out " +
|
|
458
|
+
q(n(certificateSigningRequestFilename)), options, (err) => {
|
|
459
|
+
callback(err ? err : undefined);
|
|
460
|
+
});
|
|
461
|
+
},
|
|
462
|
+
], (err) => callback(err));
|
|
463
|
+
}
|
|
464
|
+
exports.createCertificateSigningRequest = createCertificateSigningRequest;
|
|
465
|
+
function x509Date(date) {
|
|
466
|
+
date = date || new Date();
|
|
467
|
+
const Y = date.getUTCFullYear();
|
|
468
|
+
const M = date.getUTCMonth() + 1;
|
|
469
|
+
const D = date.getUTCDate();
|
|
470
|
+
const h = date.getUTCHours();
|
|
471
|
+
const m = date.getUTCMinutes();
|
|
472
|
+
const s = date.getUTCSeconds();
|
|
473
|
+
function w(s, l) {
|
|
474
|
+
return ("" + s).padStart(l, "0");
|
|
475
|
+
}
|
|
476
|
+
if (openssl_require2DigitYearInDate()) {
|
|
477
|
+
// for example: on MacOS , where openssl 0.98 is installed by default
|
|
478
|
+
return w(Y, 2) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2) + "Z";
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
// for instance when openssl version is greater than 1.0.0
|
|
482
|
+
return w(Y, 4) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2) + "Z";
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
exports.x509Date = x509Date;
|
|
486
|
+
// purpose of self-signed certificate
|
|
487
|
+
var CertificatePurpose;
|
|
488
|
+
(function (CertificatePurpose) {
|
|
489
|
+
CertificatePurpose[CertificatePurpose["NotSpecified"] = 0] = "NotSpecified";
|
|
490
|
+
CertificatePurpose[CertificatePurpose["ForCertificateAuthority"] = 1] = "ForCertificateAuthority";
|
|
491
|
+
CertificatePurpose[CertificatePurpose["ForApplication"] = 2] = "ForApplication";
|
|
492
|
+
CertificatePurpose[CertificatePurpose["ForUserAuthentication"] = 3] = "ForUserAuthentication";
|
|
493
|
+
})(CertificatePurpose = exports.CertificatePurpose || (exports.CertificatePurpose = {}));
|
|
494
|
+
function adjustDate(params) {
|
|
495
|
+
assert(params instanceof Object);
|
|
496
|
+
params.startDate = params.startDate || new Date();
|
|
497
|
+
assert(params.startDate instanceof Date);
|
|
498
|
+
params.validity = params.validity || 365; // one year
|
|
499
|
+
params.endDate = new Date(params.startDate.getTime());
|
|
500
|
+
params.endDate.setDate(params.startDate.getDate() + params.validity);
|
|
501
|
+
// xx params.endDate = x509Date(endDate);
|
|
502
|
+
// xx params.startDate = x509Date(startDate);
|
|
503
|
+
assert(params.endDate instanceof Date);
|
|
504
|
+
assert(params.startDate instanceof Date);
|
|
505
|
+
// istanbul ignore next
|
|
506
|
+
if (!exports.g_config.silent) {
|
|
507
|
+
console.log(" start Date ", params.startDate.toUTCString(), x509Date(params.startDate));
|
|
508
|
+
console.log(" end Date ", params.endDate.toUTCString(), x509Date(params.endDate));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
exports.adjustDate = adjustDate;
|
|
512
|
+
function adjustApplicationUri(params) {
|
|
513
|
+
const applicationUri = params.applicationUri || "";
|
|
514
|
+
if (applicationUri.length > 200) {
|
|
515
|
+
throw new Error("Openssl doesn't support urn with length greater than 200" + applicationUri);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
exports.adjustApplicationUri = adjustApplicationUri;
|
|
519
|
+
function certificateFileExist(certificateFile) {
|
|
520
|
+
assert(typeof certificateFile === "string");
|
|
521
|
+
// istanbul ignore next
|
|
522
|
+
if (fs.existsSync(certificateFile) && !exports.g_config.force) {
|
|
523
|
+
console.log(chalk.yellow(" certificate ") + chalk.cyan(certificateFile) + chalk.yellow(" already exists => do not overwrite"));
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
exports.certificateFileExist = certificateFileExist;
|
|
529
|
+
/**
|
|
530
|
+
*
|
|
531
|
+
* @param params
|
|
532
|
+
* @param params.applicationUri
|
|
533
|
+
* @param params.dns
|
|
534
|
+
* @param params.ip
|
|
535
|
+
* @private
|
|
536
|
+
*/
|
|
537
|
+
function processAltNames(params) {
|
|
538
|
+
params.dns = params.dns || [];
|
|
539
|
+
params.ip = params.ip || [];
|
|
540
|
+
// construct subjectAtlName
|
|
541
|
+
let subjectAltName = [];
|
|
542
|
+
subjectAltName.push("URI:" + params.applicationUri);
|
|
543
|
+
subjectAltName = [].concat(subjectAltName, params.dns.map((d) => "DNS:" + d));
|
|
544
|
+
subjectAltName = [].concat(subjectAltName, params.ip.map((d) => "IP:" + d));
|
|
545
|
+
const subjectAltNameString = subjectAltName.join(", ");
|
|
546
|
+
setEnv("ALTNAME", subjectAltNameString);
|
|
547
|
+
}
|
|
548
|
+
exports.processAltNames = processAltNames;
|
|
549
|
+
/**
|
|
550
|
+
*
|
|
551
|
+
* @param certificate
|
|
552
|
+
* @param params
|
|
553
|
+
* @param params.configFile
|
|
554
|
+
* @param params.rootDir
|
|
555
|
+
* @param params.privateKey
|
|
556
|
+
* @param params.applicationUri
|
|
557
|
+
* @param params.dns
|
|
558
|
+
* @param params.ip
|
|
559
|
+
* @param params.validity certificate duration in days
|
|
560
|
+
* @param params.purpose
|
|
561
|
+
* @param [params.subject= "C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=ZZNodeOPCUA"]
|
|
562
|
+
* @param callback
|
|
563
|
+
*/
|
|
564
|
+
function createSelfSignCertificate(certificate, params, callback) {
|
|
565
|
+
ensure_openssl_installed((err) => {
|
|
566
|
+
if (err) {
|
|
567
|
+
return callback(err);
|
|
568
|
+
}
|
|
569
|
+
try {
|
|
570
|
+
params.purpose = params.purpose || CertificatePurpose.ForApplication;
|
|
571
|
+
assert(params.purpose, "Please provide a Certificate Purpose");
|
|
572
|
+
/**
|
|
573
|
+
* note: due to a limitation of openssl ,
|
|
574
|
+
* it is not possible to control the startDate of the certificate validity
|
|
575
|
+
* to achieve this the certificateAuthority tool shall be used.
|
|
576
|
+
*/
|
|
577
|
+
assert(fs.existsSync(params.configFile));
|
|
578
|
+
assert(fs.existsSync(params.rootDir));
|
|
579
|
+
assert(fs.existsSync(params.privateKey));
|
|
580
|
+
if (!params.subject) {
|
|
581
|
+
return callback(new Error("Missing subject"));
|
|
582
|
+
}
|
|
583
|
+
assert(typeof params.applicationUri === "string");
|
|
584
|
+
assert(params.dns instanceof Array);
|
|
585
|
+
// xx no key size in self-signed assert(params.keySize == 2048 || params.keySize == 4096);
|
|
586
|
+
processAltNames(params);
|
|
587
|
+
adjustDate(params);
|
|
588
|
+
assert(Object.prototype.hasOwnProperty.call(params, "validity"));
|
|
589
|
+
let subject = new subject_1.Subject(params.subject);
|
|
590
|
+
subject = subject.toString();
|
|
591
|
+
const certificateRequestFilename = certificate + ".csr";
|
|
592
|
+
const configFile = generateStaticConfig(params.configFile);
|
|
593
|
+
const configOption = " -config " + q(n(configFile));
|
|
594
|
+
let extension;
|
|
595
|
+
switch (params.purpose) {
|
|
596
|
+
case CertificatePurpose.ForApplication:
|
|
597
|
+
extension = "v3_selfsigned";
|
|
598
|
+
break;
|
|
599
|
+
case CertificatePurpose.ForCertificateAuthority:
|
|
600
|
+
extension = "v3_ca";
|
|
601
|
+
break;
|
|
602
|
+
case CertificatePurpose.ForUserAuthentication:
|
|
603
|
+
default:
|
|
604
|
+
extension = "v3_selfsigned";
|
|
605
|
+
}
|
|
606
|
+
const tasks = [
|
|
607
|
+
(callback) => {
|
|
608
|
+
displayTitle("Generate a certificate request", callback);
|
|
609
|
+
},
|
|
610
|
+
// Once the private key is generated a Certificate Signing Request can be generated.
|
|
611
|
+
// The CSR is then used in one of two ways. Ideally, the CSR will be sent to a Certificate Authority, such as
|
|
612
|
+
// Thawte or Verisign who will verify the identity of the requestor and issue a signed certificate.
|
|
613
|
+
// The second option is to self-sign the CSR, which will be demonstrated in the next section
|
|
614
|
+
(callback) => {
|
|
615
|
+
execute_openssl("req -new" +
|
|
616
|
+
" -sha256 " +
|
|
617
|
+
" -text " +
|
|
618
|
+
" -extensions " +
|
|
619
|
+
extension +
|
|
620
|
+
" " +
|
|
621
|
+
configOption +
|
|
622
|
+
" -key " +
|
|
623
|
+
q(n(params.privateKey)) +
|
|
624
|
+
" -out " +
|
|
625
|
+
q(n(certificateRequestFilename)) +
|
|
626
|
+
" -subj \"" +
|
|
627
|
+
subject +
|
|
628
|
+
"\"", {}, callback);
|
|
629
|
+
},
|
|
630
|
+
// Xx // Step 3: Remove Passphrase from Key
|
|
631
|
+
// Xx execute("cp private/cakey.pem private/cakey.pem.org");
|
|
632
|
+
// Xx execute(openssl_path + " rsa -in private/cakey.pem.org
|
|
633
|
+
// Xx -out private/cakey.pem -passin pass:"+paraphrase);
|
|
634
|
+
(callback) => {
|
|
635
|
+
displayTitle("Generate Certificate (self-signed)", callback);
|
|
636
|
+
},
|
|
637
|
+
(callback) => {
|
|
638
|
+
execute_openssl(" x509 -req " +
|
|
639
|
+
" -days " +
|
|
640
|
+
params.validity +
|
|
641
|
+
" -extensions " +
|
|
642
|
+
extension +
|
|
643
|
+
" " +
|
|
644
|
+
" -extfile " +
|
|
645
|
+
q(n(configFile)) +
|
|
646
|
+
" -in " +
|
|
647
|
+
q(n(certificateRequestFilename)) +
|
|
648
|
+
" -signkey " +
|
|
649
|
+
q(n(params.privateKey)) +
|
|
650
|
+
" -text " +
|
|
651
|
+
" -out " +
|
|
652
|
+
q(certificate) +
|
|
653
|
+
" -text ", {}, callback);
|
|
654
|
+
},
|
|
655
|
+
// remove unnecessary certificate request file
|
|
656
|
+
(callback) => {
|
|
657
|
+
fs.unlink(certificateRequestFilename, callback);
|
|
658
|
+
},
|
|
659
|
+
];
|
|
660
|
+
async.series(tasks, callback);
|
|
661
|
+
}
|
|
662
|
+
catch (err) {
|
|
663
|
+
callback(err);
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
exports.createSelfSignCertificate = createSelfSignCertificate;
|
|
668
|
+
// tslint:disable-next-line:variable-name
|
|
669
|
+
exports.configurationFileTemplate = ca_config_template_cnf_1.default;
|
|
670
|
+
/**
|
|
671
|
+
*
|
|
672
|
+
* a minimalist config file for openssl that allows
|
|
673
|
+
* self-signed certificate to be generated.
|
|
674
|
+
*
|
|
675
|
+
*/
|
|
676
|
+
// tslint:disable-next-line:variable-name
|
|
677
|
+
exports.configurationFileSimpleTemplate = simple_config_template_cnf_1.default;
|
|
678
|
+
/**
|
|
679
|
+
* @param certificate - the certificate file in PEM format, file must exist
|
|
680
|
+
* @param callback
|
|
681
|
+
*/
|
|
682
|
+
function dumpCertificate(certificate, callback) {
|
|
683
|
+
assert(fs.existsSync(certificate));
|
|
684
|
+
assert(typeof callback === "function");
|
|
685
|
+
execute_openssl("x509 " + " -in " + q(n(certificate)) + " -text " + " -noout", {}, callback);
|
|
686
|
+
}
|
|
687
|
+
exports.dumpCertificate = dumpCertificate;
|
|
688
|
+
function toDer(certificatePem, callback) {
|
|
689
|
+
assert(fs.existsSync(certificatePem));
|
|
690
|
+
const certificateDer = certificatePem.replace(".pem", ".der");
|
|
691
|
+
execute_openssl("x509 " + " -outform der " + " -in " + certificatePem + " -out " + certificateDer, {}, callback);
|
|
692
|
+
}
|
|
693
|
+
exports.toDer = toDer;
|
|
694
|
+
function fingerprint(certificatePem, callback) {
|
|
695
|
+
// openssl x509 -in my_certificate.pem -hash -dates -noout -fingerprint
|
|
696
|
+
assert(fs.existsSync(certificatePem));
|
|
697
|
+
execute_openssl("x509 " + " -fingerprint " + " -noout " + " -in " + certificatePem, {}, callback);
|
|
698
|
+
}
|
|
699
|
+
exports.fingerprint = fingerprint;
|
|
700
700
|
//# sourceMappingURL=toolbox.js.map
|