API request signature

Facing insecure integration, you may be requested to implement an asymmetric request signature.

To have a possibility to sign requests, you need to perform the following steps:

  1. Generate and upload a certificate.
  2. Calculate a hash and a signature using your private key and pass the generated hash (X-Hash) and the signature value (X-Signature) in the request header.

These steps are described below in details.

Generating and uploading a certificate

  1. Generate 2048-bit RSA private key. The way of generation depends on privacy policy in your company. For example, you can do it using OpenSSL:

    openssl genrsa -des3 -out private.key 2048

  2. Generate public CSR (Certificate Signing Request) using the generated private key:

    openssl req -key private.key -new -out public.csr

  3. Generate a certificate using the generated private key and CSR. The example of generating a certificate for 5 years:

    openssl x509 -signkey private.key -in public.csr -req -days 1825 -out public.cer

  4. Upload the certificate in the Merchant Portal. For that, go to Certificates > Merchant API, click Add a certificate, then drag-and-drop the generated public certificate.

To use this function in the Merchant Portal, a permission in the bank is required. If the certificates section is not displayed in the menu, contact the support service to upload the certificate.

Calculating a hash and a signature

  1. Calculate SHA256 hash of the request body as follows:

    • Use request body as a string, for example {"orderId": "243d940e-bedd-4e95-8e78-b08f459a242f","password": "test-user-password","userName": "test-user"}.
    • Calculate SHA256 hash from this string, in raw bytes (hex format).
    • Convert the raw bytes into base64 encoding.

    Hash Base64: MmI1ZmUwZjc2YmUxNGJiY2ZjN2M3NzQ3OGYwYTU4ODUwYzhmNzEyNDg5YjczOTg3NzZlYTg4ODEwNDczYzFmYg==

  2. Generate a signature for the calculated SHA256 hash (raw hex, not base64) with RSA algorythm using the private key.

    In our example we use the following private key with the password 12345:

    MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC0NQCjnqUmdEK1aisFXGMIihI73zofZycI/EGTJkQ6HOT2e/dG4j1RqHwuE6FEPL8lGG3EddFSiZzjDbIH+bm5m0CpcnFN1ShTxbA8BzM+OGxRHsuHU168HiS3OSIO9IxCKlaYzNgL1IygkjF6xqXLRvAPJ70zb/ymD2Cszfbw3ZNcZnr07QtJF/Jbn/+UJt+1hin9y6ikKZH+laZlMOx6LLGsj3W1PjFyEuzNUmau3iX6I6PdfgVwbB7TRVFuFDfxzBZxtBDympGRxpTGNFHug1NNPA9ekWa+zVlNVbzPrMuf6rF4yvKc8Tv9OkQ7RfGv1YmFCgEjqgnW2puIuufJAgMBAAECggEAdzDzakVdMJEHKly9db9ElXpVUxpzpB+jFvNaIAzYZaOE4W7fABhVvHP3Jra/HJYdS1jcCWxv9eqlRRxi25mV+Six3SGfxX4uPTJtPVb50raZFhKLRcejykMZv8JfP4sKVh0Qx+H9J93+4Zmwdmd7c3dJAo0jPCle7ysOo11bbrXTMwgg/i9Rxfzf5j4HYVTqYGIOL8cwNDE9XzDV7ekYJ147Ro15QnXK315fij2y7HFEJoyXp4BySm2nGNjxBjPn7sd/0Y8IrnYtpAnD7PG6oHRMITAEpZGEGyAi5cxAwTK1p43megM9ZaddSYPGtyrelNW6xm86x5Ulv8Bqhg1gzQKBgQDcAkILsNGV1eazxsv7QEg9Xs7h9VHrmPVTSGx6OjcZNP/VSZCzLT6IiK68c5ywkTe8SuDCLgU6Tcf4YOuno7gRplnLm6KzTatu/WE4qiz+WzrT4/+uspOli5YdTBXWtGXgWWvcnuQ1TTECdFf83na/cPDM5qKfJuoOan3gbMNVowKBgQDRr+MarMxNnoaH/XlY0HURtAUENieoyll72iHLofdlyQPDqoo0G00zjlV11DH/pH8qQ1PsswJDxL6oiY9XkxZ0P2Pd5BnYW8wSUdQ033ReSxc9WHlnTnixPp/YHraAmzZXncM6B8YJWAO5CtLW7Xym58BLUgMMH6FUM0gKiLMrowKBgQCx1alpJb3jrYjTnEdZifZalP4JK3DSTUtPzGTSz6el2m9JCjPKgTHgzwrfDVyEZH1219ehXe2f2Stgm4cgdHfe3GYM7HqxEIEYL/ucAAJqf3enus37eiFaWOA4Qj4M1LjchatoI483fnO1FjHhFjlKOZKLLYoZtyzOBkpFU+T4nQKBgQDNCpYj3ncFK4/X6NfBLk5b2lHRdXdAiWYJQxsq+Z1m7bJ9ogT0wQGz+Wm+B5pApkUnOaEWY1FCnV/mhGUjuJQLZnUsZEGVnOYnv9anQR6Umg8GkL5ec5B3mYpKlnXVunDgKkfeNf3D40n4pwnW23G58ALMZEzQjRl/sYmvq06wywKBgQCer3/ehngsL6xpdmWaWOyZja992F9s7JKb+M5esEltp5utpB9DEGrV1KKnLTWtM99vbxBEm5zeaYimdy1359YxzaDdmDm+w2tV+qjA3u3RgZpjqfw5uiASgL8hT12Rgme4itBC6tuhf3fZEGlQI5cKSJn+fB2XMXw/3S7RfGRT9w==

    and get the signature:

    WR5bDw3XFuf29y9j1IJIM3E96g9d1fgyaWwUVKYIHOhOflKWuT0ei1Myz8aJzP1k2XlztQARK1h4Rj5LHMbZDZMSkkwfdNdr0VoKrfxPv6bJqW/paduYv47wQ5Q6YSqpuLsNjCjy5MMLKtLnuff9Drc18lEQU2RGq7eh8lELUVB6asts+8FOp7PIawfeSGbM+kSKPdH181E3XR0/V+ck1/m6cBouGeZHM7767S6nkbl8b7wx9kCj5z1j0mbvQxU3lPjK1qS+ntkbP4V0oMaDKe8kfnbZK9/LX6tRZFHoDjjMspwy3M72pe+ohn9FkfPg5G39H2L2dVtV/fb5BXlOBQ==

  3. Now you should pass the generated hash (X-Hash) and the signature value (X-Signature) in the request header. The request will look like this:

