Closed antoniowd closed 4 years ago
Hello!
This might help you:
sig.computeSignature(xml, { location: { reference: "//*[local-name(.)='ExtensionContent']", action: "prepend" }, prefix: 'ds', attrs: {Id: 'SignatureSP'} });
Compatriota también estas implementando F.E. en nodejs? podemos ayudarnos!
Hi Diego. After trying some variants I found the solution. It's exactly as you say. En efecto estoy haciendo una version de FE en node.js. Como podemos hablar mas?
por hangouts? dfloresgonz@gmail.com por alli compartimos num_celular
@tonitim13 @dfloresgonz compatriotas estoy atorado a ver si a uds les ha pasado firmo mi xml y cuando lo envío a la SUNAT me responde esto:
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<soap-env:Header/>
<soap-env:Body>
<soap-env:Fault>
<faultcode>soap-env:Client.2335</faultcode>
<faultstring>El documento electrónico ingresado ha sido alterado - Detalle: Incorrect reference digest value</faultstring>
</soap-env:Fault>
</soap-env:Body>
</soap-env:Envelope>
en mi xml firmado hay algunas cosas que son diferentes a lo que deberían, por ejemplo en tag
<ds:Reference URI="#_0">
debería ser <ds:Reference URI="">
Hay otras diferencias (comparo con un comprobante que sí pasa la validación) como el ds:X509Certificate
que está como que separado en partes y creo que debería ser un string. También está el <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SignatureSP">
que debería tener solo Id
y <ext:ExtensionContent Id="_0">
que NO debería tener Id
https://i.imgur.com/aDAZz9i.png
EDIT:----------------------
arreglé lo del <ds:Reference URI="">
😃
lo demás sigue igual y sigo con el mismo error 😢
@kamilml Puedes enviar tu xml que es rechazado?
Hola tengo el mismo problema, al generar mi documento se realiza el firmado pero la respuesta es: El documento electrónico ingresado ha sido alterado - Detalle: Incorrect reference digest value
Este es mi documento XML:
`<?xml version='1.0' encoding='UTF-8'?>
@acporras Prueba esto para hacer tu firmado: Lss funciones crearPEM_from_PFX y crearXML son funciones útiles que creé, si los necesitas me avisas, pero creo que te servirá más saber cómo crear el objeto new SignedXml()
localName = 'Invoice';
pin_certif = 'clave de tu certificado';
ruta_certif = 'ruta donde esta tu certificado';
rutaXML = 'ruta donde esta tu xml';
function signXML(rutaXML, ruta_certif, pin_certif, localName) {
return new Promise( async (resolve, reject) => {
try {
let SignedXml = require('xml-crypto').SignedXml;
let DOMParser = require('xmldom').DOMParser;
///////////////////////////////
let xml = fs.readFileSync(rutaXML, 'utf8');
///
let json_pem = await crearPEM_from_PFX(ruta_certif, pin_certif);
///
let transf = ['http://www.w3.org/2000/09/xmldsig#enveloped-signature'];
let sig = new SignedXml()
sig.addReference(`//*[local-name(.)='${localName}']`, transf, '', '', '', '', true)
sig.signingKey = new Buffer(json_pem.value.key);
sig.keyInfoProvider = KeyInfoProvider(json_pem.value);
sig.canonicalizationAlgorithm = 'http://www.w3.org/2001/10/xml-exc-c14n#';
sig.signatureAlgorithm = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
sig.computeSignature(xml, { location: { reference: `//*[local-name(.)='ExtensionContent']`}, prefix: 'ds', attrs: {Id: 'SignatureSP'} });
const __signedXML = sig.getSignedXml();
await crearXML(rutaXML, __signedXML);
///////////////////////////////
let parser = new DOMParser();
let doc = parser.parseFromString(__signedXML, 'text/xml');
let hash_cpe = doc.getElementsByTagName('ds:DigestValue')[0].firstChild.data;
return resolve({ msj : 'El xml ha sido firmado', hash_cpe : hash_cpe, __signedXML : __signedXML });
} catch(err) {
return reject({ msj : 'Hubo un error al firmar el XML', stack : err });
}
});
}
hola @dfloresgonz gracias por tu respuesta, reutilice la función que me has brindado y a eso después de la siguiente linea:
let hash_cpe = doc.getElementsByTagName('ds:DigestValue')[0].firstChild.data;
Añadí el siguiente código solo para validar mi firma:
var Signature = doc.getElementsByTagName('ds:Signature')[0] sig.loadSignature(xmlserializer.serializeToString(Signature)); var res = sig.checkSignature(__signedXML); if (!res) { console.error(sig.validationErrors); } else { console.log('success'); }
por lo que obtengo el siguiente mensaje:
invalid signature: for uri calculated digest is "primer hash" but the xml to validate supplies digest "segundo hash diferente" es decir que mi firma aun sigue estando mal calculada, tu función me ha ayudado a darme cuenta de que probablemente estoy realizando mal la obtención del proveedor esta es mi función:
`function MyKeyInfo(pem) { this.getKeyInfo = function (key, prefix) { const certLines = pem.cert.split('\n'); let dat = certLines.filter((e, i) => i && e && e.indexOf('-----') !== 0). join('');
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
return `<${prefix}X509Data><${prefix}X509Certificate>${dat}</${prefix}X509Certificate></${prefix}X509Data>`
}
this.getCert = function () {
//you can use the keyInfo parameter to extract the key in any way you want
const certLines = pem.cert.split('\n');
return certLines.filter((e, i) => i && e && e.indexOf('-----') !== 0).
join('');
}
this.getKey = function (keyinfo) {
return pem.key
}
}` el cual instancio de la siguiente forma:
sig.keyInfoProvider = new MyKeyInfo(cert);
si pudieras apoyarme mostrándome tu función para poder compararla con la mía te lo agradecería mucho.
Ok usa este KeyInfoProvider, puedes modificarlo para que tu prefix este dinamico en mi caso lo uso estatico.
function KeyInfoProvider(pem) {
return {
getKey() {
const cert = this.getCert();
return `<ds:X509Data><ds:X509Certificate>${cert}</ds:X509Certificate></ds:X509Data>`;
},
getKeyInfo() {
const cert = this.getCert();
return `<ds:X509Data><ds:X509Certificate>${cert}</ds:X509Certificate></ds:X509Data>`;
},
getCert() {
try {
const certLines = pem.cert.split('\n');
return certLines.filter((e, i) => i && e && e.indexOf('-----') !== 0).join('');
} catch (err) {
throw Error(err);
}
}
};
}
Hola amigos, su apoyo por favor: Al firmar una factura xml tengo el siguiente mensaje de error de validación:
SignatureValue SÍ es válido La forma canónica del nodo SignedInfo es
Al enviarlo a la SUNAT me indica que el documento ha sido alterado
Este es mi código donde realizo la firma del xml: ` var sig = new SignedXml() const localName = 'Invoice'; const cpath = "//[name()='ext:UBLExtensions']/[name()='ext:UBLExtension']/[name()='ext:ExtensionContent']"; const transf = ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"];
sig.addReference(`//*[local-name(.)='${localName}']`, transf, '', '', '', '', true)
sig.signingKey = fs.readFileSync(pathfilepem)
sig.keyInfoProvider = new MyKeyInfo()
sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"
sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
sig.computeSignature(xml, {
location:{
reference: "//*[local-name(.)='ExtensionContent']"
},
prefix: 'ds',
attrs: {Id: 'SignatureSP'}
});
fs.writeFileSync(`${pathfilexml}`, sig.getSignedXml())`
Y este es el código de mi función MyKeyInfo()
`function MyKeyInfo() { this.getKeyInfo = function(key, prefix) { var keyInfoXml, certObj, certBodyInB64;
prefix = prefix || '';
prefix = prefix ? prefix + ':' : prefix;
certBodyInB64 = forge.util.encode64(forge.pem.decode(certificatePEM)[0].body);
certObj = pki.certificateFromPem(certificatePEM);
keyInfoXml = '<' + prefix + 'X509Data>';
keyInfoXml += '<' + prefix + 'X509SubjectName>';
keyInfoXml += getSubjectName(certObj);
keyInfoXml += '</' + prefix + 'X509SubjectName>';
keyInfoXml += '<' + prefix + 'X509Certificate>';
keyInfoXml += certBodyInB64;
keyInfoXml += '</' + prefix + 'X509Certificate>';
keyInfoXml += '</' + prefix + 'X509Data>';
return keyInfoXml;
}
}`
`function getSubjectName(certObj) { var subjectFields, fields = ['CN', 'OU', 'O', 'L', 'ST', 'C'];
if (certObj.subject) {
subjectFields = fields.reduce(function(subjects, fieldName) {
var certAttr = certObj.subject.getField(fieldName);
if (certAttr) {
subjects.push(fieldName + '=' + certAttr.value);
}
return subjects;
}, []);
}
return Array.isArray(subjectFields) ? subjectFields.join(',') : '';
}`
Desde ya gracias por tu valioso apoyo
@tonitim13 @dfloresgonz compatriotas estoy atorado a ver si a uds les ha pasado firmo mi xml y cuando lo envío a la SUNAT me responde esto:
<?xml version="1.0" encoding="UTF-8"?> <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <soap-env:Header/> <soap-env:Body> <soap-env:Fault> <faultcode>soap-env:Client.2335</faultcode> <faultstring>El documento electrónico ingresado ha sido alterado - Detalle: Incorrect reference digest value</faultstring> </soap-env:Fault> </soap-env:Body> </soap-env:Envelope>
en mi xml firmado hay algunas cosas que son diferentes a lo que deberían, por ejemplo en tag
<ds:Reference URI="#_0">
debería ser
<ds:Reference URI="">
Hay otras diferencias (comparo con un comprobante que sí pasa la validación) como el
ds:X509Certificate
que está como que separado en partes y creo que debería ser un string. También está el<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SignatureSP">
que debería tener soloId
y<ext:ExtensionContent Id="_0">
que NO debería tenerId
https://i.imgur.com/aDAZz9i.pngEDIT:---------------------- arreglé lo del
<ds:Reference URI="">
😃 lo demás sigue igual y sigo con el mismo error 😢
Tengo el mismo problema, @kamilml como lo solucionaste?
@erwn2793 Hi! How did you fix <ds:Reference URI="#_0">
?
@erwn2793 Hi! How did you fix
<ds:Reference URI="#_0">
?
Hi! consider this configuration:
sig.addReference( "//*[local-name(.)='ExtensionContent']", ['http://www.w3.org/2000/09/xmldsig#enveloped-signature'], 'http://www.w3.org/2000/09/xmldsig#sha1', '', '', '', true );
The last value is that defines that part.
@erwn2793 muchas gracias!
@acporras Prueba esto para hacer tu firmado: Lss funciones crearPEM_from_PFX y crearXML son funciones útiles que creé, si los necesitas me avisas, pero creo que te servirá más saber cómo crear el objeto new SignedXml()
localName = 'Invoice'; pin_certif = 'clave de tu certificado'; ruta_certif = 'ruta donde esta tu certificado'; rutaXML = 'ruta donde esta tu xml'; function signXML(rutaXML, ruta_certif, pin_certif, localName) { return new Promise( async (resolve, reject) => { try { let SignedXml = require('xml-crypto').SignedXml; let DOMParser = require('xmldom').DOMParser; /////////////////////////////// let xml = fs.readFileSync(rutaXML, 'utf8'); /// let json_pem = await crearPEM_from_PFX(ruta_certif, pin_certif); /// let transf = ['http://www.w3.org/2000/09/xmldsig#enveloped-signature']; let sig = new SignedXml() sig.addReference(`//*[local-name(.)='${localName}']`, transf, '', '', '', '', true) sig.signingKey = new Buffer(json_pem.value.key); sig.keyInfoProvider = KeyInfoProvider(json_pem.value); sig.canonicalizationAlgorithm = 'http://www.w3.org/2001/10/xml-exc-c14n#'; sig.signatureAlgorithm = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'; sig.computeSignature(xml, { location: { reference: `//*[local-name(.)='ExtensionContent']`}, prefix: 'ds', attrs: {Id: 'SignatureSP'} }); const __signedXML = sig.getSignedXml(); await crearXML(rutaXML, __signedXML); /////////////////////////////// let parser = new DOMParser(); let doc = parser.parseFromString(__signedXML, 'text/xml'); let hash_cpe = doc.getElementsByTagName('ds:DigestValue')[0].firstChild.data; return resolve({ msj : 'El xml ha sido firmado', hash_cpe : hash_cpe, __signedXML : __signedXML }); } catch(err) { return reject({ msj : 'Hubo un error al firmar el XML', stack : err }); } }); }
@dfloresgonz Como escribes el xml?, ya logré realizar la firma y se valida de forma correcta pero cuando envío el xml a sunat me devuelve que el documento fue alterado, sin embargo si pinto el xml en un log y lo escribo manualmente como xml sunat lo acepta
@acporras si pudieras apoyarme con las funciones que creaste para probar el codigo te lo agradeceria bastante, joe.bm@msn.com
Hola gente,
Por si lo necesitan:
function crearXML(rutaFile, xmlData) {
return new Promise( (resolve, reject) => {
try {
fs.writeFileSync(rutaFile, xmlData, 'utf8');
return resolve({ msj : 'Se creo el xml' });
} catch(err) {
return reject({ err : err, msj : 'Hubo un error al crear el XML' });
}
});
}
Ojo aqui con la ruta del openssl lo puse asi porque lo tengo subido en AWS
function crearPEM_from_PFX(ruta_certif, pin_certif) {
return new Promise( (resolve, reject) => {
try {
const pem = require('pem');
pem.config({
pathOpenSSL: path.resolve('/opt/nodejs/openssl/bin/openssl')
});
const pfx = fs.readFileSync(ruta_certif);
pem.readPkcs12(pfx, { p12Password : pin_certif }, async (err, new_pem) => {
if(err) {
return reject({ msj : 'Hubo un error en el pem.readPkcs12', stack : err });
}
return resolve({ value : new_pem });
});
} catch(err) {
return reject({ err : err, msj : 'Hubo un error al crear el PEM' });
}
});
}
Hola gente,
Por si lo necesitan:
function crearXML(rutaFile, xmlData) { return new Promise( (resolve, reject) => { try { fs.writeFileSync(rutaFile, xmlData, 'utf8'); return resolve({ msj : 'Se creo el xml' }); } catch(err) { return reject({ err : err, msj : 'Hubo un error al crear el XML' }); } }); }
Ojo aqui con la ruta del openssl lo puse asi porque lo tengo subido en AWS
function crearPEM_from_PFX(ruta_certif, pin_certif) { return new Promise( (resolve, reject) => { try { const pem = require('pem'); pem.config({ pathOpenSSL: path.resolve('/opt/nodejs/openssl/bin/openssl') }); const pfx = fs.readFileSync(ruta_certif); pem.readPkcs12(pfx, { p12Password : pin_certif }, async (err, new_pem) => { if(err) { return reject({ msj : 'Hubo un error en el pem.readPkcs12', stack : err }); } return resolve({ value : new_pem }); }); } catch(err) { return reject({ err : err, msj : 'Hubo un error al crear el PEM' }); } }); }
Gracias!!! funciono para mi.
@dfloresgonz tmb estoy intentado implementar FE en node, pero mis métodos no funcionan, me sale el error de q el documento es alterado, probé con tus funciones mencionadas y sigue igual, el error es el siguiente
`SignatureValue SÍ es válido La forma canónica del nodo SignedInfo es
Buenas, lograron solucionar esto: Signature Id="SignatureSP" xmlns="http://www.w3.org/2000/09/xmldsig#"
Hola compatriotas, buenos dias, una consulta lograon solucionar el mensaje de: "El documento electrónico ingresado ha sido alterado - Detalle: Incorrect reference digest value", estoy buscando alguna opcion y no logro obtenerla.
Hi, I have a doubt. How can I reference the tag with the xpath?
`<?xml version="1.0"?> <Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ccts="urn:un:unece:uncefact:documentation:2" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns:qdt="urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2" xmlns:udt="urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2">