La huella del documento: qué función hash usa tu firma digital, SHA-1 frente a SHA-256 y qué acepta SUNAT en el XML
Diego programa el módulo de facturación de una ferretería de Chiclayo (el caso es inventado). Su sistema firma el XML con SHA-1 porque así venía en el código de ejemplo que encontró. Lee en un foro que «SUNAT ya no acepta SHA-1» y decide comprobarlo: ejecuta openssl dgst -sha1 -sign en su Fedora y recibe invalid digest. Conclusión: tenían razón. Solo que ese rechazo lo dio su propio equipo, no SUNAT.
Este artículo separa las dos cosas. Leímos el 23 de octubre de 2026 la hoja Firma de las reglas de validación de SUNAT, el manual del programador, las resoluciones de superintendencia 097-2012 y 117-2017, la declaración de prácticas de ECEP-RENIEC y la especificación XMLDSig del W3C, y ejecutamos los comandos con OpenSSL 3.5.8 y xmlsec1 1.2.41. Lo que dice cada texto va con su cita. Lo que concluimos nosotros va marcado como «nuestra lectura». El tamaño del par RSA es otro tema, que tiene su propio artículo, y cómo se firma por dentro lo explica el pilar de esta serie.
Tres lugares donde aparece un hash
En una factura firmada hay más de una función hash en juego, y la confusión de Diego nace de mezclarlas.
- La huella del documento. Se guarda en
ds:DigestValuey el algoritmo se declara ends:DigestMethod/@Algorithm. Es la que cambia si alguien toca una cifra del XML. - La huella de
ds:SignedInfo, que es lo que firma la llave privada del titular. Su algoritmo se declara ends:SignatureMethod/@Algorithm, por ejemplorsa-sha256: RSA para la operación con la llave privada, SHA-256 para la huella. - La firma de la entidad de certificación sobre tu certificado. Se ve en el campo
Signature Algorithmdel certificado. La eligió la entidad, no tu programa.
El pilar ya mostró el mecanismo con OpenSSL; aquí solo nos interesa que son tres decisiones distintas, de tres actores distintos. Tu programa decide las dos primeras. La tercera viene con el certificado.
Qué es una huella de 20 bytes y una de 32
Con dos textos que solo difieren en una cifra (118.00 y 119.00), el resultado real fue este:
$ sha1sum f1.txt f2.txt
dce3471594308ca78fa5dcdeee00b65d97e80afa f1.txt
1123c2ae4efdd54845a8b25b9f5bf50e6a61b20b f2.txt
$ sha256sum f1.txt f2.txt
7ac19a64a13470f1eacaf846637967bf6f61328d8cbace5f69429e3cef4534a4 f1.txt
68d9b55b2c3a2914cc312d83cb14f880fefa07443f26ae3472c05aec2480b0da f2.txt
SHA-1 produce 160 bits (20 bytes), SHA-256 produce 256 (32 bytes). En el XML esa huella va en Base64, y la diferencia se ve a simple vista:
$ openssl dgst -sha1 -binary f1.txt | base64
3ONHFZQwjKePpdze7gC2XZfoCvo=
$ openssl dgst -sha256 -binary f1.txt | base64
esGaZKE0cPHqyvhGY3lnv29hMo2Mus5faUKePO9FNKQ=
Veintiocho caracteres frente a cuarenta y cuatro. Si tienes un XML firmado delante, el largo del DigestValue te dice qué hash usó antes de abrir nada más. Probamos con el que aparece en el manual del programador de SUNAT, 2Hp6yx1+sD9H6n0hDMC625+I40U=: decodificado, son 20 bytes, es decir, SHA-1.
Los identificadores: lo que el W3C pone en cada URI
El valor de @Algorithm no es un nombre libre sino una URI definida por la recomendación XML Signature Syntax and Processing Version 1.1 (W3C, 11 de abril de 2013). Los cuatro que importan aquí:
| Qué declara | SHA-1 | SHA-256 |
|---|---|---|
DigestMethod | http://www.w3.org/2000/09/xmldsig#sha1 | http://www.w3.org/2001/04/xmlenc#sha256 |
SignatureMethod con RSA | http://www.w3.org/2000/09/xmldsig#rsa-sha1 | http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 |
Fíjate en que el de SHA-256 para la huella cuelga de xmlenc#, no de xmldsig#: es un error frecuente al copiar. Sobre cuál usar, la especificación es explícita en su apartado 6.2:
«Use of SHA-256 is strongly recommended over SHA-1 because recent advances in cryptanalysis (see e.g. [SHA-1-Analysis]) have cast doubt on the long-term collision resistance of SHA-1.»
Y añade que el soporte de SHA-1 es obligatorio «only for backwards-compatibility reasons». En las tablas de identificadores, SHA-1 y RSA con SHA-1 llevan la marca «Use is DISCOURAGED; see SHA-1 Warning». Nuestra lectura: el W3C no prohíbe SHA-1, lo desaconseja, y distingue entre verificar firmas antiguas y producir firmas nuevas. Para el RSA con SHA-1 dice «signature verification; use for signature generation is DISCOURAGED».
Lo que declara ECEP-RENIEC
La declaración de prácticas de ECEP-RENIEC, versión 5.1 (4 de junio de 2026), numeral 6.1.5, trae la tabla 8 «Algoritmo de firma y tamaño de llave»: los niveles offline y online usan sha512WithRSAEncryption con 4096 bits, y las entidades finales sha256WithRSAEncryption con 2048 bits. Es el tercer lugar de la lista de arriba, el de la firma de la entidad sobre tu certificado. Si tu certificado sale de ECEP-RENIEC o de una entidad que siga ese perfil, el campo Signature Algorithm dirá sha256WithRSAEncryption. Lo que ese documento no dice es qué debe usar tu programa para firmar el XML; no es su materia.
SHA-1 aparece en esa declaración en otro papel: los identificadores de llave de sujeto y de autoridad, definidos como un «resumen SHA-1 (160 bits)» de la llave pública. Sirven para identificar una llave, no para firmar nada, y no cuentan como firma con SHA-1.
Lo que lee SUNAT en el XML: la hoja Firma
Las reglas de validación de comprobantes (Excel «actualizado al 26.08.2026», md5 72e866f7960c811da16c8cc71ed8b303, la versión que pudimos bajar hoy de cpe.sunat.gob.pe) tienen una hoja Firma con 25 reglas para los bloques ds:Signature y cac:Signature. Las cuatro que tocan algoritmos son estas:
| Tag | Condición | Error | Mensaje |
|---|---|---|---|
ds:CanonicalizationMethod/@Algorithm | No existe / formato distinto de alfanumérico de hasta 3000 caracteres | 2087 / 2086 | «No cumple con el estandar» |
ds:SignatureMethod/@Algorithm | No existe / ídem | 2089 / 2088 | ídem |
ds:Transform@Algorithm | No existe / ídem | 2093 / 2092 | ídem |
ds:DigestMethod/@Algorithm | No existe / ídem | 2095 / 2094 | ídem |
La condición de formato dice, literalmente, «El formato del Tag UBL es diferente a alfanumérico de hasta 3000 caracteres». Es decir: la hoja pide que el atributo exista y que sea texto de hasta 3000 caracteres. No aparece ninguna lista de valores admitidos, ni sha256, ni sha1, ni rsa, ni 1024 o 2048. Buscamos esas cadenas en las 23 hojas del libro y las únicas coincidencias son falsos positivos, como el código 2048 del importe de IGV.
Dos precisiones sobre la numeración. La hoja Firma no incluye el 2096, pero la hoja CódigosRetorno sí: «ds:DigestValue - No cumple con el estandar». De ahí que el artículo sobre dónde va la firma cuente veintiséis códigos y la hoja Firma enumere veinticinco reglas. Y el rango 2076-2101 es de estructura: del 2076 al 2083 son los datos del firmante en cac:Signature, del 2084 al 2101 el bloque XMLDSig.
Lo que sí verifica la criptografía está en otras hojas, y tampoco nombra algoritmos:
- 2335, «El documento electrónico ingresado ha sido alterado», con la condición «La firma no coincide con el comprobante».
- 2336, «Ocurrió un error en el proceso de validación de la firma digital».
- 2325 a 2328: el certificado no es el comunicado a SUNAT, está de baja, no está vigente o está revocado.
- 1059, «El XML no contiene firma digital».
Nuestra lectura: las reglas públicas comprueban que el XML traiga declarados los algoritmos, no cuáles. Con ellas en la mano no se puede afirmar que SUNAT acepte SHA-1, y tampoco que lo rechace. Cómo valida internamente la firma no está en un documento que hayamos podido leer. Dicho de otro modo, el 2335 que verás si calculas mal una huella es una cosa; que SUNAT descarte un algoritmo por débil es otra, y esa segunda no figura en ninguna regla.
El manual del programador: ejemplos con SHA-1
El manual del programador del SEE-Del contribuyente, versión 2.1 (12 de mayo de 2021, 54 páginas), dedica su sección 3 a la firma. Dice que se debe «utilizar el estándar de firmas XMLDSig» y describe ds:DigestMethod/@Algorithm solo como «Define la función hash utilizada». No indica qué función. Pide para el certificado una longitud mínima de 1024 bits para la llave privada, cifra que no coincide con lo que ves en la entidad de certificación y que tratamos en el artículo de la longitud.
Donde sí hay algoritmos con nombre es en los ejemplos de la constancia de recepción (la respuesta ApplicationResponse que SUNAT firma como SignSUNAT), y esos ejemplos usan SHA-1: SignatureMethod con http://www.w3.org/2000/09/xmldsig#rsa-sha1 y DigestMethod con http://www.w3.org/2000/09/xmldsig#sha1. El registro de cambios del manual dice que la versión 2.1 solo modificó los ejemplos SOAP, y la respuesta de ejemplo lleva fecha de recepción de junio de 2012.
Nuestra lectura, y conviene leerla con cautela: ese ejemplo es una firma de SUNAT sobre su respuesta, de 2012, y el manual no lo presenta como requisito para el emisor. Tampoco sabemos qué algoritmo usa SUNAT hoy en un CDR real; no tenemos uno firmado en esta fecha. Lo que sí se puede decir es que quien lea ese manual para saber con qué firmar se encuentra con un ejemplo de SHA-1 y ninguna regla.
Las resoluciones: piden otra cosa
Las dos resoluciones que se citan para esto hablan de a quién pertenece el certificado, no del hash:
- RS 097-2012/SUNAT (firmada en Lima el 27 de abril de 2012, copia escaneada, texto leído por OCR y contrastado con la imagen). El numeral 2.12 define «Firma digital» como la firma electrónica del artículo 6 del reglamento de la Ley de Firmas y Certificados Digitales (D.S. 052-2008-PCM) y exige que el certificado digital deba «Contar con los nombres y apellidos, la denominación o razón social y el número de RUC del titular» y «Tener, por lo menos, un nivel de seguridad medio».
- RS 117-2017/SUNAT (firmada el 9 de mayo de 2017), texto original. El numeral 1.17 repite lo de nombres y RUC. El artículo 5, numeral 5.1 h), pide a quien quiera inscribirse como OSE registrar certificados que «deben tener 2048 bits y generar una firma digital válida y vigente»; es un requisito de longitud y solo para el OSE.
Ninguna de las dos contiene las cadenas SHA, RSA, hash ni algoritmo en el sentido de función de huella. Leímos el texto original de ambas; las resoluciones modificatorias son aparte y no las revisamos.
Qué usar, y por qué
Nuestra opinión: firma con SHA-256 en las dos declaraciones, rsa-sha256 en SignatureMethod y xmlenc#sha256 en DigestMethod, y no gastes tiempo en averiguar si SHA-1 pasa. Tres razones. La especificación del W3C que cita el propio manual de SUNAT recomienda con fuerza SHA-256. La entidad de certificación que revisamos emite con sha256WithRSAEncryption, así que tu certificado ya va por ahí. Y si te toca explicar un rechazo, «usé el algoritmo que recomienda el estándar» es una posición que se defiende y «copié un ejemplo de 2012» no.
Esto es lo que produce una firma así, hecha con un par RSA de 2048 bits de prueba y xmlsec1 sobre una factura mínima de ejemplo, con los algoritmos extraídos de la salida:
$ grep -o "Algorithm=\"[^\"]*\"" firmada.xml
Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"
Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"
$ xmlsec1 --verify --trusted-pem k.crt firmada.xml
OK
$ xmlsec1 --verify --trusted-pem k.crt alterada.xml (tras cambiar 118.00 por 119.00)
FAIL
SignedInfo References (ok/all): 0/1
Ese comando grep sobre tu XML real te dice en segundos qué declara. Si en el tuyo ves rsa-sha1 o #sha1, cambia la configuración de la biblioteca que firma, no el certificado.
Si tu equipo rechaza SHA-1, no culpes todavía a SUNAT
Volvamos a Diego. En Fedora con la política criptográfica DEFAULT, OpenSSL 3.5.8 se niega a firmar con SHA-1:
$ openssl dgst -sha1 -sign k.pem -out f1.sha1.sig f1.txt
Error setting context
...error:03000098:digital envelope routines:do_sigver_init:invalid digest
Es una decisión del sistema operativo, con una variable de entorno para saltársela. Con OPENSSL_ENABLE_SHA1_SIGNATURES=1 la misma orden firma, verifica con Verified OK, falla con el texto alterado y, al abrir la firma, el sobre contiene el nombre sha1 y 20 bytes iguales a los de sha1sum. Por eso un invalid digest en tu máquina no demuestra nada sobre SUNAT: demuestra que tu herramienta, o su política, ya dejó atrás SHA-1. Las salidas completas están en la carpeta de fuentes indicada al final.
Lo que sale mal
- Declarar una cosa y calcular otra. Si
DigestMethoddicesha256pero la biblioteca calculó SHA-1, el verificador recalcula con el algoritmo declarado y no coincide. Es la causa que esperaríamos detrás de un 2335 con un XML que «se ve bien»; es nuestra lectura, no una causa documentada por SUNAT. - Confundir la URI de la huella.
xmldsig#sha256no figura en la especificación; la correcta esxmlenc#sha256. - Tocar el XML después de firmar. Cualquier cambio, incluso un espacio en blanco, cambia la huella. Está en el artículo sobre la firma del XML y lo comprobamos arriba con 118.00 y 119.00.
- Asumir que el certificado y la firma del XML comparten algoritmo. Son decisiones distintas: la entidad eligió la del certificado y tu biblioteca la del XML.
Lo que este artículo no afirma
- Que SUNAT acepte o rechace SHA-1 en la firma del XML. Ninguna regla, código, manual ni resolución que leímos lo dice. Si lo necesitas con certeza, la vía es probar un comprobante firmado en el ambiente de pruebas de SUNAT y mirar el CDR; no lo hicimos.
- Que la versión del 26.08.2026 de las reglas sea la última. Es la que se descarga del enlace conocido; la página de listado de cpe.sunat.gob.pe devuelve 403 desde nuestro equipo y no pudimos comprobar si hay una más reciente.
- Qué algoritmo firma hoy el CDR de SUNAT. Solo vimos los ejemplos del manual.
- Qué hacen otras entidades. Leímos la declaración de ECEP-RENIEC, no las de otras.
- El texto vigente de las resoluciones con sus modificatorias. Leímos el original de la 117-2017 y el escaneo de la 097-2012.
- Que 1024 o 2048 bits sea el piso correcto. Eso queda para el artículo de la longitud.
Fuentes
Consultadas el 23 de octubre de 2026. Copias locales en reports/redaccion-diaria/2026-10-23-notas/fuentes/mfd-hash/, salvo indicación.
- SUNAT, Reglas de validación, actualizado al 26.08.2026, hojas
Firma,General,CódigosRetorno. Copias:reglas.xlsx,hoja-firma.txt,codigos-2076-2101.txt,codigos-generales.txt. - SUNAT, Manual del programador, SEE-Del contribuyente, v. 2.1 (12 de mayo de 2021), sección 3 y ejemplos del anexo C. Copia:
manual_programador.txt. - SUNAT, Resolución de Superintendencia 117-2017/SUNAT (firmada el 9 de mayo de 2017), numerales 1.17 y 5.1 h). Copia:
117-2017.txt. - SUNAT, Resolución de Superintendencia 097-2012/SUNAT (firmada el 27 de abril de 2012), numeral 2.12. Copia escaneada:
2026-10-19-notas/fuentes/vfc-contingencia/ocr097/p-04yp-05. - RENIEC, Declaración de Prácticas de Certificación ECEP-RENIEC v. 5.1 (4 de junio de 2026), numeral 6.1.5. Copia:
2026-10-19-notas/fuentes/mfd-dnie/cps-ecep-v5.1.txt. - W3C, XML Signature Syntax and Processing Version 1.1 (Recomendación, 11 de abril de 2013), apartados 6.2 y 6.4 solo para los identificadores. Copia:
xmldsig-core1.txt. - Salida de los comandos (OpenSSL 3.5.8, xmlsec1 1.2.41, 23 de octubre de 2026):
demo-salida.txt,demo-sha1-politica.txt,xml-firma-salida.txty sus guionesdemo.sh,xml-firma.sh.
Si necesitas un certificado con el que firmar tus comprobantes, en mifirmadigital.pe vendemos certificados digitales emitidos dentro de la Infraestructura Oficial de Firma Electrónica, y puedes comprobar con los comandos de arriba qué algoritmo trae el tuyo.
Última revisión: 23 de octubre de 2026.