Replies: 4 comments
-
It would have been nice to have info about version of xml-crypto. Decided to go with following {
"name": "foo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"node-forge": "^1.3.1",
"xml-crypto": "^6.0.0"
}
} Furthermore removed / simplified your example code quite a bit in order be able to run it (you could have done that also :) i.e. to provide immediately runnable version): //import forge from 'node-forge'
const forge=require('node-forge')
//import { readFileSync } from 'fs'
// ^ not needed to debugging
//import { SignedXml } from 'xml-crypto'
const SignedXml=require('xml-crypto').SignedXml
//import { createClient, WSSecurityCert } from 'soap'
// ^ not needed to debugging
//import { EXTRACT_X509_CERTS } from 'xml-crypto/lib/utils.js'
const EXTRACT_X509_CERTS=require('xml-crypto/lib/utils.js').EXTRACT_X509_CERTS
// https://raw.githubusercontent.com/node-saml/xml-crypto/21201723d2ca9bc11288f62cf72552b7d659b000/test/static/client_public.pem
var certificate = `
-----BEGIN CERTIFICATE-----
MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAW
MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEy
MzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPd
Vu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9x
O3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8juf
z2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEU
MBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcN
AQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5
sT/txBnVJGziyO8DPYdu2fPMER8ajJfl
-----END CERTIFICATE-----
`
// https://raw.githubusercontent.com/node-saml/xml-crypto/21201723d2ca9bc11288f62cf72552b7d659b000/test/static/client.pem
var privateKey = `
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL4vpoH3H3byehjj
7RAGxefGRATiq4mXtzc9Q91W7uT0DTaFEbjzVch9aGsNjmLs4QHsoZbuoUmi0st4
x5z9SQpTAKC/dW8muzacT3E7dJJYh03MAO6RiH4LG34VRTq1SQN6qDt2rCK85eG4
5NHI4jceptZNu6Zot1zyO5/PYuFpAgMBAAECgYAhspeyF3M/xB7WIixy1oBiXMLY
isESFAumgfhwU2LotkVRD6rgNl1QtMe3kCNWa9pCWQcYkxeI0IzA+JmFu2shVvoR
oL7eV4VCe1Af33z24E46+cY5grxNhHt/LyCnZKcitvCcrzXExUc5n6KngX0mMKgk
W7skZDwsnKzhyUV8wQJBAN2bQMeASQVOqdfqBdFgC/NPnKY2cuDi6h659QN1l+kg
X3ywdZ7KKftJo1G9l45SN9YpkyEd9zEO6PMFaufJvZUCQQDbtAWxk0i8BT3UTNWC
T/9bUQROPcGZagwwnRFByX7gpmfkf1ImIvbWVXSpX68/IjbjSkTw1nj/Yj1NwFZ0
nxeFAkEAzPhRpXVBlPgaXkvlz7AfvY+wW4hXHyyi0YK8XdPBi25XA5SPZiylQfjt
Z6iN6qSfYqYXoPT/c0/QJR+orvVJNQJBANhRPNXljVTK2GDCseoXd/ZiI5ohxg+W
UaA/1fDvQsRQM7TQA4NXI7BO/YmSk4rW1jIeOxjiIspY4MFAIh+7UL0CQFL6zTg6
wfeMlEZzvgqwCGoLuvTnqtvyg45z7pfcrg2cHdgCXIy9kErcjwGiu6BOevEA1qTW
Rk+bv0tknWvcz/s=
-----END PRIVATE KEY-----
`
// reduced version (no need to have that much data to debug this)
const inputXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DGICFE:EnvioCFE version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd"
xmlns:DGICFE="http://cfe.dgi.gub.uy"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DGICFE:Caratula version="1.0">
<DGICFE:RutReceptor>219999830019</DGICFE:RutReceptor>
</DGICFE:Caratula>
<ns0:CFE version="1.0"
xmlns:ns0="http://cfe.dgi.gub.uy">
<ns0:eFact>
<ns0:TmstFirma>2024-05-22T11:51:55-03:00</ns0:TmstFirma>
</ns0:eFact>
</ns0:CFE>
</DGICFE:EnvioCFE>
`
SignedXml.getKeyInfoContent = ({ publicCert, prefix }) => {
if (publicCert == null) return null
prefix = prefix ? `${prefix}:` : ''
let x509Certs = ''
if (Buffer.isBuffer(publicCert)) {
publicCert = publicCert.toString('latin1')
}
let publicCertMatches = []
if (typeof publicCert === 'string') {
publicCertMatches = publicCert.match(EXTRACT_X509_CERTS) || []
}
if (publicCertMatches.length > 0) {
x509Certs = publicCertMatches
.map(c => {
const certificate = forge.pki.certificateFromPem(c)
const issuerName = certificate.issuer.attributes.map(attr => `${attr.shortName}=${attr.value}`).join(', ')
const serialNumber = certificate.serialNumber
return (
`<${prefix}X509IssuerSerial>` +
`<${prefix}X509IssuerName>${issuerName}</${prefix}X509IssuerName>` +
`<${prefix}X509SerialNumber>${BigInt(`0x${serialNumber}`).toString()}</${prefix}X509SerialNumber>` +
`</${prefix}X509IssuerSerial>`
)
})
.join('')
}
return `<${prefix}X509Data>${x509Certs}</${prefix}X509Data>`
}
const firmarXml = (xml) => {
// const { privateKey, certificate } = retornaCertificado(certPath, password)
const sig = new SignedXml({ privateKey: privateKey, publicCert: certificate })
sig.signatureAlgorithm = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
sig.canonicalizationAlgorithm = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
sig.addReference({
xpath: "//*[local-name(.)='eFact']",
transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"],
digestAlgorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
isEmptyUri: true,
})
sig.computeSignature(xml, {
location: {
reference: "//*[local-name(.)='CFE']",
action: 'append',
},
})
return { Datain: { xmlData: sig.getSignedXml() } }
}
console.log(firmarXml(inputXml).Datain.xmlData) and when following result is pasted to chilkat it infact says that digests do not match: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DGICFE:EnvioCFE version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd" xmlns:DGICFE="http://cfe.dgi.gub.uy" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DGICFE:Caratula version="1.0">
<DGICFE:RutReceptor>219999830019</DGICFE:RutReceptor>
</DGICFE:Caratula>
<ns0:CFE version="1.0" xmlns:ns0="http://cfe.dgi.gub.uy">
<ns0:eFact>
<ns0:TmstFirma>2024-05-22T11:51:55-03:00</ns0:TmstFirma>
</ns0:eFact>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>gNFx5CofL1bIBBsVCoo+dX11Nw7o2FzLZ8wZXV8EAac=</DigestValue></Reference></SignedInfo><SignatureValue>lpSZ7wi0NVJJWR19QtvFyOXx7b4Xq7dJgvka+DMqi6C9wrhhdagbpOOENPzD/nB3x4gjtUgZOjrZqEfoQ6aqRAqP9esnYN4Zdy0f0f2jQkwa2s2L4sOatbkpZYAEPXgPQJIjBA6+Qp1SWYRIN1j9UZy3DwFKCwzMC8z56QBQUl8=</SignatureValue><KeyInfo><X509Data><X509IssuerSerial><X509IssuerName>CN=Root Agency</X509IssuerName><X509SerialNumber>262214055838675473468217939292187724434</X509SerialNumber></X509IssuerSerial></X509Data></KeyInfo></Signature></ns0:CFE>
</DGICFE:EnvioCFE> pretty formatted version being: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DGICFE:EnvioCFE xmlns:DGICFE="http://cfe.dgi.gub.uy" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd">
<DGICFE:Caratula version="1.0">
<DGICFE:RutReceptor>219999830019</DGICFE:RutReceptor>
</DGICFE:Caratula>
<ns0:CFE xmlns:ns0="http://cfe.dgi.gub.uy" version="1.0">
<ns0:eFact>
<ns0:TmstFirma>2024-05-22T11:51:55-03:00</ns0:TmstFirma>
</ns0:eFact>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>gNFx5CofL1bIBBsVCoo+dX11Nw7o2FzLZ8wZXV8EAac=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>lpSZ7wi0NVJJWR19QtvFyOXx7b4Xq7dJgvka+DMqi6C9wrhhdagbpOOENPzD/nB3x4gjtUgZOjrZqEfoQ6aqRAqP9esnYN4Zdy0f0f2jQkwa2s2L4sOatbkpZYAEPXgPQJIjBA6+Qp1SWYRIN1j9UZy3DwFKCwzMC8z56QBQUl8=</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Root Agency</X509IssuerName>
<X509SerialNumber>262214055838675473468217939292187724434</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</KeyInfo>
</Signature>
</ns0:CFE>
</DGICFE:EnvioCFE> What caught my eye is that you select document fragment to be signed but you set I must emphasize that I am NOT xml signature expert. But lets test this assumption and modify xpath: "//*[local-name(.)='eFact']",
// to -->
xpath: "//*[local-name(.)='EnvioCFE']", i.e. test code is //import forge from 'node-forge'
const forge=require('node-forge')
//import { readFileSync } from 'fs'
// ^ not needed to debugging
//import { SignedXml } from 'xml-crypto'
const SignedXml=require('xml-crypto').SignedXml
//import { createClient, WSSecurityCert } from 'soap'
// ^ not needed to debugging
//import { EXTRACT_X509_CERTS } from 'xml-crypto/lib/utils.js'
const EXTRACT_X509_CERTS=require('xml-crypto/lib/utils.js').EXTRACT_X509_CERTS
// https://raw.githubusercontent.com/node-saml/xml-crypto/21201723d2ca9bc11288f62cf72552b7d659b000/test/static/client_public.pem
var certificate = `
-----BEGIN CERTIFICATE-----
MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAW
MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEy
MzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPd
Vu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9x
O3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8juf
z2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEU
MBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcN
AQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5
sT/txBnVJGziyO8DPYdu2fPMER8ajJfl
-----END CERTIFICATE-----
`
// https://raw.githubusercontent.com/node-saml/xml-crypto/21201723d2ca9bc11288f62cf72552b7d659b000/test/static/client.pem
var privateKey = `
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL4vpoH3H3byehjj
7RAGxefGRATiq4mXtzc9Q91W7uT0DTaFEbjzVch9aGsNjmLs4QHsoZbuoUmi0st4
x5z9SQpTAKC/dW8muzacT3E7dJJYh03MAO6RiH4LG34VRTq1SQN6qDt2rCK85eG4
5NHI4jceptZNu6Zot1zyO5/PYuFpAgMBAAECgYAhspeyF3M/xB7WIixy1oBiXMLY
isESFAumgfhwU2LotkVRD6rgNl1QtMe3kCNWa9pCWQcYkxeI0IzA+JmFu2shVvoR
oL7eV4VCe1Af33z24E46+cY5grxNhHt/LyCnZKcitvCcrzXExUc5n6KngX0mMKgk
W7skZDwsnKzhyUV8wQJBAN2bQMeASQVOqdfqBdFgC/NPnKY2cuDi6h659QN1l+kg
X3ywdZ7KKftJo1G9l45SN9YpkyEd9zEO6PMFaufJvZUCQQDbtAWxk0i8BT3UTNWC
T/9bUQROPcGZagwwnRFByX7gpmfkf1ImIvbWVXSpX68/IjbjSkTw1nj/Yj1NwFZ0
nxeFAkEAzPhRpXVBlPgaXkvlz7AfvY+wW4hXHyyi0YK8XdPBi25XA5SPZiylQfjt
Z6iN6qSfYqYXoPT/c0/QJR+orvVJNQJBANhRPNXljVTK2GDCseoXd/ZiI5ohxg+W
UaA/1fDvQsRQM7TQA4NXI7BO/YmSk4rW1jIeOxjiIspY4MFAIh+7UL0CQFL6zTg6
wfeMlEZzvgqwCGoLuvTnqtvyg45z7pfcrg2cHdgCXIy9kErcjwGiu6BOevEA1qTW
Rk+bv0tknWvcz/s=
-----END PRIVATE KEY-----
`
// reduced version (no need to have that much data to debug this)
const inputXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DGICFE:EnvioCFE version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd"
xmlns:DGICFE="http://cfe.dgi.gub.uy"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DGICFE:Caratula version="1.0">
<DGICFE:RutReceptor>219999830019</DGICFE:RutReceptor>
</DGICFE:Caratula>
<ns0:CFE version="1.0"
xmlns:ns0="http://cfe.dgi.gub.uy">
<ns0:eFact>
<ns0:TmstFirma>2024-05-22T11:51:55-03:00</ns0:TmstFirma>
</ns0:eFact>
</ns0:CFE>
</DGICFE:EnvioCFE>
`
SignedXml.getKeyInfoContent = ({ publicCert, prefix }) => {
if (publicCert == null) return null
prefix = prefix ? `${prefix}:` : ''
let x509Certs = ''
if (Buffer.isBuffer(publicCert)) {
publicCert = publicCert.toString('latin1')
}
let publicCertMatches = []
if (typeof publicCert === 'string') {
publicCertMatches = publicCert.match(EXTRACT_X509_CERTS) || []
}
if (publicCertMatches.length > 0) {
x509Certs = publicCertMatches
.map(c => {
const certificate = forge.pki.certificateFromPem(c)
const issuerName = certificate.issuer.attributes.map(attr => `${attr.shortName}=${attr.value}`).join(', ')
const serialNumber = certificate.serialNumber
return (
`<${prefix}X509IssuerSerial>` +
`<${prefix}X509IssuerName>${issuerName}</${prefix}X509IssuerName>` +
`<${prefix}X509SerialNumber>${BigInt(`0x${serialNumber}`).toString()}</${prefix}X509SerialNumber>` +
`</${prefix}X509IssuerSerial>`
)
})
.join('')
}
return `<${prefix}X509Data>${x509Certs}</${prefix}X509Data>`
}
const firmarXml = (xml) => {
// const { privateKey, certificate } = retornaCertificado(certPath, password)
const sig = new SignedXml({ privateKey: privateKey, publicCert: certificate })
sig.signatureAlgorithm = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
sig.canonicalizationAlgorithm = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
sig.addReference({
xpath: "//*[local-name(.)='EnvioCFE']",
transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"],
digestAlgorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
isEmptyUri: true,
})
sig.computeSignature(xml, {
location: {
reference: "//*[local-name(.)='CFE']",
action: 'append',
},
})
return { Datain: { xmlData: sig.getSignedXml() } }
}
console.log(firmarXml(inputXml).Datain.xmlData) output is (and when pasted to chilkat it says that reference 1 digest is valid): <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DGICFE:EnvioCFE version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd" xmlns:DGICFE="http://cfe.dgi.gub.uy" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DGICFE:Caratula version="1.0">
<DGICFE:RutReceptor>219999830019</DGICFE:RutReceptor>
</DGICFE:Caratula>
<ns0:CFE version="1.0" xmlns:ns0="http://cfe.dgi.gub.uy">
<ns0:eFact>
<ns0:TmstFirma>2024-05-22T11:51:55-03:00</ns0:TmstFirma>
</ns0:eFact>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>sZ3A0cgXIJEMyI5hdGk4aRR8BzVrJgjBPyxBBuhwTXU=</DigestValue></Reference></SignedInfo><SignatureValue>ZFEOBc0jQpBdARFvSp5USZvxhgtL8T+XJpFprrsOIHHDf6SUKlhefqtpbbRMZ2ZRHvLlzomPGhO5BJIiomtB3UuRoEYbduU1wtxoQys8JK6qapan6P6bIUfv4sj1hWHAHWoE5XP9/0LEny8PFVLlbVh4LLcam/wwPWIQQblsmt4=</SignatureValue><KeyInfo><X509Data><X509IssuerSerial><X509IssuerName>CN=Root Agency</X509IssuerName><X509SerialNumber>262214055838675473468217939292187724434</X509SerialNumber></X509IssuerSerial></X509Data></KeyInfo></Signature></ns0:CFE>
</DGICFE:EnvioCFE> pretty printed version: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DGICFE:EnvioCFE xmlns:DGICFE="http://cfe.dgi.gub.uy" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd">
<DGICFE:Caratula version="1.0">
<DGICFE:RutReceptor>219999830019</DGICFE:RutReceptor>
</DGICFE:Caratula>
<ns0:CFE xmlns:ns0="http://cfe.dgi.gub.uy" version="1.0">
<ns0:eFact>
<ns0:TmstFirma>2024-05-22T11:51:55-03:00</ns0:TmstFirma>
</ns0:eFact>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>sZ3A0cgXIJEMyI5hdGk4aRR8BzVrJgjBPyxBBuhwTXU=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>ZFEOBc0jQpBdARFvSp5USZvxhgtL8T+XJpFprrsOIHHDf6SUKlhefqtpbbRMZ2ZRHvLlzomPGhO5BJIiomtB3UuRoEYbduU1wtxoQys8JK6qapan6P6bIUfv4sj1hWHAHWoE5XP9/0LEny8PFVLlbVh4LLcam/wwPWIQQblsmt4=</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Root Agency</X509IssuerName>
<X509SerialNumber>262214055838675473468217939292187724434</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</KeyInfo>
</Signature>
</ns0:CFE>
</DGICFE:EnvioCFE> Based on my assumtions (and I must emphasize again that I am NOT xml signature expert) if you sign some fragment / subtree of XML document you must add URI reference to that subtree. I.e. lets change this (from original reduced example code): isEmptyUri: true,
// to -->
isEmptyUri: false, i.e. your reduced example code would look something like this: //import forge from 'node-forge'
const forge=require('node-forge')
//import { readFileSync } from 'fs'
// ^ not needed to debugging
//import { SignedXml } from 'xml-crypto'
const SignedXml=require('xml-crypto').SignedXml
//import { createClient, WSSecurityCert } from 'soap'
// ^ not needed to debugging
//import { EXTRACT_X509_CERTS } from 'xml-crypto/lib/utils.js'
const EXTRACT_X509_CERTS=require('xml-crypto/lib/utils.js').EXTRACT_X509_CERTS
// https://raw.githubusercontent.com/node-saml/xml-crypto/21201723d2ca9bc11288f62cf72552b7d659b000/test/static/client_public.pem
var certificate = `
-----BEGIN CERTIFICATE-----
MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAW
MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEy
MzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPd
Vu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9x
O3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8juf
z2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEU
MBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcN
AQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5
sT/txBnVJGziyO8DPYdu2fPMER8ajJfl
-----END CERTIFICATE-----
`
// https://raw.githubusercontent.com/node-saml/xml-crypto/21201723d2ca9bc11288f62cf72552b7d659b000/test/static/client.pem
var privateKey = `
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL4vpoH3H3byehjj
7RAGxefGRATiq4mXtzc9Q91W7uT0DTaFEbjzVch9aGsNjmLs4QHsoZbuoUmi0st4
x5z9SQpTAKC/dW8muzacT3E7dJJYh03MAO6RiH4LG34VRTq1SQN6qDt2rCK85eG4
5NHI4jceptZNu6Zot1zyO5/PYuFpAgMBAAECgYAhspeyF3M/xB7WIixy1oBiXMLY
isESFAumgfhwU2LotkVRD6rgNl1QtMe3kCNWa9pCWQcYkxeI0IzA+JmFu2shVvoR
oL7eV4VCe1Af33z24E46+cY5grxNhHt/LyCnZKcitvCcrzXExUc5n6KngX0mMKgk
W7skZDwsnKzhyUV8wQJBAN2bQMeASQVOqdfqBdFgC/NPnKY2cuDi6h659QN1l+kg
X3ywdZ7KKftJo1G9l45SN9YpkyEd9zEO6PMFaufJvZUCQQDbtAWxk0i8BT3UTNWC
T/9bUQROPcGZagwwnRFByX7gpmfkf1ImIvbWVXSpX68/IjbjSkTw1nj/Yj1NwFZ0
nxeFAkEAzPhRpXVBlPgaXkvlz7AfvY+wW4hXHyyi0YK8XdPBi25XA5SPZiylQfjt
Z6iN6qSfYqYXoPT/c0/QJR+orvVJNQJBANhRPNXljVTK2GDCseoXd/ZiI5ohxg+W
UaA/1fDvQsRQM7TQA4NXI7BO/YmSk4rW1jIeOxjiIspY4MFAIh+7UL0CQFL6zTg6
wfeMlEZzvgqwCGoLuvTnqtvyg45z7pfcrg2cHdgCXIy9kErcjwGiu6BOevEA1qTW
Rk+bv0tknWvcz/s=
-----END PRIVATE KEY-----
`
// reduced version (no need to have that much data to debug this)
const inputXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DGICFE:EnvioCFE version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd"
xmlns:DGICFE="http://cfe.dgi.gub.uy"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DGICFE:Caratula version="1.0">
<DGICFE:RutReceptor>219999830019</DGICFE:RutReceptor>
</DGICFE:Caratula>
<ns0:CFE version="1.0"
xmlns:ns0="http://cfe.dgi.gub.uy">
<ns0:eFact>
<ns0:TmstFirma>2024-05-22T11:51:55-03:00</ns0:TmstFirma>
</ns0:eFact>
</ns0:CFE>
</DGICFE:EnvioCFE>
`
SignedXml.getKeyInfoContent = ({ publicCert, prefix }) => {
if (publicCert == null) return null
prefix = prefix ? `${prefix}:` : ''
let x509Certs = ''
if (Buffer.isBuffer(publicCert)) {
publicCert = publicCert.toString('latin1')
}
let publicCertMatches = []
if (typeof publicCert === 'string') {
publicCertMatches = publicCert.match(EXTRACT_X509_CERTS) || []
}
if (publicCertMatches.length > 0) {
x509Certs = publicCertMatches
.map(c => {
const certificate = forge.pki.certificateFromPem(c)
const issuerName = certificate.issuer.attributes.map(attr => `${attr.shortName}=${attr.value}`).join(', ')
const serialNumber = certificate.serialNumber
return (
`<${prefix}X509IssuerSerial>` +
`<${prefix}X509IssuerName>${issuerName}</${prefix}X509IssuerName>` +
`<${prefix}X509SerialNumber>${BigInt(`0x${serialNumber}`).toString()}</${prefix}X509SerialNumber>` +
`</${prefix}X509IssuerSerial>`
)
})
.join('')
}
return `<${prefix}X509Data>${x509Certs}</${prefix}X509Data>`
}
const firmarXml = (xml) => {
// const { privateKey, certificate } = retornaCertificado(certPath, password)
const sig = new SignedXml({ privateKey: privateKey, publicCert: certificate })
sig.signatureAlgorithm = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
sig.canonicalizationAlgorithm = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
sig.addReference({
xpath: "//*[local-name(.)='eFact']",
transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"],
digestAlgorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
isEmptyUri: false,
})
sig.computeSignature(xml, {
location: {
reference: "//*[local-name(.)='CFE']",
action: 'append',
},
})
return { Datain: { xmlData: sig.getSignedXml() } }
}
console.log(firmarXml(inputXml).Datain.xmlData) and resulting xml is (and when pasted to chilkat it reports that digests match) <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DGICFE:EnvioCFE version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd" xmlns:DGICFE="http://cfe.dgi.gub.uy" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DGICFE:Caratula version="1.0">
<DGICFE:RutReceptor>219999830019</DGICFE:RutReceptor>
</DGICFE:Caratula>
<ns0:CFE version="1.0" xmlns:ns0="http://cfe.dgi.gub.uy">
<ns0:eFact Id="_0">
<ns0:TmstFirma>2024-05-22T11:51:55-03:00</ns0:TmstFirma>
</ns0:eFact>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_0"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>F/TXqkksBOGoTcRU6pKJ5cCRPZtYGblpEeDz4aI+S6Y=</DigestValue></Reference></SignedInfo><SignatureValue>cMKIFLrUlgWxBKTe/TOPHpszA4B66l0noE8ED/sSCr7FimrmDs3U0Cx8bTI9C4Zc4bF+215N4pwVcdwqsq3/4LUUl9npmRNSfuQsbhUjdUMXclKALXXY/ORErWQVXDYRWmWAFhWmxiWYCVhP4Wx775ZnbCMqg86sm7VFjWBuP9Y=</SignatureValue><KeyInfo><X509Data><X509IssuerSerial><X509IssuerName>CN=Root Agency</X509IssuerName><X509SerialNumber>262214055838675473468217939292187724434</X509SerialNumber></X509IssuerSerial></X509Data></KeyInfo></Signature></ns0:CFE>
</DGICFE:EnvioCFE> and pretty printed: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DGICFE:EnvioCFE xmlns:DGICFE="http://cfe.dgi.gub.uy" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd">
<DGICFE:Caratula version="1.0">
<DGICFE:RutReceptor>219999830019</DGICFE:RutReceptor>
</DGICFE:Caratula>
<ns0:CFE xmlns:ns0="http://cfe.dgi.gub.uy" version="1.0">
<ns0:eFact Id="_0">
<ns0:TmstFirma>2024-05-22T11:51:55-03:00</ns0:TmstFirma>
</ns0:eFact>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="#_0">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>F/TXqkksBOGoTcRU6pKJ5cCRPZtYGblpEeDz4aI+S6Y=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>cMKIFLrUlgWxBKTe/TOPHpszA4B66l0noE8ED/sSCr7FimrmDs3U0Cx8bTI9C4Zc4bF+215N4pwVcdwqsq3/4LUUl9npmRNSfuQsbhUjdUMXclKALXXY/ORErWQVXDYRWmWAFhWmxiWYCVhP4Wx775ZnbCMqg86sm7VFjWBuP9Y=</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Root Agency</X509IssuerName>
<X509SerialNumber>262214055838675473468217939292187724434</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</KeyInfo>
</Signature>
</ns0:CFE>
</DGICFE:EnvioCFE> Here is link to https://www.w3.org/TR/2008/REC-xmldsig-core-20080610/ for you in order to be able to make your own interpretation of spec. Happy debugging / spec interpretation and/or maybe someone with more knowledge about xml signature can jump in and continue from this point on. |
Beta Was this translation helpful? Give feedback.
-
Thank you so much for de response. Indeed im using "node-forge": "^1.3.1" and “xml-crypto": "^6.0.0”. Yesterday i was trying to replicate your recommendations and i didn't get the digest value error anymore, but the signature still is invalid for some reason that i cant find . I used your generated certificate and private key too, but the result was the same when i use chillket to test. Does anyone know why this could be happening? |
Beta Was this translation helpful? Give feedback.
-
Adding more context to the problem: In each EnvioCFE we should put a lot of CFEs, each one with his individual signature for his subtree. Like in this example: <?xml version="1.0" encoding="iso-8859-1"?>
<DGICFE:EnvioCFE version="1.0" xsi:schemaLocation="http://cfe.dgi.gub.uy EnvioCFE_v1.24.xsd"
xmlns:DGICFE="http://cfe.dgi.gub.uy"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DGICFE:Caratula version="1.0"> Bla bla bla </DGICFE:Caratula>
<ns0:CFE version="1.0" xmlns:ns0="http://cfe.dgi.gub.uy">
<ns0:eTck>
<ns0:TmstFirma>2016-07-19T15:56:20-03:00</ns0:TmstFirma>
</ns0:eTck>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>xpXiLtg0nWejadP44Gm7ArFMk7Y=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>ke8XBfPeT4OzEK5enxCJ6nK367fS9RFx007Yy+A8NAz1+cPIfSgqJhg68nesUCy/XlxbYkFAEU62GvhDg/XdiBFzkwMXac896pVFZr4S45v4FZX3YkosGIi64honz/2tvyUiN977Ek0HtLMoz6DVK5L/5FspvanZqJLEDqEGqJk=</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Correo Uruguayo - CA, OU=SERVICIOS ELECTRONICOS, O=ADMINISTRACION NACIONAL DE CORREOS, C=UY</X509IssuerName>
<X509SerialNumber>155761856642617054135126896023459966393</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</KeyInfo>
</Signature>
</ns0:CFE>
<ns0:CFE version="1.0" xmlns:ns0="http://cfe.dgi.gub.uy">
<ns0:eTck>
<ns0:TmstFirma>2016-07-19T15:56:20-03:00</ns0:TmstFirma>
</ns0:eTck>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>wwNHpZT9gDKlCUalcLcwo5DIkNk=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Oa6tS9NreWBwRR8RhT2rTOr4Gm3OSWklWzKR3Z+u+LGzQo1C+EskajdxmzdkKvVMgQ4O9CmNL+6RILCFTtFi7j1jFVuAyH5eejLNnNJxFziS0FMYj0uLdj6lqkEO+txlYMXhUG8/k/ZKXC3Ur9Em2/MCAYZB+qFw3DBe7OZcxEk=</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Correo Uruguayo - CA, OU=SERVICIOS ELECTRONICOS, O=ADMINISTRACION NACIONAL DE CORREOS, C=UY</X509IssuerName>
<X509SerialNumber>155761856642617054135126896023459966393</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</KeyInfo>
</Signature>
</ns0:CFE>
<ns0:CFE version="1.0" xmlns:ns0="http://cfe.dgi.gub.uy">
<ns0:eTck>
<ns0:TmstFirma>2016-07-19T15:56:20-03:00</ns0:TmstFirma>
</ns0:eTck>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>p1VrpYT0zI7huek2QhjKTGbCBsY=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Ad2/KMLEZgYPxlDVBUenLJ1L/AGYd9cskYzk09vJr2e6qoO/y6Swt5J5eN2ruPpNDj5HK80Ric+IiEDUdLEjBaveg/6Ao7v8v+biDUWCii7uWhGZ8wfyyEXbcrsZ+oATMmn0g36JWI3nFMkPlTkKvIIGJHPJR8xIQxcOI3vVgr0=</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Correo Uruguayo - CA, OU=SERVICIOS ELECTRONICOS, O=ADMINISTRACION NACIONAL DE CORREOS, C=UY</X509IssuerName>
<X509SerialNumber>155761856642617054135126896023459966393</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</KeyInfo>
</Signature>
</ns0:CFE>
Having spoken with the institution that receives and validates the xml (They practically have no idea how it works. So if then i use the chillkat to comprobate if digest value is correct i only should pass a signed subsecciton? I'm not entirely sure how to do it with the library. I appreciate your understanding for my lack of knowledge on the subject. Any other information you need that may help understand the problem, please ask me. |
Beta Was this translation helpful? Give feedback.
-
Chilkat doesn't have your private key's public certificate which it could use to verify signature. Use some other online service or use some other tool like: xmlsec1 --verify ... (I don't have time to lookup and provide rest of options for that)
I get a feeling that no-one knows what proper outcome should be in your application's case. I.e. admins at the receiving side cannot provide you spec and therefore they are also unable to check that you are signing stuff correctly and that they are performing proper validation of input. IMHO |
Beta Was this translation helpful? Give feedback.
-
Hello. Im getting a problem when i try to sign an xml document. I readed some issues related but i cant fin de solution yet.
After executing my code, y tried to validate the signature with this tool https://tools.chilkat.io/xmlDsigVerify.cshtml and i get:
Right now my code is this:
and the xml im trying to sign is this, with the signature generated there
If someone knows the reason off this problem please share it, im new in the word of signing files, so every help is welcome.
Beta Was this translation helpful? Give feedback.
All reactions