Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pemToDer() return type #364

Merged
merged 1 commit into from
Jul 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
});
});
});