PeculiarVentures/xadesjs

How to create reference for document, signedprops and keyInfo with xadesjs?

Jairos2015 opened this issue · 12 comments

I have to create a structure like the following:

ds:SignedInfo
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference Id="xmldsig-50280329-cdf3-4bb7-9d8f-edd480c8079c-ref0" URI="">
ds:Transforms
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
ds:DigestValuevDUXUvy+JoIsT1k4dFv7ay8eJ+7jOMyRTcqiVKkdXHI=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#xmldsig-50280329-cdf3-4bb7-9d8f-edd480c8079c-keyinfo">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
ds:DigestValueO5Bin7GRCjlH8qG1BFc3Cd2GlFx+IAp5DoEpn3nArgk=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-50280329-cdf3-4bb7-9d8f-edd480c8079c-signedprops">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
ds:DigestValuescoM3Nb4cTlMm1GHP9ECfFetSUP+S9DqTVYVHW99KEw=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>

Embebed certificate in base64 format:
<ds:KeyInfo Id="xmldsig-50280329-cdf3-4bb7-9d8f-edd480c8079c-keyinfo">
ds:X509Data
ds:X509Certificate
BASE64_CERTIFICATE=
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>

An invoice is created with an element called CUFE that concatenates data from the invoice and is performed SHA1:
CUFE = SHA1 (DATA CONCATATED)

Uri of SignedProperties and KeyInfo should be a function of CUFE as follows:

var urikeyInfo = "#" + cufe + "-KeyInfo" and

I build the signature like this:

                var urikeyInfo = "#" + cufe + "-KeyInfo"
                //
                //
               // const x509 = preparePem(certPem);
                const signature = await xadesXml.Sign(   // Signing document
                  alg,                                    // algorithm
                  key,                                    // key
                  xml,                                    // document
                  {
                    //key,    // agregado                   // options
                    KeyValue: keyPub,
                    x509: [x509],
                    references: [
                       // { id: "", uri: "", hash, transforms: ["c14n", "enveloped"] }
                       { uri: "", hash: "sha-256", transforms: ["c14n", "enveloped"] },
                       { uri: urikeyInfo, hash: "sha-256"}
                    ],
                    policy: { 
                      hash,
                      identifier: {
                        Description: "Política de firma para facturas electrónicas de la República de Colombia.",
                        qualifier: "CO, DIAN (Direccion de Impuestos y Aduanas Nacionales)",
                        value: "https://facturaelectronica.dian.gov.co/politicadefirma/v2/politicadefirmav2.pdf",
                      },
                    // Empresa operadora
                    /**/
                    qualifiers: [
                        {
                            noticeRef: {
                                organization: "Ing. Jairo Sanchez UD"//,
                                // noticeNumbers: [1, 2, 3, 4, 5]
                            }
                        }
                      ]
                    },/**/
                    productionPlace: {
                        country: "Colombia",
                        state: "Bogota DC",
                        city: "Bogota",
                        code: "732020",
                    },
                    signingCertificate: x509,
                    signerRole: {
                        claimed: ["supplier"]
                    },
                    signingTime: {
                      format: "isoDateTime" 
                    }
                  });
                  // console.log(xml)
                  xml.documentElement.childNodes[0].childNodes[2].childNodes[1].appendChild(signature.GetXml())
                  console.log("Exitosamente firmado XML...");
                  // Agregamos signatureValue id
                  const signatureValue = signature.GetXml().getElementsByTagName(`ds:SignatureValue`)
                  if (signatureValue.length) {
                    signatureValue[0].setAttribute("Id", "xmldsig-5afb5c11-d347-4177-b8b4-fc13e56eed92-signature-value");
                  }
                  //
                  const signedInfoIns = signature.GetXml().getElementsByTagName(`ds:SignedInfo`)
                  if (signedInfoIns.length) {
                    console.log(signedInfoIns)
                    signedInfoIns[0].setAttribute("Uri", `#xmldsig-${cufe}-Signedprops`);
                  }

But I throw the error:

XmlError {
prefix: 'XMLJS',
code: 13,
name: 'XmlError',
message: 'XMLJS0013: Cryptographic error: Cannot get object by reference: 95002bf20d2ba94aa3c4348ed5f9aed2212cafc9-KeyInfo',
stack: 'Error: XMLJS0013: Cryptographic error: Cannot get object by reference: 95002bf20d2ba94aa3c4348ed5f9aed2212cafc9-KeyInfo\n at new XmlError (C:\Users\jairo\packmotos\server\node_modules\xml-core\dist\index.js:216:22)\n at DigestReference.Promise.resolve.then (C:\Users\jairo\packmotos\server\node_modules\xmldsigjs\dist\index.js:3058:31)\n at '

How to create reference for document, signedprops and keyInfo with xadesjs?
Issue#69 I can not use it because some xmlsigd objects are not exported to xadesjs
thanks in advance.

Please see this issue #69.
Is it helpful?

I'll try. Thank you

I do the following:

const xadesjs = require("xadesjs");
...
var xadesXml = new xadesjs.SignedXml();
....
// Set Id for
xadesXml.XmlSignature.Id = "Signature001";
// Set Id for KeyInfo
xadesXml.XmlSignature.KeyInfo.Id = "KeyInfo001";

// Create Reference for KeyInfo
const keyInfoRef = new xadesjs.Reference("#KeyInfo001");
keyInfoRef.DigestMethod.Algorithm = xadesjs.SHA256_NAMESPACE;

// Add Reference to SignedInfo
xadesXml.XmlSignature.SignedInfo.References.Add(keyInfoRef);
//

but
And it gives me the following error:

TypeError: xadesXml.Reference is not a constructor

npm list xadesjs xmldsigjs >>>> @2.0.15 @2.0.23 @1.0.15

The example with xmldsigjs works for me but I have to make the signed document in xades-epes.

Thank you in advance for your help.

When printing
console.log(xadesXml.XmlSignature) (with xadesjs)
and
console.log(xmldsigjs.XmlSignature) (with xmldsigjs)

the latter contains reference elements, that of xadesjs not.

@Jairos2015 Here is my example

Example

import { Crypto } from "@peculiar/webcrypto";
import * as xades from "xadesjs";
import * as xmldsig from "xmldsigjs";

async function main() {
    const crypto = new Crypto();

    // Set crypto engine for XAdES
    xades.Application.setEngine("NodeJS", crypto);

    // Generate RSA keys
    const algorithm = {
        name: "RSASSA-PKCS1-v1_5",
        hash: "SHA-256",
        publicExponent: new Uint8Array([1, 0, 1]),
        modulusLength: 2048,
    };
    const keys = await crypto.subtle.generateKey(algorithm, false, ["sign", "verify"]);

    // Sign XML
    const xmlDoc = xades.Parse(`<root><child attr="Some"/></root>`);
    const signedXml = new xades.SignedXml();

    // NOTE: You need to set new Reference before Sign calling
    // Set Id for KeyInfo
    signedXml.XmlSignature.KeyInfo.Id = "KeyInfo001";
    // Create Reference for KeyInfo
    const keyInfoRef = new xmldsig.Reference("#KeyInfo001");
    keyInfoRef.DigestMethod.Algorithm = xmldsig.SHA1_NAMESPACE;
    // Add Reference to SignedInfo
    signedXml.XmlSignature.SignedInfo.References.Add(keyInfoRef);

    const signature = await signedXml.Sign(algorithm, // algorithm
        keys.privateKey, // key
        xmlDoc, // document
        {
            keyValue: keys.publicKey,
            id: "Signature001",
            references: [
                { hash: "SHA-256", transforms: ["enveloped"] }
            ],
            productionPlace: {
                country: "Country",
                state: "State",
                city: "City",
                code: "Code",
            },
            signingTime: {
                format: "isoDateTime"
            }
        });
    console.log(signature.toString());
}

main()
    .catch(e => console.error(e));

Formatted Signature

<ds:Signature Id="Signature001"
    xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
        <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
        <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
        <ds:Reference URI="#KeyInfo001">
            <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
            <ds:DigestValue>ez6zjEDC1n/5XfziuoGnru580M23zPpsVhPcMpA0FwI=</ds:DigestValue>
        </ds:Reference>
        <ds:Reference>
            <ds:Transforms>
                <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
            <ds:DigestValue>0Ul7cHlzBRgEcKcYhAO7u3oWL55aFk1y4mKYnzbFljs=</ds:DigestValue>
        </ds:Reference>
        <ds:Reference URI="#xades-id-0f6a7d013fc7" Type="http://uri.etsi.org/01903#SignedProperties">
            <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
            <ds:DigestValue>zye5S26g8X/qCcWQRdODNO5EOq2FmHORGJriNymncxA=</ds:DigestValue>
        </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>oBZx4EG6RwMEw7vli23KRM6sJrJAZCAeQchV3OIIF/zhv5IO3vvz5rx/3WWChZr6XsxsrvM2LRanDbfDeMuu5yxfJZBvGZYBJdhn5qeMKQjcsdn+1yUwcBgR0ZucCg0WebMGYKt7MIadOKQwMiL1uq6OhLK+dEW+WrytLuBKOZntHnQoOKK70rXeAJh1tez9zu3IK/V28JNSaWxkGIQxDt5RsJ52orKkoABRBDSa/+kZNSzM1jClCDDIjq5rkfx2AW6HaTyoMNi4Kyag3ymu45ffRrErQI8EO6Bn6oycZfLTQfYNbx5HtRn7i0HI9XzZvsAi7lQqhwcPER3DqH/AmQ==</ds:SignatureValue>
    <ds:KeyInfo Id="KeyInfo001">
        <ds:KeyValue>
            <ds:RSAKeyValue>
                <ds:Modulus>vt7MCtVLUWJn5H8a4tvP+uov47JXuXF8lxbAEMSSd9MwmrbbtwxE7dMQS1PauWG8631fXWIou+Yuu0hsvSUTZ4hHieHjSKJNvIKFlkEktI7iUW11WSuDrnchRZNnqqYrC65O9dFKKViV8D3VKC+Uhdj1UnLjKcGKITgEYsqjjPpdEU74KlUDYiI7d7l0f+LRhzQa/cAIBFIkHn2L/kFwrrAFDlCZ0PlVK6g/gNocO2vVEi0D/cEGP7a5wlHFTpcFHmzx8KHudBGtIqTk5DdE4DrZ1PprzCOIvgqold/6SiHniTKKzr2eHf12zum6VD9Ty4J8OwcqDdoMBHEKXZYpiw==</ds:Modulus>
                <ds:Exponent>AQAB</ds:Exponent>
            </ds:RSAKeyValue>
        </ds:KeyValue>
    </ds:KeyInfo>
    <ds:Object>
        <xades:QualifyingProperties Target="#id-0f6a7d013fc7"
            xmlns:xades="http://uri.etsi.org/01903/v1.3.2#">
            <xades:SignedProperties Id="xades-id-0f6a7d013fc7">
                <xades:SignedSignatureProperties>
                    <xades:SigningTime>2019-05-03T12:44:12+0300</xades:SigningTime>
                    <xades:SignatureProductionPlace>
                        <xades:City>City</xades:City>
                        <xades:StateOrProvince>State</xades:StateOrProvince>
                        <xades:PostalCode>Code</xades:PostalCode>
                        <xades:CountryName>Country</xades:CountryName>
                    </xades:SignatureProductionPlace>
                </xades:SignedSignatureProperties>
            </xades:SignedProperties>
        </xades:QualifyingProperties>
    </ds:Object>
</ds:Signature>

Thank you. I test but error.

My headers:
const fs = require("fs");
const {Crypto} = require("@peculiar/webcrypto");
const xades = require("xadesjs");
const xmldsig = require("xmldsigjs")

The rest is the same as your example. The error:
TypeError: crypto.generateKeyPairSync is not a function
at Function.generateKey (C:\Users\jairo\proyectos\xades_epes\node_modules@peculiar\webcrypto\build\webcrypto.js:872:29)
at RsaSsaProvider.onGenerateKey (C:\Users\jairo\proyectos\xades_epes\node_modules@peculiar\webcrypto\build\webcrypto.js:1061:37)
at RsaSsaProvider.generateKey (C:\Users\jairo\proyectos\xades_epes\node_modules@peculiar\webcrypto\node_modules\webcrypto-core\build\webcrypto-core.js:133:35)
at SubtleCrypto.generateKey (C:\Users\jairo\proyectos\xades_epes\node_modules@peculiar\webcrypto\node_modules\webcrypto-core\build\webcrypto-core.js:774:39)
at main (C:\Users\jairo\proyectos\xades_epes\ejemploventura.js:24:38)
at Object. (C:\Users\jairo\proyectos\xades_epes\ejemploventura.js:61:1)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)

I was running an earlier version of node. Updated v10.15.3. Import added after v10.12.0

With your example, error:

import { Crypto } from "@peculiar/webcrypto";
^

SyntaxError: Unexpected token {
at new Script (vm.js:80:7)
at createScript (vm.js:274:10)
at Object.runInThisContext (vm.js:326:10)
at Module._compile (internal/modules/cjs/loader.js:664:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
at Module.load (internal/modules/cjs/loader.js:600:32)
at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
at Function.Module._load (internal/modules/cjs/loader.js:531:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:754:12)
at startup (internal/bootstrap/node.js:283:19)

But changed import --> require

const fs = require("fs");
const { Crypto } = require("@peculiar/webcrypto");
const xades = require("xadesjs");
const xmldsig = require("xmldsigjs");

The rest is the same as your example. Success:

<ds:Signature Id="Signature001" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">ds:SignedInfo<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#KeyInfo001"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>ds:DigestValuea7iLfqhFD2btw2hvNUUmmAyIqTgW80qccwZfx1QwTP0=</ds:DigestValue></ds:Reference>ds:Referenceds:Transforms<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>ds:DigestValue0Ul7cHlzBRgEcKcYhAO7u3oWL55aFk1y4mKYnzbFljs=</ds:DigestValue></ds:Reference><ds:Reference URI="#xades-id-39c8bdb151fa" Type="http://uri.etsi.org/01903#SignedProperties"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>ds:DigestValueNh9Ccnygur165eM8tTjvg6R41IP526dHQAAGzAkojUw=</ds:DigestValue></ds:Reference></ds:SignedInfo>ds:SignatureValueMIe7oiHAooNti7pW7vLCTCfHybuYhL1fv+ck6BHcvrMVj8gzf3DAUhiijmEmGuwBCauFCsn37VWXR8SRJrFdV8v3ATWUpM+7AUNVHl6bXPvTcNK4MxKo62NKJy0Y4Rkuy0u44fuUUFzKcX5sFt6p2afTaBvnWNHia4vlH2Txfm5NuYRxN2Af5Yn0Q4SDATCNMlGCS3PzbMrnbtpxSLPr/JEQB4Kq+zpisYki5R8D/m3K5OQtQe3Yx8RjALy0ddI4cVNHpTltS0xGsbycLAFl9/ahKQkeKhhsQbd3Q4Fa+7bvLEFsTRehNLIxWW1qhT/IQ6EL7rqsedgnYkgF/Wfbzg==</ds:SignatureValue><ds:KeyInfo Id="KeyInfo001">ds:KeyValueds:RSAKeyValueds:ModulusrUCodqrfm86QXEdDKlzcTmeM7nCvHYB0EyKBJLNzTw+vfKPFE50vNiFrc/sytTOt7EIMIH0ZENmyO5EbqSi5Lgtd7wzp5rJAAEC4Fn3Qnq+5Uk6NqtdEajInsIQxEE2ljTrFe4OCdb/eRrSsNmxA5JXcsq/qNr6Tv3H1lEgaoVR4HrdcyIleWDk9kv6a6SvWdKsfm1NTcBn5JVmmnkXoCLv0twJkdegpsw/SkODGhcV0Nxuzwjdml/L6lG5+fUoCESG/rjW/+ubGTpyhwZW4cappHnvXWAK4WqoyLkXwc9PFNWce/qjqZ6MyadPOcsygaznJGJ1CIaADh4lH11VmJQ==</ds:Modulus>ds:ExponentAQAB</ds:Exponent></ds:RSAKeyValue></ds:KeyValue></ds:KeyInfo>ds:Object<xades:QualifyingProperties Target="#id-39c8bdb151fa" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"><xades:SignedProperties Id="xades-id-39c8bdb151fa">xades:SignedSignaturePropertiesxades:SigningTime2019-05-03T10:53:10-0500</xades:SigningTime>xades:SignatureProductionPlacexades:CityCity</xades:City>xades:StateOrProvinceState</xades:StateOrProvince>xades:PostalCodeCode</xades:PostalCode>xades:CountryNameCountry</xades:CountryName></xades:SignatureProductionPlace></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object></ds:Signature>

Import Dont work fo me...

Thank you for your help but excuse me:

Your example success when Uri="xxxxx" ---> fixed data but

Invoice is created with an element called CUFE that concatenates data from the invoice and is performed SHA1:
CUFE = SHA1 (DATA CONCATENATED)

Uri of SignedProperties and KeyInfo should be a function of CUFE >> UUID as follows:

Reference KeyInfo
var urikeyInfo = "#" + {UUID} + "-KeyInfo" and

Reference SignedProperties
var uriSignedProps = "#xmldsig-" + {UUID} + "-signedprops"

In doing so... Cannot get object by reference: #cufe-KeyInfo',

XmlError {
prefix: 'XMLJS',
code: 13,
name: 'XmlError',
message:
'XMLJS0013: Cryptographic error: Cannot get object by reference: #cufe-KeyInfo',
stack:
'Error: XMLJS0013: Cryptographic error: Cannot get object by reference: #cufe-KeyInfo\n at new XmlError (C:\Users\jairo\packmotos\server\node_modules\xml-core\dist\index.js:216:22)\n at DigestReference.Promise.resolve.then (C:\Users\jairo\packmotos\server\node_modules\xmldsigjs\dist\index.js:3058:31)\n at process._tickCallback (internal/process/next_tick.js:68:7)' }

Ok.Ok. Thank you.
I managed to do it. Before injecting the signature, I made the URI updates.

const getReference = signature.GetXml().getElementsByTagName(ds:Reference)
if (getReference.length) {
console.log(getReference.length)
getReference[0].setAttribute("URI", #${cufe}-KeyInfo);
// getReference[1] --> All Document
getReference[2].setAttribute("URI", #xmldsig-${cufe}-signedprops);
}
xml.documentElement.childNodes[0].appendChild(signature.GetXml())
console.log("Exitosamente firmado XML...");

Resolved?

I made the changes about the signature. I do not know if it's all right. If so Yes. Now I'm finding out how to update DisgestMethod value for each Reference. Thank you.

Sounds like the original question is resolved, the other topic seems like a different question, closing this one. Please open a new issue if you have any other issues.