node-opcua-pki 4.12.0 → 4.13.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.
Files changed (61) hide show
  1. package/dist/lib/ca/certificate_authority.d.ts +61 -0
  2. package/dist/lib/ca/certificate_authority.js +497 -0
  3. package/dist/lib/ca/crypto_create_CA.d.ts +2 -0
  4. package/dist/lib/ca/crypto_create_CA.js +903 -0
  5. package/dist/lib/ca/index.d.ts +1 -0
  6. package/dist/lib/ca/index.js +18 -0
  7. package/dist/lib/ca/templates/ca_config_template.cnf.d.ts +2 -0
  8. package/dist/lib/ca/templates/ca_config_template.cnf.js +130 -0
  9. package/dist/lib/index.d.ts +5 -0
  10. package/dist/lib/index.js +48 -0
  11. package/dist/lib/misc/applicationurn.d.ts +1 -0
  12. package/dist/lib/misc/applicationurn.js +47 -0
  13. package/dist/lib/misc/hostname.d.ts +8 -0
  14. package/dist/lib/misc/hostname.js +106 -0
  15. package/dist/lib/misc/subject.d.ts +1 -0
  16. package/dist/lib/misc/subject.js +6 -0
  17. package/dist/lib/pki/certificate_manager.d.ts +165 -0
  18. package/dist/lib/pki/certificate_manager.js +991 -0
  19. package/dist/lib/pki/templates/simple_config_template.cnf.d.ts +2 -0
  20. package/dist/lib/pki/templates/simple_config_template.cnf.js +76 -0
  21. package/dist/lib/pki/toolbox_pfx.d.ts +0 -0
  22. package/dist/lib/pki/toolbox_pfx.js +19 -0
  23. package/dist/lib/toolbox/common.d.ts +47 -0
  24. package/dist/lib/toolbox/common.js +56 -0
  25. package/dist/lib/toolbox/common2.d.ts +3 -0
  26. package/dist/lib/toolbox/common2.js +65 -0
  27. package/dist/lib/toolbox/config.d.ts +5 -0
  28. package/dist/lib/toolbox/config.js +31 -0
  29. package/dist/lib/toolbox/debug.d.ts +5 -0
  30. package/dist/lib/toolbox/debug.js +41 -0
  31. package/dist/lib/toolbox/display.d.ts +4 -0
  32. package/dist/lib/toolbox/display.js +78 -0
  33. package/dist/lib/toolbox/index.d.ts +5 -0
  34. package/dist/lib/toolbox/index.js +44 -0
  35. package/dist/lib/toolbox/with_openssl/_create_random_file.d.ts +5 -0
  36. package/dist/lib/toolbox/with_openssl/_create_random_file.js +63 -0
  37. package/dist/lib/toolbox/with_openssl/_env.d.ts +7 -0
  38. package/dist/lib/toolbox/with_openssl/_env.js +47 -0
  39. package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.d.ts +10 -0
  40. package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.js +92 -0
  41. package/dist/lib/toolbox/with_openssl/create_private_key.d.ts +11 -0
  42. package/dist/lib/toolbox/with_openssl/create_private_key.js +118 -0
  43. package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.d.ts +5 -0
  44. package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.js +153 -0
  45. package/dist/lib/toolbox/with_openssl/execute_openssl.d.ts +15 -0
  46. package/dist/lib/toolbox/with_openssl/execute_openssl.js +192 -0
  47. package/dist/lib/toolbox/with_openssl/index.d.ts +5 -0
  48. package/dist/lib/toolbox/with_openssl/index.js +22 -0
  49. package/dist/lib/toolbox/with_openssl/install_prerequisite.d.ts +9 -0
  50. package/dist/lib/toolbox/with_openssl/install_prerequisite.js +368 -0
  51. package/dist/lib/toolbox/with_openssl/toolbox.d.ts +31 -0
  52. package/dist/lib/toolbox/with_openssl/toolbox.js +132 -0
  53. package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.d.ts +10 -0
  54. package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.js +86 -0
  55. package/dist/lib/toolbox/without_openssl/create_private_key.d.ts +2 -0
  56. package/dist/lib/toolbox/without_openssl/create_private_key.js +32 -0
  57. package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.d.ts +3 -0
  58. package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.js +89 -0
  59. package/dist/lib/toolbox/without_openssl/index.d.ts +3 -0
  60. package/dist/lib/toolbox/without_openssl/index.js +42 -0
  61. package/package.json +2 -8
@@ -0,0 +1,61 @@
1
+ import { Subject, SubjectOptions } from "node-opcua-crypto";
2
+ import { ErrorCallback, Filename, KeySize, Params } from "../toolbox";
3
+ export declare const defaultSubject = "/C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=NodeOPCUA-CA";
4
+ export declare const configurationFileTemplate: string;
5
+ export interface CertificateAuthorityOptions {
6
+ keySize: KeySize;
7
+ location: string;
8
+ subject?: string | SubjectOptions;
9
+ }
10
+ export declare class CertificateAuthority {
11
+ readonly keySize: KeySize;
12
+ readonly location: string;
13
+ readonly subject: Subject;
14
+ constructor(options: CertificateAuthorityOptions);
15
+ get rootDir(): string;
16
+ get configFile(): string;
17
+ get caCertificate(): string;
18
+ /**
19
+ * the file name where the current Certificate Revocation List is stored (in DER format)
20
+ */
21
+ get revocationListDER(): string;
22
+ /**
23
+ * the file name where the current Certificate Revocation List is stored (in PEM format)
24
+ */
25
+ get revocationList(): string;
26
+ get caCertificateWithCrl(): string;
27
+ initialize(): Promise<void>;
28
+ initialize(callback: ErrorCallback): void;
29
+ constructCACertificateWithCRL(): Promise<void>;
30
+ constructCACertificateWithCRL(callback: ErrorCallback): void;
31
+ constructCertificateChain(certificate: Filename): Promise<void>;
32
+ constructCertificateChain(certificate: Filename, callback: ErrorCallback): void;
33
+ createSelfSignedCertificate(certificateFile: Filename, privateKey: Filename, params: Params): Promise<void>;
34
+ createSelfSignedCertificate(certificateFile: Filename, privateKey: Filename, params: Params, callback: ErrorCallback): void;
35
+ /**
36
+ * revoke a certificate and update the CRL
37
+ *
38
+ * @method revokeCertificate
39
+ * @param certificate - the certificate to revoke
40
+ * @param params
41
+ * @param [params.reason = "keyCompromise" {String}]
42
+ * @param callback
43
+ * @async
44
+ */
45
+ revokeCertificate(certificate: Filename, params: Params, callback: ErrorCallback): void;
46
+ revokeCertificate(certificate: Filename, params: Params): Promise<void>;
47
+ /**
48
+ *
49
+ * @param certificate - the certificate filename to generate
50
+ * @param certificateSigningRequestFilename - the certificate signing request
51
+ * @param params - parameters
52
+ * @param params.applicationUri - the applicationUri
53
+ * @param params.startDate - startDate of the certificate
54
+ * @param params.validity - number of day of validity of the certificate
55
+ * @param callback
56
+ */
57
+ signCertificateRequest(certificate: Filename, certificateSigningRequestFilename: Filename, params: Params): Promise<Filename>;
58
+ signCertificateRequest(certificate: Filename, certificateSigningRequestFilename: Filename, params: Params, callback: (err: Error | null, certificate?: Filename) => void): void;
59
+ verifyCertificate(certificate: Filename): Promise<void>;
60
+ verifyCertificate(certificate: Filename, callback: ErrorCallback): void;
61
+ }
@@ -0,0 +1,497 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CertificateAuthority = exports.configurationFileTemplate = exports.defaultSubject = void 0;
7
+ // ---------------------------------------------------------------------------------------------------------------------
8
+ // node-opcua
9
+ // ---------------------------------------------------------------------------------------------------------------------
10
+ // Copyright (c) 2014-2022 - Etienne Rossignon - etienne.rossignon (at) gadz.org
11
+ // Copyright (c) 2022-2024 - Sterfive.com
12
+ // ---------------------------------------------------------------------------------------------------------------------
13
+ //
14
+ // This project is licensed under the terms of the MIT license.
15
+ //
16
+ // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
17
+ // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
18
+ // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
19
+ // permit persons to whom the Software is furnished to do so, subject to the following conditions:
20
+ //
21
+ // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
22
+ // Software.
23
+ //
24
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
25
+ // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
26
+ // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
27
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
+ // ---------------------------------------------------------------------------------------------------------------------
29
+ // tslint:disable:no-shadowed-variable
30
+ const assert_1 = __importDefault(require("assert"));
31
+ const async_1 = __importDefault(require("async"));
32
+ const chalk_1 = __importDefault(require("chalk"));
33
+ const fs_1 = __importDefault(require("fs"));
34
+ const path_1 = __importDefault(require("path"));
35
+ const node_opcua_crypto_1 = require("node-opcua-crypto");
36
+ const toolbox_1 = require("../toolbox");
37
+ const with_openssl_1 = require("../toolbox/with_openssl");
38
+ const create_private_key_1 = require("../toolbox/without_openssl/create_private_key");
39
+ exports.defaultSubject = "/C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=NodeOPCUA-CA";
40
+ const ca_config_template_cnf_1 = __importDefault(require("./templates/ca_config_template.cnf"));
41
+ // tslint:disable-next-line:variable-name
42
+ exports.configurationFileTemplate = ca_config_template_cnf_1.default;
43
+ const config = {
44
+ certificateDir: "INVALID",
45
+ forceCA: false,
46
+ pkiDir: "INVALID",
47
+ };
48
+ const n = toolbox_1.make_path;
49
+ const q = toolbox_1.quote;
50
+ // convert 'c07b9179' to "192.123.145.121"
51
+ function octetStringToIpAddress(a) {
52
+ return (parseInt(a.substring(0, 2), 16).toString() +
53
+ "." +
54
+ parseInt(a.substring(2, 4), 16).toString() +
55
+ "." +
56
+ parseInt(a.substring(4, 6), 16).toString() +
57
+ "." +
58
+ parseInt(a.substring(6, 8), 16).toString());
59
+ }
60
+ (0, assert_1.default)(octetStringToIpAddress("c07b9179") === "192.123.145.121");
61
+ function construct_CertificateAuthority(certificateAuthority, callback) {
62
+ // create the CA directory store
63
+ // create the CA directory store
64
+ //
65
+ // PKI/CA
66
+ // |
67
+ // +-+> private
68
+ // |
69
+ // +-+> public
70
+ // |
71
+ // +-+> certs
72
+ // |
73
+ // +-+> crl
74
+ // |
75
+ // +-+> conf
76
+ // |
77
+ // +-f: serial
78
+ // +-f: crlNumber
79
+ // +-f: index.txt
80
+ //
81
+ const subject = certificateAuthority.subject;
82
+ const caRootDir = certificateAuthority.rootDir;
83
+ function make_folders() {
84
+ (0, toolbox_1.mkdir)(caRootDir);
85
+ (0, toolbox_1.mkdir)(path_1.default.join(caRootDir, "private"));
86
+ (0, toolbox_1.mkdir)(path_1.default.join(caRootDir, "public"));
87
+ // xx execute("chmod 700 private");
88
+ (0, toolbox_1.mkdir)(path_1.default.join(caRootDir, "certs"));
89
+ (0, toolbox_1.mkdir)(path_1.default.join(caRootDir, "crl"));
90
+ (0, toolbox_1.mkdir)(path_1.default.join(caRootDir, "conf"));
91
+ }
92
+ make_folders();
93
+ function construct_default_files() {
94
+ const serial = path_1.default.join(caRootDir, "serial");
95
+ if (!fs_1.default.existsSync(serial)) {
96
+ fs_1.default.writeFileSync(serial, "1000");
97
+ }
98
+ const crlNumber = path_1.default.join(caRootDir, "crlnumber");
99
+ if (!fs_1.default.existsSync(crlNumber)) {
100
+ fs_1.default.writeFileSync(crlNumber, "1000");
101
+ }
102
+ const indexFile = path_1.default.join(caRootDir, "index.txt");
103
+ if (!fs_1.default.existsSync(indexFile)) {
104
+ fs_1.default.writeFileSync(indexFile, "");
105
+ }
106
+ }
107
+ construct_default_files();
108
+ if (fs_1.default.existsSync(path_1.default.join(caRootDir, "private/cakey.pem")) && !config.forceCA) {
109
+ // certificate already exists => do not overwrite
110
+ (0, toolbox_1.debugLog)("CA private key already exists ... skipping");
111
+ return callback();
112
+ }
113
+ // tslint:disable:no-empty
114
+ (0, toolbox_1.displayTitle)("Create Certificate Authority (CA)", (_err) => {
115
+ /** */
116
+ _err;
117
+ });
118
+ const indexFileAttr = path_1.default.join(caRootDir, "index.txt.attr");
119
+ if (!fs_1.default.existsSync(indexFileAttr)) {
120
+ fs_1.default.writeFileSync(indexFileAttr, "unique_subject = no");
121
+ }
122
+ const caConfigFile = certificateAuthority.configFile;
123
+ // eslint-disable-next-line no-constant-condition
124
+ if (1 || !fs_1.default.existsSync(caConfigFile)) {
125
+ let data = exports.configurationFileTemplate; // inlineText(configurationFile);
126
+ data = data.replace(/%%ROOT_FOLDER%%/, (0, toolbox_1.make_path)(caRootDir));
127
+ fs_1.default.writeFileSync(caConfigFile, data);
128
+ }
129
+ // http://www.akadia.com/services/ssh_test_certificate.html
130
+ const subjectOpt = ' -subj "' + subject.toString() + '" ';
131
+ const options = { cwd: caRootDir };
132
+ (0, with_openssl_1.processAltNames)({});
133
+ const configFile = (0, with_openssl_1.generateStaticConfig)("conf/caconfig.cnf", options);
134
+ const configOption = " -config " + q(n(configFile));
135
+ const keySize = certificateAuthority.keySize;
136
+ const privateKeyFilename = path_1.default.join(caRootDir, "private/cakey.pem");
137
+ const csrFilename = path_1.default.join(caRootDir, "private/cakey.csr");
138
+ const tasks = [
139
+ (callback) => (0, toolbox_1.displayTitle)("Generate the CA private Key - " + keySize, callback),
140
+ // The first step is to create your RSA Private Key.
141
+ // This key is a 1025,2048,3072 or 2038 bit RSA key which is encrypted using
142
+ // Triple-DES and stored in a PEM format so that it is readable as ASCII text.
143
+ (callback) => (0, create_private_key_1.generatePrivateKeyFileCallback)(privateKeyFilename, keySize, callback),
144
+ (callback) => (0, toolbox_1.displayTitle)("Generate a certificate request for the CA key", callback),
145
+ // Once the private key is generated a Certificate Signing Request can be generated.
146
+ // The CSR is then used in one of two ways. Ideally, the CSR will be sent to a Certificate Authority, such as
147
+ // Thawte or Verisign who will verify the identity of the requestor and issue a signed certificate.
148
+ // The second option is to self-sign the CSR, which will be demonstrated in the next section
149
+ (callback) => (0, with_openssl_1.execute_openssl)("req -new" +
150
+ " -sha256 " +
151
+ " -text " +
152
+ " -extensions v3_ca" +
153
+ configOption +
154
+ " -key " +
155
+ q(n(privateKeyFilename)) +
156
+ " -out " +
157
+ q(n(csrFilename)) +
158
+ " " +
159
+ subjectOpt, options, callback),
160
+ // xx // Step 3: Remove Passphrase from Key
161
+ // xx execute("cp private/cakey.pem private/cakey.pem.org");
162
+ // xx execute(openssl_path + " rsa -in private/cakey.pem.org -out private/cakey.pem -passin pass:"+paraphrase);
163
+ (callback) => (0, toolbox_1.displayTitle)("Generate CA Certificate (self-signed)", callback),
164
+ (callback) => (0, with_openssl_1.execute_openssl)(" x509 -sha256 -req -days 3650 " +
165
+ " -text " +
166
+ " -extensions v3_ca" +
167
+ " -extfile " +
168
+ q(n(configFile)) +
169
+ " -in private/cakey.csr " +
170
+ " -signkey " +
171
+ q(n(privateKeyFilename)) +
172
+ " -out public/cacert.pem", options, callback),
173
+ (callback) => (0, toolbox_1.displaySubtitle)("generate initial CRL (Certificate Revocation List)", callback),
174
+ (callback) => regenerateCrl(certificateAuthority.revocationList, configOption, options, callback),
175
+ (callback) => (0, toolbox_1.displayTitle)("Create Certificate Authority (CA) ---> DONE", callback),
176
+ ];
177
+ async_1.default.series(tasks, callback);
178
+ }
179
+ function regenerateCrl(revocationList, configOption, options, callback) {
180
+ const tasks = [
181
+ (callback) => (0, toolbox_1.displaySubtitle)("regenerate CRL (Certificate Revocation List)", callback),
182
+ (callback) =>
183
+ // produce a CRL in PEM format
184
+ (0, with_openssl_1.execute_openssl)("ca -gencrl " + configOption + " -out crl/revocation_list.crl", options, callback),
185
+ (callback) => (0, with_openssl_1.execute_openssl)("crl " + " -in crl/revocation_list.crl -out crl/revocation_list.der " + " -outform der", options, callback),
186
+ (callback) => (0, toolbox_1.displaySubtitle)("Display (Certificate Revocation List)", callback),
187
+ (callback) => (0, with_openssl_1.execute_openssl)("crl " + " -in " + q(n(revocationList)) + " -text " + " -noout", options, callback),
188
+ ];
189
+ async_1.default.series(tasks, callback);
190
+ }
191
+ class CertificateAuthority {
192
+ constructor(options) {
193
+ (0, assert_1.default)(Object.prototype.hasOwnProperty.call(options, "location"));
194
+ (0, assert_1.default)(Object.prototype.hasOwnProperty.call(options, "keySize"));
195
+ this.location = options.location;
196
+ this.keySize = options.keySize || 2048;
197
+ this.subject = new node_opcua_crypto_1.Subject(options.subject || exports.defaultSubject);
198
+ }
199
+ get rootDir() {
200
+ return this.location;
201
+ }
202
+ get configFile() {
203
+ return path_1.default.normalize(path_1.default.join(this.rootDir, "./conf/caconfig.cnf"));
204
+ }
205
+ get caCertificate() {
206
+ // the Certificate Authority Certificate
207
+ return (0, toolbox_1.make_path)(this.rootDir, "./public/cacert.pem");
208
+ }
209
+ /**
210
+ * the file name where the current Certificate Revocation List is stored (in DER format)
211
+ */
212
+ get revocationListDER() {
213
+ return (0, toolbox_1.make_path)(this.rootDir, "./crl/revocation_list.der");
214
+ }
215
+ /**
216
+ * the file name where the current Certificate Revocation List is stored (in PEM format)
217
+ */
218
+ get revocationList() {
219
+ return (0, toolbox_1.make_path)(this.rootDir, "./crl/revocation_list.crl");
220
+ }
221
+ get caCertificateWithCrl() {
222
+ return (0, toolbox_1.make_path)(this.rootDir, "./public/cacertificate_with_crl.pem");
223
+ }
224
+ initialize(callback) {
225
+ (0, assert_1.default)(typeof callback === "function");
226
+ construct_CertificateAuthority(this, callback);
227
+ }
228
+ constructCACertificateWithCRL(callback) {
229
+ (0, assert_1.default)(typeof callback === "function");
230
+ const cacertWithCRL = this.caCertificateWithCrl;
231
+ // note : in order to check if the certificate is revoked,
232
+ // you need to specify -crl_check and have both the CA cert and the (applicable) CRL in your trust store.
233
+ // There are two ways to do that:
234
+ // 1. concatenate cacert.pem and crl.pem into one file and use that for -CAfile.
235
+ // 2. use some linked
236
+ // ( from http://security.stackexchange.com/a/58305/59982)
237
+ if (fs_1.default.existsSync(this.revocationList)) {
238
+ fs_1.default.writeFileSync(cacertWithCRL, fs_1.default.readFileSync(this.caCertificate, "utf8") + fs_1.default.readFileSync(this.revocationList, "utf8"));
239
+ }
240
+ else {
241
+ // there is no revocation list yet
242
+ fs_1.default.writeFileSync(cacertWithCRL, fs_1.default.readFileSync(this.caCertificate));
243
+ }
244
+ callback();
245
+ }
246
+ constructCertificateChain(certificate, callback) {
247
+ (0, assert_1.default)(typeof callback === "function");
248
+ (0, assert_1.default)(fs_1.default.existsSync(certificate));
249
+ (0, assert_1.default)(fs_1.default.existsSync(this.caCertificate));
250
+ (0, toolbox_1.debugLog)(chalk_1.default.yellow(" certificate file :"), chalk_1.default.cyan(certificate));
251
+ // append
252
+ fs_1.default.writeFileSync(certificate, fs_1.default.readFileSync(certificate, "utf8") + fs_1.default.readFileSync(this.caCertificate, "utf8")
253
+ // + fs.readFileSync(this.revocationList)
254
+ );
255
+ callback();
256
+ }
257
+ createSelfSignedCertificate(certificateFile, privateKey, params, callback) {
258
+ (0, assert_1.default)(typeof privateKey === "string");
259
+ (0, assert_1.default)(fs_1.default.existsSync(privateKey));
260
+ (0, assert_1.default)(typeof callback === "function");
261
+ if (!(0, toolbox_1.certificateFileExist)(certificateFile)) {
262
+ return callback();
263
+ }
264
+ (0, toolbox_1.adjustDate)(params);
265
+ (0, toolbox_1.adjustApplicationUri)(params);
266
+ (0, with_openssl_1.processAltNames)(params);
267
+ const csrFile = certificateFile + "_csr";
268
+ (0, assert_1.default)(csrFile);
269
+ const configFile = (0, with_openssl_1.generateStaticConfig)(this.configFile);
270
+ const options = {
271
+ cwd: this.rootDir,
272
+ openssl_conf: (0, toolbox_1.make_path)(configFile),
273
+ };
274
+ const configOption = "";
275
+ const subject = params.subject ? new node_opcua_crypto_1.Subject(params.subject).toString() : "";
276
+ const subjectOptions = subject && subject.length > 1 ? " -subj " + subject + " " : "";
277
+ const tasks = [];
278
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- the certificate signing request", callback));
279
+ tasks.push((callback) => (0, with_openssl_1.execute_openssl)("req " +
280
+ " -new -sha256 -text " +
281
+ configOption +
282
+ subjectOptions +
283
+ " -batch -key " +
284
+ q(n(privateKey)) +
285
+ " -out " +
286
+ q(n(csrFile)), options, callback));
287
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- creating the self-signed certificate", callback));
288
+ tasks.push((callback) => (0, with_openssl_1.execute_openssl)("ca " +
289
+ " -selfsign " +
290
+ " -keyfile " +
291
+ q(n(privateKey)) +
292
+ " -startdate " +
293
+ (0, with_openssl_1.x509Date)(params.startDate) +
294
+ " -enddate " +
295
+ (0, with_openssl_1.x509Date)(params.endDate) +
296
+ " -batch -out " +
297
+ q(n(certificateFile)) +
298
+ " -in " +
299
+ q(n(csrFile)), options, callback));
300
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- dump the certificate for a check", callback));
301
+ tasks.push((callback) => (0, with_openssl_1.execute_openssl)("x509 -in " + q(n(certificateFile)) + " -dates -fingerprint -purpose -noout", {}, callback));
302
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- verify self-signed certificate", callback));
303
+ tasks.push((callback) => (0, with_openssl_1.execute_openssl_no_failure)("verify -verbose -CAfile " + q(n(certificateFile)) + " " + q(n(certificateFile)), options, callback));
304
+ tasks.push((callback) => fs_1.default.unlink(csrFile, callback));
305
+ async_1.default.series(tasks, (err) => {
306
+ callback(err);
307
+ });
308
+ }
309
+ revokeCertificate(certificate, params, callback) {
310
+ (0, assert_1.default)(typeof callback === "function");
311
+ const crlReasons = [
312
+ "unspecified",
313
+ "keyCompromise",
314
+ "CACompromise",
315
+ "affiliationChanged",
316
+ "superseded",
317
+ "cessationOfOperation",
318
+ "certificateHold",
319
+ "removeFromCRL",
320
+ ];
321
+ const configFile = (0, with_openssl_1.generateStaticConfig)("conf/caconfig.cnf", { cwd: this.rootDir });
322
+ const options = {
323
+ cwd: this.rootDir,
324
+ openssl_conf: (0, toolbox_1.make_path)(configFile),
325
+ };
326
+ (0, with_openssl_1.setEnv)("ALTNAME", "");
327
+ const randomFile = path_1.default.join(this.rootDir, "random.rnd");
328
+ (0, with_openssl_1.setEnv)("RANDFILE", randomFile);
329
+ // // tslint:disable-next-line:no-string-literal
330
+ // if (!fs.existsSync((process.env as any)["OPENSSL_CONF"])) {
331
+ // throw new Error("Cannot find OPENSSL_CONF");
332
+ // }
333
+ const configOption = " -config " + q(n(configFile));
334
+ const reason = params.reason || "keyCompromise";
335
+ (0, assert_1.default)(crlReasons.indexOf(reason) >= 0);
336
+ const tasks = [
337
+ (callback) => (0, toolbox_1.displayTitle)("Revoking certificate " + certificate, callback),
338
+ (callback) => (0, toolbox_1.displaySubtitle)("Revoke certificate", callback),
339
+ (callback) => {
340
+ (0, with_openssl_1.execute_openssl_no_failure)("ca -verbose " + configOption + " -revoke " + q(certificate) + " -crl_reason " + reason, options, callback);
341
+ },
342
+ // regenerate CRL (Certificate Revocation List)
343
+ (callback) => regenerateCrl(this.revocationList, configOption, options, callback),
344
+ (callback) => (0, toolbox_1.displaySubtitle)("Verify that certificate is revoked ", callback),
345
+ (callback) => {
346
+ (0, with_openssl_1.execute_openssl_no_failure)("verify -verbose" +
347
+ // configOption +
348
+ " -CRLfile " +
349
+ q(n(this.revocationList)) +
350
+ " -CAfile " +
351
+ q(n(this.caCertificate)) +
352
+ " -crl_check " +
353
+ q(n(certificate)), options, (err, output) => {
354
+ err;
355
+ output;
356
+ callback();
357
+ });
358
+ },
359
+ // produce CRL in DER format
360
+ (callback) => (0, toolbox_1.displaySubtitle)("Produce CRL in DER form ", callback),
361
+ (callback) => (0, with_openssl_1.execute_openssl)("crl " + " -in " + q(n(this.revocationList)) + " -out " + "crl/revocation_list.der " + " -outform der", options, callback),
362
+ // produce CRL in PEM format with text
363
+ (callback) => (0, toolbox_1.displaySubtitle)("Produce CRL in PEM form ", callback),
364
+ (callback) => (0, with_openssl_1.execute_openssl)("crl " +
365
+ " -in " +
366
+ q(n(this.revocationList)) +
367
+ " -out " +
368
+ "crl/revocation_list.pem " +
369
+ " -outform pem" +
370
+ " -text ", options, callback),
371
+ ];
372
+ async_1.default.series(tasks, callback);
373
+ }
374
+ signCertificateRequest(certificate, certificateSigningRequestFilename, params, callback) {
375
+ // istanbul ignore next
376
+ if (!callback) {
377
+ throw new Error("Internal Error");
378
+ }
379
+ (0, with_openssl_1.ensure_openssl_installed)(() => {
380
+ try {
381
+ (0, assert_1.default)(fs_1.default.existsSync(certificateSigningRequestFilename));
382
+ (0, assert_1.default)(typeof callback === "function");
383
+ if (!(0, toolbox_1.certificateFileExist)(certificate)) {
384
+ return callback(null);
385
+ }
386
+ (0, toolbox_1.adjustDate)(params);
387
+ (0, toolbox_1.adjustApplicationUri)(params);
388
+ (0, with_openssl_1.processAltNames)(params);
389
+ const options = { cwd: this.rootDir };
390
+ let configFile;
391
+ const tasks = [];
392
+ let csrInfo;
393
+ // note :
394
+ // subjectAltName is not copied across
395
+ // see https://github.com/openssl/openssl/issues/10458
396
+ tasks.push((callback) => {
397
+ (0, node_opcua_crypto_1.readCertificateSigningRequest)(certificateSigningRequestFilename)
398
+ .then((csr) => {
399
+ csrInfo = (0, node_opcua_crypto_1.exploreCertificateSigningRequest)(csr);
400
+ callback();
401
+ })
402
+ .catch((err) => callback(err));
403
+ });
404
+ tasks.push((callback) => {
405
+ const applicationUri = csrInfo.extensionRequest.subjectAltName.uniformResourceIdentifier[0];
406
+ if (typeof applicationUri !== "string") {
407
+ return callback(new Error("Cannot find applicationUri in CSR"));
408
+ }
409
+ const dns = csrInfo.extensionRequest.subjectAltName.dNSName || [];
410
+ let ip = csrInfo.extensionRequest.subjectAltName.iPAddress || [];
411
+ ip = ip.map(octetStringToIpAddress);
412
+ const params = {
413
+ applicationUri,
414
+ dns,
415
+ ip,
416
+ };
417
+ (0, with_openssl_1.processAltNames)(params);
418
+ configFile = (0, with_openssl_1.generateStaticConfig)("conf/caconfig.cnf", options);
419
+ callback();
420
+ });
421
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- then we ask the authority to sign the certificate signing request", callback));
422
+ tasks.push((callback) => {
423
+ const configOption = " -config " + configFile;
424
+ (0, with_openssl_1.execute_openssl)("ca " +
425
+ configOption +
426
+ " -startdate " +
427
+ (0, with_openssl_1.x509Date)(params.startDate) +
428
+ " -enddate " +
429
+ (0, with_openssl_1.x509Date)(params.endDate) +
430
+ " -batch -out " +
431
+ q(n(certificate)) +
432
+ " -in " +
433
+ q(n(certificateSigningRequestFilename)), options, callback);
434
+ });
435
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- dump the certificate for a check", callback));
436
+ tasks.push((callback) => (0, with_openssl_1.execute_openssl)("x509 -in " + q(n(certificate)) + " -dates -fingerprint -purpose -noout", options, callback));
437
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- construct CA certificate with CRL", callback));
438
+ tasks.push((callback) => {
439
+ this.constructCACertificateWithCRL(callback);
440
+ });
441
+ // construct certificate chain
442
+ // concatenate certificate with CA Certificate and revocation list
443
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- construct certificate chain", callback));
444
+ tasks.push((callback) => {
445
+ this.constructCertificateChain(certificate, callback);
446
+ });
447
+ // todo
448
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- verify certificate against the root CA", callback));
449
+ tasks.push((callback) => {
450
+ this.verifyCertificate(certificate, callback);
451
+ });
452
+ async_1.default.series(tasks, (err) => {
453
+ // istanbul ignore next
454
+ if (err) {
455
+ return callback(err);
456
+ }
457
+ callback(null, certificate);
458
+ });
459
+ }
460
+ catch (err) {
461
+ callback(err);
462
+ }
463
+ });
464
+ }
465
+ verifyCertificate(certificate, callback) {
466
+ // openssl verify crashes on windows! we cannot use it reliably
467
+ // istanbul ignore next
468
+ const isImplemented = false;
469
+ // istanbul ignore next
470
+ if (isImplemented) {
471
+ const options = { cwd: this.rootDir };
472
+ const configFile = (0, with_openssl_1.generateStaticConfig)("conf/caconfig.cnf", options);
473
+ (0, with_openssl_1.setEnv)("OPENSSL_CONF", (0, toolbox_1.make_path)(configFile));
474
+ const configOption = " -config " + configFile;
475
+ configOption;
476
+ (0, with_openssl_1.execute_openssl_no_failure)("verify -verbose " + " -CAfile " + q(n(this.caCertificateWithCrl)) + " " + q(n(certificate)), options, (err) => {
477
+ callback(err ? err : undefined);
478
+ });
479
+ }
480
+ else {
481
+ return callback();
482
+ }
483
+ }
484
+ }
485
+ exports.CertificateAuthority = CertificateAuthority;
486
+ // tslint:disable:no-var-requires
487
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
488
+ const thenify = require("thenify");
489
+ const opts = { multiArgs: false };
490
+ CertificateAuthority.prototype.initialize = thenify.withCallback(CertificateAuthority.prototype.initialize, opts);
491
+ CertificateAuthority.prototype.constructCACertificateWithCRL = thenify.withCallback(CertificateAuthority.prototype.constructCACertificateWithCRL, opts);
492
+ CertificateAuthority.prototype.constructCertificateChain = thenify.withCallback(CertificateAuthority.prototype.constructCertificateChain, opts);
493
+ CertificateAuthority.prototype.createSelfSignedCertificate = thenify.withCallback(CertificateAuthority.prototype.createSelfSignedCertificate, opts);
494
+ CertificateAuthority.prototype.revokeCertificate = thenify.withCallback(CertificateAuthority.prototype.revokeCertificate, opts);
495
+ CertificateAuthority.prototype.verifyCertificate = thenify.withCallback(CertificateAuthority.prototype.verifyCertificate, opts);
496
+ CertificateAuthority.prototype.signCertificateRequest = thenify.withCallback(CertificateAuthority.prototype.signCertificateRequest, opts);
497
+ //# sourceMappingURL=certificate_authority.js.map
@@ -0,0 +1,2 @@
1
+ import { ErrorCallback } from "../toolbox";
2
+ export declare function main(argumentsList: string, _done?: ErrorCallback): void;