Code Monkey home page Code Monkey logo

Comments (10)

microshine avatar microshine commented on June 14, 2024

xmldsigjs works in nodejs and browser. For nodejs you need crypto engine which implements WebCrypto API.

Here is simple code you can try

Install dependencies

npm install node-webcrypto-ossl xmldsigjs

Init key and cert files.

NOTE: files must be in DER format. WebCrypto allows to import object from DER or JWK formats. If you have PEM you have to convert it or update JS to do it from app.

openssl genrsa -out key.pem
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -out key.der -nocrypt
openssl rsa -in key.pem -pubout -outform DER -out pubkey.der
openssl req -x509 -key key.pem -outform DER -out cert.der -days 365

index.js

// @ts-check

const fs = require("fs");
const xmldsigjs = require("xmldsigjs");
const OSSLCrypto = require("node-webcrypto-ossl");
const crypto = new OSSLCrypto();

// Set crypto for XmlDSig (it uses browser's crypto by default);
xmldsigjs.Application.setEngine("OpenSSL", crypto);

function checkFile(filename) {
    if (!fs.existsSync(filename)) {
        throw new Error(`File '${filename}' not found`);
    }
}

// Loads private key from DER file
function loadPrivateKey(filename, alg) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return crypto.subtle.importKey("pkcs8", der, alg, false, ["sign"]);
        });
}

// Loads public key from DER file
function loadPublicKey(filename, alg) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return crypto.subtle.importKey("spki", der, alg, true, ["verify"]);
        });
}

// Loads certificate from DER file and returns it in BASE64
function loadCert(filename) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return der.toString("base64");
        });
}

// Signs xml document and adds public key and x509 certificate to it
function signXml(alg, privateKey, publicKey, xml, x509) {
    return Promise.resolve()
        .then(() => {
            let signature = new xmldsigjs.SignedXml();

            return signature.Sign(                                  // Signing document
                alg,                                                  // algorithm 
                privateKey,                                           // key 
                xmldsigjs.Parse(xml),                                 // document
                {                                                     // options
                    keyValue: publicKey,
                    references: [
                        { hash: "SHA-512", transforms: ["enveloped", "c14n"] },
                    ],
                    x509: [x509],
                })
                .then(() => {
                    return signature.toString()
                })
        })
}

// Main
(() => {
    const alg = {
        name: "RSASSA-PKCS1-v1_5",
        hash: "SHA-256",
    };
    let privateKey, publicKey, certificate;
    return Promise.resolve()
        .then(() => {
            // Get private key
            return loadPrivateKey("key.der", alg)
                .then((key) => {
                    privateKey = key;
                })
        })
        .then(() => {
            // Get public key
            return loadPublicKey("pubkey.der", alg)
                .then((key) => {
                    publicKey = key;
                })
        })
        .then(() => {
            // get certificate
            return loadCert("cert.der")
                .then((cert) => {
                    certificate = cert;
                })
        })
        .then(() => {
            // Sign document
            return signXml(
                alg,
                privateKey,
                publicKey,
                "<root><child>Some text</child></root>",
                certificate
            )
                .then((xmlString) => {
                    console.log(xmlString);
                })
        })
})()
    .catch((err) => {
        console.error(err);
    });

run app

node index > signed.xml

You can check your signed xml online on aleksey.com

from xmldsigjs.

alphanso avatar alphanso commented on June 14, 2024

Thanks for the prompt response. I am able to generate signedxml but it has default prefix which I don't want. Also, I want to add 'X509SubjectName' under 'X509Data' node. I am building it for a 3rd party which only accepts in a specific format. Please provide guidance.

from xmldsigjs.

microshine avatar microshine commented on June 14, 2024

Xmldsigjs API supports must support this. I'll update example a bit later

from xmldsigjs.

alphanso avatar alphanso commented on June 14, 2024

Does above comment mean that it doesn't support these functionality as of now?

from xmldsigjs.

microshine avatar microshine commented on June 14, 2024

It supports. I can't update code from phone

from xmldsigjs.

rmhrisk avatar rmhrisk commented on June 14, 2024

No, he just needs to find time to update the sample

from xmldsigjs.

alphanso avatar alphanso commented on June 14, 2024

Great. Awaiting the update from @microshine.

from xmldsigjs.

microshine avatar microshine commented on June 14, 2024

I updated above code a bit

// Loads certificate from DER file
function loadCert(filename) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return new xmldsigjs.X509Certificate(der);
        });
}

