Skip to content

Commit 6e95c60

Browse files
authored
Enhance derToPem to support XML pretty-print (#439)
1 parent 7e2d5a5 commit 6e95c60

File tree

5 files changed

+113
-5
lines changed

5 files changed

+113
-5
lines changed

src/signed-xml.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,9 @@ export class SignedXml {
216216
*/
217217
static getCertFromKeyInfo(keyInfo?: Node | null): string | null {
218218
if (keyInfo != null) {
219-
const certs = xpath.select1(".//*[local-name(.)='X509Certificate']", keyInfo);
220-
if (isDomNode.isNodeLike(certs)) {
221-
return utils.derToPem(certs.textContent || "", "CERTIFICATE");
219+
const cert = xpath.select1(".//*[local-name(.)='X509Certificate']", keyInfo);
220+
if (isDomNode.isNodeLike(cert)) {
221+
return utils.derToPem(cert.textContent ?? "", "CERTIFICATE");
222222
}
223223
}
224224

src/utils.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,14 @@ export function derToPem(
177177
return normalizePem(base64Der);
178178
}
179179

180-
if (BASE64_REGEX.test(base64Der)) {
180+
if (BASE64_REGEX.test(base64Der.replace(/ /g, ""))) {
181181
if (pemLabel == null) {
182182
throw new Error("PEM label is required when DER is given.");
183183
}
184-
const pem = `-----BEGIN ${pemLabel}-----\n${base64Der}\n-----END ${pemLabel}-----`;
184+
const pem = `-----BEGIN ${pemLabel}-----\n${base64Der.replace(
185+
/ /g,
186+
"",
187+
)}\n-----END ${pemLabel}-----`;
185188

186189
return normalizePem(pem);
187190
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="https://evil-corp.madness.com/sso/callback" ID="pfx16b904df-7571-d25a-8694-23a70b1b2181" InResponseTo="_e8df3fe5f04237d25670" IssueInstant="2015-08-31T08:54:06+00:00" Version="2.0">
3+
<saml:Issuer>https://evil-corp.com</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
4+
<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
5+
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
6+
<ds:Reference URI="#pfx16b904df-7571-d25a-8694-23a70b1b2181"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>uAP/VBhhaK2+Bn1lYjHB5tSHTcE=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>Q2F/63pvENmI+eURRIRmVI1fNpW+CxDan6YmGo2G5il+XOx72sBHS+FspoWDyezYUfO/Wfi+tBupK&#xd;
7+
/9eHOeg60uPUkQFwkdPmUsH2hJAPVCatQReSUJDhHCO6a2rrQixecPDBhzJjstCpibgvgNzevVVu&#xd;
8+
9h3eMH/BNzdwO5EbnVTcb2JQj2F18MAh1LlVMBWWDaZmIQTk7npMY/NVEajM1fbwXyrPdNRU8poK&#xd;
9+
bjVoM7IL9s0TesQIeyQ01QOQQQ3kHZWnoqlWE6VbbTqHwuidfkqaQLLvF9sDneqXKBa7y2YEJXVm4&#xd;
10+
GMzNoL/JOdSuNnU3rF0ET8UDSleV953/lbUA==</ds:SignatureValue>
11+
<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDtTCCAp2gAwIBAgIJAKg4VeVcIDz1MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAlVTMRMwE&#xd;
12+
QYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMT&#xd;
13+
UwODEzMDE1NDIwWhcNMTUwOTEyMDE1NDIwWjBFMQswCQYDVQQGEwJVUzETMBEGA1UECBMKU29tZS1&#xd;
14+
TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEF&#xd;
15+
AAOCAQ8AMIIBCgKCAQEAxG3ouM7U+fXbJt69X1H6d4UNg/uRr06pFuU9RkfIwNC+yaXyptqB3ynXK&#xd;
16+
sL7BFt4DCd0fflRvJAx3feJIDp16wN9GDVHcufWMYPhh2j5HcTW/j9JoIJzGhJyvO00YKBt+hHy83&#xd;
17+
iN1SdChKv5y0iSyiPP5GnqFw+ayyHoM6hSO0PqBou1Xb0ZSIE+DHosBnvVna5w2AiPY4xrJl9yZHZ&#xd;
18+
4Q7DfMiYTgstjETio4bX+6oLiBnYktn7DjdEslqhffVme4PuBxNojI+uCeg/sn4QVLd/iogMJfDWN&#xd;
19+
uLD8326Mi/FE9cCRvFlvAiMSaebMI3zPaySsxTK7Zgj5TpEbmbHI9wIDAQABo4GnMIGkMB0GA1UdD&#xd;
20+
gQWBBSVGgvoW4MhMuzBGce29PY8vSzHFzB1BgNVHSMEbjBsgBSVGgvoW4MhMuzBGce29PY8vSzHF6&#xd;
21+
FJpEcwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV&#xd;
22+
0IFdpZGdpdHMgUHR5IEx0ZIIJAKg4VeVcIDz1MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD&#xd;
23+
ggEBAJu1rqs+anD74dbdwgd3CnqnQsQDJiEXmBhG2leaGt3ve9b/9gKaJg2pyb2NyppDe1uLqh6nN&#xd;
24+
XDuzg1oNZrPz5pJL/eCXPl7FhxhMUi04TtLf8LeNTCIWYZiFuO4pmhohHcv8kRvYR1+6SkLTC8j/T&#xd;
25+
Zerm7qvesSiTQFNapa1eNdVQ8nFwVkEtWl+JzKEM1BlRcn42sjJkijeFp7DpI7pU+PnYeiaXpRv5p&#xd;
26+
Jo8ogM1iFxN+SnfEs0EuQ7fhKIG9aHKi7bKZ7L6SyX7MDIGLeulEU6lf5D9BfXNmcMambiS0pXhL2&#xd;
27+
QXajt96UBq8FT2KNXY8XNtR4y6MyyCzhaiZZcc8=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature>
28+
<samlp:Status>
29+
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
30+
</samlp:Status>
31+
<saml:Assertion ID="_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" IssueInstant="2020-09-25T16:00:00+00:00" Version="2.0">
32+
<saml:Issuer>https://evil-corp.com</saml:Issuer>
33+
<saml:Subject>
34+
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">[email protected]
35+
</saml:NameID>
36+
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
37+
<saml:SubjectConfirmationData InResponseTo="_e8df3fe5f04237d25670" NotOnOrAfter="2020-09-25T17:00:00+00:00" Recipient="https://evil-corp.madness.com/sso/callback"/>
38+
</saml:SubjectConfirmation>
39+
</saml:Subject>
40+
<saml:Conditions NotBefore="2020-09-25T16:00:00+00:00" NotOnOrAfter="2020-09-25T17:00:00+00:00"/>
41+
<saml:AuthnStatement AuthnInstant="2020-09-25T16:00:00+00:00" SessionIndex="_9e315bdf7b1b6732be33c377cf6f5c4f">
42+
<saml:AuthnContext>
43+
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
44+
</saml:AuthnContextClassRef>
45+
</saml:AuthnContext>
46+
</saml:AuthnStatement>
47+
<saml:AttributeStatement>
48+
<saml:Attribute Name="evil-corp.egroupid">
49+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
50+
51+
</saml:AttributeValue>
52+
</saml:Attribute>
53+
<saml:Attribute Name="evilcorp.givenname">
54+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Vincent
55+
</saml:AttributeValue>
56+
</saml:Attribute>
57+
<saml:Attribute Name="evilcorp.sn">
58+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">VEGA
59+
</saml:AttributeValue>
60+
</saml:Attribute>
61+
<saml:Attribute Name="evilcorp.addr">
62+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">123 Main St.&#xd;
63+
Suite 11
64+
</saml:AttributeValue>
65+
</saml:Attribute>
66+
</saml:AttributeStatement>
67+
</saml:Assertion>
68+
</samlp:Response>

test/static/keyinfo.pem

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDtTCCAp2gAwIBAgIJAKg4VeVcIDz1MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
3+
BAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4+
aWRnaXRzIFB0eSBMdGQwHhcNMTUwODEzMDE1NDIwWhcNMTUwOTEyMDE1NDIwWjBF
5+
MQswCQYDVQQGEwJVUzETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
6+
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
7+
CgKCAQEAxG3ouM7U+fXbJt69X1H6d4UNg/uRr06pFuU9RkfIwNC+yaXyptqB3ynX
8+
KsL7BFt4DCd0fflRvJAx3feJIDp16wN9GDVHcufWMYPhh2j5HcTW/j9JoIJzGhJy
9+
vO00YKBt+hHy83iN1SdChKv5y0iSyiPP5GnqFw+ayyHoM6hSO0PqBou1Xb0ZSIE+
10+
DHosBnvVna5w2AiPY4xrJl9yZHZ4Q7DfMiYTgstjETio4bX+6oLiBnYktn7DjdEs
11+
lqhffVme4PuBxNojI+uCeg/sn4QVLd/iogMJfDWNuLD8326Mi/FE9cCRvFlvAiMS
12+
aebMI3zPaySsxTK7Zgj5TpEbmbHI9wIDAQABo4GnMIGkMB0GA1UdDgQWBBSVGgvo
13+
W4MhMuzBGce29PY8vSzHFzB1BgNVHSMEbjBsgBSVGgvoW4MhMuzBGce29PY8vSzH
14+
F6FJpEcwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV
15+
BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAKg4VeVcIDz1MAwGA1UdEwQF
16+
MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJu1rqs+anD74dbdwgd3CnqnQsQDJiEX
17+
mBhG2leaGt3ve9b/9gKaJg2pyb2NyppDe1uLqh6nNXDuzg1oNZrPz5pJL/eCXPl7
18+
FhxhMUi04TtLf8LeNTCIWYZiFuO4pmhohHcv8kRvYR1+6SkLTC8j/TZerm7qvesS
19+
iTQFNapa1eNdVQ8nFwVkEtWl+JzKEM1BlRcn42sjJkijeFp7DpI7pU+PnYeiaXpR
20+
v5pJo8ogM1iFxN+SnfEs0EuQ7fhKIG9aHKi7bKZ7L6SyX7MDIGLeulEU6lf5D9Bf
21+
XNmcMambiS0pXhL2QXajt96UBq8FT2KNXY8XNtR4y6MyyCzhaiZZcc8=
22+
-----END CERTIFICATE-----

test/utils-tests.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import * as fs from "fs";
22
import * as utils from "../src/utils";
33
import { expect } from "chai";
4+
import * as xmldom from "@xmldom/xmldom";
5+
import * as xpath from "xpath";
6+
import * as isDomNode from "@xmldom/is-dom-node";
47

58
describe("Utils tests", function () {
69
describe("derToPem", function () {
@@ -41,6 +44,18 @@ describe("Utils tests", function () {
4144
expect(utils.derToPem(base64String, "CERTIFICATE")).to.equal(normalizedPem);
4245
});
4346

47+
it("will return a normalized PEM format when given a base64 string with line breaks and spaces at the line breaks", function () {
48+
const xml = new xmldom.DOMParser().parseFromString(
49+
fs.readFileSync("./test/static/keyinfo - pretty-printed.xml", "latin1"),
50+
);
51+
const cert = xpath.select1(".//*[local-name(.)='X509Certificate']", xml);
52+
isDomNode.assertIsNodeLike(cert);
53+
54+
const normalizedPem = fs.readFileSync("./test/static/keyinfo.pem", "latin1");
55+
56+
expect(utils.derToPem(cert.textContent ?? "", "CERTIFICATE")).to.equal(normalizedPem);
57+
});
58+
4459
it("will throw if the DER string is not base64 encoded", function () {
4560
expect(() => utils.derToPem("not base64", "CERTIFICATE")).to.throw();
4661
});

0 commit comments

Comments
 (0)