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.
- package/dist/lib/ca/certificate_authority.d.ts +61 -0
- package/dist/lib/ca/certificate_authority.js +497 -0
- package/dist/lib/ca/crypto_create_CA.d.ts +2 -0
- package/dist/lib/ca/crypto_create_CA.js +903 -0
- package/dist/lib/ca/index.d.ts +1 -0
- package/dist/lib/ca/index.js +18 -0
- package/dist/lib/ca/templates/ca_config_template.cnf.d.ts +2 -0
- package/dist/lib/ca/templates/ca_config_template.cnf.js +130 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.js +48 -0
- package/dist/lib/misc/applicationurn.d.ts +1 -0
- package/dist/lib/misc/applicationurn.js +47 -0
- package/dist/lib/misc/hostname.d.ts +8 -0
- package/dist/lib/misc/hostname.js +106 -0
- package/dist/lib/misc/subject.d.ts +1 -0
- package/dist/lib/misc/subject.js +6 -0
- package/dist/lib/pki/certificate_manager.d.ts +165 -0
- package/dist/lib/pki/certificate_manager.js +991 -0
- package/dist/lib/pki/templates/simple_config_template.cnf.d.ts +2 -0
- package/dist/lib/pki/templates/simple_config_template.cnf.js +76 -0
- package/dist/lib/pki/toolbox_pfx.d.ts +0 -0
- package/dist/lib/pki/toolbox_pfx.js +19 -0
- package/dist/lib/toolbox/common.d.ts +47 -0
- package/dist/lib/toolbox/common.js +56 -0
- package/dist/lib/toolbox/common2.d.ts +3 -0
- package/dist/lib/toolbox/common2.js +65 -0
- package/dist/lib/toolbox/config.d.ts +5 -0
- package/dist/lib/toolbox/config.js +31 -0
- package/dist/lib/toolbox/debug.d.ts +5 -0
- package/dist/lib/toolbox/debug.js +41 -0
- package/dist/lib/toolbox/display.d.ts +4 -0
- package/dist/lib/toolbox/display.js +78 -0
- package/dist/lib/toolbox/index.d.ts +5 -0
- package/dist/lib/toolbox/index.js +44 -0
- package/dist/lib/toolbox/with_openssl/_create_random_file.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/_create_random_file.js +63 -0
- package/dist/lib/toolbox/with_openssl/_env.d.ts +7 -0
- package/dist/lib/toolbox/with_openssl/_env.js +47 -0
- package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.d.ts +10 -0
- package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.js +92 -0
- package/dist/lib/toolbox/with_openssl/create_private_key.d.ts +11 -0
- package/dist/lib/toolbox/with_openssl/create_private_key.js +118 -0
- package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.js +153 -0
- package/dist/lib/toolbox/with_openssl/execute_openssl.d.ts +15 -0
- package/dist/lib/toolbox/with_openssl/execute_openssl.js +192 -0
- package/dist/lib/toolbox/with_openssl/index.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/index.js +22 -0
- package/dist/lib/toolbox/with_openssl/install_prerequisite.d.ts +9 -0
- package/dist/lib/toolbox/with_openssl/install_prerequisite.js +368 -0
- package/dist/lib/toolbox/with_openssl/toolbox.d.ts +31 -0
- package/dist/lib/toolbox/with_openssl/toolbox.js +132 -0
- package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.d.ts +10 -0
- package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.js +86 -0
- package/dist/lib/toolbox/without_openssl/create_private_key.d.ts +2 -0
- package/dist/lib/toolbox/without_openssl/create_private_key.js +32 -0
- package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.d.ts +3 -0
- package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.js +89 -0
- package/dist/lib/toolbox/without_openssl/index.d.ts +3 -0
- package/dist/lib/toolbox/without_openssl/index.js +42 -0
- 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
|