// Signs xml document and adds public key and x509 certificate to it
function signXml(alg, privateKey, publicKey, xml, x509) {
    return Promise.resolve()
        .then(() => {
            let signature = new xmldsigjs.SignedXml();

            // Add X509Data
            const X509Data = new xmldsigjs.KeyInfoX509Data()
            X509Data.AddSubjectName(x509.Subject);
            X509Data.AddCertificate(x509);
            X509Data.AddIssuerSerial(x509.Issuer, x509.SerialNumber);
            signature.XmlSignature.KeyInfo.Add(X509Data);

            return signature.Sign(                                  // Signing document
                alg,                                                  // algorithm 
                privateKey,                                           // key 
                xmldsigjs.Parse(xml),                                 // document
                {                                                     // options
                    keyValue: publicKey,
                    references: [
                        { hash: "SHA-512", transforms: ["enveloped", "c14n"] },
                    ],
                })
                .then(() => {
                    return signature.toString()
                })
        })
}

This allows to get X509Data like

<ds:X509Data>
    <ds:X509IssuerSerial>
        <ds:X509IssuerName>C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=My cert</ds:X509IssuerName>
        <ds:X509SerialNumber>14245677082242946981</ds:X509SerialNumber>
    </ds:X509IssuerSerial>
    <ds:X509SubjectName>C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=My cert</ds:X509SubjectName>
    <ds:X509Certificate>MIIDgTCCAmmgAwIBAgIJAMWyz9RYA5elMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEDAOBgNVBAMMB015IGNlcnQwHhcNMTcwNzExMTIyNzIwWhcNMTgwNzExMTIyNzIwWjBXMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRAwDgYDVQQDDAdNeSBjZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw6gvnGXHmvaF8J+PxeGpzBBAglWI9rYeUuvYa0NEgaK62qyM7tk/5YotMoxUlLnsbppG5ttajyA23Q72oVHUyo/tez1ZRfRKl+VOhAL7aiLm/jH4QwnJ6pQVzFWz2KY94Nja1vqfpnSLpLv8s065jgCNH5llL4QBlR15a+NUWXJIccEFmr9J+lC6ymbbbNrqBk2QFSEwNiUpp8LOTzO/Sk8JFnbpT5/Lnif1OxQBQm9JUnjR42TL9rCh51iEJJEp3+8yZxEKD6lOP5SyH2DqfbET6kcw5f0EDigcm87R+qoz+3tcH1GCGIlkdKnE5Iw9cKopQRkSHHz3sRYG3I43mwIDAQABo1AwTjAdBgNVHQ4EFgQUvxWd0AuVuqtO/6yu54rlSLBFs2gwHwYDVR0jBBgwFoAUvxWd0AuVuqtO/6yu54rlSLBFs2gwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAoXQhlJ3GC+NoRamL7kLvHCLtVOIN7Cpg41Xp/zUONhs6iap5yTmsa0TVi6pWbnOei5H0lgZFu1Yh6cAAspK9DQwtqEMNb3Cmsy+65Z61EL41QtC0lm2NLVtQo7FtsPiGmor80TsUaKWTyDv5luKePMvGXSFQ6d5K+g6AQmNV21l1AYXIxZfq9hTsV2p8gHkX7pHzs6orzC6kkbMalzRc5cadwkaTu7jqdJd+b3PRPde+rP7jSBDnLgo2vPzLok9FlFEf17WZPbisSPcsyBXY/sVDez8m/KIRnwEj4Fbi22KXCtjaWnnO6xcC1fTYXQus4QLFPcfnNqKIQ8r1ciMrwg==</ds:X509Certificate>
</ds:X509Data>

But prefix changing has a problem

from xmldsigjs.

alphanso avatar alphanso commented on June 14, 2024

Hi @microshine,

I generated the desired output using the following code but it has 3 problem: -

  1. Fails Signature Verification
  2. XML Header is missing
  3. Reference Tag is missing URI

Can you help me fix errors?
Thanks

// @ts-check

const fs = require("fs");
const xmldsigjs = require("xmldsigjs");
const OSSLCrypto = require("node-webcrypto-ossl");
const crypto = new OSSLCrypto();

