Search code examples
pythonnode.jspython-3.7cryptojspython-cryptography

Nodejs crypto to Python jwt convert


I'm doing an integration with a gateway and they provided me with a mechanism to create signature of some payload and I'm trying to convert this Nodejs code into python

here is the payload we want to sign:

actual = {"IntraTransferRqHeader": {"CorporateID": "XXX", "CorpReferenceNumber": "47165454456"},
                            "IntraTransferRqBody": {"AccountNumber": "05100004061100",
                                                    "DebitCurrency": "SAR", "BeneficiaryAccountNumber": "30008776337526",
                                                    "TransferAmount": "100.23",
                                                    "TransferCurrency": "SAR",
                                                    "Description": "Transfer fund to beneficiary", "AMLPurposeCode": "BC"}};

and here is the Nodejs provided code

const cert = fs.readFileSync('/etc/ssl/nginx/snbcsr.key', "utf8");

var privateKey = crypto.createPrivateKey({
    'key': cert,
    'format': 'pem',
});
var signerObject = crypto.createSign("RSA-SHA256");
signerObject.update(JSON.stringify(actual).trim());
var signature = signerObject.sign({ key: privateKey }, "base64");

and here is what i did to make the same signature

private_key_pem = "/etc/ssl/nginx/snbcsr.key"
with open(private_key_pem, "rb") as key_file:
   private_key = serialization.load_pem_private_key(key_file.read(), password=None, backend=default_backend())
signature = jwt.encode(actual, private_key, algorithm="RS256")

and i still get signature error. I don't know Nodejs and i don't know what am i doing wrong here


Solution

  • You shouldn't confuse JWT with plain signing.

    Given the Node.js code

    const fs = require('fs');
    const crypto = require('crypto');
    const cert = fs.readFileSync('key.pem', 'utf8');
    var privateKey = crypto.createPrivateKey({
      'key': cert,
      'format': 'pem',
    });
    const payload = 'foople';
    const signerObject = crypto.createSign('RSA-SHA256');
    signerObject.update(payload);
    const signature = signerObject.sign({key: privateKey}, 'base64');
    console.log(signature);
    

    the equivalent Python code, using the cryptography library's signing primitives, is

    import base64
    
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
    from cryptography.hazmat.primitives.hashes import SHA256
    from cryptography.hazmat.primitives.serialization import load_pem_private_key
    
    bk = default_backend()
    
    with open("./key.pem", "rb") as key_file:
        private_key = load_pem_private_key(key_file.read(), password=None, backend=bk)
    
    payload = b'foople'
    signature = base64.b64encode(private_key.sign(
        payload,
        padding=PKCS1v15(),
        algorithm=SHA256(),
    )).decode()
    

    Given my freshly-generated mock 512-bit RSA key

    -----BEGIN RSA PRIVATE KEY-----
    MIIBPAIBAAJBAMd6HFNMreA1zwFpTA7vGFwSJFFZlKYtgJVsnSW3rc9zsisewUIC
    U0MnvNucHeKifhniFOoimUr8hTiZMywEmFMCAwEAAQJBAJKYX+arzICgqr+rxZSY
    C/vl7UDHp6G0gPHPP3HvmdGBNLst0mqV8GKbNEr1Myb7vQOjbYDno2OVFNL+jeMI
    JYkCIQD2r+1dvpSDrSvelBIyymE1D42dQFviyoY8URCefRzJhQIhAM8B7VbcYNo1
    Tm0BLCKIPO1CZKmPsYDb2byk8mtakVX3AiEAiDBF9iwh57Qx9PaAOYQbOGT2xKrk
    T4eJpkEG0Mi3nNUCIQC6bdPf3E1ld4iP5vRmjSfBzX92rbCAin8Hw82HHWOydQIg
    fLfsV/P5F0LseV5KRPVombYNc/bh4oU467kEEPXDX5w=
    -----END RSA PRIVATE KEY-----
    

    both programs output

    Wvy7cxuaUIpuiadTH2iOm6ayZUNrKY1whZElBIGBWbglEf0yA07wGbhD0qsPTWY7PzMtvuPV2xre+pCQsMwxmw==