XAdESjs

repository·master·Indexed 41 days ago

https://github.com/peculiarventures/xadesjs

A TypeScript/JavaScript implementation of XML Advanced Electronic Signatures (XAdES) based on XMLDSIGjs. It supports the XAdES-BES profile and allows for the creation, decoding, and verification of other profiles (XAdES-T, XAdES-C, XAdES-X, XAdES-X-L, XAdES-A, XAdES-EPES). Designed for use in browsers and Node.js environments using the Web Crypto API, version 2.4.4.

Tokens
11.5K
Snippets
24
Records
40
Agent score
47%

What's inside XAdESjs

  1. Understand XAdES profiles and support

    master

    XAdES (XML Advanced Electronic Signatures) is a superset of XMLDSIG. XAdESjs provides a full implementation of the XAdES-BES (Basic Electronic Signature) profile.

    For other profiles (XAdES-T, XAdES-C, XAdES-X, XAdES-X-L, XAdES-A, XAdES-EPES), the library allows you to create, decode, and verify them, but the caller is responsible for the construction and policy to ensure compliance.

  2. Configure the cryptographic engine for Node.js

    master

    XAdESjs relies on the Web Crypto API. While browsers support this natively, Node.js requires a polyfill. You must explicitly set the engine using xadesjs.Application.setEngine to use a specific polyfill.

    To use @peculiar/webcrypto:

    var xadesjs = require("./built/xades.js");
    var { Crypto } = require("@peculiar/webcrypto");
    
    xadesjs.Application.setEngine("NodeJS", new Crypto());

    To use a Hardware Security Module via node-webcrypto-p11:

    var xadesjs = require("./built/xades.js");
    var WebCrypto = require("node-webcrypto-p11").WebCrypto;
    
    xadesjs.Application.setEngine("PKCS11", new WebCrypto({
        library: "/path/to/pkcs11.so",
        name: "Name of PKCS11 lib",
        slot: 0,
        sessionFlags: 2 | 4, // RW_SESSION | SERIAL_SESSION
        pin: "token pin"
    }));
  3. Install xadesjs via npm

    master

    Install the library using npm to get access to the UMD modules and ES2015 files.

    Available files in the dist folder:

    • index.js: UMD module with external modules.
    • xades.js: UMD bundle module.
    • xades.min.js: Minified UMD bundle module.

    The lib folder contains an ES2015 JS file suitable for use with bundlers like rollup.

    npm install xadesjs
  4. Use SignaturePolicyIdentifier to define explicit or implied policies

    master

    The SignaturePolicyIdentifier class allows you to specify how a signature policy is identified in an XML document.

    Explicit Policy

    To use an explicit policy, populate the SignaturePolicyId property. This requires a SigPolicyId (an ObjectIdentifier) and a SigPolicyHash (DigestAlgAndValueType).

    Implied Policy

    To indicate that the signature policy is implied, set the SignaturePolicyImplied property to true. This corresponds to the <SignaturePolicyImplied/> element in the XAdES XML schema.

    import { SignaturePolicyIdentifier } from 'xadesjs';
    
    // Implied Policy
    const implied = new SignaturePolicyIdentifier();
    implied.SignaturePolicyImplied = true;
    
    // Explicit Policy
    const explicit = new SignaturePolicyIdentifier();
    // explicit.SignaturePolicyId = ... (must be initialized with SigPolicyId and SigPolicyHash)
  5. Create an XAdES signature in the browser

    master

    In a browser environment, you can use the native window.crypto.subtle API for key generation and signing. Ensure you include the necessary polyfills and dependencies (like asmCrypto, elliptic, and webcrypto-liner) and the xadesjs build.

    Note that in the browser, the library is typically accessed via the global XAdES object. Use XAdES.Parse to parse the XML and new XAdES.SignedXml().Sign(...) to sign it. You can also provide a signerRole in the options object.

    <script src="https://unpkg.com/xadesjs@2.0.16/build/xades.js"></script>
    <script type="text/javascript">
        // ... key generation ...
        var xmlDoc = XAdES.Parse(xmlString);
        var signedXml = new XAdES.SignedXml();
        
        await signedXml.Sign(
            { name: "ECDSA", hash: { name: "SHA-1" } },
            keys.privateKey,
            xmlDoc,
            {
                keyValue: keys.publicKey,
                references: [{ hash: "SHA-256", transforms: ["enveloped"] }],
                productionPlace: { country: "Country", state: "State", city: "City", code: "Code" },
                signerRole: { claimed: ["Some role"] }
            }
        );
        var signatureXml = await signedXml.toString();
    </script>
  6. Verify an XAdES signature in the browser

    master

    To verify a signature in the browser:

    1. Fetch the signed XML content.
    2. Parse it using XAdES.Parse(xmlString).
    3. Find the Signature element using getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature").
    4. Instantiate xadesjs.SignedXml(signedDocument).
    5. Load the signature with .LoadXml(xmlSignature[0]).
    6. Call .Verify(). The promise resolves to the signed document or rejects with an error.
    fetch("https://example.com/valid_signature.xml")
        .then(response => response.text())
        .then(body => {
            var signedDocument = XAdES.Parse(body);
            var xmlSignature = signedDocument.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature");
    
            var signedXml = new xadesjs.SignedXml(signedDocument);
            signedXml.LoadXml(xmlSignature[0]);
            signedXml.Verify().then(function (res) {
                alert("Valid signature");
            }).catch(function (e) {
                alert(e.message);
            });
        });
  7. Create an XAdES-EPES signature

    master

    XAdES-EPES allows for more complex signing policies. When using SignedXml.Sign, you can provide a policy object in the options.

    The policy object supports:

    • hash: The hash algorithm name.
    • identifier: An object with qualifier (e.g., "OIDAsURI") and value.
    • qualifiers: An array of qualifier objects, such as noticeRef which contains organization and noticeNumbers.

    Example configuration for policy:

    policy: {
        hash: "SHA-256",
        identifier: {
            qualifier: "OIDAsURI",
            value: "qualifier.uri",
        },
        qualifiers: [
            {
                noticeRef: {
                    organization: "PeculiarVentures",
                    noticeNumbers: [1, 2, 3, 4, 5]
                }
            }
        ]
    }
    // ... setup engine and keys ...
    var xadesXml = new xadesjs.SignedXml();
    const signature = await xadesXml.Sign(
        alg,
        key,
        xml,
        {
            references: [{ hash, transforms: ["c14n", "enveloped"] }],
            policy: {
                hash,
                identifier: { qualifier: "OIDAsURI", value: "qualifier.uri" },
                qualifiers: [{ noticeRef: { organization: "PeculiarVentures", noticeNumbers: [1, 2, 3, 4, 5] } }]
            },
            productionPlace: { country: "Russia", state: "Marij El", city: "Yoshkar-Ola", code: "424000" },
            signingCertificate: x509
        }
    );
    // Append signature to original XML
    xml.documentElement.appendChild(signature.GetXml());
  8. Create an XAdES-BES signature in Node.js

    master

    To create an XAdES-BES signature in a Node.js environment, you must first set the cryptographic engine using xadesjs.Application.setEngine. You can then use xadesjs.Parse to parse your XML string and xadesjs.SignedXml.Sign to generate the signature.

    Key steps:

    1. Initialize the engine with @peculiar/webcrypto.
    2. Parse the XML document.
    3. Instantiate xadesjs.SignedXml.
    4. Call .Sign() with the algorithm, private key, parsed document, and an options object containing keyValue (public key), references (with hash and transforms), productionPlace, and signingCertificate.
    var xadesjs = require("xadesjs");
    var { Crypto } = require("@peculiar/webcrypto");
    
    xadesjs.Application.setEngine("NodeJS", new Crypto());
    
    // ... (key generation logic) ...
    
    var xmlString = '<player bats="left" id="10012" throws="right">...</player>';
    var xmlDoc = xadesjs.Parse(xmlString);
    var signedXml = new xadesjs.SignedXml();
    
    await signedXml.Sign(
        { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-1" } },
        privateKey,
        xmlDoc,
        {
            keyValue: publicKey,
            references: [{ hash: "SHA-256", transforms: ["enveloped"] }],
            productionPlace: {
                country: "Country",
                state: "State",
                city: "City",
                code: "Code",
            },
            signingCertificate: "BASE64_CERT_STRING"
        }
    );
  9. Verify an XAdES signature in Node.js

    master

    To verify an existing signature in Node.js:

    1. Parse the signed XML document using XAdES.Parse(xmlString, "application/xml").
    2. Locate the Signature element using getElementsByTagNameNS with the namespace http://www.w3.org/2000/09/xmldsig#.
    3. Create a xadesjs.SignedXml instance passing the signed document.
    4. Load the signature element using .LoadXml(xmlSignatureElement).
    5. Call .Verify(), which returns a Promise resolving to a boolean (true if valid, false if invalid).
    var XAdES = require("xadesjs");
    var { Crypto } = require("@peculiar/webcrypto");
    
    XAdES.Application.setEngine("NodeJS", new Crypto());
    
    var xmlString = fs.readFileSync("some.xml","utf8");
    var signedDocument = XAdES.Parse(xmlString, "application/xml");
    var xmlSignature = signedDocument.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature");
    
    var signedXml = new XAdES.SignedXml(signedDocument);
    signedXml.LoadXml(xmlSignature[0]);
    
    signedXml.Verify().then(res => {
        console.log((res ? "Valid" : "Invalid") + " signature");
    });
  10. Verify a signature with Verify()

    master

    Use the Verify method to check the validity of a signature.

    Signature: Verify(key?: CryptoKey): PromiseLike<boolean>

    Parameters:

    • key: An optional CryptoKey for verification. If not provided, the method attempts to locate the keys within the KeyInfo element of the Signature.
  11. Sign an XML document with SignedXml.Sign()

    master

    Use SignedXml.Sign to create a digital signature for an XML document.

    Signature: SignedXml.Sign(algorithm: Algorithm, key: CryptoKey, data: Document, options?: OptionsXAdES): PromiseLike<Signature>

    Parameters:

    • algorithm: The signing algorithm (Web Crypto standard).
    • key: The CryptoKey used for signing.
    • data: The XML Document to be signed.
    • options: An OptionsXAdES object to configure the signature.

    OptionsXAdES properties:

    • keyValue: Public key for the KeyInfo block.
    • x509: An array of X509 certificate strings.
    • references: An array of OptionsSignReference objects. Defaults to a Reference with SHA-256 hash and exc-c14n transform.
    • signingCertificate: A string representing the signing certificate.
    • signingTime: OptionsSigningTime object (contains value as a Date and format).
    • policy: OptionsPolicyId.
    • productionPlace: OptionsProductionPlace (contains city, state, code, country).
    • signerRole: OptionsSignerRole (contains claimed and certified arrays).