xmldsigjs.Signature.prefix = "";
xmldsigjs.DigestMethod.prefix = "";
xmldsigjs.CanonicalizationMethod.prefix = "";
xmldsigjs.KeyInfo.prefix = "";
xmldsigjs.Transform.prefix = "";
xmldsigjs.Transforms.prefix = "";
xmldsigjs.Reference.prefix = "";
xmldsigjs.References.prefix = "";
xmldsigjs.SignedInfo.prefix = "";
xmldsigjs.SignatureMethod.prefix = "";
xmldsigjs.Signature.items.SignatureValue.prefix = "";
xmldsigjs.Reference.items.DigestValue.prefix = ""
xmldsigjs.KeyInfoX509Data.prefix = "";

// Set crypto for XmlDSig (it uses browser's crypto by default);
xmldsigjs.Application.setEngine("OpenSSL", crypto);
function checkFile(filename) {
    if (!fs.existsSync(filename)) {
        throw new Error(`File '${filename}' not found`);
    }
}

// Loads private key from DER file
function loadPrivateKey(filename, alg) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return crypto.subtle.importKey("pkcs8", der, alg, false, ["sign"]);
        });
}

// Loads public key from DER file
function loadPublicKey(filename, alg) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return crypto.subtle.importKey("spki", der, alg, true, ["verify"]);
        });
}

// Loads certificate from DER file and returns it in BASE64
function loadCert(filename) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return new xmldsigjs.X509Certificate(der);
        });
}

// Signs xml document and adds public key and x509 certificate to it
function signXml(alg, privateKey, publicKey, xml, x509) {
    return Promise.resolve()
        .then(() => {
            let signature = new xmldsigjs.SignedXml();

            // Add x509 certificate
            const X509Data = new xmldsigjs.KeyInfoX509Data()
            X509Data.AddSubjectName(x509.Subject);
            X509Data.AddCertificate(x509);
            signature.XmlSignature.KeyInfo.Add(X509Data);

            return signature.Sign(                                  // Signing document
                alg,                                                  // algorithm 
                privateKey,                                           // key 
                xmldsigjs.Parse(xml),                                 // document
                {                                                     // options
                    references: [
                        { hash: "SHA-1", transforms: ["enveloped"] },
                    ]
                })
                .then(() => {
                    return signature.toString()
                })
        })
}

// Main
(() => {
    const alg = {
        name: "RSASSA-PKCS1-v1_5",
        hash: "SHA-1",
    };
    let privateKey, publicKey, certificate;
    return Promise.resolve()
        .then(() => {
            // Get private key
            return loadPrivateKey("key.der", alg)
                .then((key) => {
                    privateKey = key;
                })
        })
        .then(() => {
            // Get public key
            return loadPublicKey("pubkey.der", alg)
                .then((key) => {
                    publicKey = key;
                })
        })
        .then(() => {
            // get certificate
            return loadCert("cert.der")
                .then((cert) => {
                    certificate = cert;
                })
        })
        .then(() => {
            // Sign document
            return signXml(
                alg,
                privateKey,
                publicKey,
                '<?xml version="1.0" encoding="UTF-8"?><OTP aspId="UTII-900" ver="1.0" ts="2017-07-11T18:37:17.692" uid="789456123654" txn="f37f4548-1dbd-4489-ba44-a9b02573dffb"></OTP>',
                certificate
            )
                .then((xmlString) => {
                    console.log(xmlString);
                })
        })
})()
    .catch((err) => {
        console.error(err);
    });

Desired Output

<?xml version="1.0" encoding="UTF-8"?>
<OTP aspId="UTII-900" ts="2016-05-16T12:13:58.315" txn="050b6a62-841d-42b5-8e63-6e034ea0f75f" uid="789456123654" ver="1.0">
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
            <Reference URI="">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <DigestValue>MUakshYMaksbPAXKpv1o7zK3c1A=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>
            YcOJfkczUdlfvs3bN7dzuNLCwDhAGavnDwR6O1EeB0ENJQKkaHHV882wp8kPF+Y2B7Snu/UeKDJ7p42kd0fng1jHRKJj7ltN0h5S2rH7FxrCV9v/MAnzxjilb6juVdaVIwaBq+jPCDo7sXGUvwOXF+rQT2bcbwfhQHHvMo4OMfoV3576c/XAec4Bn0rLKv40qf8sls8/i9OZyMfIIJ+cNPprTyIyId0kidER+d8pb8cwQd3rgTTKe0emgQgbUCWt07GcLK8HjdE8CgHLISAWLR3FyHqM+XeGv7Xc+YBaY1ca7DKs4Oe/uMPizkO/PIps4wgEGbuXafVPDEuTN/Vurw==
        </SignatureValue>
        <KeyInfo>
            <X509Data>
                <X509SubjectName>
                    ST=MAHARASHTRA,2.5.4.17=#0c06343030363134,2.5.4.5=#134038393832373944353736343135453130364537344430334532393542433338423041453335463733413142323334393746463145444244464432384338413631,CN=VICHARE SANDEEP,OU=INFORMATION TECHNOLOGY,O=UTI INFRASTRUCTURE TECHNOLOGY AND SERVICES LIMITED,C=IN
                </X509SubjectName>
                <X509Certificate>
                    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
                </X509Certificate>
            </X509Data>
        </KeyInfo>
    </Signature>
