Skip to content

Commit

Permalink
Fix pemToDer() return type (#364)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjbarth committed Jul 29, 2023
1 parent be61682 commit 06cab68
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/signed-xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export class SignedXml {

if (publicCertMatches.length > 0) {
x509Certs = publicCertMatches
.map((c) => `<X509Certificate>${utils.pemToDer(c)}</X509Certificate>`)
.map((c) => `<X509Certificate>${utils.pemToDer(c).toString("base64")}</X509Certificate>`)
.join("");
}

Expand Down
26 changes: 19 additions & 7 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
);
}

/**
Expand All @@ -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);
Expand Down
Binary file added test/static/client_public.der
Binary file not shown.
40 changes: 38 additions & 2 deletions test/utils-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ describe("Utils tests", function () {
pemAsArray[pemAsArray.length - 1]
}`;

// @ts-expect-error FIXME
expect(utils.derToPem(nonNormalizedPem)).to.equal(normalizedPem);
});

Expand All @@ -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();
});
});
});

0 comments on commit 06cab68

Please sign in to comment.