node-opcua-pki 6.14.0 → 6.15.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/bin/pki.mjs +140 -5
- package/dist/bin/pki.mjs.map +1 -1
- package/dist/index.d.mts +76 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +140 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +140 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -120,9 +120,15 @@ function setEnv(varName, value) {
|
|
|
120
120
|
process.env[varName] = value;
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
function hasEnv(varName) {
|
|
124
|
+
return Object.prototype.hasOwnProperty.call(exportedEnvVars, varName);
|
|
125
|
+
}
|
|
123
126
|
function getEnv(varName) {
|
|
124
127
|
return exportedEnvVars[varName];
|
|
125
128
|
}
|
|
129
|
+
function unsetEnv(varName) {
|
|
130
|
+
delete exportedEnvVars[varName];
|
|
131
|
+
}
|
|
126
132
|
function getEnvironmentVarNames() {
|
|
127
133
|
return Object.keys(exportedEnvVars).map((varName) => {
|
|
128
134
|
return { key: varName, pattern: `\\$ENV\\:\\:${varName}` };
|
|
@@ -639,10 +645,17 @@ function openssl_require2DigitYearInDate() {
|
|
|
639
645
|
}
|
|
640
646
|
g_config.opensslVersion = "";
|
|
641
647
|
var _counter = 0;
|
|
648
|
+
function stripConditionalBlocks(template) {
|
|
649
|
+
return template.replace(/\{\{#([A-Z_][A-Z0-9_]*)\}\}([\s\S]*?)\{\{\/\1\}\}\r?\n?/g, (_match, key, content) => {
|
|
650
|
+
const keep = hasEnv(key) && getEnv(key) !== "";
|
|
651
|
+
return keep ? content : "";
|
|
652
|
+
});
|
|
653
|
+
}
|
|
642
654
|
function generateStaticConfig(configPath, options) {
|
|
643
655
|
const prePath = options?.cwd || "";
|
|
644
656
|
const originalFilename = !path3.isAbsolute(configPath) ? path3.join(prePath, configPath) : configPath;
|
|
645
657
|
let staticConfig = fs5.readFileSync(originalFilename, { encoding: "utf8" });
|
|
658
|
+
staticConfig = stripConditionalBlocks(staticConfig);
|
|
646
659
|
for (const envVar of getEnvironmentVarNames()) {
|
|
647
660
|
staticConfig = staticConfig.replace(new RegExp(envVar.pattern, "gi"), getEnv(envVar.key));
|
|
648
661
|
}
|
|
@@ -797,7 +810,9 @@ nsComment = ''OpenSSL Generated Certificate''
|
|
|
797
810
|
#nsSslServerName =
|
|
798
811
|
keyUsage = critical, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement
|
|
799
812
|
extendedKeyUsage = critical,serverAuth ,clientAuth
|
|
800
|
-
|
|
813
|
+
{{#CDP_URL}}crlDistributionPoints = URI:$ENV::CDP_URL
|
|
814
|
+
{{/CDP_URL}}{{#AIA_VALUE}}authorityInfoAccess = $ENV::AIA_VALUE
|
|
815
|
+
{{/AIA_VALUE}}
|
|
801
816
|
[ v3_req ]
|
|
802
817
|
basicConstraints = critical, CA:FALSE
|
|
803
818
|
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyAgreement
|
|
@@ -820,10 +835,9 @@ nsComment = "CA Certificate generated by Node-OPCUA Certificate
|
|
|
820
835
|
#nsCertType = sslCA, emailCA
|
|
821
836
|
#issuerAltName = issuer:copy
|
|
822
837
|
#obj = DER:02:03
|
|
823
|
-
crlDistributionPoints =
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
[ v3_selfsigned]
|
|
838
|
+
{{#CDP_URL}}crlDistributionPoints = URI:$ENV::CDP_URL
|
|
839
|
+
{{/CDP_URL}}{{#AIA_VALUE}}authorityInfoAccess = $ENV::AIA_VALUE
|
|
840
|
+
{{/AIA_VALUE}}[ v3_selfsigned]
|
|
827
841
|
basicConstraints = critical, CA:FALSE
|
|
828
842
|
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyAgreement
|
|
829
843
|
extendedKeyUsage = critical,serverAuth ,clientAuth
|
|
@@ -906,6 +920,7 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
906
920
|
const subjectOpt = ` -subj "${subject.toString()}" `;
|
|
907
921
|
const caCommonName = subject.commonName || "NodeOPCUA-CA";
|
|
908
922
|
setEnv("ALTNAME", `URI:urn:${caCommonName}`);
|
|
923
|
+
certificateAuthority._wireRevocationEnvVars();
|
|
909
924
|
const options = { cwd: caRootDir };
|
|
910
925
|
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
911
926
|
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
@@ -959,6 +974,33 @@ function parseOpenSSLDate(dateStr) {
|
|
|
959
974
|
const sec = raw.substring(10, 12);
|
|
960
975
|
return `${year}-${month}-${day}T${hour}:${min}:${sec}Z`;
|
|
961
976
|
}
|
|
977
|
+
function validateRevocationUrl(url2, fieldName) {
|
|
978
|
+
if (url2 === void 0) {
|
|
979
|
+
return void 0;
|
|
980
|
+
}
|
|
981
|
+
if (url2 === "") {
|
|
982
|
+
throw new Error(`${fieldName} must not be empty \u2014 pass undefined to disable the extension`);
|
|
983
|
+
}
|
|
984
|
+
let parsed;
|
|
985
|
+
try {
|
|
986
|
+
parsed = new URL(url2);
|
|
987
|
+
} catch {
|
|
988
|
+
throw new Error(`${fieldName} is not a valid URL: ${url2}`);
|
|
989
|
+
}
|
|
990
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
991
|
+
throw new Error(`${fieldName} must use http: or https: (got ${parsed.protocol} in ${url2})`);
|
|
992
|
+
}
|
|
993
|
+
if (!parsed.pathname || parsed.pathname === "/") {
|
|
994
|
+
throw new Error(`${fieldName} must include a path component (got ${url2})`);
|
|
995
|
+
}
|
|
996
|
+
const isLoopback = parsed.hostname === "localhost" || parsed.hostname === "::1" || parsed.hostname.startsWith("127.");
|
|
997
|
+
if (isLoopback) {
|
|
998
|
+
console.warn(
|
|
999
|
+
`[node-opcua-pki] ${fieldName} points at loopback (${url2}) \u2014 certificates issued with this URL will be unreachable from any other host.`
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
return url2;
|
|
1003
|
+
}
|
|
962
1004
|
var CertificateAuthority = class {
|
|
963
1005
|
/** RSA key size used when generating the CA private key. */
|
|
964
1006
|
keySize;
|
|
@@ -968,6 +1010,10 @@ var CertificateAuthority = class {
|
|
|
968
1010
|
subject;
|
|
969
1011
|
/** @internal Parent CA (undefined for root CAs). */
|
|
970
1012
|
_issuerCA;
|
|
1013
|
+
/** @internal Configured CDP / AIA URLs (US-202). */
|
|
1014
|
+
_crlDistributionUrl;
|
|
1015
|
+
_ocspResponderUrl;
|
|
1016
|
+
_caIssuersUrl;
|
|
971
1017
|
constructor(options) {
|
|
972
1018
|
assert7(Object.prototype.hasOwnProperty.call(options, "location"));
|
|
973
1019
|
assert7(Object.prototype.hasOwnProperty.call(options, "keySize"));
|
|
@@ -975,6 +1021,93 @@ var CertificateAuthority = class {
|
|
|
975
1021
|
this.keySize = options.keySize || 2048;
|
|
976
1022
|
this.subject = new Subject2(options.subject || defaultSubject);
|
|
977
1023
|
this._issuerCA = options.issuerCA;
|
|
1024
|
+
if (options.crlDistributionUrl !== void 0) {
|
|
1025
|
+
this.setCrlDistributionUrl(options.crlDistributionUrl);
|
|
1026
|
+
}
|
|
1027
|
+
if (options.ocspResponderUrl !== void 0) {
|
|
1028
|
+
this.setOcspResponderUrl(options.ocspResponderUrl);
|
|
1029
|
+
}
|
|
1030
|
+
if (options.caIssuersUrl !== void 0) {
|
|
1031
|
+
this.setCaIssuersUrl(options.caIssuersUrl);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Public URL where the CRL produced by this CA is reachable, or
|
|
1036
|
+
* `undefined` if no CDP extension should be emitted on issued certs.
|
|
1037
|
+
*/
|
|
1038
|
+
get crlDistributionUrl() {
|
|
1039
|
+
return this._crlDistributionUrl;
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Public URL of the OCSP responder, or `undefined` if no AIA OCSP
|
|
1043
|
+
* leg should be emitted on issued certs.
|
|
1044
|
+
*/
|
|
1045
|
+
get ocspResponderUrl() {
|
|
1046
|
+
return this._ocspResponderUrl;
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Public URL where the issuer's certificate can be fetched, or
|
|
1050
|
+
* `undefined` if no AIA caIssuers leg should be emitted.
|
|
1051
|
+
*/
|
|
1052
|
+
get caIssuersUrl() {
|
|
1053
|
+
return this._caIssuersUrl;
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Configure the URL embedded as `crlDistributionPoints` in every
|
|
1057
|
+
* subsequently-issued certificate. Pass `undefined` to disable
|
|
1058
|
+
* the extension entirely. Validated synchronously — throws on
|
|
1059
|
+
* empty string, non-http(s) protocol, missing path. Warns (does
|
|
1060
|
+
* not throw) when the URL points at loopback.
|
|
1061
|
+
*
|
|
1062
|
+
* @see US-202
|
|
1063
|
+
*/
|
|
1064
|
+
setCrlDistributionUrl(url2) {
|
|
1065
|
+
this._crlDistributionUrl = validateRevocationUrl(url2, "crlDistributionUrl");
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Configure the OCSP responder URL embedded as the `OCSP` leg of
|
|
1069
|
+
* the `authorityInfoAccess` extension on every subsequently-issued
|
|
1070
|
+
* certificate. Pass `undefined` to disable.
|
|
1071
|
+
*
|
|
1072
|
+
* @see US-202
|
|
1073
|
+
*/
|
|
1074
|
+
setOcspResponderUrl(url2) {
|
|
1075
|
+
this._ocspResponderUrl = validateRevocationUrl(url2, "ocspResponderUrl");
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Configure the caIssuers URL embedded as the `caIssuers` leg of
|
|
1079
|
+
* the `authorityInfoAccess` extension on every subsequently-issued
|
|
1080
|
+
* certificate. Pass `undefined` to disable.
|
|
1081
|
+
*
|
|
1082
|
+
* @see US-202
|
|
1083
|
+
*/
|
|
1084
|
+
setCaIssuersUrl(url2) {
|
|
1085
|
+
this._caIssuersUrl = validateRevocationUrl(url2, "caIssuersUrl");
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* @internal
|
|
1089
|
+
* Populate the OpenSSL config substitution env vars (`CDP_URL` and
|
|
1090
|
+
* `AIA_VALUE`) from the configured URLs, or unset them so the
|
|
1091
|
+
* matching `{{#KEY}}...{{/KEY}}` blocks in the templates are
|
|
1092
|
+
* stripped. MUST be called before every `generateStaticConfig`
|
|
1093
|
+
* invocation that signs a certificate.
|
|
1094
|
+
*/
|
|
1095
|
+
_wireRevocationEnvVars() {
|
|
1096
|
+
unsetEnv("CDP_URL");
|
|
1097
|
+
unsetEnv("AIA_VALUE");
|
|
1098
|
+
if (this._crlDistributionUrl) {
|
|
1099
|
+
setEnv("CDP_URL", this._crlDistributionUrl);
|
|
1100
|
+
}
|
|
1101
|
+
const aiaLegs = [];
|
|
1102
|
+
if (this._ocspResponderUrl) {
|
|
1103
|
+
aiaLegs.push(`OCSP;URI:${this._ocspResponderUrl}`);
|
|
1104
|
+
}
|
|
1105
|
+
if (this._caIssuersUrl) {
|
|
1106
|
+
aiaLegs.push(`caIssuers;URI:${this._caIssuersUrl}`);
|
|
1107
|
+
}
|
|
1108
|
+
if (aiaLegs.length > 0) {
|
|
1109
|
+
setEnv("AIA_VALUE", aiaLegs.join(","));
|
|
1110
|
+
}
|
|
978
1111
|
}
|
|
979
1112
|
/** Absolute path to the CA root directory (alias for {@link location}). */
|
|
980
1113
|
get rootDir() {
|
|
@@ -1577,6 +1710,7 @@ var CertificateAuthority = class {
|
|
|
1577
1710
|
async signCACertificateRequest(certFile, csrFile, params) {
|
|
1578
1711
|
const caRootDir = path5.resolve(this.rootDir);
|
|
1579
1712
|
const options = { cwd: caRootDir };
|
|
1713
|
+
this._wireRevocationEnvVars();
|
|
1580
1714
|
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
1581
1715
|
const validity = params.validity ?? 3650;
|
|
1582
1716
|
await execute_openssl(
|
|
@@ -1743,6 +1877,7 @@ var CertificateAuthority = class {
|
|
|
1743
1877
|
ip
|
|
1744
1878
|
};
|
|
1745
1879
|
processAltNames(params);
|
|
1880
|
+
this._wireRevocationEnvVars();
|
|
1746
1881
|
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
1747
1882
|
displaySubtitle("- then we ask the authority to sign the certificate signing request");
|
|
1748
1883
|
const configOption = ` -config ${configFile}`;
|