</OTP>

Actual Output

<OTP aspId="UTII-900" ver="1.0" ts="2017-07-11T18:37:17.692" uid="789456123654" txn="f37f4548-1dbd-4489-ba44-a9b02573dffb">
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
            <Reference>
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <DigestValue>nf9oQPVj6b1eBotzjL9zY5Zjnkw=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>
            wihjNbjPFnf6fniQ6qjWfNIUwpNdxDepHHGuj4c1RHOEntd1GdmSw/CCG4eO8IIWv76hgpVzpVw965kn/W0x/Q==
        </SignatureValue>
        <KeyInfo>
            <X509Data>
                <X509SubjectName>
                    C=IN, ST=Uttar-Pradesh, L=Noida, O=Tarsiers Technologies Pvt Ltd, CN=Shobha Singhal, [email protected]
                </X509SubjectName>
                <X509Certificate>
                    MIIDNzCCAuGgAwIBAgIJAP4+kHY1VqGRMA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJJTjEWMBQGA1UECBMNVXR0YXItUHJhZGVzaDEOMAwGA1UEBxMFTm9pZGExJjAkBgNVBAoTHVRhcnNpZXJzIFRlY2hub2xvZ2llcyBQdnQgTHRkMRcwFQYDVQQDEw5TaG9iaGEgU2luZ2hhbDEjMCEGCSqGSIb3DQEJARYUY29udGFjdHNAdGFyc2llcnMuaW4wHhcNMTcwNzExMTMzMDUyWhcNMTgwNzExMTMzMDUyWjCBmzELMAkGA1UEBhMCSU4xFjAUBgNVBAgTDVV0dGFyLVByYWRlc2gxDjAMBgNVBAcTBU5vaWRhMSYwJAYDVQQKEx1UYXJzaWVycyBUZWNobm9sb2dpZXMgUHZ0IEx0ZDEXMBUGA1UEAxMOU2hvYmhhIFNpbmdoYWwxIzAhBgkqhkiG9w0BCQEWFGNvbnRhY3RzQHRhcnNpZXJzLmluMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAOJ6bgnfYXGXQUiYhIxoR1+nq/wR9wTMR6mEKY9bi/vGTO6tq37jhiWr5kqhJaOiBREcaTSfuhLBqZ75+Aix7u8CAwEAAaOCAQQwggEAMB0GA1UdDgQWBBRkAYe+ek9zFoHUtqGfmQSdCzCgqDCB0AYDVR0jBIHIMIHFgBRkAYe+ek9zFoHUtqGfmQSdCzCgqKGBoaSBnjCBmzELMAkGA1UEBhMCSU4xFjAUBgNVBAgTDVV0dGFyLVByYWRlc2gxDjAMBgNVBAcTBU5vaWRhMSYwJAYDVQQKEx1UYXJzaWVycyBUZWNobm9sb2dpZXMgUHZ0IEx0ZDEXMBUGA1UEAxMOU2hvYmhhIFNpbmdoYWwxIzAhBgkqhkiG9w0BCQEWFGNvbnRhY3RzQHRhcnNpZXJzLmluggkA/j6QdjVWoZEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBACavhhBL+KawZGPe90JbDXgGPZMV9KIbd4YrOL9vvHDCBK7z1itE/TWwHHnx+JnLWsEQx8RgmGMsrHeViQhOY7o=
                </X509Certificate>
            </X509Data>
        </KeyInfo>
    </Signature>
</OTP>

from xmldsigjs.

microshine avatar microshine commented on June 14, 2024

@alphanso
I updates script.

  • updates prefix for xmldsig
  • adds URI to Reference
// @ts-check

const fs = require("fs");
const xmldsigjs = require("xmldsigjs");
const OSSLCrypto = require("node-webcrypto-ossl");
const crypto = new OSSLCrypto();

