Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xml signature verification against custom CA

I need to verify an xml signature contained in the answer to a POST request.

The signature is defined by:

<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>htti3M3ikfm2RooDTNo3Kv7g0K2ongShUfCDUAWpytc=</DigestValue>                                                                                              
  </Reference>                                                                                                                                                           
</SignedInfo>                                                                                                                                                            

and is against a custom CA.

I have no way to change the answer (it is issued by a State Agency) and all my attempts to verify against the certificate supposedly used to sign did result in errors.

One of my attempts has been using the following short test program:

#!/usr/bin/python3                                                                                                                                                       

import signxml                                                                                                                                                           

cf = 'certificate.cer'                                                                                                                                                          
with open(cf, 'r') as fi:                                                                                                                                                
    cer = fi.read()                                                                                                                                                      

ver = signxml.XMLVerifier()                                                                                                                                              

f = 'response.xml'                                                                                                                                 
with open(f, 'rb') as fi:                                                                                                                                            
    xml = fi.read()                                                                                                                                                  
try:                                                                                                                                                                 
    vd = ver.verify(xml, x509_cert=cer)                                                                                                                              
    print('OK')                                                                                                                                                      
except signxml.exceptions.InvalidSignature as e:                                                                                                                     
    print(e)                                                                                                                                                         

This results in:

Signature verification failed: wrong signature length

Other variations have different errors including:

Signature verification failed: invalid padding

and:

unable to get local issuer certificate

I am a seasoned programmer, but NOT a cryptography expert, so it's quite likely I forgot something trivial (to the knowledgeable). Please point me in the right direction.

Note:: if required I can provide a full example (of failure) as certificate/answer are not "secret".

like image 768
ZioByte Avatar asked Oct 15 '25 18:10

ZioByte


1 Answers

Unfortunately things are always a bit more complex than expected.

Answer from @stovfl completely missed the relevant point: I need to verify against a non-standard CA.

I already was struggling to use sigxml package, and I had to overcome the following problems:

  • sigxml will not work (for me) with plain xml.etree; I had to use lxml.etree with a different syntax.
  • sigxml installed by plain pip3 install sigxml would bomb with a deprecation error; I had to get latest (non-tagged master) from github with pip3 install git+https://github.com/XML-Security/signxml.git.
  • Examples on sigxml site completely disregard python3 issues with str vs. bytes.
  • CA Authority used to sign the Certificate I used to sign the outgoing message is different from the CA Authority used to sign response (I have no way to change this!).

This story has an Happy Ending though.

The following test program works (for me):

from signxml import XMLSigner, XMLVerifier, InvalidCertificate
from lxml import etree

outCAroot = 'outCAroot.pem'
inCAroot = 'inCAroot.pem'
cert = open("example.pem").read().encode()
key = open("example.key").read().encode()
file_name = 'test.tosend'
resp_name = 'test.rsp'

xml = etree.parse(file_name)  # (data_to_sign)
signer = XMLSigner(c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
signed_xml = signer.sign(xml, key=key, cert=cert)
try:
    result = XMLVerifier().verify(signed_xml, ca_pem_file=outCAroot)
except InvalidCertificate as e:
    print(e)
else:
    print('outgoing signature Ok.')

# here I send signed_xml to remote server and get the response (NO ERRORS!)

answer_xml = etree.parse(resp_name)  # (signed answer)
try:
    result = XMLVerifier().verify(answer_xml, ca_pem_file=inCAroot)
except InvalidCertificate as e:
    print(e)
else:
    print('incoming signature Ok.')
    print('===================')
    print(result.signed_data.decode())
    print('===================')

I hope this will help whoever is (or will be) in my situation.

like image 125
ZioByte Avatar answered Oct 17 '25 07:10

ZioByte