All requests to the server should be signed with the private RSA key using the RSASSA_PSS_SHA_512 signing algorithm. Paybis will use the public key to verify the signature and authenticity of your request.

📘

Keys are generated by the Partner, and the public key is provided to Paybis integration manager.

🚧

If the private key was lost, you need to notify the integration team: the related public key stored on the Paybis end should be revoked and the new key pair should be generated.

Forming signature

A signature should be generated from the private 2048-bit RSA key and HTTP request body hash using the RSASSA_PSS_SHA_512 asymmetric signing algorithm. After that, the signature is encoded with Base64.

Signature = Base64(RSASSA_PSS_SHA_512(PRIVATE_KEY, SHA512(requestBody)))

Note: Whenever the request body is changed, a new signature should be generated. Requests with empty bodies should not be signed (the GET methods requests).

Generating a signature sample code: PHP

<?php

use phpseclib\Crypt\RSA;

$privateKey = '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';
$requestBody = '{
  "partnerUserId": "1bc166bd-1808-4009-8454-aceb47ba8753",
  "cryptoWalletAddress": null,
  "email": "[email protected]",
  "applicantSumsubToken": "token_hash",
  "locale": "en"
}';

$requestBodyHash = hash('sha512', $requestBody);
$verifier = new RSA();

$verifier->setSignatureMode(RSA::SIGNATURE_PSS);
$verifier->setHash('sha512');
$verifier->setMGFHash('sha512');
$verifier->loadKey($privateKey);

$signature = base64_encode($verifier->sign($requestBodyHash));

echo $signature . PHP_EOL;
composer require phpseclib/phpseclib;

Generating a signature sample code: Node.js

const crypto = require("crypto")

const privateKey = `-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----`

// Request body to sign
const verifiableData = '{"cryptoWalletAddress":{"address":"valid_btc_crypto_wallet_address","currencyCode":"BTC"},"locale":"en","partnerUserId":"johnDoe1","email":"[email protected]","quoteId":"7604aac0-7938-4e54-8a80-c3172a043c44"}'

const hash = crypto.createHash('sha512');
const hashedData = hash.update(verifiableData, 'utf-8').digest('hex');

// The signature method takes the data we want to sign, the
// hashing algorithm, and the padding scheme, and generates
// a signature in the form of bytes
const signature = crypto.sign("sha512", Buffer.from(hashedData), {
    key: privateKey,
    padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
    saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST,
})

console.log(signature.toString("base64"))

Verify a signature sample code: Node.js

const crypto = require("crypto")

const publicKey = `-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEAut917lnm3PFcVqbqmVGu+GPHfk5jeebukMekdrrJEuJnQf4Slu6Q
+wwAneHDoJVEKa3ecyLjYp9D1wxXCL2m3Cu+Ev4ueOwIKaAFv+ZHl5naHV6T114h
C3dIXW31qT9t8n6WvYVz7aAdhRLZBry4eVe0HDabFujM7g+N2eS+XM38zhJvHO1w
5TZcK7EQP25NuLSzyp/QDG/3vb8f4ZrYAgbtmXRnDfu5b88iB2noPfTJcruJ/rr0
+/yic8ytUi5dzOfXGbpDtpcry8DeeJeG9efLsNL7IoTcFgrgTvhKtxbLy3QmPHgA
xKvGdP4jSWLJJfMAlIFz8jPuUSxuPKSoKocXZaHb+Zpt6PQl6qotwkHH4Acb/uha
jb1sBLbZBbqyjaOZyuWt7yMcdkdkbV14h3S6eaBsLXeeZ/kI+y3j2MKajDQ7k8kM
q8wY7Ypco+TAP04EIySuNlrwZlUhZaDpGO77i8yreUCrLerAqikyGygo9jcbsJ93
JsYkvWG5f7qb0+/JsZ0YdoIs19aA4H46rz/eWJT7HPsMk9P+eX4S6xSY/Xjj873v
nKHyFsg3Ovq3sb5ovtmJfbLXeWFlvtn/EWXjvyuyf6wJWQK8TAPg3bdpS3DyNm/D
Ru94PjdPAQ2mX86dz0D7OWqrhlXHdwn1vopiTjmKtBGdAoR4lsZKTy0CAwEAAQ==
-----END RSA PUBLIC KEY-----`

const verifiableData = '{"cryptoWalletAddress":{"address":"valid_btc_crypto_wallet_address","currencyCode":"BTC"},"locale":"en","partnerUserId":"johnDoe1","email":"[email protected]","quoteId":"7604aac0-7938-4e54-8a80-c3172a043c44"}'

