From dafd07ff44da8f8dc3beaf6e0ab154da9f6306ea Mon Sep 17 00:00:00 2001 From: Etienne Rossignon Date: Thu, 17 Aug 2023 10:36:05 +0200 Subject: [PATCH] Fix issue with netscape_comment causing random crashes this commit fixes node opcua issue 1289 --- package.json | 5 +- source/x509/create_self_signed_certificate.ts | 6 +- .../generate_private_key_filename.ts | 15 + test-fixtures/peculiar_cert_in_base64.txt | 110 ++++ test/test_crypto_explore_certificate.ts | 61 ++- test/test_jsrsasign.ts | 41 ++ test/test_peculiar_edge_case.ts | 477 ++++++++++++++++++ 7 files changed, 702 insertions(+), 13 deletions(-) create mode 100644 test-fixtures/peculiar_cert_in_base64.txt create mode 100644 test/test_jsrsasign.ts create mode 100644 test/test_peculiar_edge_case.ts diff --git a/package.json b/package.json index e6a1225..c3c6cfe 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "cost-of-modules": "npx cost-of-modules --no-install", "release-it": "npx release-it", "prettier-format": "prettier --config .prettierrc.js source/**/*.ts test/**/*.ts --write", - "ncu": "npx npm-check-updates -u -x env-paths,chalk" + "ncu": "npx npm-check-updates -u -x env-paths,chalk", + "experiment": "tsc test/test_peculiar_edge_case.ts -t es2021 -m nodenext" }, "keywords": [ "OPCUA", @@ -56,7 +57,6 @@ "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", "cross-env": "^7.0.3", - "crypto": "^1.0.1", "esbuild-plugin-polyfill-node": "^0.3.0", "eslint": "^8.47.0", "eslint-config-prettier": "^9.0.0", @@ -76,6 +76,7 @@ "assert": "^2.0.0", "better-assert": "^1.0.2", "chalk": "^4.1.2", + "crypto": "^1.0.1", "hexy": "0.3.5", "jsrsasign": "^10.8.6", "sshpk": "^1.17.0" diff --git a/source/x509/create_self_signed_certificate.ts b/source/x509/create_self_signed_certificate.ts index 7b72f26..52865b8 100644 --- a/source/x509/create_self_signed_certificate.ts +++ b/source/x509/create_self_signed_certificate.ts @@ -25,7 +25,8 @@ import { CertificatePurpose } from "../common.js"; import { getCrypto, x509 } from "./_crypto.js"; import { getAttributes } from "./_get_attributes.js"; import { buildPublicKey } from "./_build_public_key.js"; - +import { AsnConvert, AsnUtf8StringConverter } from "@peculiar/asn1-schema"; + export interface CreateSelfSignCertificateOptions { privateKey: CryptoKey; notBefore?: Date; @@ -75,6 +76,7 @@ export async function createSelfSignedCertificate({ // https://opensource.apple.com/source/OpenSSH/OpenSSH-186/osslshim/heimdal-asn1/rfc2459.asn1.auto.html const ID_NETSCAPE_COMMENT = "2.16.840.1.113730.1.13"; + const s = new Subject(subject || ""); const s1 = s.toStringInternal(", "); const name = s1; @@ -91,7 +93,7 @@ export async function createSelfSignedCertificate({ keys, extensions: [ - new x509.Extension(ID_NETSCAPE_COMMENT, false, Buffer.from(nsComment, "ascii")), + new x509.Extension(ID_NETSCAPE_COMMENT, false, AsnConvert.serialize(AsnUtf8StringConverter.toASN(nsComment))), // new x509.BasicConstraintsExtension(true, 2, true), basicConstraints, new x509.ExtendedKeyUsageExtension(keyUsageExtension, true), diff --git a/source_nodejs/generate_private_key_filename.ts b/source_nodejs/generate_private_key_filename.ts index d794894..fbb4ff1 100644 --- a/source_nodejs/generate_private_key_filename.ts +++ b/source_nodejs/generate_private_key_filename.ts @@ -31,3 +31,18 @@ export async function generatePrivateKeyFile(privateKeyFilename: string, modulus privateKeyPem.privPem = ""; privateKeyPem.privDer = new Uint8Array(0); } + +/** + * alternate function to generate PrivateKeyFile, using jsrsasign. + * + * This function is slower than generatePrivateKeyFile + */ +export async function generatePrivateKeyFileAlternate(privateKeyFilename: string, modulusLength: 2048 | 3072 | 4096) { + const rs = require("jsrsasign"); + const kp = rs.KEYUTIL.generateKeypair("RSA", modulusLength); + const prv = kp.prvKeyObj; + const pub = kp.pubKeyObj; + const prvpem = rs.KEYUTIL.getPEM(prv, "PKCS8PRV"); + const pubpem = rs.KEYUTIL.getPEM(pub, "PKCS8PUB"); + await fs.promises.writeFile(privateKeyFilename, prvpem, "utf-8"); +} diff --git a/test-fixtures/peculiar_cert_in_base64.txt b/test-fixtures/peculiar_cert_in_base64.txt new file mode 100644 index 0000000..c99620b --- /dev/null +++ b/test-fixtures/peculiar_cert_in_base64.txt @@ -0,0 +1,110 @@ +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 +kRA= diff --git a/test/test_crypto_explore_certificate.ts b/test/test_crypto_explore_certificate.ts index a137901..54a2b27 100644 --- a/test/test_crypto_explore_certificate.ts +++ b/test/test_crypto_explore_certificate.ts @@ -25,7 +25,17 @@ import should from "should"; import path from "path"; import fs from "fs"; -import { readCertificate, exploreCertificate, combine_der, split_der } from ".."; +import { + readCertificate, + exploreCertificate, + combine_der, + split_der, + CertificatePurpose, + createSelfSignedCertificate, + pemToPrivateKey, + generatePrivateKeyFile, + convertPEMtoDER, +} from ".."; describe(" exploring Certificates", function (this: Mocha.Suite) { this.timeout(200000); @@ -169,11 +179,50 @@ describe(" exploring Certificates", function (this: Mocha.Suite) { certificate_info.tbsCertificate.version.should.eql(3); should(certificate_info.tbsCertificate.extensions).not.eql(null); - (certificate_info.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey - .modulus as any) = certificate_info.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.modulus.toString("base64"); + (certificate_info.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.modulus as any) = + certificate_info.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.modulus.toString("base64"); // console.log(JSON.stringify(certificate_info, null, " ")); }); + + it("PEC-1: investigate certificate generated by @peculiar/webcrypto", () => { + const content = fs.readFileSync(path.join(__dirname, "../test-fixtures/peculiar_cert_in_base64.txt"), "utf-8"); + const certificate = Buffer.from(content, "base64"); + const info = exploreCertificate(certificate); + }); + + it("PEC-2 create a certificate with subtle and explore it with @peculiar/x509", async () => { + // generate private key , not using SSL + const privateKeyFilename = path.join(__dirname, "../tmp/pec2-privatekey.pem"); + + await generatePrivateKeyFile(privateKeyFilename, 2048); + + const privateKeyPem = await fs.promises.readFile(privateKeyFilename, "utf-8"); + const privateKey = await pemToPrivateKey(privateKeyPem); + + const startDate = new Date(2020, 1, 20); + const endDate = new Date(2021, 1, 2); + const validity = 365; + const dns: string[] = []; + const ip: string[] = []; + const subject = "CN=TOTO"; + const applicationUri = "uri:application"; + const purpose = CertificatePurpose.ForApplication; + const { cert } = await createSelfSignedCertificate({ + privateKey, + notBefore: startDate, + notAfter: endDate, + validity: validity, + dns, + ip, + subject, + applicationUri: applicationUri, + purpose, + }); + + const certificateDer = convertPEMtoDER(cert); + const info = exploreCertificate(certificateDer); + }); }); describe("exploring certificate chains", () => { @@ -256,7 +305,6 @@ describe("exploring certificate chains", () => { describe("explore ECC certificates", () => { it("should extract information from a prime256v1 ECC certificate", () => { - //generated by following command lines //openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem //openssl ec -in private-key.pem -pubout -out public-key.pem @@ -268,12 +316,10 @@ describe("explore ECC certificates", () => { console.log(" Version : ", certificate_info.tbsCertificate.version); console.log(" issuer.countryName : ", certificate_info.tbsCertificate.issuer.countryName); - certificate_info.tbsCertificate.version.should.eql(3); certificate_info.tbsCertificate.subjectPublicKeyInfo.keyLength.should.eql(64); }); it("should do the same but with a brainpool256r1 certificate", () => { - //generated by following command lines //openssl ecparam -name brainpoolP256r1 -genkey -noout -out private-key.pem //openssl ec -in private-key.pem -pubout -out public-key.pem @@ -286,7 +332,6 @@ describe("explore ECC certificates", () => { certificate_info.tbsCertificate.subjectPublicKeyInfo.keyLength.should.eql(64); }); it("should do the same but with a brainpoolP384r1 certificate", () => { - //generated by following command lines //openssl ecparam -name brainpoolP384r1 -genkey -noout -out private-key.pem //openssl ec -in private-key.pem -pubout -out public-key.pem @@ -298,7 +343,5 @@ describe("explore ECC certificates", () => { certificate_info.tbsCertificate.subjectPublicKeyInfo.algorithm.should.eql("brainpoolP384r1"); certificate_info.tbsCertificate.subject.organizationName!.should.eql("Sterfive"); certificate_info.tbsCertificate.subjectPublicKeyInfo.keyLength.should.eql(96); - }); - }); diff --git a/test/test_jsrsasign.ts b/test/test_jsrsasign.ts new file mode 100644 index 0000000..6d4647d --- /dev/null +++ b/test/test_jsrsasign.ts @@ -0,0 +1,41 @@ +// based on +// https://github.com/kjur/jsrsasign/wiki/Tutorial-for-generating-X.509-certificate + +const rs = require("jsrsasign"); + +describe("testing X509 certificate creating with jsrsasign", () =>{ + it("should create an X509 certificate using jsrsasign", ()=>{ + // STEP1. generate a key pair + const kp = rs.KEYUTIL.generateKeypair("RSA", 2048); + const prv = kp.prvKeyObj; + const pub = kp.pubKeyObj; + const prvpem = rs.KEYUTIL.getPEM(prv, "PKCS8PRV"); + const pubpem = rs.KEYUTIL.getPEM(pub, "PKCS8PUB"); + + // STEP2. specify certificate parameters + const x = new rs.KJUR.asn1.x509.Certificate({ + version: 3, + serial: { int: 4 }, + issuer: { str: "/CN=UserCA" }, + notbefore: "201231235959Z", + notafter: "221231235959Z", + subject: { str: "/CN=User1" }, + sbjpubkey: pub, // can specify public key object or PEM string + ext: [ + { extname: "basicConstraints", cA: false }, + { extname: "keyUsage", critical: true, names: ["digitalSignature"] }, + { extname: "cRLDistributionPoints", array: [{ fulluri: "http://example.com/a.crl" }] }, + ], + sigalg: "SHA256withRSA", + cakey: prv, // can specify private key object or PEM string + }); + + // you can modify any fields until the certificate is signed. + x.params.subject = { str: "/CN=User2" }; + + // STEP3. show PEM strings of keys and a certificate + console.log(prvpem); + console.log(pubpem); + console.log(x.getPEM()); // certificate object is signed automatically with "cakey" value + }); +}) diff --git a/test/test_peculiar_edge_case.ts b/test/test_peculiar_edge_case.ts new file mode 100644 index 0000000..8df7b18 --- /dev/null +++ b/test/test_peculiar_edge_case.ts @@ -0,0 +1,477 @@ +import * as path from "path"; +import * as fs from "fs"; +import { tmpdir } from "os"; +import { Crypto as PeculiarWebCrypto } from "@peculiar/webcrypto"; +import * as x509 from "@peculiar/x509"; +import { AsnConvert, AsnUtf8StringConverter } from "@peculiar/asn1-schema"; + +const privateKeyFilename = path.join(tmpdir(), "_private_key.pem"); +console.log(privateKeyFilename); + +// problematic key generated by crypto.subtle +const privateKey1 = `-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCWZladPrpQPac0 +oHvBzcWBDqZ/nixuRm42SAuYXRw4txneooLfBT1v4TkUWzopgGTeA0B0IkiysAaJ +InrcKT3Tytjl3VOm+l2Ezqk42XiKN7zkjaKYnMfEpB3snbWul+uEOPqE7IZ5x85D +8UQute1CAP7ZycR8mnTA8Tcp6rvrAP8TGLUM/Tvgl2oRGYSvnDNzQuS09AwZir+X +AtFXqsOoANDnVQRfhcG1P5hkZA9DZRleo6p4nUCdk/JClrXr88QvRhWtweUq2DNj +zIRzNA7Kvuh+YmMY6xbznOQw2KelupB3x/agWvSArfFUYnq80HSwoiTJHAmR6JN0 +iizlylPHAgMBAAECggEACNuYIesa2ZteW9jqN2mhRmhdezCoPz2b3UvBWxGvL58h +DlEV67Q8TRJZStvTzhpT8BUGJV2lJT9jjMB6HSAhfKkt791o9HGhMy9hg6Ns2DJP +yEoxl4YytPmErA94LtAFvL2gvryYPIqwmQtkz53cjls1Dao2p0jVgLONyZGbzBhY +zYtwsTOuyJzkJwadRjD0iSLJ+7Mx2J9Ynm1uJiw8vsRttjRpQTGgIDhXitUTtYTW +275fON209g6fHb29JuQQo6d7SHhs8UFzIFL0sSa5ZYCVcMWVpC4sfH0ghq67cp+c +/cQ25MnnyziUvgf5jgzg1QVz0j6FP1ns5iFEp2WFuQKBgQDKkWPWTxVODynbf+cc +UN9ybkCg6uezmbc0M8eqDtM71MEXpxIp4Cm9916DIwxTQQLrcWK0DSPwasvUlCZ6 +X6iedACObIOJ/sS8AtTVmimwYCkXg06Qxu/Zgn3XwJ9LqUjYdl8KMCR1iVFO9sri +6Ka95E18ljBcvfU2yH5GeC3D9QKBgQC+Ej0IPrkyk8EaDbDGrvLM2IlY1bWmT2+1 +t8CB5akwNcGOOwo1UnqVFSUOGAjP2pr7OrSpX+KRmM2wYXwgBXRQKHywJ2INHau8 +WEzQ+K0iDyNX/oZPc2Gq3DVOYLNBZJHmBd3Sz227ePZCBYS7NGXaECKsA0bdjvzv +mqUjQcJfSwKBgHoHS9V2nqb/i3+ndVohffo5YMWPvTT8jNjtuIJBnA6XBBtzkgWX +/I1rz4vAOVSN/WxISeWdZOEX9OKCvQtLRRDvYMZrqHIg//Mi4YQr8qFFzHtVpqag +sSye56BpcYzq1e9Qn8BLcCs+JbUkBuTaslgCiItdDpVP+cCe1zMsgqVhAoGAfyAB +tBcHlP1f5QYNGwX+HOYjDsh5Iw/0Pkz1M6wgeb8qgu+YB0vv8vBehUur8SFcEPYV +yUb5aboSsIqzE1OylL5Pjx34JZ+XsnQ4hHgejC4lzH/O4yrfwwBfotloay9RqdB4 +qbvUv9PKmSPJv8/u42dxWS0j46H0KGl9U9RypXsCgYBTXe6iz0iSF0wIsjlg86su +LDIeWVwx67nM2VQ0cI9dHRAtAY/lTa3DC/dJWHSmiD2ngPPIsgHntWLN0PL1PkHN +ceVSxCWfoBmyvdEAdzdryB6gP/4bK4+DnpyTadEpxxDaBKPGtVQvvKiysrr2eVEI +TU1WcJPHPFOWVmsq4mlY7Q== +-----END PRIVATE KEY----- +`; + +// valid private key generated by jsrsasign +const ok_privateKey = `-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCeksos8uYyXxXQ +a7XsdORlZofL1Vk4g7D6vF4Gs7BadJduTU27RTlgo77nUB6z+IsfdeLdrWbpcmZC +baWVrp/kCnFbZpFm+mpasRRRQ8FUFygAFcSInjtxBi7xhHtUwbvT+HI1HUUd5gbf +zCA6gTMKHF6NdQB4JkNdK/w41E4MeeGegBsvjS+732jYQl9PTQA/6dPWYam+n3h2 +ww7tJJEPCvr+CT6WC73yHhKJQd9aE3mdsXKwO5E1N5CpMWVIr7Qrv8XfjYpS64tC +rudfDtouJ4Xgz0DtHaAAjsT4M7/p/Hf7+XnBwXmZunfjIIQpJqjoaQm45ujnMEwc +dhlS2rbXAgMBAAECggEAXmfT18jQhYKYcRn/GARLiZbuF8svr/avIceNTv4haujo +0rFRKsG+tCsoV3wam1jIMvWzF/jJQQhrmva+UwvAgzo4XIsG28EQGmg8SVlGOvMC +THKpLBDQIKzmu8D8z+v7D+pky/xeDrvIsepL8ajDoyxammri2aUmC81I/uhegwwF +Z343g+d4UHq/CBJbxVIKAqWbLrDNmPM0R/eeDixug+ELkYKsI9bBzBs2uJwIYUwo +wCWYUbnC/oXkhQFPXCuEPk21Labj5KkrWAzDWbH/akFBbSLm2aSsJZAfxrCHLfxQ +LTWJVrPcDvKNT9H/6cb5bgL+4n8zrhcjkyHItetWsQKBgQDvg97EoNUL7hxjmicH +q1/JFZj8pfQKdvg5a/eVh/wi3wyxr+6GWprr3uxJEsCBZnDUlEGIIPJzMJq57zOR +aPKUEuLpwov6IVcTS0TYtQcdfMi77KH+W+DO66jTpDed/PT4nHI78qS3So93AOi+ +X1UT1fSlis1aQie/DgG7CtkUPwKBgQCpfMTrcBdtZsu6/yzlCzVY/ZVwgqWxUy7G +FaQnC96NiPkjr5Oc41z+suosEQEpDzIRChqpuCsONBw37OoYd/eLQarcKCjmoLvK +RO2YLaIisTi1ozwzN44ZSVwxmL6l4dotkGyvO05RUcFzd32bDMU02f+5/3ZChOrU +rV2RqhBXaQKBgHfn05kqTx3G2Y1/ebScNbqsRkeNKQwoHQJaK7s/NZmbgnZd9hJq +v43/rtiyO49MYoX5pojovZevKHaW6oEMQgyhG9oc3AifskDleJToo6Q+eRujTkHR +a00Lqxww5OsB3P2tDH84bP+ZoxLXcK0FeskQXoaVY1KhNdauw20I9D3vAoGAGs18 +Zq8nRUnIVh4cf2wyV4xioZRHl69L6k9p0jLyUveiTp5pfZoHDtBEcAuQX2njxQYQ +CV7ykCB1hfKVYqE2KHOODZrcPPyWNfqIiFRPG6VjDnZuArt6YU1UoxNAswLwedwp +E90RGZMQQK5Y0rhGR4FiC4v2q7ZRXKi971cxlmECgYEAl/9+CYcPue/DheB10aFj +8g33g7Jr2EU9yuN547IPQ/eSE27GtclR9HHXQO4bdD+8+PLwO0rZ5w0b5/ijkOKx +3+VqqjrjVDyuWs0K02egpAOpFzhd1ZBvTRJBxJF1g1EtEo9wsvvoHAHRDRu+5zK9 +mK3gTv2HExCJojJSXLe/A0s= +-----END PRIVATE KEY-----`; + +/** + * + * // dump of the valid key + Certificate SEQUENCE (3 elem) + tbsCertificate TBSCertificate INTEGER 0 + signatureAlgorithm AlgorithmIdentifier SEQUENCE (2 elem) + algorithm OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1) + parameters ANY NULL + signature BIT STRING OCTET STRING (1191 byte) 308204A302010002820101009E92CA2CF2E6325F15D06BB5EC74E4656687CBD55938… + SEQUENCE (9 elem) + INTEGER 0 + INTEGER (2048 bit) 200180369073714278831739278449003774074597054048214887574056452546509… + INTEGER 65537 + INTEGER (2047 bit) 119175999208830279341104588663575999100791205850118257907395221157623… + INTEGER (1024 bit) 168193235618184182434131608698373145194108940032957500147962911293855… + INTEGER (1024 bit) 119018085559721411901982988389191088594078705579204164333890645439912… + INTEGER (1023 bit) 842005564398279573596811564426953403523737768518745536468638350342571… + INTEGER (1021 bit) 188214815865992143819850113930386871872229687752395129340134984716916… + INTEGER (1024 bit) 106736637320045350647169232509502844342836430219909303120677032102485… + + // dump of the problematic key +Certificate SEQUENCE (3 elem) + tbsCertificate TBSCertificate INTEGER 0 + signatureAlgorithm AlgorithmIdentifier SEQUENCE (2 elem) + algorithm OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1) + parameters ANY NULL + signature BIT STRING OCTET STRING (1190 byte) 308204A202010002820101009666569D3EBA503DA734A07BC1CDC5810EA67F9E2C6E… + SEQUENCE (9 elem) + INTEGER 0 + INTEGER (2048 bit) 189862106596719048129752073156943138642968958120308513743641089887432… + INTEGER 65537 + INTEGER (2044 bit) 111819240893238351156021339391667169161562412767256176106276741191618… + INTEGER (1024 bit) 142248037681310581229645699628768184951961139839271120785531410694391… + INTEGER (1024 bit) 133472566435033708035703101049362867736421698786549054494358095666029… + INTEGER (1023 bit) 856913274586591047339123277132576703526775073752906579277778978930155… + INTEGER (1023 bit) 892702291003071932546343901658104976042628182997513573348062683267325… + INTEGER (1023 bit) 585422438810792541886454800476608824191572635894667134864663963894195… + */ +let _crypto: Crypto | undefined; + +declare const crypto: any; +declare const window: any; + +const ignoreCrypto = process.env.IGNORE_SUBTLE_FROM_CRYPTO; + +if (typeof window === "undefined") { + _crypto = require("crypto"); + if (!_crypto?.subtle || ignoreCrypto) { + _crypto = new PeculiarWebCrypto(); + console.warn("using @peculiar/webcrypto"); + } else { + console.warn("using nodejs crypto (native)"); + } + x509.cryptoProvider.set(_crypto); +} else { + // using browser crypto + console.warn("using browser crypto (native)"); + _crypto = crypto; + x509.cryptoProvider.set(crypto); +} + +export function getCrypto(): Crypto { + return _crypto || crypto; + // || require("crypto"); +} + +async function generateKeyPair(modulusLength: 1024 | 2048 | 3072 | 4096 = 2048): Promise { + const crypto = getCrypto(); + const alg: RsaHashedKeyGenParams = { + name: "RSASSA-PKCS1-v1_5", + hash: { name: "SHA-256" }, + publicExponent: new Uint8Array([1, 0, 1]), + modulusLength, + }; + const keys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]); + return keys; +} +async function generatePrivateKey(modulusLength: 1024 | 2048 | 3072 | 4096 = 2048): Promise { + return (await generateKeyPair(modulusLength)).privateKey; +} + +async function generatePrivateKeyFile(privateKeyFilename: string, modulusLength: 1024 | 2048 | 3072 | 4096) { + const keys = await generateKeyPair(modulusLength); + const privateKeyPem = await privateKeyToPEM(keys.privateKey); + await fs.promises.writeFile(privateKeyFilename, privateKeyPem.privPem, "utf-8"); + privateKeyPem.privPem = ""; + privateKeyPem.privDer = new Uint8Array(0); +} + + +async function generatePrivateKeyFileJSRSA(privateKeyFilename:string, modulusLength: 2048 | 3072 | 4096) +{ + const rs = require("jsrsasign"); + const kp = rs.KEYUTIL.generateKeypair("RSA", modulusLength); + const prv = kp.prvKeyObj; + const pub = kp.pubKeyObj; + const prvpem = rs.KEYUTIL.getPEM(prv, "PKCS8PRV"); + const pubpem = rs.KEYUTIL.getPEM(pub, "PKCS8PUB"); + await fs.promises.writeFile(privateKeyFilename, prvpem, "utf-8"); +} + +async function privateKeyToPEM(privateKey: CryptoKey) { + const crypto = getCrypto(); + const privDer = await crypto.subtle.exportKey("pkcs8", privateKey); + const privPem = x509.PemConverter.encode(privDer, "PRIVATE KEY"); + return { privPem, privDer }; +} +async function generatePrivateKeyPem(modulusLength: 2048): Promise { + const cryptoKey = await generatePrivateKey(modulusLength); + const { privPem, privDer } = await privateKeyToPEM(cryptoKey); + return privPem; +} + +async function derToPrivateKey(privDer: ArrayBuffer): Promise { + const crypto = getCrypto(); + + return await crypto.subtle.importKey( + "pkcs8", + privDer, + { + name: "RSASSA-PKCS1-v1_5", + hash: { name: "SHA-256" }, + }, + true, + [ + "sign", + // "encrypt", + // "decrypt", + // "verify", + // "wrapKey", + // "unwrapKey", + // "deriveKey", + // "deriveBits" + ] + ); +} + +async function pemToPrivateKey(pem: string): Promise { + // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey + const privDer = x509.PemConverter.decode(pem); + return derToPrivateKey(privDer[0]); +} + +// async function generatePrivateKeyPEM(modulusLength: 1024 | 2048 | 3072 | 4096 = 2048): Promise { +// const privateKey = await generatePrivateKey(modulusLength); +// return await privateKeyToPem(privateKey); +// } + +// async function generatePrivateKeyDer1(modulusLength: 2048): Promise { +// // generate private key , not using SSL +// const privateKeyFilename = "/tmp/pec2-privatekey.pem"; +// await fs.promises.writeFile(privateKeyFilename, privateKey1, "utf-8"); + +// await generatePrivateKeyFile(privateKeyFilename, 2048); +// await generatePrivateKeyFile(privateKeyFilename, 2048); + +// const privateKeyPem = await fs.promises.readFile(privateKeyFilename, "utf-8"); +// const privateKey = await pemToPrivateKey(privateKeyPem); +// return privateKey; +// } + +enum CertificatePurpose { + ForApplication = 1, +} + +export interface CreateSelfSignCertificateOptions { + privateKey: CryptoKey; + notBefore?: Date; + notAfter?: Date; + validity?: number; + // CN=common/O=Org/C=US/ST=State/L=City + subject?: string; + dns?: string[]; + ip?: string[]; + applicationUri?: string; + purpose: CertificatePurpose; +} + +async function createSelfSignedCertificate({ + privateKey, + notAfter, + notBefore, + validity, + subject, + dns, + ip, + applicationUri, + purpose, +}: CreateSelfSignCertificateOptions) { + const crypto = getCrypto(); + + const publicKey = await buildPublicKey(privateKey); + + const keys = { + privateKey, + publicKey, + }; + + const keyUsageApplication = + x509.KeyUsageFlags.keyEncipherment | + x509.KeyUsageFlags.nonRepudiation | + x509.KeyUsageFlags.dataEncipherment | + x509.KeyUsageFlags.keyCertSign | + x509.KeyUsageFlags.digitalSignature; + + const { nsComment, basicConstraints, keyUsageExtension, usages } = { + // extension: "v3_selfsigned", + basicConstraints: new x509.BasicConstraintsExtension(false, undefined, true), + usages: keyUsageApplication, + keyUsageExtension: [x509.ExtendedKeyUsage.serverAuth, x509.ExtendedKeyUsage.clientAuth], + nsComment: "Self-signed certificate generated by Node-OPCUA Certificate utility V2", + }; + + notBefore = notBefore || new Date(); + validity = validity || 0; + if (!notAfter) { + validity = validity || 365; + } + notAfter = notAfter || new Date(notBefore.getTime() + validity * 24 * 60 * 60 * 1000); + + const alternativeNameExtensions: x509.JsonGeneralName[] = []; + dns && dns.forEach((d) => alternativeNameExtensions.push({ type: "dns", value: d })); + ip && ip.forEach((d) => alternativeNameExtensions.push({ type: "ip", value: d })); + applicationUri && alternativeNameExtensions.push({ type: "url", value: applicationUri }); + + // https://opensource.apple.com/source/OpenSSH/OpenSSH-186/osslshim/heimdal-asn1/rfc2459.asn1.auto.html + const ID_NETSCAPE_COMMENT = "2.16.840.1.113730.1.13"; + + // const s = new Subject(subject || ""); + // const s1 = s.toStringInternal(", "); + const name = subject; + + const cert = await x509.X509CertificateGenerator.createSelfSigned( + { + serialNumber: Date.now().toString(), + name, + notBefore, + notAfter, + + signingAlgorithm: { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } }, + + keys, + + extensions: [ + new x509.Extension( + ID_NETSCAPE_COMMENT, + false, + AsnConvert.serialize(AsnUtf8StringConverter.toASN(nsComment)) + ), + // new x509.BasicConstraintsExtension(true, 2, true), + basicConstraints, + new x509.ExtendedKeyUsageExtension(keyUsageExtension, true), + new x509.KeyUsagesExtension(usages, true), + await x509.SubjectKeyIdentifierExtension.create(keys.publicKey), + new x509.SubjectAlternativeNameExtension(alternativeNameExtensions), + ], + }, + crypto + ); + + return { cert: cert.toString("pem"), der: cert }; +} +async function createSelfSignedCertificate2(privateKey: CryptoKey) { + const startDate = new Date(2020, 1, 20); + const endDate = new Date(2021, 1, 2); + const validity = 365; + const dns: string[] = []; + const ip: string[] = []; + const subject = "CN=TOTO"; + const applicationUri = "uri:application"; + const purpose = CertificatePurpose.ForApplication; + const { cert } = await createSelfSignedCertificate({ + privateKey, + notBefore: startDate, + notAfter: endDate, + validity: validity, + dns, + ip, + subject, + applicationUri: applicationUri, + purpose, + }); + return cert; +} + +// https://stackoverflow.com/questions/56807959/generate-public-key-from-private-key-using-webcrypto-api +async function buildPublicKey(privateKey: CryptoKey): Promise { + const crypto = getCrypto(); + // export private key to JWK + const jwk = await crypto.subtle.exportKey("jwk", privateKey); + // remove private data from JWK + delete jwk.d; + delete jwk.dp; + delete jwk.dq; + delete jwk.q; + delete jwk.qi; + jwk.key_ops = [ + "encrypt", + "sign", + // "wrapKey" + ]; + // import public key + // const publicKey = await crypto.subtle.importKey("jwk", jwk, { name: "RSA-OAEP", hash: { name: "SHA-256" } }, true, [ + const publicKey = await crypto.subtle.importKey("jwk", jwk, { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } }, true, [ + // "encrypt", + // "sign", + // "wrapKey", + ]); + return publicKey; +} +async function testStuff() { + console.log("generatePrivateKeyPem:"); + const privateKeyPem = await generatePrivateKeyPem(2048); + console.log("pemToPrivateKey:"); + const privateKey = await pemToPrivateKey(privateKeyPem); + console.log("buildPublicKey:"); + const publicKey = await buildPublicKey(privateKey); + + const cert = await createSelfSignedCertificate2(privateKey); + console.log("cert = ", cert); + /* + const certificateDer = convertPEMtoDER(cert); + const info = exploreCertificate(certificateDer); + console.log(info); + */ + console.log("IGNORE_SUBTLE_FROM_CRYPTO", process.env.IGNORE_SUBTLE_FROM_CRYPTO); + console.log("nodejs version", process.version); +} + +describe("Test @peculiar/x509 (investigate github node-opcua issue#1289 ", function (this) { + + this.timeout(1000000); + + it("T1 - should create a certificate with a pre-existing private key generated by subtle", async () => { + await testStuff(); + }); + it("T2 - should convert a PEM private key and generate a certificate for it", async () => { + // + const pem = privateKey1; + console.log("convert PEM Private key to DER"); + const privDer = x509.PemConverter.decode(pem); + const privateKey = await derToPrivateKey(privDer[0]); + + await createSelfSignedCertificate2(privateKey); + }); + it("T3 - should convert a PEM private key written in PEM and reloaded and generate a certificate for it", async () => { + // + await generatePrivateKeyFile(privateKeyFilename, 2048); + const privateKeyPem = await fs.promises.readFile(privateKeyFilename, "utf-8"); + const privateKey = await pemToPrivateKey(privateKeyPem); + await createSelfSignedCertificate2(privateKey); + }); + + xit("T4 - messing with Private Key", async () => { + let success = 0; + for (let i = 0; i < 100; i++) { + // + if (!(i % 10)) console.log("i=", i); + await generatePrivateKeyFile(privateKeyFilename, 2048); + const privateKeyPem = await fs.promises.readFile(privateKeyFilename, "utf-8"); + const privateKey = await pemToPrivateKey(privateKeyPem); + + // now back to PEM + const privateKeyPem2 = (await privateKeyToPEM(privateKey)).privPem; + + // should be identical + if (privateKeyPem2.replace(/\r|\n/gm, "") != privateKeyPem.replace(/\r|\n/gm, "")) { + console.log(privateKeyPem); + console.log(privateKeyPem2); + throw new Error(" Found issue here with key "); + } + try { + await createSelfSignedCertificate2(privateKey); + success++; + } catch (err) { + console.log((err as any).message); + // console.log(privateKeyPem2); + console.log(" this private key didn't fit the createSelfSignedCertificate"); + } + } + console.log("success rate", success, "%"); + if (success != 100) { + throw new Error(`success rate should be 100% (was ${success})`); + } + }); + xit("T5 - messing with Private Key directly", async () => { + let success = 0; + for (let i = 0; i < 100; i++) { + // + if (!( i % 10)) console.log("i=", i); + const privateKey = await generatePrivateKey(2048); + try { + await createSelfSignedCertificate2(privateKey); + success++; + } catch (err) { + // console.log(privateKeyPem2); + console.log((err as any).message); + console.log(" this private key didn't fit the createSelfSignedCertificate"); + } + } + if (success != 100) { + throw new Error(`success rate should be 100% (was ${success})`); + } + console.log("success rate", success, "%"); + }); +});