// Set crypto for XmlDSig (it uses browser's crypto by default);
xmldsigjs.Application.setEngine("OpenSSL", crypto);

function hackXmlDSigPrefix(prefix) {
    xmldsigjs.XmlSignature.DefaultPrefix = prefix;
    for (const key in xmldsigjs) {
        const object = xmldsigjs[key];
        if (object.namespaceURI === xmldsigjs.XmlSignature.NamespaceURI) {
            object.prefix = prefix;
        }
        for (const i in object.items) {
            const item = object.items[i];
            if (item.namespaceURI === xmldsigjs.XmlSignature.NamespaceURI) {
                item.prefix = prefix;
            }
        }
    }
}

function checkFile(filename) {
    if (!fs.existsSync(filename)) {
        throw new Error(`File '${filename}' not found`);
    }
}

// Loads private key from DER file
function loadPrivateKey(filename, alg) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return crypto.subtle.importKey("pkcs8", der, alg, false, ["sign"]);
        });
}

// Loads public key from DER file
function loadPublicKey(filename, alg) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return crypto.subtle.importKey("spki", der, alg, true, ["verify"]);
        });
}

// Loads certificate from DER file
function loadCert(filename) {
    return Promise.resolve()
        .then(() => {
            checkFile(filename);

            const der = fs.readFileSync(filename);
            return new xmldsigjs.X509Certificate(der);
        });
}

// Signs xml document and adds public key and x509 certificate to it
function signXml(alg, privateKey, publicKey, xml, x509) {
    return Promise.resolve()
        .then(() => {
            let signature = new xmldsigjs.SignedXml();

            // Add X509Data
            const X509Data = new xmldsigjs.KeyInfoX509Data()
            X509Data.AddSubjectName(x509.Subject);
            X509Data.AddCertificate(x509);
            X509Data.AddIssuerSerial(x509.Issuer, x509.SerialNumber);
            signature.XmlSignature.KeyInfo.Add(X509Data);

            return signature.Sign(                                  // Signing document
                alg,                                                  // algorithm 
                privateKey,                                           // key 
                xmldsigjs.Parse(xml),                                 // document
                {                                                     // options
                    keyValue: publicKey,
                    references: [
                        { hash: "SHA-512", transforms: ["enveloped", "c14n"], uri: `id("root-id")` },
                    ],
                })
                .then(() => {
                    return signature.toString()
                })
        })
}

// Main
(() => {
    const alg = {
        name: "RSASSA-PKCS1-v1_5",
        hash: "SHA-256",
    };
    let privateKey, publicKey, certificate;
    return Promise.resolve()
        .then(() => {
            hackXmlDSigPrefix("dsig");

            // Get private key
            return loadPrivateKey("key.der", alg)
                .then((key) => {
                    privateKey = key;
                })
        })
        .then(() => {
            // Get public key
            return loadPublicKey("pubkey.der", alg)
                .then((key) => {
                    publicKey = key;
                })
        })
        .then(() => {
            // get certificate
            return loadCert("cert.der")
                .then((cert) => {
                    certificate = cert;
                })
        })
        .then(() => {
            // Sign document
            return signXml(
                alg,
                privateKey,
                publicKey,
                `<root id="root-id"><child>Some text</child></root>`,
                certificate
            )
                .then((xmlString) => {
                    // output signed xml
                    console.log(xmlString);
                })
        })
})()
    .catch((err) => {
        console.error(err);
    });
