diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7d354e7..4ed9395 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ name: CI on: # Triggers the workflow on push or pull request events but only for the master branch push: - branches: [master] + branches: [master] pull_request: branches: [master] diff --git a/source/crypto_utils.ts b/source/crypto_utils.ts index a53043d..3517bae 100644 --- a/source/crypto_utils.ts +++ b/source/crypto_utils.ts @@ -63,13 +63,17 @@ export function identifyPemType(rawKey: Buffer | string): undefined | string { return !match ? undefined : match[2]; } +export function removeTrailingLF(str: string): string { + const tmp = str.replace(/(\r|\n)+$/m, "").replace(/\r\n/gm, "\n"); + return tmp; +} export function toPem(raw_key: Buffer | string, pem: string): string { assert(raw_key, "expecting a key"); assert(typeof pem === "string"); let pemType = identifyPemType(raw_key); if (pemType) { - return raw_key instanceof Buffer ? raw_key.toString("utf8").replace(/(\r|\n)+$/gm, "") : raw_key.replace(/(\r|\n)+$/gm, ""); + return raw_key instanceof Buffer ? removeTrailingLF(raw_key.toString("utf8")) : removeTrailingLF(raw_key); } else { pemType = pem; assert(["CERTIFICATE REQUEST", "CERTIFICATE", "RSA PRIVATE KEY", "PUBLIC KEY", "X509 CRL"].indexOf(pemType) >= 0); @@ -80,7 +84,7 @@ export function toPem(raw_key: Buffer | string, pem: string): string { b = b.substring(64); } str += "-----END " + pemType + "-----"; - // str += "\n"; + // no leading \n return str; } } @@ -200,7 +204,7 @@ export function publicEncrypt_native(buffer: Buffer, publicKey: KeyLike, algorit if (algorithm === undefined) { algorithm = PaddingAlgorithm.RSA_PKCS1_PADDING; } - return publicEncrypt1( + return publicEncrypt1( { key: publicKey, padding: algorithm, @@ -260,12 +264,7 @@ export function publicEncrypt_long( return outputBuffer; } -export function privateDecrypt_long( - buffer: Buffer, - privateKey: PrivateKey, - blockSize: number, - paddingAlgorithm?: number -): Buffer { +export function privateDecrypt_long(buffer: Buffer, privateKey: PrivateKey, blockSize: number, paddingAlgorithm?: number): Buffer { paddingAlgorithm = paddingAlgorithm || RSA_PKCS1_PADDING; // istanbul ignore next if (paddingAlgorithm !== RSA_PKCS1_PADDING && paddingAlgorithm !== RSA_PKCS1_OAEP_PADDING) { diff --git a/source/crypto_utils2.ts b/source/crypto_utils2.ts index f56480a..2611660 100644 --- a/source/crypto_utils2.ts +++ b/source/crypto_utils2.ts @@ -31,8 +31,7 @@ import assert from "assert"; import { createPrivateKey as createPrivateKeyFromNodeJSCrypto, KeyObject } from "crypto"; import { PublicKey, PublicKeyPEM, PrivateKeyPEM, PrivateKey } from "./common.js"; -import { toPem } from "./crypto_utils.js"; -import { type } from "os"; +import { removeTrailingLF, toPem } from "./crypto_utils.js"; const jsrsasign = require("jsrsasign"); @@ -71,9 +70,9 @@ export function toPem2(raw_key: Buffer | string | KeyObject | PrivateKey, pem: s if (raw_key instanceof KeyObject) { if (pem === "RSA PRIVATE KEY") { - return raw_key.export({ format: "pem", type: "pkcs1" }).toString(); + return removeTrailingLF(raw_key.export({ format: "pem", type: "pkcs1" }).toString()); } else if (pem === "PRIVATE KEY") { - return raw_key.export({ format: "pem", type: "pkcs8" }).toString(); + return removeTrailingLF(raw_key.export({ format: "pem", type: "pkcs8" }).toString()); } else { throw new Error("Unsupported case!"); } diff --git a/source/x509/_crypto.ts b/source/x509/_crypto.ts index cfa6616..cea584a 100644 --- a/source/x509/_crypto.ts +++ b/source/x509/_crypto.ts @@ -10,9 +10,9 @@ if (typeof window === "undefined") { _crypto = require("crypto"); if (!_crypto?.subtle) { _crypto = new Crypto(); - //xx console.warn("using @peculiar/webcrypto"); + console.warn("using @peculiar/webcrypto"); } else { - //xx console.warn("using nodejs crypto (native)"); + console.warn("using nodejs crypto (native)"); } x509.cryptoProvider.set(_crypto); } else { diff --git a/source_nodejs/generate_private_key_filename.ts b/source_nodejs/generate_private_key_filename.ts index c2d5042..d794894 100644 --- a/source_nodejs/generate_private_key_filename.ts +++ b/source_nodejs/generate_private_key_filename.ts @@ -27,7 +27,7 @@ import { generateKeyPair, privateKeyToPEM } from "../source/index.js"; export 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); + await fs.promises.writeFile(privateKeyFilename, privateKeyPem.privPem, "utf-8"); privateKeyPem.privPem = ""; privateKeyPem.privDer = new Uint8Array(0); } diff --git a/source_nodejs/read.ts b/source_nodejs/read.ts index 43e0273..c3e10ec 100644 --- a/source_nodejs/read.ts +++ b/source_nodejs/read.ts @@ -25,24 +25,14 @@ import assert from "assert"; import fs from "fs"; import path from "path"; import { createPrivateKey, createPublicKey } from "crypto"; -import { - Certificate, - CertificatePEM, - DER, - PEM, - PublicKey, - PublicKeyPEM, - PrivateKeyPEM, - PrivateKey, -} from "../source/common.js"; -import { convertPEMtoDER, identifyPemType, makeSHA1Thumbprint, toPem } from "../source/crypto_utils.js"; -import { toPem2 } from "../source/crypto_utils2.js"; +import { Certificate, CertificatePEM, DER, PEM, PublicKey, PublicKeyPEM, PrivateKeyPEM, PrivateKey } from "../source/common.js"; +import { convertPEMtoDER, identifyPemType, removeTrailingLF, toPem } from "../source/crypto_utils.js"; const sshpk = require("sshpk"); function _readPemFile(filename: string): PEM { assert(typeof filename === "string"); - return fs.readFileSync(filename, "ascii"); + return removeTrailingLF(fs.readFileSync(filename, "utf-8")); } function _readPemOrDerFileAsDER(filename: string): DER { @@ -75,7 +65,6 @@ export function readPublicKey(filename: string): PublicKey { // console.log("createPrivateKey", (crypto as any).createPrivateKey, process.env.NO_CREATE_PRIVATEKEY); - function myCreatePrivateKey(rawKey: string | Buffer): PrivateKey { if (!createPrivateKey || process.env.NO_CREATE_PRIVATEKEY) { // we are not running nodejs or createPrivateKey is not supported in the environment @@ -84,7 +73,7 @@ function myCreatePrivateKey(rawKey: string | Buffer): PrivateKey { assert(["RSA PRIVATE KEY", "PRIVATE KEY"].indexOf(identifyPemType(pemKey) as string) >= 0); return { hidden: pemKey }; } - return { hidden: rawKey }; + return { hidden: ensureTrailingLF(rawKey) }; } // see https://askubuntu.com/questions/1409458/openssl-config-cuases-error-in-node-js-crypto-how-should-the-config-be-updated const backup = process.env.OPENSSL_CONF; @@ -94,7 +83,6 @@ function myCreatePrivateKey(rawKey: string | Buffer): PrivateKey { return { hidden: retValue }; } - export function makePrivateKeyThumbPrint(privateKey: PrivateKey): Buffer { // // .export({ format: "der", type: "pkcs1" }); // if (typeof privateKey === "string") { @@ -102,18 +90,19 @@ export function makePrivateKeyThumbPrint(privateKey: PrivateKey): Buffer { // } else { // return makeSHA1Thumbprint(privateKey.hidden); // } - // to do + // to do return Buffer.alloc(0); } - - +function ensureTrailingLF(str: string): string { + return str.match(/\n$/) ? str : str + "\n"; +} /** * read a DER or PEM certificate from file */ export function readPrivateKey(filename: string): PrivateKey { if (filename.match(/.*\.der/)) { - const der = fs.readFileSync(filename) as Buffer; + const der: Buffer = fs.readFileSync(filename); return myCreatePrivateKey(der); } else { const raw_key: string = _readPemFile(filename); @@ -129,7 +118,7 @@ export function readPublicKeyPEM(filename: string): PublicKeyPEM { return _readPemFile(filename); } /** - * + * * @deprecated */ export function readPrivateKeyPEM(filename: string): PrivateKeyPEM { @@ -155,7 +144,7 @@ export function readPrivateRsaKey(filename: string): PrivateKey { if (filename.substring(0, 1) !== "." && !fs.existsSync(filename)) { filename = __certificate_store + filename; } - const content = fs.readFileSync(filename, "ascii"); + const content = fs.readFileSync(filename, "utf8"); const sshKey = sshpk.parsePrivateKey(content, "auto"); const key = sshKey.toString("pkcs1") as PEM; const hidden = createPrivateKey({ format: "pem", type: "pkcs1", key }); @@ -166,7 +155,7 @@ export function readPublicRsaKey(filename: string): PublicKey { if (filename.substring(0, 1) !== "." && !fs.existsSync(filename)) { filename = __certificate_store + filename; } - const content = fs.readFileSync(filename, "ascii"); + const content = fs.readFileSync(filename, "utf-8"); const sshKey = sshpk.parseKey(content, "ssh"); const key = sshKey.toString("pkcs1") as PEM; return createPublicKey({ format: "pem", type: "pkcs1", key }); diff --git a/test/test_create_self_signed_certificate.ts b/test/test_create_self_signed_certificate.ts index f420720..30ba4c5 100644 --- a/test/test_create_self_signed_certificate.ts +++ b/test/test_create_self_signed_certificate.ts @@ -36,6 +36,7 @@ import { pemToPrivateKey, privateKeyToPEM, createSelfSignedCertificate, + removeTrailingLF, } from ".."; const tmpTestFolder = os.tmpdir(); @@ -88,7 +89,7 @@ describe("creating X509 self-signed certificates", function () { await fs.promises.writeFile(tmpPrivateKeyFilename, privPem); } - const privateKeyPem = await fs.promises.readFile(tmpPrivateKeyFilename, "utf-8"); + const privateKeyPem = removeTrailingLF(await fs.promises.readFile(tmpPrivateKeyFilename, "utf-8")); const privateKey = await pemToPrivateKey(privateKeyPem); const { cert } = await createSelfSignedCertificate({ @@ -109,4 +110,27 @@ describe("creating X509 self-signed certificates", function () { uniformResourceIdentifier: ["urn:HOSTNAME:ServerDescription"], }); }); + + it("ZZ1 - should create self-signed certificate with those parameters ", async () => { + const tmpPrivateKeyFilename = path.join(tmpTestFolder, "_tmp_privatekey.pem"); + { + const { privateKey } = await generateKeyPair(); + const { privPem } = await privateKeyToPEM(privateKey); + await fs.promises.writeFile(tmpPrivateKeyFilename, privPem); + } + + const privateKeyPem = removeTrailingLF(await fs.promises.readFile(tmpPrivateKeyFilename, "utf-8")); + const privateKey = await pemToPrivateKey(privateKeyPem); + + const { cert } = await createSelfSignedCertificate({ + privateKey, + notAfter: new Date(2020, 1, 1), + notBefore: new Date(2019, 1, 1), + subject: "/CN=OPCUA-Server/O=COMPANY/L=CITY/ST=REGION/C=FR/DC=company.com", + dns: ["DNS1", "DNS2"], + ip: ["192.168.1.1"], + applicationUri: "urn:HOSTNAME:ServerDescription", + purpose: CertificatePurpose.ForApplication, + }); + }); }); diff --git a/test/test_crypto_utils.ts b/test/test_crypto_utils.ts index c658763..b6b7269 100644 --- a/test/test_crypto_utils.ts +++ b/test/test_crypto_utils.ts @@ -29,7 +29,7 @@ import * as loremIpsum from "lorem-ipsum"; import "should"; import "mocha"; -import { exploreCertificateInfo, makeSHA1Thumbprint, split_der, toPem } from ".."; +import { exploreCertificateInfo, makeSHA1Thumbprint, readCertificatePEM, removeTrailingLF, split_der, toPem } from ".."; import { readCertificate } from ".."; @@ -106,15 +106,15 @@ describe("Crypto utils", function () { }); it("toPem should return a string if provided certificate is a buffer containing a PEM string", () => { - const certificate = fs.readFileSync(path.join(__dirname, "../test-fixtures/certs/cert1.pem"), null); + const certificate = fs.readFileSync(path.join(__dirname, "../test-fixtures/certs/cert1.pem"), "binary"); const pemCertificate = toPem(certificate, "CERTIFICATE"); pemCertificate.should.be.type('string'); }); it("toPem should return a certificate directly if provided certificate is PEM string", () => { - const certificate = fs.readFileSync(path.join(__dirname, "../test-fixtures/certs/cert1.pem"), 'ascii'); + const certificate = readCertificatePEM(path.join(__dirname, "../test-fixtures/certs/cert1.pem")); const pemCertificate = toPem(certificate, "CERTIFICATE"); - pemCertificate.should.eql(certificate); + pemCertificate.should.eql(removeTrailingLF(certificate)); }); }); diff --git a/test/test_make_private_key_from_pem.ts b/test/test_make_private_key_from_pem.ts index 838c36b..5948432 100644 --- a/test/test_make_private_key_from_pem.ts +++ b/test/test_make_private_key_from_pem.ts @@ -24,7 +24,7 @@ describe("makePrivateKeyFromPem", () => { const privateKey2 = makePrivateKeyFromPem(pem); const pem2 = coercePrivateKeyPem(privateKey2); - pem2.should.eql(pem); + pem2.trimEnd().should.eql(pem.trimEnd()); rsaLengthPrivateKey(privateKey).should.eql(2048 / 8); });