Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Namespace prefix on root creates invalid signatures #59

Open
QAnders opened this issue Sep 2, 2021 · 7 comments
Open

Namespace prefix on root creates invalid signatures #59

QAnders opened this issue Sep 2, 2021 · 7 comments
Assignees

Comments

@QAnders
Copy link

QAnders commented Sep 2, 2021

Working through my issues trying to solve the Peppol (Peppol.eu) standard signature I realized that the signature becomes invalid if the root element has a namespace prefix...

This snippet generates a valid signature:

  let xml2 = xmldsigjs.Parse(`<?xml version="1.0" encoding="UTF-8"?>
<SignedServiceMetadata xmlns="http://www.w3.org/2005/08/addressing" xmlns:ns2="http://busdox.org/serviceMetadata/publishing/1.0/">
  <ns2:xml>
    <test>hello</test>
  </ns2:xml>
</SignedServiceMetadata>
`);

But adding the prefix to the root generates a invalid signature:

  let xml2 = xmldsigjs.Parse(`<?xml version="1.0" encoding="UTF-8"?>
<ns2:SignedServiceMetadata xmlns="http://www.w3.org/2005/08/addressing" xmlns:ns2="http://busdox.org/serviceMetadata/publishing/1.0/">
  <xml>
    <test>hello</test>
  </xml>
</ns2:SignedServiceMetadata>
`);
@QAnders
Copy link
Author

QAnders commented Sep 3, 2021

I'll share my code as well which works now and signature is validated using xmlsec1. I send in the certificates in PEM (CER) format (base64 with the BEGIN/END headers/footers).

const { XMLSerializer } = require('xmldom-alpha');
const xmldsigjs = require('xmldsigjs');
const { Crypto } = require('@peculiar/webcrypto');

const crypto = new Crypto();
xmldsigjs.Application.setEngine('NodeJS', crypto);

function preparePem(pem) {
  return (
    pem
      // remove BEGIN/END
      .replace(/-----(BEGIN|END)[\w\d\s]+-----/g, '')
      // remove \r, \n
      .replace(/[\r\n]/g, '')
  );
}

function pem2der(pem) {
  pem = preparePem(pem);
  // convert base64 to ArrayBuffer
  return new Uint8Array(Buffer.from(pem, 'base64')).buffer;
}

async function signXML(xmlString, publicKeyPem, privateKeyPem) {
  // const fs = require('fs');

  const hash = 'SHA-1';
  const alg = {
      name: 'RSASSA-PKCS1-v1_5',
      hash,
      publicExponent: new Uint8Array([1,0,1]),
      modulusLength: 2048,
  };


  const keyDer = pem2der(privateKeyPem);
  const key = await crypto.subtle.importKey('pkcs8', keyDer, alg, false, [
    'sign'
  ]);

  let xml = xmldsigjs.Parse(xmlString);

  let digSigXml = new xmldsigjs.SignedXml();

  const X509Data = new xmldsigjs.KeyInfoX509Data();
  X509Data.AddSubjectName(process.env.SMP_QVALIA_CN);
  digSigXml.XmlSignature.KeyInfo.Add(X509Data);

  const signature = await digSigXml.Sign(
      // Signing document
      alg, // algorithm
      key, // key
      xml, // document
      {
          // options
          references: [
              {
                  hash,
                  transforms: ['enveloped']
              },
          ],
          x509: [preparePem(publicKeyPem)],
          signingCertificate: preparePem(publicKeyPem)
      }
  );

  // append signature
  xml.documentElement.appendChild(signature.GetXml());

  // serialize XML
  const oSerializer = new XMLSerializer();
  const sXML = oSerializer.serializeToString(xml);
  // fs.writeFileSync("/home/anders/CodeStuff/signed3.xml", sXML.toString());
  return sXML.toString();
}

@rmhrisk rmhrisk closed this as completed Sep 3, 2021
@QAnders
Copy link
Author

QAnders commented Sep 4, 2021

So this is not a bug?

@rmhrisk
Copy link
Contributor

rmhrisk commented Sep 4, 2021

Your post made it sound like you solved your problem?

@rmhrisk rmhrisk reopened this Sep 4, 2021
@QAnders
Copy link
Author

QAnders commented Sep 5, 2021

Ah, no, sorry if I'm being a pain... I just wanted to add my code in full incase I am missing something...

I meant that it is producing valid signatures IF I don't have any namespace prefix on the root element.

@QAnders
Copy link
Author

QAnders commented Jan 24, 2022

Is this one dea, or something someone is looking at?

@rmhrisk
Copy link
Contributor

rmhrisk commented Feb 20, 2022

As I understand it the signature validates with xmlsec1 but the other tool your using doesn’t like it.

this would require us to 1) have an example of a file it does like, 2) have access to the tool

@ivanbreet
Copy link

We are also experiencing this issue. We validated using multiple tools, and having a namespace prefix in the root element causes validation issues.

Use an online validator like:
https://tools.chilkat.io/xmlDsigVerify.cshtml

Steps to re-create
The below example succeeds when using no root namespace prefixes

<InitSessionSignedRequest
        xmlns="http://example.com/online/types/2021/10/01/0001"
        xmlns:ns2="http://example.com/types/2021/10/01/0001"
        xmlns:ns3="http://example.com/online/auth/request/2021/10/01/0001">
</InitSessionSignedRequest>

CleanShot 2022-11-07 at 17 03 03@2x

The below example fails when using a root prefix:

<ns3:InitSessionSignedRequest
        xmlns="http://example.com/online/types/2021/10/01/0001"
        xmlns:ns2="http://example.com/types/2021/10/01/0001"
        xmlns:ns3="http://example.com/online/auth/request/2021/10/01/0001">
</ns3:InitSessionSignedRequest>

CleanShot 2022-11-07 at 17 02 04@2x

We tried multiple permutations and transforms, and the issue appears related to how the XML (and prefix) root is being handled in this library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants