> For the complete documentation index, see [llms.txt](https://developer.ellypayapp.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.ellypayapp.com/callbacks/rsa-signature-verification.md).

# RSA Signature Verification

## Obtain the Public Key - Based on environment

Copy the public key for the environment you're working with from [here](/getting-started/rsa-public-keys.md). This document assumes that the public key would be stored somewhere on your server under the name `ellypay.public.key.pem`

## Next Steps

Below is the sample callback data for this demonstration

```json
{
    "event": "transaction.charges",
    "payload": {
        "id": 11832,
        "merchant_reference": "MCTREFNGKLP5VQCQSBH2",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "COLLECTION",
        "request_currency": "UGX",
        "transaction_amount": 100000,
        "transaction_currency": "UGX",
        "transaction_charge": 4000,
        "transaction_account": "256777000001",
        "charge_customer": false,
        "total_credit": 96000,
        "provider_code": "mtn_momo_ug",
        "request_amount": 100000,
        "institution_name": "MTN Mobile Money Uganda",
        "customer_name": "JOHN DOE",
        "transaction_status": "PENDING",
        "status_message": "Collection initialized successfully. Confirm charges"
    }
}
```

1. Obtain the value of the `rsa-signature` header.
2. Form the string payload to be used in signature verification. This is obtained by concatenating values of the callback data in the format; `event:merchant_reference:internal_reference:transaction_type:transaction_status` and these values are obtained from the callback data. The string payload in this case would therefore be `transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING`
3. Use the public key obtained above to verify the signature as described in the sample source codes below;&#x20;

{% tabs %}
{% tab title="PHP" %}

```php
<?php

public function isValidSignature() {
    $file = "path-to-file/ellypay.public.key.pem";
    $keyContent = file_get_contents($file);
    $publicKey = openssl_get_publickey($keyContent);
    $strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
    $signature = base64_decode("value-of-rsa-signature");

    /*true or false*/
    return openssl_verify($strPayload, $signature, $publicKey, "sha256") == 1;
}

?>
```

{% endtab %}

{% tab title="NodeJS" %}

```javascript
const crypto = require('crypto');
const fs = require('fs');

function isValidSignature() {
    const strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
    const signature = "value-of-rsa-signature";
    const publicKeyFile = "path-to-file/ellypay.public.key.pem";
    const publicKey = fs.readFileSync(publicKeyFile).toString().replace(/\\n/g, '\n');

    const verify = crypto.createVerify("SHA256");
    verify.write(strPayload);
    verify.end();

    /*true or false*/
    return verify.verify(publicKey, signature, 'base64');
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class SignatureVerifier {

    public boolean isValidSignature() throws Exception {
        // Read public key from file
        Path pathToFile = Paths.get("path-to-file/ellypay.public.key.pem");
        byte[] keyBytes = Files.readAllBytes(pathToFile);

        // Decode public key
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);

        // Signature and payload
        String strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
        byte[] signatureBytes = Base64.getDecoder().decode("value-of-rsa-signature");

        // Verify signature
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(strPayload.getBytes());
        return signature.verify(signatureBytes);
    }

    public static void main(String[] args) throws Exception {
        SignatureVerifier verifier = new SignatureVerifier();
        System.out.println(verifier.isValidSignature());
    }
}

```

{% endtab %}

{% tab title="C#" %}

```csharp
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public class SignatureVerifier
{
    public bool IsValidSignature()
    {
        // Read public key from file
        string pathToFile = @"path-to-file\ellypay.public.key.pem";
        string keyContent = File.ReadAllText(pathToFile);

        // Decode public key
        RSA rsa = RSA.Create();
        rsa.ImportFromPem(keyContent);

        // Signature and payload
        string strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
        byte[] signatureBytes = Convert.FromBase64String("value-of-rsa-signature");

        // Verify signature
        var verifier = new RSAPKCS1SignatureDeformatter(rsa);
        verifier.SetHashAlgorithm("SHA256");
        byte[] payloadBytes = Encoding.UTF8.GetBytes(strPayload);
        byte[] sha256Hash;

        using (SHA256 sha256 = SHA256.Create())
        {
            sha256Hash = sha256.ComputeHash(payloadBytes);
        }

        return verifier.VerifySignature(sha256Hash, signatureBytes);
    }

    public static void Main(string[] args)
    {
        SignatureVerifier verifier = new SignatureVerifier();
        Console.WriteLine(verifier.IsValidSignature());
    }
}

```

{% endtab %}

{% tab title="Python" %}

```python
import base64
import hashlib
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.serialization import load_pem_public_key

def is_valid_signature():
    # Read public key from file
    with open("path-to-file/ellypay.public.key.pem", "rb") as key_file:
        key_content = key_file.read()

    # Decode public key
    public_key = load_pem_public_key(key_content, backend=default_backend())

    # Signature and payload
    str_payload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING"
    signature_bytes = base64.b64decode("value-of-rsa-signature")

    # Verify signature
    verifier = public_key.verifier(
        signature_bytes,
        padding.PKCS1v15(),
        hashlib.sha256
    )
    verifier.update(str_payload.encode())

    try:
        verifier.verify()
        return True
    except Exception as e:
        print("Signature verification failed:", e)
        return False

```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require 'openssl'
require 'base64'

def is_valid_signature
  # Read public key from file
  file = File.read("path-to-file/ellypay.public.key.pem")

  # Decode public key
  public_key = OpenSSL::PKey::RSA.new(file)

  # Signature and payload
  str_payload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING"
  signature_bytes = Base64.decode64("value-of-rsa-signature")

  # Verify signature
  sha256 = OpenSSL::Digest::SHA256.new
  result = public_key.verify(sha256, signature_bytes, str_payload)

  result
end

```

{% endtab %}
{% endtabs %}

Below is an example of a generated RSA signature (rsa-signature) on the sandbox environment. Feel free to verify it using the sample payload above and the public key.

{% code overflow="wrap" %}

```
TrvB4sLobRvrrsc1BnXFM08WeJiCkEnQKPYAXuIk31sc/8zosdTLGgyU5jx2Vr2yehGD/vILh3I/8rni5Q7kyLiOwkGC7pA6OioV9B2gTJBFfPkkwNGdZCuAAG2EkAZJAfXNu0WhwhGjO80ffdgxxBpLwodcmoEDCppF0tyXTVp9qhfzJg0Yg7U2w5wBjnxp0Cp3Rc/Q7X8xxMHU7q1cI0WSUZJmq7CkwDjn3RpUtgD/lRMfHdkoDbKPvxWl7lBJHaQc4nq6a0qdIdWLsCh+TjsZTmJybw9GZ0bSof8VkfOrCraKSbj5NDWEHXAMTnIezpSYmc0fM2NjClTIDLiSL9VfcopMG7D7V8igpmoRdem0cGkAM3q1GtxxK0j2aPftpWqfEjZPaW+kAT3hkS/B9w9fu8ju1YmlBeAbPD5iNN+TSOk7ZfYeQm3/tfV0gqD8mydppXIWg5Ex5KiC5EnxlYanNlekbB+hnBIGH8VTf57PxmtJL+WxN5CHymPFvYJXjJsux0E+smHEqTKmCWcgS/U0AgqsvwJARFcYm2ka4hMM2em/PlGth1wXwMTVQmVqGhwtKDKBdAzIknSZN1JR8hwxzjCzsHnGHWru+2m7EQurBfGskpjnW7NJt4cr7lQ7g8yOj9fTfogByHGQ4akcIbYJhLifqIXQajvZhcNJ9eM=
```

{% endcode %}
