Why is my signed XML getting rejected by authorization?

Viewed 175

I'm trying to create a signed XML post request. The XML needs to be sent in the body of the request. The signing needs to have the following:

  1. The digest needs to have inclusive canonicalization algorithm
  2. The signature needs to have exclusive canonicalization algorithm
  3. The syntax enveloped signature must be used
  4. SHA-256 needs to be used for hashing
  5. The digital signature needs to be RSAWithSHA256 algorithm with 2048 bits
  6. The fingerprint needs to be an X.509 certificate. The fingerprint can be calculated with the following formula: HEX(SHA-1(DER certificate))

All these requirements are coming straight out of the documentation.

I have used xml-crypto for signing the XML The .cer en .pem file are created with the following commands:

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout privateKey.pem -out certificate.cer

The code below I have used for signing the XML.

const xml = `<?xml version="1.0" encoding="UTF-8"?>
            <someTag xmlns="some_url" version="3.3.1">
                <createDateTimestamp>${new Date().toISOString()}</createDateTimestamp>
                <anotherTag>
                    <id>${id}</id>
                    <subID>0</subID>
                </anotherTag>
            </someTag>`;
        try {  
            // certificate file
            const cer = Buffer.from((await bucket.file(`certificates/certificate.crt`).download())[0]).toString('base64');
            logger.log('pem', cer.toString());
            const cerShasum = crypto.createHash('sha1');
            cerShasum.update(cer);
            // private key file
            const pem = Buffer.from((await bucket.file(`certificates/privateKey.pem`).download())[0]);
            logger.log('pem', pem.toString());
            // const decryptedKey = forge.pki.decryptRsaPrivateKey(pem.toString('ascii'), pemPassphrase);
            // const decryptedKeyBuffer = Buffer.from(forge.pki.privateKeyToPem(decryptedKey), 'ascii');
            // logger.log('decryptedKeyBuffer', decryptedKeyBuffer.toString());

            // sign xml document
            const sig = new SignedXml();
            sig.signingKey = pem;
            sig.signatureAlgorithm = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'; // set signing algorithme
            sig.addReference('//*[local-name()="someTag"]', ['http://www.w3.org/2000/09/xmldsig#enveloped-signature'], 'http://www.w3.org/2001/04/xmlenc#sha256');
            sig.keyInfoProvider = new FileKeyInfo(); 
            logger.log('xml', xml);
            sig.computeSignature(xml);
            let postData = sig.getSignedXml();
            postData = postData.replace('<X509Data/>', `<KeyName>${cerShasum.digest('hex')}</KeyName>`).replace('#_0', '').replace('Id="_0"', '').trim(); 
            logger.log('postData', postData);

            // sent req
            const url = host;
            const result = await axios.post(url, postData);
            logger.log('result', result);
            logger.log('res.data', result.data);
            const resDataJson = await parseStringPromise(result.data);
            logger.log('resDataJson', resDataJson);
        } catch (e) {
            logger.error('error: ', e);
        }

And the error I got:

<Error>
        <errorCode>SE2000</errorCode>
        <errorMessage>Authentication error</errorMessage>
        <errorDetail>Field generating error: Signature</errorDetail>
 </Error>

When I try to validate the signature to an checker I get the following error

func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=279:obj=sha256:subj=unknown:error=12:invalid data:data and digest do not match
RESULT: Signature is INVALID

Is the problem that the DigestValue is not being generated properly?

Does anybody see what I'm doing wrong?

1 Answers
sig.computeSignature(xml);
            let postData = sig.getSignedXml();
            postData = postData.replace('<X509Data/>', `<KeyName>${cerShasum.digest('hex')}</KeyName>`).replace('#_0', '').replace('Id="_0"', '').trim(); 
            logger.log('postData', postData);

So you firstly compute the signature and after modify its content? This will break the computed signature. Secondly, it seems like the signature you send does not include the originally signed content, therefore the validator is not able to de-reference the signed data and correctly compare the digests.

More likely, you want to use either an enveloped signature format or enveloping. For more information please see XMLDSig specification.

Related