PeculiarVentures / xadesjs

A pure Typescript/Javascript implementation of XAdES based on XMLDSIGjs. (Keywords: WebCrypto, XMLDSIG, XADES, eIDAS, Trust List, X.509, CRL, OCSP)
https://xadesjs.com
MIT License
141 stars 49 forks source link

Include UBL extension in my signed document #73

Open jerojasol opened 6 years ago

jerojasol commented 6 years ago

Hi, I use xadesjs to generate signed documents, when I add the signature to the document like in the example xml.documentElement.appendChild(signature.GetXml());, the verify() function works fine and the signature is added in the root tag, for example:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <content>....</content>
  <ds:Signature Id="id-fea3a1cabb60">...</ds:Signature>
</root>

But my documents have UBL format so I need to include the signature in an UBL Extension tag, for example:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <ext:Extensions>
    <ext:Extension>
      <ext:Extensioncontent>
          <ds:Signature Id="id-fea3a1cabb60">...</ds:Signature>
      <ext:Extensioncontent>
    <ext:Extension>
  <ext:Extensions>
<content>....</content>
</root>

But when the signature is added in that way, the verify() function shows the next error message:

message: 'XMLJS0013: Cryptographic error: Invalid digest for uri \'#xades-id-fea3a1cabb60\'. Calculated digest is rZyuhWUmuiAHQLuQSX9tl8PEcPFrjcpJ2Iz2nwDKcZo= but the xml to validate supplies digest nIflO2RU6U8+d8Y+wrKAFTU9cE9pjcwbaFPp5879RxA='

It seems the original XML is modified and the digest is different. How can I add the signature in a UBL extension tag so that the verify() function works fine?

I really appreciate your help. Thanks in advance.

microshine commented 6 years ago

Example

// TypeScript
import OsslCrypto from "node-webcrypto-ossl";
import * as xades from "xadesjs";
import * as xmlCore from "xml-core";

const RSA_ALG: RsaHashedKeyGenParams = {
  name: "RSASSA-PKCS1-v1_5",
  hash: "SHA-256",
  publicExponent: new Uint8Array([1, 0, 1]),
  modulusLength: 2048,
}

const XML_DOC = `
<?xml version="1.0" encoding="utf-8"?>
<root>
  <ext:Extensions>
    <ext:Extension>
      <ext:Extensioncontent id="xades-signature">
      </ext:Extensioncontent>
    </ext:Extension>
  </ext:Extensions>
<content>....</content>
</root>
`

async function sign(xml: Document) {
  var signedXml = new xades.SignedXml();

  const crypto = xades.Application.crypto;
  const keys = await crypto.subtle.generateKey(RSA_ALG, false, ["sign", "verify"]);

  const signature = await signedXml.Sign(               // Signing document
    RSA_ALG,                                // algorithm
    keys.privateKey,                        // key
    xml,                                 // document
    {                                       // options
      keyValue: keys.publicKey,
      references: [
        { hash: "SHA-256", transforms: ["enveloped"] }
      ],
    });

  // add Id to Signature
  const signatureXml = signature.GetXml()!;
  signatureXml.setAttribute("Id", "xades-1234567890");

  // Add signature to document
  const xmlEl = xml.getElementById("xades-signature");
  if (!xmlEl) {
    throw new Error("Cannot get XML element by Id `xades-signature`");
  }
  xmlEl.appendChild(signature.GetXml()!);

  return xml;
}

async function verify(xml: Document) {
  var signedXml = new xades.SignedXml(xml);
  signedXml.LoadXml(xml.getElementsByTagName("ds:Signature")[0]);
  const ok = await signedXml.Verify();
  return ok;
}

async function main() {
  // Set crypto engine
  xades.Application.setEngine("OpenSSL", new OsslCrypto() as any);

  var xmlDoc = xades.Parse(XML_DOC);
  console.log(`--------------XML--------------\n${XML_DOC}\n--------------------------------\n`);

  const signedXml = await sign(xmlDoc);
  console.log(`----------XML Signature----------\n${xmlCore.Stringify(signedXml)}\n--------------------------------\n`);

  console.log("Signature:", (await verify(signedXml)) ? "Valid" : "Invalid");
}

main().catch((err) => { console.error(err); });

Output

--------------XML--------------

