diff --git a/src/signed-xml.ts b/src/signed-xml.ts index 13de8fbb..8c24b5fe 100644 --- a/src/signed-xml.ts +++ b/src/signed-xml.ts @@ -194,7 +194,7 @@ export class SignedXml { if (publicCertMatches.length > 0) { x509Certs = publicCertMatches - .map((c) => `${utils.pemToDer(c)}`) + .map((c) => `${utils.pemToDer(c).toString("base64")}`) .join(""); } diff --git a/src/utils.ts b/src/utils.ts index 3146cc5b..4009b9c7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -142,11 +142,18 @@ export function normalizePem(pem: string): string { /** * @param pem The PEM-encoded base64 certificate to strip headers from */ -export function pemToDer(pem: string): string { - return pem - .replace(/(\r\n|\r)/g, "\n") - .replace(/-----BEGIN [A-Z\x20]{1,48}-----\n?/, "") - .replace(/-----END [A-Z\x20]{1,48}-----\n?/, ""); +export function pemToDer(pem: string): Buffer { + if (!PEM_FORMAT_REGEX.test(pem.trim())) { + throw new Error("Invalid PEM format."); + } + + return Buffer.from( + pem + .replace(/(\r\n|\r)/g, "") + .replace(/-----BEGIN [A-Z\x20]{1,48}-----\n?/, "") + .replace(/-----END [A-Z\x20]{1,48}-----\n?/, ""), + "base64", + ); } /** @@ -155,15 +162,20 @@ export function pemToDer(pem: string): string { */ export function derToPem( der: string | Buffer, - pemLabel: "CERTIFICATE" | "PRIVATE KEY" | "RSA PUBLIC KEY", + pemLabel?: "CERTIFICATE" | "PRIVATE KEY" | "RSA PUBLIC KEY", ): string { - const base64Der = Buffer.isBuffer(der) ? der.toString("latin1").trim() : der.trim(); + const base64Der = Buffer.isBuffer(der) + ? der.toString("base64").trim() + : der.replace(/(\r\n|\r)/g, "").trim(); if (PEM_FORMAT_REGEX.test(base64Der)) { return normalizePem(base64Der); } if (BASE64_REGEX.test(base64Der)) { + if (pemLabel == null) { + throw new Error("PEM label is required when DER is given."); + } const pem = `-----BEGIN ${pemLabel}-----\n${base64Der}\n-----END ${pemLabel}-----`; return normalizePem(pem); diff --git a/test/static/client_public.der b/test/static/client_public.der new file mode 100644 index 00000000..1fe1f1b9 Binary files /dev/null and b/test/static/client_public.der differ diff --git a/test/utils-tests.spec.ts b/test/utils-tests.spec.ts index ab89a2f1..561d4fc5 100644 --- a/test/utils-tests.spec.ts +++ b/test/utils-tests.spec.ts @@ -12,7 +12,6 @@ describe("Utils tests", function () { pemAsArray[pemAsArray.length - 1] }`; - // @ts-expect-error FIXME expect(utils.derToPem(nonNormalizedPem)).to.equal(normalizedPem); }); @@ -25,8 +24,45 @@ describe("Utils tests", function () { }); it("will throw if the format is neither PEM nor DER", function () { - // @ts-expect-error FIXME expect(() => utils.derToPem("not a pem")).to.throw(); }); + + it("will return a normalized PEM format when given a DER Buffer", function () { + const normalizedPem = fs.readFileSync("./test/static/client_public.pem", "latin1"); + const derBuffer = fs.readFileSync("./test/static/client_public.der"); + + expect(utils.derToPem(derBuffer, "CERTIFICATE")).to.equal(normalizedPem); + }); + + it("will return a normalized PEM format when given a base64 string with line breaks", function () { + const normalizedPem = fs.readFileSync("./test/static/client_public.pem", "latin1"); + const base64String = fs.readFileSync("./test/static/client_public.der", "base64"); + + expect(utils.derToPem(base64String, "CERTIFICATE")).to.equal(normalizedPem); + }); + + it("will throw if the DER string is not base64 encoded", function () { + expect(() => utils.derToPem("not base64", "CERTIFICATE")).to.throw(); + }); + + it("will throw if the PEM label is not provided", function () { + const derBuffer = fs.readFileSync("./test/static/client_public.der"); + expect(() => utils.derToPem(derBuffer)).to.throw(); + }); + }); + + describe("pemToDer", function () { + it("will return a Buffer of binary DER when given a normalized PEM format", function () { + const pem = fs.readFileSync("./test/static/client_public.pem", "latin1"); + const derBuffer = fs.readFileSync("./test/static/client_public.der"); + + const result = utils.pemToDer(pem); + expect(result).to.be.instanceOf(Buffer); + expect(result).to.deep.equal(derBuffer); + }); + + it("will throw if the format is not PEM", function () { + expect(() => utils.pemToDer("not a pem")).to.throw(); + }); }); });