Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bad Signature - unsure of how to debug further #254

Open
IanBarnesZa opened this issue Apr 9, 2024 · 1 comment
Open

Bad Signature - unsure of how to debug further #254

IanBarnesZa opened this issue Apr 9, 2024 · 1 comment

Comments

@IanBarnesZa
Copy link

We're using this package to sign an XML message that is going to a Modirum MPI server - however we are getting a "bad signature" error and we can't figure out why - so any assistance would be appreciated.

Here is the code:

import os
import base64
import requests
import signxml

from lxml import etree as lxml_ET
from signxml import XMLSigner, XMLVerifier, CanonicalizationMethod

TERM_URL = os.environ.get('TERM_URL', 'https://somedomain.url')
SELF_ENVIRONMENT = os.environ.get('SELF_ENVIRONMENT', 'staging')

POST_URL = 'https://modirumServerHere/url/things'


class ModiumSignXML:
    def __init__(self, xml):
        self.xml = xml

    def sign_xml(self, xml, message_id):

        cert, key = [open(f, "rb").read() for f in ("staging.cert.crt", "staging.private.key")]

        reference_uri_link = f'''#{message_id}'''

        temp = f'''<ModirumMPI xmlns="http://www.modirum.com/schemas/mpiapi" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
                        {xml}
                    </ModirumMPI>'''


        data = lxml_ET.fromstring(temp.encode('utf-8'))
        signer = XMLSigner(method=signxml.methods.detached,
                           c14n_algorithm=signxml.CanonicalizationMethod.CANONICAL_XML_1_0)
        signed_root = signer.sign(data, key=key, cert=cert, reference_uri=reference_uri_link)
        data_serialized = lxml_ET.tostring(signed_root)

        temp = temp.replace('</ModirumMPI>', data_serialized.decode('utf-8') + '</ModirumMPI>')

        # data_parsed = lxml_ET.fromstring(temp)
        XMLVerifier().verify(temp.encode('utf-8'), x509_cert=cert, id_attribute=reference_uri_link)

        return temp

    def process(self, pan, expiry, amount, exponent, description, currency, term_url, transaction_id, merchant_id,
                merchant_name):

        # message_id = f"{merchant_name}{transaction_id}"
        message_id = "M1561102563170" #Hardcoded for testing

        xid = str(transaction_id).zfill(20).encode('utf-8')
        xid = base64.b64encode(xid).decode('utf-8')

        to_encrypt = f'''
        <Message md="{transaction_id}" merchantId="{merchant_id}" messageId="{message_id}" version="4.0">
            <Request>
                <EnrollmentRequest>
                    <Parameters>
                        <pan>{pan}</pan>
                        <expiry>{expiry}</expiry>
                        <deviceCategory>0</deviceCategory>
                        <purchAmount>{amount}</purchAmount>
                        <exponent>{exponent}</exponent>
                        <description>{description}</description>
                        <currency>{currency}</currency>
                        <termUrl>{term_url}</termUrl>
                        <TDS2Attributes>
                            <Attribute name="TDS2.transType">01</Attribute>
                        </TDS2Attributes>
                        <xid>{xid}</xid>
                    </Parameters>
                </EnrollmentRequest>
            </Request>
        </Message>'''

        xml = self.sign_xml(to_encrypt, message_id)

        print("REQUEST")
        print("=========="*10)
        print(xml)
        print("=========="*10)
        print("")
        print("")
        print("")

        return xml

    def post_xml(self, xml):
        headers = {'Content-Type': 'application/xml'}

        r = requests.post(POST_URL, data=xml, headers=headers)

        print("RESPONSE")
        print("=========="*10)
        print(r.text)
        print("=========="*10)
        print("")
        print("")
        print("")


def main():
    pan = '4111111111111111'
    expiry = '2312'
    amount = '100'
    exponent = '2'
    description = 'Test'
    currency = '710'
    term_url = TERM_URL
    transaction_id = '90010'
    merchant_id = '123456'
    merchant_name = 'TestMerchant'

    test = ModiumSignXML('')
    xml = test.process(pan=pan, expiry=expiry, amount=amount, exponent=exponent, description=description,
                       currency=currency, term_url=term_url, transaction_id=transaction_id, merchant_id=merchant_id,
                       merchant_name=merchant_name)
    test.post_xml(xml)



if __name__ == "__main__":
    main()

Essentially the function sign_xml does the signing and then verification - and that's where the error comes in. According to the spec, it needs to be a detached signature, appearing after the last "message" and before the end of the XML tag.

We've compared the outputted code to spec docs and other working logs and can't see anything different, yet our verification fails both locally and on the MPI server. So any help would be grand!

We tried various things - including the different signing methods and no joy on any of them. We have re-verified the cert and the key and they are both fine and have been extracted from the same pfx file.

@kislyuk
Copy link
Member

kislyuk commented Apr 10, 2024

It looks like you're altering the signed data after signing after inserting the signature into the data. You're also trying to splice XML documents together via string concatenation. Neither of these things will work, and they will both break the signature.

I suggest that you gain a clearer understanding of the three different available signature methods (enveloping, enveloped, and detached) and how to apply them in your case - ideally by taking an example provided by your integration partner and changing the SignXML parameters until you obtain the same shape of output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants