node-opcua-client
Version:
pure nodejs OPCUA SDK - module client
130 lines • 8.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyIsOPCUAValidCertificate = verifyIsOPCUAValidCertificate;
exports.performCertificateSanityCheck = performCertificateSanityCheck;
const web_1 = require("node-opcua-crypto/web");
const node_opcua_debug_1 = require("node-opcua-debug");
const _doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
const _debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename);
const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename);
function verifyIsOPCUAValidCertificate(certificate, certificateFile, type, applicationUri) {
const certificateInfo = (0, web_1.exploreCertificate)(certificate);
const now = new Date();
if (certificateInfo.tbsCertificate.validity.notBefore.getTime() > now.getTime()) {
// check that certificate is active
// certificate is not active yet
warningLog(`[NODE-OPCUA-W02] The certificate is not active yet\n` +
`notBefore ${certificateInfo.tbsCertificate.validity.notBefore.toISOString()}\n` +
`certificateFile ${certificateFile}`);
}
// check that certificate has not expired
if (certificateInfo.tbsCertificate.validity.notAfter.getTime() <= now.getTime()) {
// certificate is obsolete
warningLog(`[NODE-OPCUA-W03] The certificate has expired\n` +
`Please regenerate a valid certificate\n` +
`notAfter = ${certificateInfo.tbsCertificate.validity.notAfter.toISOString()}\n` +
`certificateFile= ${certificateFile}`);
}
else {
const tenDays = 10 * 24 * 60 * 60 * 1000;
if (certificateInfo.tbsCertificate.validity.notAfter.getTime() <= now.getTime() + tenDays) {
// certificate is going to expired very soon
warningLog(`[NODE-OPCUA-W05] The certificate is about to expire in less than 10 days.\n` +
`Please regenerate a valid certificate as soon as possible\n` +
`notAfter = ${certificateInfo.tbsCertificate.validity.notAfter.toISOString()}\n` +
`certificateFile= ${certificateFile}\n`);
}
}
// check that server certificate matches Application URI
const uniformResourceIdentifier = certificateInfo?.tbsCertificate?.extensions?.subjectAltName?.uniformResourceIdentifier;
if (!uniformResourceIdentifier) {
warningLog(`[NODE-OPCUA-W14] The certificate subjectAltName uniformResourceIdentifier is missing.\n` +
`Please regenerate a specific certificate with a uniformResourceIdentifier that matches your ${type} applicationUri\n` +
`applicationUri = ${applicationUri}\n` +
`certificateFile = ${certificateFile}\n`);
return;
}
else if (uniformResourceIdentifier[0] !== applicationUri) {
warningLog(`[NODE-OPCUA-W06] The certificate subjectAltName does not match the ${type} applicationUri\n` +
`Please regenerate a specific certificate that matches your ${type} applicationUri\n` +
`certificate subjectAltName = ${uniformResourceIdentifier[0]}\n` +
`${type} applicationUri = ${applicationUri}\n` +
`certificateFile = ${certificateFile}\n`);
}
const keyUsage = certificateInfo.tbsCertificate.extensions?.keyUsage;
if (!keyUsage) {
warningLog(`[NODE-OPCUA-W15] The certificate keyUsage is missing\ncertificateFile = ${certificateFile}`);
}
else {
// spec says that certificate shall include digitalSignature, nonRepudiation, keyEncipherment and dataEncipherment.
// Other key uses are allowed.
if (!keyUsage.digitalSignature || !keyUsage.nonRepudiation || !keyUsage.keyEncipherment || !keyUsage.dataEncipherment) {
warningLog(`[NODE-OPCUA-W16] The certificate keyUsage must include digitalSignature, nonRepudiation, keyEncipherment and dataEncipherment.\n` +
`see https://reference.opcfoundation.org/v104/Core/docs/Part6/6.2.2/\n` +
`certificateFile = ${certificateFile}`);
warningLog(`keyUsage = ${JSON.stringify(keyUsage, null, " ")}`);
}
}
const extKeyUsage = certificateInfo.tbsCertificate.extensions?.extKeyUsage;
if (!extKeyUsage) {
warningLog(`[NODE-OPCUA-W17] The certificate extKeyUsage is missing\ncertificateFile = ${certificateFile}`);
}
else {
// spec says that certificate shall include digitalSignature, nonRepudiation, keyEncipherment and dataEncipherment.
// Other key uses are allowed.
if (!extKeyUsage.clientAuth && !extKeyUsage.serverAuth) {
warningLog(`[NODE-OPCUA-W18] The certificate extKeyUsage must include clientAuth and/or serverAuth.\n` +
`see https://reference.opcfoundation.org/v104/Core/docs/Part6/6.2.2/\n` +
`certificateFile = ${certificateFile}`);
}
}
const keyLengthInBits = certificateInfo.tbsCertificate.subjectPublicKeyInfo.keyLength * 8;
if (keyLengthInBits < 1024) {
errorLog(`[NODE-OPCUA-W19] The public key length shall be greater than or equal to 1024 bits. key length is ${keyLengthInBits}.\n` +
`see https://reference.opcfoundation.org/v104/GDS/docs/7.6.3/\n` +
`certificateFile = ${certificateFile}`);
}
else if (keyLengthInBits < 2048) {
warningLog(`[NODE-OPCUA-W23] key lengths less than 2048 are considered insecure. key length is ${keyLengthInBits}\n` +
`see https://reference.opcfoundation.org/v104/Core/docs/Part2/6.8/\n` +
`certificateFile = ${certificateFile}`);
}
}
async function performCertificateSanityCheck(secureObject, serverOrClient, certificateManager, applicationUri) {
// verify that certificate is matching private key, and inform the developer if not
const certificate = secureObject.getCertificate();
const privateKey = secureObject.getPrivateKey();
//
if (!(0, web_1.publicKeyAndPrivateKeyMatches)(certificate, privateKey)) {
errorLog("[NODE-OPCUA-E01] Configuration error : the certificate and the private key do not match !");
errorLog(" please check the configuration of the OPCUA Server");
errorLog(" privateKey= ", secureObject.privateKeyFile);
errorLog(" certificateManager.privateKey= ", certificateManager.privateKey);
errorLog(" certificateFile= ", secureObject.certificateFile);
throw new Error("[NODE-OPCUA-E01] Configuration error : the certificate and the private key do not match ! please fix your configuration");
}
// verify that the certificate provided has the right key length ( at least 2048)
const privateKeyInfo = (0, web_1.explorePrivateKey)(privateKey);
const keyLengthInBits = privateKeyInfo.modulus.length * 8;
if (keyLengthInBits <= 1024) {
warningLog(`[NODE-OPCUA-W04] The public/private key pair uses a key length which is equal or lower than 1024 bits. ( key length was ${keyLengthInBits} )\n` +
`OPCUA version 1.04 requires that security key length are greater or equal to 2048 bits.\n` +
`The ${serverOrClient} is operating at risk. `);
}
const options = {
acceptOutdatedCertificate: false,
acceptOutDatedIssuerCertificate: false,
acceptPendingCertificate: false
};
const status = await certificateManager.verifyCertificate(certificate, options);
// BadCertificateUntrusted is expected for the application's own
// certificate — it does not need to be in its own trust list.
// Only warn about genuinely problematic statuses (expired,
// revoked, invalid signature, etc.).
if (status !== "Good" && status !== "BadCertificateUntrusted") {
warningLog("[NODE-OPCUA-W35] Warning: the certificate status is = ", status, " file = ", secureObject.certificateFile);
}
verifyIsOPCUAValidCertificate(certificate, secureObject.certificateFile, serverOrClient, applicationUri);
}
//# sourceMappingURL=verify.js.map