DEV Community

Cover image for Python y firma XML
1N0T
1N0T

Posted on

Python y firma XML

Vamos a ver, cómo firmar documentos XML y, cómo validar si el contenido de un documento firmado, se mantiene inalterado o, por el contrario, ha sufrido alguna modificación.

Como no puede ser de otra manera, lo primero que vamos a hacer es crear un nuevo entorno virtual y a activarlo.

mkdir firmaXML
cd firmaXML
python3 -m venv venv
source venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

Ahora instalamos las librerías que vamos a necesitar.

pip install pip --update
pip install lxml
pip install signxml
Enter fullscreen mode Exit fullscreen mode

Para poder proceder a firmar documentos, deberemos disponer de un certificado x509, aunque podemos crear uno autofirmado con el siguiente comando:

openssl req -x509 -nodes \
        -subj "/CN=Certificado autofirmado" -days 365 \
        -newkey rsa \
        -keyout clave_privada.key \
        -out clave_publica.crt

Enter fullscreen mode Exit fullscreen mode

Ya estamos en disposición de crear un ejemplo de como firmar un XML.

from lxml import etree
from signxml import XMLSigner, XMLVerifier 
from signxml.exceptions import InvalidSignature

# Nuestro contenido XML
XML = """
<test>
   Contenido importante
</test>
"""

# Recuperamos nuestras claves de certificado.Para la firma, se necesitan tanto la
# clave pública como la privada. Para la validación, sólo se requiere la pública.
clave_publica = open("clave_publica.crt", "r").read()
clave_privada = open("clave_privada.key", "r").read()

# Construimos un objeto XNL a partir de string con el contenido
nodo_raiz = etree.fromstring(XML)

# Creamos objeto XML firmado
nodo_raiz_firmado = XMLSigner().sign( 
    data=nodo_raiz,
    key=clave_privada,
    cert=clave_publica 
)

# Convertimos objeto XML a formato string y lo guardamos en fichero para comprobar después
# que se produce un error si manipulamos su contenido
XML_firmado = etree.tostring(nodo_raiz_firmado, pretty_print=True).decode()
with open("XML_firmado.xml", "w") as f:
    f.write(XML_firmado)


# Validamos integridad del contenido XML
try:
    XML_validado = XMLVerifier().verify(
        data=etree.fromstring(XML_firmado),
        x509_cert=clave_publica
    ).signed_xml
except InvalidSignature as err:
    print("ERROR validando integridad XML")
    exit()

print("=== XML original =============================================================")
print(XML)

print("=== XML firmado ==============================================================")
print(XML_firmado)

print("=== XML validado =============================================================")
print(etree.tostring(XML_validado, pretty_print=True).decode())

Enter fullscreen mode Exit fullscreen mode

Creo que el código es bastante autoexplicativo y no necesita de una mayor esplicación. Tras su ejecución, dispondremos de un fichero llamado XML_firmado.xml. Por lo que ahora, ya podemos proceder a comprobar la integridad del fichero.

from lxml import etree
from signxml import XMLSigner, XMLVerifier 
from signxml.exceptions import InvalidSignature

# Recuperamos contenido fichero XML
XML = open("XML_firmado.xml", "r").read()

# Para la validación, sólo se requiere la clave pública.
clave_publica = open("clave_publica.crt", "r").read()

# Construimos un objeto XNL a partir de string con el contenido
nodo_raiz = etree.fromstring(XML)

# Validamos integridad del contenido XML
try:
    XML_validado = XMLVerifier().verify(
        data=etree.fromstring(XML),
        x509_cert=clave_publica
    ).signed_xml
except InvalidSignature as err:
    print("ERROR validando integridad XML")
    exit()

print("=== XML original =============================================================")
print(XML)

print("=== XML validado =============================================================")
print(etree.tostring(XML_validado, pretty_print=True).decode())
Enter fullscreen mode Exit fullscreen mode

Si lo ejecutamos, sin haber modificado el fichero, y después de haber realizado alguna modificación del contenido, podremos observar la diferencia.

Como puedes comprobar, la cosa es bastante simple ¿no?

Top comments (0)