<?xml version="1.0" encoding="utf-8"?>
<root>
  <ext:Extensions>
    <ext:Extension>
      <ext:Extensioncontent id="xades-signature">
      </ext:Extensioncontent>
    </ext:Extension>
  </ext:Extensions>
<content>....</content>
</root>

--------------------------------

----------XML Signature----------

<?xml version="1.0" encoding="utf-8"?>
<root>
  <ext:Extensions xmlns:ext="">
    <ext:Extension>
      <ext:Extensioncontent id="xades-signature">
      <ds:Signature Id="xades-1234567890" 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><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>PXl01v2DAwSB6Lp1BkIoG3yvstd0t5PMJM6Br/Wet3U=</ds:DigestValue></ds:Reference><ds:Reference URI="#xades-id-7c4875c4cd27" Type="http://uri.etsi.org/01903#SignedProperties"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>VY0TSO976h5vzls+ppNf/RAU9AAXKth2aRmVPDoIrlc=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>YL5yiKCrAkB0O1CXn6eDJ7H7Cxyiq6cuiVPRQ+P9JLbkqzAlUQRl5x9b1hXZQ/mMGJ1vmGjhHUSrreH0CHoLv/APJE3EiOu0GjYyHxjSoLUM9yfX/CCtbKL/lvg22q/Yt1M7m5fYaK1T+63Cie6zDot7nKOeoxkDYl8PysD5XHpEEscn6/3jOLbt07vWpeXOW7eE0Gh1UIzHiC24FpPluZaa814fmbGRnMeMlNwP+bIS8kIKIqzDQatoaGGCWQRTQgzy5DCNjhih3PWGkiSwwTfec56ETsj+5Jju89LUo4nLHujXAbXTyErhKEzHj88mc3t18+HUZza2/zPhxWR+aw==</ds:SignatureValue><ds:KeyInfo><ds:KeyValue><ds:RSAKeyValue><ds:Modulus>x9KKOtI2hY6Qw3WJScwngTUuXXJmpRpTGqx2BJySpU5Q6Amtl4TgQK2YAiG6hNt621hA4bzhJ4wwxRUZpmuKRuNnnzXKYOIOpWqKjf6uQORlif3gGgy19aKmgXSD8qjXGvrU9i2cV8O67hABInjj6hOepADuztIVEebev0QUvw6pQnPN4QbwkeG6FveDVTQJoYDEs7tz93KtpUrjgwAhxCF2DjNTKAnv7HdUc/TqgqfL9wtI85c6JcV2SBCmVMplfF9XfTY3YetDA59EoOzXCi4Ss32TNg25ByanAUHjxb4M7+CXig9gcmV29KKwhJBuMu/RGNa3x2Q0vFXW1NVObw==</ds:Modulus><ds:Exponent>AQAB</ds:Exponent></ds:RSAKeyValue></ds:KeyValue></ds:KeyInfo><ds:Object><xades:QualifyingProperties Target="#id-7c4875c4cd27" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"><xades:SignedProperties Id="xades-id-7c4875c4cd27"><xades:SignedSignatureProperties><xades:SigningTime>2018-09-07T05:41:15.265Z</xades:SigningTime><xades:SignaturePolicyIdentifier><xades:SignaturePolicyImplied/></xades:SignaturePolicyIdentifier></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object></ds:Signature></ext:Extensioncontent>
    </ext:Extension>
  </ext:Extensions>
<content>....</content>
</root>
--------------------------------

Signature: Valid
gponceleon commented 6 years ago

Hello @microshine, sorry bother, but I cand add this xsd: xmlns:xades141="http://uri.etsi.org/01903/v1.4.1# in the QualifyingProperties, I have tried in the core code, but the sign fails. Thanks

microshine commented 6 years ago

You can add this code after Id adding to Signature

const props = signatureXml.getElementsByTagName("xades:QualifyingProperties");
props[0].setAttribute("xmlns:xades141","http://uri.etsi.org/01903/v1.4.1#");
gponceleon commented 6 years ago

After this line? signedXml.XmlSignature.KeyInfo.Id = keyInfoId;

microshine commented 6 years ago

After

// add Id to Signature
const signatureXml = signature.GetXml()!;
signatureXml.setAttribute("Id", "xades-1234567890");
gponceleon commented 6 years ago

I already placed it after the signature, but it gets damaged. There is no way to place it before the signature in the CreateQualifyingProperties method?

microshine commented 6 years ago

image