const hash = crypto.createHash('sha512');
const hashedData = hash.update(verifiableData, 'utf-8').digest('hex');

const signature = 'B+Dyfoaeb8YkTHPIAJ54baa8XD14fCdPUhOq1A+9togM2DsyfaYq7qnswy9/LS00x8JzXkIdb7WGGk90qEXEKWUpc8KJ2epHi/sVAMFtudAGulWf/ib2BvRt1us0bylBCtwcXfh9lB4EnjSdoMvCCejSG5Jc5CYIX+v72HCimTjVwaiizIE9/RyBDbe97+1kA/l1CfzQL2QOcX5MjQn7ED6A5r/7tAQNp1KJLLKb8yeTpqppEGvOj6HIe0dZs1lEs1lXWhmBFllQmChSsjf94BDuJK780PePwlIUDKDra/ntQnGOaDZd41ugAjlPnPCC9UhACHL+PG+yKH1a6hIVdA0ZldTzq0kh+Ft1jBY1g21AUS8/Tcqr0Tw3ETchdl2OdxrypOwsZS8VCc+22DSY7MYeKSTHC1xzyS+Arw4VBu08E8d8z9sb9XxiMk1kbBgTNS5OEkYRXwGWy9q76S1xqPZpU9PRpbj1DRfGrcm2sY4rkGrpIT0o78LfeXQ5BPXPdVmObR29s6mDqQ+vwWMX4UiH2o9I58GqDk4/dz3LZLmCzdgUdUmdg7V2FUdQ8/c7Gi5kXLyN5UIJSPfSguMtZZLHTpVorTUY5M6THuctFepGv14b0IlzH/VLgHLUAkONL8/qcK0lv4oubVsisTxr2082M6Bj7J+BLhSLPuAx1YI='
// To verify the data, we provide the same hashing algorithm and
// padding scheme we provided to generate the signature, along
// with the signature itself, the data that we want to
// verify against the signature, and the public key
const isVerified = crypto.verify(
    "sha512",
    Buffer.from(hashedData),
    {
        key: publicKey,
        padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
        saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST,
    },
    Buffer.from(signature, 'base64')
)

// isVerified should be `true` if the signature is valid
console.log("signature verified: ", isVerified)

Example signature:

mVuJJRvdDJMPWkDHMx0Fj0lOs021Usv6iEHpJVRzX/hNfEawOtJEIGU5hfu6GXrqwWj/+JuGjejNQo2pkoN+OUREv9gylD91GROaLA7zbaQLyiIch97UVi4iyg9d4mVHXtr3kbuxwYDBSOgHV+4mJuGM2jOOXY86bidKdrt+UdcdQlt62DkvmHTLIqfXrLQevTEEs0nzSTorfZkMLKwmXTu+1qJv3+R3QxWFhf2QqMi GR4Y8BiMp/URo7y6fVlI4dbVnqoOU37izXguheDR17R/KFe/SyhtltsAvhVBrbrTd9y34efbTCtNgDe9qdwTolgp7DnDSqHGW9zLrjQdbYdGlLzjbok5tfmtcQUcBhXiEOPmKO+s5ZSR46NAURxg9oh9BjscmG+moMQj244WtT3AAkJVPsTItwnUriZahYDplrppZz9BZXHGjcRf/nh/Xvd3aNv4HKyKPQnpYGNqV9ZvDXYkTdjFp57/vY5h48dg9wOd4BiLJ7FiOWpgtpwUUS1+gbzjvDvgpOPCFVRpgeYqq/LNIfcN1nfqcDSKSvjMzJlsX1xZN/ezinyqGZNaLRiil+wX02ihdMtVknco/ne4MsJvhNg2uTyjBv0VSji8rMDzeYLiDiapj0nm47lQHhH1vthd8BH83oosHBDLaJuPo4M4WuIfdgxfRjohHFKI=

This example is generated using the data and PHP sample code provided above.

Generating RSA keys

To generate the key pair on the Partner's end and share the public key with Paybis, use the following commands:

  1. Generate a private 2048-bit RSA key.
openssl genrsa -out private.key 4096
  1. Create a public key from private one.
openssl rsa -in private.key -outform PEM -pubout -out public.pub

Signing HTTP Requests

The signature is passed in the X-Request-Signature header. Paybis decodes and verifies the signature with the public RSA key. If the request signature validation fails, 403 HTTP error code is returned.