curl 'https://api.uat.all2pay.net/v1/getOrderStatusExtended.do' \
-H 'X-hash: MmI1ZmUwZjc2YmUxNGJiY2ZjN2M3NzQ3OGYwYTU4ODUwYzhmNzEyNDg5YjczOTg3NzZlYTg4ODEwNDczYzFmYg==' \
-H 'X-signature: WR5bDw3XFuf29y9j1IJIM3E96g9d1fgyaWwUVKYIHOhOflKWuT0ei1Myz8aJzP1k2XlztQARK1h4Rj5LHMbZDZMSkkwfdNdr0VoKrfxPv6bJqW/paduYv47wQ5Q6YSqpuLsNjCjy5MMLKtLnuff9Drc18lEQU2RGq7eh8lELUVB6asts+8FOp7PIawfeSGbM+kSKPdH181E3XR0/V+ck1/m6cBouGeZHM7767S6nkbl8b7wx9kCj5z1j0mbvQxU3lPjK1qS+ntkbP4V0oMaDKe8kfnbZK9/LX6tRZFHoDjjMspwy3M72pe+ohn9FkfPg5G39H2L2dVtV/fb5BXlOBQ==' \
-H 'Content-Type: application/json' \
-d '{"orderId": "243d940e-bedd-4e95-8e78-b08f459a242f","password": "test-user-password","userName": "test-user"}''

The request should meet the following requirements:

Groovy code example

Below is the Groovy code example that generates the hash and signature:

import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.Key;
import java.security.KeyFactory;

Base64.Decoder b64d = Base64.getDecoder();
Base64.Encoder b64e = Base64.getEncoder();