<root id="root-id">
    <child>Some text</child>
    <dsig:Signature
        xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
        <dsig:SignedInfo>
            <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            <dsig:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
            <dsig:Reference URI="id(&quot;root-id&quot;)">
                <dsig:Transforms>
                    <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                    <dsig:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
                </dsig:Transforms>
                <dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>
                <dsig:DigestValue>ury5ZNtzrhhk0tlpM/dUmeth3VFoftfJxd9winqbTye1aWfJMhadkMCEr8P/HWodeTwWTyCLw85ZbAENNi6v9w==</dsig:DigestValue>
            </dsig:Reference>
        </dsig:SignedInfo>
        <dsig:SignatureValue>fEVRdKkXLLyuqqWQRjgRsJsK+W+qzm2dBfyBD7in/YfX9BSURKxy7GhrYU/6BCu5lnWGbGxClnX53n0XDllDnZGRN8TOHtbt6Svg5fnKCo6MTnwMuyx5M4L4Y8keYAWPOQJQaQuDP+yBC1bv+jmciakiNwh0F3J9vQ7N2Ja+YQIKJ3w8ZjKrgh60NTT3BOJFEqkHaR4511XtjURCR5NrAnWg890Og3lpPv9bBlEnpH++tq/TBuohlESkSqU/rhLvuNhQ3SLYckQiOJdOMguUMd2U6oG0Z4VWqiNJgq4N8S8eTHHiI27VM9AQKtCp3iL4kSekiIccEJZqmfkwVpwc8g==</dsig:SignatureValue>
        <dsig:KeyInfo>
            <dsig:X509Data>
                <dsig:X509IssuerSerial>
                    <dsig:X509IssuerName>C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=My cert</dsig:X509IssuerName>
                    <dsig:X509SerialNumber>14245677082242946981</dsig:X509SerialNumber>
                </dsig:X509IssuerSerial>
                <dsig:X509SubjectName>C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=My cert</dsig:X509SubjectName>
                <dsig:X509Certificate>MIIDgTCCAmmgAwIBAgIJAMWyz9RYA5elMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEDAOBgNVBAMMB015IGNlcnQwHhcNMTcwNzExMTIyNzIwWhcNMTgwNzExMTIyNzIwWjBXMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRAwDgYDVQQDDAdNeSBjZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw6gvnGXHmvaF8J+PxeGpzBBAglWI9rYeUuvYa0NEgaK62qyM7tk/5YotMoxUlLnsbppG5ttajyA23Q72oVHUyo/tez1ZRfRKl+VOhAL7aiLm/jH4QwnJ6pQVzFWz2KY94Nja1vqfpnSLpLv8s065jgCNH5llL4QBlR15a+NUWXJIccEFmr9J+lC6ymbbbNrqBk2QFSEwNiUpp8LOTzO/Sk8JFnbpT5/Lnif1OxQBQm9JUnjR42TL9rCh51iEJJEp3+8yZxEKD6lOP5SyH2DqfbET6kcw5f0EDigcm87R+qoz+3tcH1GCGIlkdKnE5Iw9cKopQRkSHHz3sRYG3I43mwIDAQABo1AwTjAdBgNVHQ4EFgQUvxWd0AuVuqtO/6yu54rlSLBFs2gwHwYDVR0jBBgwFoAUvxWd0AuVuqtO/6yu54rlSLBFs2gwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAoXQhlJ3GC+NoRamL7kLvHCLtVOIN7Cpg41Xp/zUONhs6iap5yTmsa0TVi6pWbnOei5H0lgZFu1Yh6cAAspK9DQwtqEMNb3Cmsy+65Z61EL41QtC0lm2NLVtQo7FtsPiGmor80TsUaKWTyDv5luKePMvGXSFQ6d5K+g6AQmNV21l1AYXIxZfq9hTsV2p8gHkX7pHzs6orzC6kkbMalzRc5cadwkaTu7jqdJd+b3PRPde+rP7jSBDnLgo2vPzLok9FlFEf17WZPbisSPcsyBXY/sVDez8m/KIRnwEj4Fbi22KXCtjaWnnO6xcC1fTYXQus4QLFPcfnNqKIQ8r1ciMrwg==</dsig:X509Certificate>
            </dsig:X509Data>
            <dsig:KeyValue>
                <dsig:RSAKeyValue>
                    <dsig:Modulus>w6gvnGXHmvaF8J+PxeGpzBBAglWI9rYeUuvYa0NEgaK62qyM7tk/5YotMoxUlLnsbppG5ttajyA23Q72oVHUyo/tez1ZRfRKl+VOhAL7aiLm/jH4QwnJ6pQVzFWz2KY94Nja1vqfpnSLpLv8s065jgCNH5llL4QBlR15a+NUWXJIccEFmr9J+lC6ymbbbNrqBk2QFSEwNiUpp8LOTzO/Sk8JFnbpT5/Lnif1OxQBQm9JUnjR42TL9rCh51iEJJEp3+8yZxEKD6lOP5SyH2DqfbET6kcw5f0EDigcm87R+qoz+3tcH1GCGIlkdKnE5Iw9cKopQRkSHHz3sRYG3I43mw==</dsig:Modulus>
                    <dsig:Exponent>AQAB</dsig:Exponent>
                </dsig:RSAKeyValue>
            </dsig:KeyValue>
        </dsig:KeyInfo>
    </dsig:Signature>
</root>

from xmldsigjs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.