Repository navigation
digest is invalid because the computed digest differs from the digest in the XML #472
Copy link
Copy link
Closed
Description
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:
Signature is Invalid
Number of Reference Digests = 1
Reference 1 digest is invalid because the computed digest differs from the digest in the XML.
Right now my code is this:
import forge from 'node-forge'
import { readFileSync } from 'fs'
import { SignedXml } from 'xml-crypto'
import { createClient, WSSecurityCert } from 'soap'
import { EXTRACT_X509_CERTS } from 'xml-crypto/lib/utils.js'
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, certPath, password) => {
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() } }
}and the xml im trying to sign is this, with the signature generated there
<?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:RUCEmisor>219470980013</DGICFE:RUCEmisor>
<DGICFE:Idemisor>3169</DGICFE:Idemisor>
<DGICFE:CantCFE>1</DGICFE:CantCFE>
<DGICFE:Fecha>2024-05-20T13:10:00-03:00</DGICFE:Fecha>
<DGICFE:X509Certificate>MIIGwzCCBKugAwIBAgIVAJq2gU...</DGICFE:X509Certificate>
</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:Encabezado>
<ns0:IdDoc>
<ns0:TipoCFE>111</ns0:TipoCFE>
<ns0:Serie>A</ns0:Serie>
<ns0:Nro>1</ns0:Nro>
<ns0:FchEmis>2024-04-25</ns0:FchEmis>
<ns0:FmaPago>1</ns0:FmaPago>
</ns0:IdDoc>
<ns0:Emisor>
<ns0:RUCEmisor>219470980013</ns0:RUCEmisor>
<ns0:RznSoc>DGI PRUEBA SERVICIOS WEB</ns0:RznSoc>
<ns0:CdgDGISucur>1</ns0:CdgDGISucur>
<ns0:DomFiscal>FERNANDEZ CRESPO AVDA. DANIEL 1534</ns0:DomFiscal>
<ns0:Ciudad>MONTEVIDEO</ns0:Ciudad>
<ns0:Departamento>MONTEVIDEO</ns0:Departamento>
</ns0:Emisor>
<ns0:Receptor>
<ns0:TipoDocRecep>2</ns0:TipoDocRecep>
<ns0:CodPaisRecep>UY</ns0:CodPaisRecep>
<ns0:DocRecep>214844360018</ns0:DocRecep>
<ns0:RznSocRecep>DGI</ns0:RznSocRecep>
<ns0:DirRecep>Fernandez Crespo 1534</ns0:DirRecep>
<ns0:CiudadRecep>Montevideo</ns0:CiudadRecep>
</ns0:Receptor>
<ns0:Totales>
<ns0:TpoMoneda>UYU</ns0:TpoMoneda>
<ns0:MntNetoIVATasaBasica>20000</ns0:MntNetoIVATasaBasica>
<ns0:IVATasaMin>10</ns0:IVATasaMin>
<ns0:IVATasaBasica>22</ns0:IVATasaBasica>
<ns0:MntIVATasaBasica>4400</ns0:MntIVATasaBasica>
<ns0:MntTotal>24400</ns0:MntTotal>
<ns0:CantLinDet>3</ns0:CantLinDet>
<ns0:MontoNF>5000</ns0:MontoNF>
<ns0:MntPagar>29400</ns0:MntPagar>
</ns0:Totales>
</ns0:Encabezado>
<ns0:Detalle>
<ns0:Item>
<ns0:NroLinDet>1</ns0:NroLinDet>
<ns0:IndFact>3</ns0:IndFact>
<ns0:NomItem>Harina</ns0:NomItem>
<ns0:Cantidad>10</ns0:Cantidad>
<ns0:UniMed>kg</ns0:UniMed>
<ns0:PrecioUnitario>1000</ns0:PrecioUnitario>
<ns0:MontoItem>10000</ns0:MontoItem>
</ns0:Item>
<ns0:Item>
<ns0:NroLinDet>2</ns0:NroLinDet>
<ns0:IndFact>3</ns0:IndFact>
<ns0:NomItem>Azucar</ns0:NomItem>
<ns0:Cantidad>5</ns0:Cantidad>
<ns0:UniMed>kg</ns0:UniMed>
<ns0:PrecioUnitario>2000</ns0:PrecioUnitario>
<ns0:MontoItem>10000</ns0:MontoItem>
</ns0:Item>
<ns0:Item>
<ns0:NroLinDet>3</ns0:NroLinDet>
<ns0:IndFact>6</ns0:IndFact>
<ns0:NomItem>Bicicleta</ns0:NomItem>
<ns0:Cantidad>1</ns0:Cantidad>
<ns0:UniMed>N/A</ns0:UniMed>
<ns0:PrecioUnitario>5000</ns0:PrecioUnitario>
<ns0:MontoItem>5000</ns0:MontoItem>
</ns0:Item>
</ns0:Detalle>
<ns0:CAEData>
<ns0:CAE_ID>90240894778</ns0:CAE_ID>
<ns0:DNro>1</ns0:DNro>
<ns0:HNro>100</ns0:HNro>
<ns0:FecVenc>2026-04-24</ns0:FecVenc>
</ns0:CAEData>
</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>Rd5Fog4wR0GkbCFCFG41hfzIViWcwzHiEAV+/zRwQZE=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Y6yZwGr/ZrsfXXSyiQyW8fyOvbZyXKXVihlom61/H8tiafAKJm8RpRqKa1dHt2pv072W5srOTb9xYqIaySLzm45TLoKpl526msbOAcCPK+PJzCQIRJB+PkPAQ7m6QXcSvUd0NsxvwZnklBDo2q7/HVv4JfM+8FKt4cgn4KAnOJxM4A2SfQ3hhFK3Uw24dsWaBNTx8ChmBf308czgjaL2RpYWsfCbuCyVROPr7rIbFYcbSvELAdtHtcjI6NTOGciICiV02t/KqH4qk6IHqnTj3elVSkVcKweI8lRDNurMfrJlZ4iKEJo8DKIlWYm5tNoBSRLkcsZNpcRqo53o6DviOg==</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>C=UY, L=Montevideo, O=Abitab S.A., OU=ID digital, CN=Abitab</X509IssuerName>
<X509SerialNumber>883254578286120403153077429716205748582139371646</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</KeyInfo>
</Signature>
</ns0:CFE>
</DGICFE:EnvioCFE>If someone knows the reason off this problem please share it, im new in the word of signing files, so every help is welcome.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
srd90 commentedon May 28, 2024
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):
and when following result is pasted to chilkat it infact says that digests do not match:
pretty formatted version being:
What caught my eye is that you select document fragment to be signed but you set
Reference UR="". I have thought that this would mean that when such reference is validated it must take whole document (starting from the root).I must emphasize that I am NOT xml signature expert.
But lets test this assumption and modify
i.e. test code is
output is (and when pasted to chilkat it says that reference 1 digest is valid):
pretty printed version:
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):
i.e. your reduced example code would look something like this:
and resulting xml is (and when pasted to chilkat it reports that digests match)
Notice how xml-crypto added implicitily
Id="_0"toeFact:and pretty printed:
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.
MateoF01 commentedon May 29, 2024
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?
MateoF01 commentedon May 29, 2024
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:
Having spoken with the institution that receives and validates the xml (They practically have no idea how it works.
), I think if i sign an CFE, the digest value must be generated by the canonization of the subsection of that CFE.
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.
srd90 commentedon May 29, 2024
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:
(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
xml-crypto's bug ticket is not correct place to have this discussion. Instead of having this bug ticket #472 this should have been discussion.