//2048 pair
def privKeyBase64 = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC0NQCjnqUmdEK1aisFXGMIihI73zofZycI/EGTJkQ6HOT2e/dG4j1RqHwuE6FEPL8lGG3EddFSiZzjDbIH+bm5m0CpcnFN1ShTxbA8BzM+OGxRHsuHU168HiS3OSIO9IxCKlaYzNgL1IygkjF6xqXLRvAPJ70zb/ymD2Cszfbw3ZNcZnr07QtJF/Jbn/+UJt+1hin9y6ikKZH+laZlMOx6LLGsj3W1PjFyEuzNUmau3iX6I6PdfgVwbB7TRVFuFDfxzBZxtBDympGRxpTGNFHug1NNPA9ekWa+zVlNVbzPrMuf6rF4yvKc8Tv9OkQ7RfGv1YmFCgEjqgnW2puIuufJAgMBAAECggEAdzDzakVdMJEHKly9db9ElXpVUxpzpB+jFvNaIAzYZaOE4W7fABhVvHP3Jra/HJYdS1jcCWxv9eqlRRxi25mV+Six3SGfxX4uPTJtPVb50raZFhKLRcejykMZv8JfP4sKVh0Qx+H9J93+4Zmwdmd7c3dJAo0jPCle7ysOo11bbrXTMwgg/i9Rxfzf5j4HYVTqYGIOL8cwNDE9XzDV7ekYJ147Ro15QnXK315fij2y7HFEJoyXp4BySm2nGNjxBjPn7sd/0Y8IrnYtpAnD7PG6oHRMITAEpZGEGyAi5cxAwTK1p43megM9ZaddSYPGtyrelNW6xm86x5Ulv8Bqhg1gzQKBgQDcAkILsNGV1eazxsv7QEg9Xs7h9VHrmPVTSGx6OjcZNP/VSZCzLT6IiK68c5ywkTe8SuDCLgU6Tcf4YOuno7gRplnLm6KzTatu/WE4qiz+WzrT4/+uspOli5YdTBXWtGXgWWvcnuQ1TTECdFf83na/cPDM5qKfJuoOan3gbMNVowKBgQDRr+MarMxNnoaH/XlY0HURtAUENieoyll72iHLofdlyQPDqoo0G00zjlV11DH/pH8qQ1PsswJDxL6oiY9XkxZ0P2Pd5BnYW8wSUdQ033ReSxc9WHlnTnixPp/YHraAmzZXncM6B8YJWAO5CtLW7Xym58BLUgMMH6FUM0gKiLMrowKBgQCx1alpJb3jrYjTnEdZifZalP4JK3DSTUtPzGTSz6el2m9JCjPKgTHgzwrfDVyEZH1219ehXe2f2Stgm4cgdHfe3GYM7HqxEIEYL/ucAAJqf3enus37eiFaWOA4Qj4M1LjchatoI483fnO1FjHhFjlKOZKLLYoZtyzOBkpFU+T4nQKBgQDNCpYj3ncFK4/X6NfBLk5b2lHRdXdAiWYJQxsq+Z1m7bJ9ogT0wQGz+Wm+B5pApkUnOaEWY1FCnV/mhGUjuJQLZnUsZEGVnOYnv9anQR6Umg8GkL5ec5B3mYpKlnXVunDgKkfeNf3D40n4pwnW23G58ALMZEzQjRl/sYmvq06wywKBgQCer3/ehngsL6xpdmWaWOyZja992F9s7JKb+M5esEltp5utpB9DEGrV1KKnLTWtM99vbxBEm5zeaYimdy1359YxzaDdmDm+w2tV+qjA3u3RgZpjqfw5uiASgL8hT12Rgme4itBC6tuhf3fZEGlQI5cKSJn+fB2XMXw/3S7RfGRT9w=="

def body = """{"orderId": "243d940e-bedd-4e95-8e78-b08f459a242f","password": "test-user-password","userName": "test-user"}"""

//// HASH /////

KeyFactory keyFactory = KeyFactory.getInstance('RSA');
Signature signatureProvider = Signature.getInstance("SHA256withRSA");
byte[] bodyHash = body.digest('SHA-256')
def bodyHashBase64 = b64e.encodeToString(bodyHash);
println "Hash Hex: " + new String(bodyHash)
println "Hash Base64: $bodyHashBase64"

//// SIGN /////

byte[] byteKey = b64d.decode(privKeyBase64.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(byteKey)
Key privKey = keyFactory.generatePrivate(keySpec);

signatureProvider.initSign(privKey);
signatureProvider.update(bodyHash);
byte[] signature = signatureProvider.sign();
def signatureBase64 = b64e.encodeToString(signature)
println "Signature: $signatureBase64"

Python code example

Same code in Python: generating hash and signature.

import base64
import hashlib
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend

data = """{"orderId": "243d940e-bedd-4e95-8e78-b08f459a242f","password": "test-user-password","userName": "test-user"}"""

priv_key_pem = """
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC0NQCjnqUmdEK1aisFXGMIihI73zofZycI/EGTJkQ6HOT2e/dG4j1RqHwuE6FEPL8lGG3EddFSiZzjDbIH+bm5m0CpcnFN1ShTxbA8BzM+OGxRHsuHU168HiS3OSIO9IxCKlaYzNgL1IygkjF6xqXLRvAPJ70zb/ymD2Cszfbw3ZNcZnr07QtJF/Jbn/+UJt+1hin9y6ikKZH+laZlMOx6LLGsj3W1PjFyEuzNUmau3iX6I6PdfgVwbB7TRVFuFDfxzBZxtBDympGRxpTGNFHug1NNPA9ekWa+zVlNVbzPrMuf6rF4yvKc8Tv9OkQ7RfGv1YmFCgEjqgnW2puIuufJAgMBAAECggEAdzDzakVdMJEHKly9db9ElXpVUxpzpB+jFvNaIAzYZaOE4W7fABhVvHP3Jra/HJYdS1jcCWxv9eqlRRxi25mV+Six3SGfxX4uPTJtPVb50raZFhKLRcejykMZv8JfP4sKVh0Qx+H9J93+4Zmwdmd7c3dJAo0jPCle7ysOo11bbrXTMwgg/i9Rxfzf5j4HYVTqYGIOL8cwNDE9XzDV7ekYJ147Ro15QnXK315fij2y7HFEJoyXp4BySm2nGNjxBjPn7sd/0Y8IrnYtpAnD7PG6oHRMITAEpZGEGyAi5cxAwTK1p43megM9ZaddSYPGtyrelNW6xm86x5Ulv8Bqhg1gzQKBgQDcAkILsNGV1eazxsv7QEg9Xs7h9VHrmPVTSGx6OjcZNP/VSZCzLT6IiK68c5ywkTe8SuDCLgU6Tcf4YOuno7gRplnLm6KzTatu/WE4qiz+WzrT4/+uspOli5YdTBXWtGXgWWvcnuQ1TTECdFf83na/cPDM5qKfJuoOan3gbMNVowKBgQDRr+MarMxNnoaH/XlY0HURtAUENieoyll72iHLofdlyQPDqoo0G00zjlV11DH/pH8qQ1PsswJDxL6oiY9XkxZ0P2Pd5BnYW8wSUdQ033ReSxc9WHlnTnixPp/YHraAmzZXncM6B8YJWAO5CtLW7Xym58BLUgMMH6FUM0gKiLMrowKBgQCx1alpJb3jrYjTnEdZifZalP4JK3DSTUtPzGTSz6el2m9JCjPKgTHgzwrfDVyEZH1219ehXe2f2Stgm4cgdHfe3GYM7HqxEIEYL/ucAAJqf3enus37eiFaWOA4Qj4M1LjchatoI483fnO1FjHhFjlKOZKLLYoZtyzOBkpFU+T4nQKBgQDNCpYj3ncFK4/X6NfBLk5b2lHRdXdAiWYJQxsq+Z1m7bJ9ogT0wQGz+Wm+B5pApkUnOaEWY1FCnV/mhGUjuJQLZnUsZEGVnOYnv9anQR6Umg8GkL5ec5B3mYpKlnXVunDgKkfeNf3D40n4pwnW23G58ALMZEzQjRl/sYmvq06wywKBgQCer3/ehngsL6xpdmWaWOyZja992F9s7JKb+M5esEltp5utpB9DEGrV1KKnLTWtM99vbxBEm5zeaYimdy1359YxzaDdmDm+w2tV+qjA3u3RgZpjqfw5uiASgL8hT12Rgme4itBC6tuhf3fZEGlQI5cKSJn+fB2XMXw/3S7RfGRT9w==
-----END PRIVATE KEY-----
""".strip()

# HASH Hex
sha256_hash = hashlib.sha256(data.encode('utf-8')).hexdigest()
print(f"Hash Hex: {sha256_hash}")

# HASH Base64
base64_hash = base64.b64encode(sha256_hash.encode('utf-8')).decode('utf-8')
print(f"Hash Base64: {base64_hash}")

# Signature
private_key = serialization.load_pem_private_key(priv_key_pem.encode('utf-8'), password=None, backend=default_backend())

signature = private_key.sign(sha256_hash.encode('utf-8'), padding.PKCS1v15(), hashes.SHA256())

signature_base64 = base64.b64encode(signature).decode('utf-8')
print(f"Signature: {signature_base64}")

Note that PEM key format (with BEGIN and END elements) is used in the Python example.

Categories:
router API V1
Categories
Search results