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:
- The digest needs to have inclusive canonicalization algorithm
- The signature needs to have exclusive canonicalization algorithm
- The syntax enveloped signature must be used
- SHA-256 needs to be used for hashing
- The digital signature needs to be RSAWithSHA256 algorithm with 2048 bits
- 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?