Skip to main content

Signature Algorithm

1 Encrypt

Steps

  1. Get the current timestamp ${timestamp}.
  2. Combine ${timestamp} and the request body into a new string $content:
    • If the HTTP method is POST, the request body is JSON text.
    • If the HTTP method is GET, the request body is the unescaped query string.
      • Example: name=foo&age=18 is valid.
      • name%3dfoo%26age%3d18 is invalid.
  3. Sign $content with SHA256 and PKCS1V15, 1024-bit RSA.
  4. Encode the signed bytes to Base64.

Example

We sign the following data. The timestamp is 1751441054, and the RSA key is:

-----The following key pairs are for testing only-----
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAOFSnhqtu40TOtok+yXeB+O76PXb/VAJU4Yih6hViOdSGd7imWmCSZyP
psl3TmLhUoB+rTzYDdYrlYYng6cVn6yUhxjpMWD4Qp5K4GzjvUM0f+AxlKYMj8OQ
AgDPmZG1a5ydFrje4PLytC7sUw3GP4TTk8xg6iMHmYPdRDv7AEWdAgMBAAE=
-----END RSA PUBLIC KEY-----

-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQDhUp4arbuNEzraJPsl3gfju+j12/1QCVOGIoeoVYjnUhne4plp
gkmcj6bJd05i4VKAfq082A3WK5WGJ4OnFZ+slIcY6TFg+EKeSuBs471DNH/gMZSm
DI/DkAIAz5mRtWucnRa43uDy8rQu7FMNxj+E05PMYOojB5mD3UQ7+wBFnQIDAQAB
AoGBAIqpeCi83516xw32EiSEleBdBIlA34vAoYj7ePZr5PtN+CPE2Von1rslYCO9
axzIwyfWTTATWbiCS9sqOAe78+1t3shU0ryXelWQ95LOdiMp0ZvU3ya6MzSVuiuL
6gOQYu8kzRqCzqvyMNdAHFMmP0iESefYegaiU46yQo2ZZAtVAkEA/N6gl4tYJBef
e/Gxi7qhuIKz0mvfA/yieRuiVhFb25oJaagU6hBuzegmbs45hh+n4bw8AA9k0bvi
3IBIdV/CbwJBAOQcsOPf+lrs4PEJYUQ8/eqiG7tgIUeXxuerfPYMAzvJ7EU1u72o
/5V9zdim+hPq+9cvsqO7dkJx+cEpgwHKzrMCQQDoYqArJP/Hi34hpaiIxOdCbbtc
MY4uV8noiqDRf/pvAkyMMM+nm5p42FQZmKP8wrLCHP72/y6kIIsIIUx1sZ85AkEA
15eWR4jEoXMIFkd7Onc6tKuJ8kokqfl08DhP3mv0EmoBeFgUnGhBRxSjnIQUDwck
Bnj6KW1fk+UM29dUDjmTqQJBAITyP16eBWjlzcgaxoT2d/d7S7AXvgCxCzRRi5ph
IavMyjrhDKyBGZ0mI6eoREaC4bxl31RRkYtg9mNeU3TxsBM=
-----END RSA PRIVATE KEY-----

JSON request body

{
"merchantId": "bybit",
"clientId": "xx",
"paymentType": "E_COMMERCE",
"merchantTradeNo": "8751738a-97a4-49da-8b64-185ffcb98a7a",
"payId": "01JXWP4XK4ZRVYD3HT9PD52KCD",
"status": "PAY_SUCCESS",
"amount": "1.23",
"currency": "USDT",
"currencyType": "crypto",
"createTime": 1750088344,
"paymentTime": 1750088344,
"finishTime": 1750088344,
"customer": {
"external_user_id": "123",
"user_name": "tester",
"register_time": "1750088344",
"kyc_country": "HK",
"uid": "foo"
}
}

Concrete signing steps

  1. Get the current timestamp:
1751441054
  1. Combine ${timestamp} and request body into a new string $content:
1751441054{"merchantId":"bybit","clientId":"xx","paymentType":"E_COMMERCE","merchantTradeNo":"8751738a-97a4-49da-8b64-185ffcb98a7a","payId":"01JXWP4XK4ZRVYD3HT9PD52KCD","status":"PAY_SUCCESS","amount":"1.23","currency":"USDT","currencyType":"crypto","createTime":1750088344,"paymentTime":1750088344,"finishTime":1750088344,"customer":{"external_user_id":"123","user_name":"tester","register_time":"1750088344","kyc_country":"HK","uid":"foo"}}
  1. Sign $content with SHA256 and PKCS1V15, 1024-bit RSA.
  2. Encode the signed bytes in Base64:
vOyN+NnfWppnhxS6y1D+CAllj6Z/3np1Tm+nrt16e/EDl4VZjU2sVPSS/cBcf5Hy/jBarA8Y7yrvYqJonJAsAZcFKu9twW2XWyMbURC63Iumh5gkAE9UEx4/irpX4W6KXhqc2+7wc1tapC5zfVWRMIQ5Dh+7VscxLY+/WjKb/Vw=

2 Decrypt

Steps

  1. Get timestamp and signature from the request header.
  2. Get the request body:
    • If the HTTP method is POST, the request body is JSON text.
    • If the HTTP method is GET, the request body is the unescaped query string.
  3. Combine ${timestamp} and the response body into a new string $content.
  4. Decode the signature bytes from Base64.
  5. Hash the string content with SHA256 and verify the signature using PKCS1V15, 1024-bit RSA.

Example

We provide the following data:

vOyN+NnfWppnhxS6y1D+CAllj6Z/3np1Tm+nrt16e/EDl4VZjU2sVPSS/cBcf5Hy/jBarA8Y7yrvYqJonJAsAZcFKu9twW2XWyMbURC63Iumh5gkAE9UEx4/irpX4W6KXhqc2+7wc1tapC5zfVWRMIQ5Dh+7VscxLY+/WjKb/Vw=

The timestamp is 1751441054, and the RSA key is:

-----The following key pairs are for testing only-----
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAOFSnhqtu40TOtok+yXeB+O76PXb/VAJU4Yih6hViOdSGd7imWmCSZyP
psl3TmLhUoB+rTzYDdYrlYYng6cVn6yUhxjpMWD4Qp5K4GzjvUM0f+AxlKYMj8OQ
AgDPmZG1a5ydFrje4PLytC7sUw3GP4TTk8xg6iMHmYPdRDv7AEWdAgMBAAE=
-----END RSA PUBLIC KEY-----

-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQDhUp4arbuNEzraJPsl3gfju+j12/1QCVOGIoeoVYjnUhne4plp
gkmcj6bJd05i4VKAfq082A3WK5WGJ4OnFZ+slIcY6TFg+EKeSuBs471DNH/gMZSm
DI/DkAIAz5mRtWucnRa43uDy8rQu7FMNxj+E05PMYOojB5mD3UQ7+wBFnQIDAQAB
AoGBAIqpeCi83516xw32EiSEleBdBIlA34vAoYj7ePZr5PtN+CPE2Von1rslYCO9
axzIwyfWTTATWbiCS9sqOAe78+1t3shU0ryXelWQ95LOdiMp0ZvU3ya6MzSVuiuL
6gOQYu8kzRqCzqvyMNdAHFMmP0iESefYegaiU46yQo2ZZAtVAkEA/N6gl4tYJBef
e/Gxi7qhuIKz0mvfA/yieRuiVhFb25oJaagU6hBuzegmbs45hh+n4bw8AA9k0bvi
3IBIdV/CbwJBAOQcsOPf+lrs4PEJYUQ8/eqiG7tgIUeXxuerfPYMAzvJ7EU1u72o
/5V9zdim+hPq+9cvsqO7dkJx+cEpgwHKzrMCQQDoYqArJP/Hi34hpaiIxOdCbbtc
MY4uV8noiqDRf/pvAkyMMM+nm5p42FQZmKP8wrLCHP72/y6kIIsIIUx1sZ85AkEA
15eWR4jEoXMIFkd7Onc6tKuJ8kokqfl08DhP3mv0EmoBeFgUnGhBRxSjnIQUDwck
Bnj6KW1fk+UM29dUDjmTqQJBAITyP16eBWjlzcgaxoT2d/d7S7AXvgCxCzRRi5ph
IavMyjrhDKyBGZ0mI6eoREaC4bxl31RRkYtg9mNeU3TxsBM=
-----END RSA PRIVATE KEY-----

Concrete verification steps

  1. Get timestamp and signature from the request header:
timestamp = 1751441054
signature = vOyN+NnfWppnhxS6y1D+CAllj6Z/3np1Tm+nrt16e/EDl4VZjU2sVPSS/cBcf5Hy/jBarA8Y7yrvYqJonJAsAZcFKu9twW2XWyMbURC63Iumh5gkAE9UEx4/irpX4W6KXhqc2+7wc1tapC5zfVWRMIQ5Dh+7VscxLY+/WjKb/Vw=
  1. Get the request body (POST: JSON text, GET: unescaped query string):
{"merchantId":"bybit","clientId":"xx","paymentType":"E_COMMERCE","merchantTradeNo":"8751738a-97a4-49da-8b64-185ffcb98a7a","payId":"01JXWP4XK4ZRVYD3HT9PD52KCD","status":"PAY_SUCCESS","amount":"1.23","currency":"USDT","currencyType":"crypto","createTime":1750088344,"paymentTime":1750088344,"finishTime":1750088344,"customer":{"external_user_id":"123","user_name":"tester","register_time":"1750088344","kyc_country":"HK","uid":"foo"}}
  1. Combine ${timestamp} and response body into a new string $content:
1751441054{"merchantId":"bybit","clientId":"xx","paymentType":"E_COMMERCE","merchantTradeNo":"8751738a-97a4-49da-8b64-185ffcb98a7a","payId":"01JXWP4XK4ZRVYD3HT9PD52KCD","status":"PAY_SUCCESS","amount":"1.23","currency":"USDT","currencyType":"crypto","createTime":1750088344,"paymentTime":1750088344,"finishTime":1750088344,"customer":{"external_user_id":"123","user_name":"tester","register_time":"1750088344","kyc_country":"HK","uid":"foo"}}
  1. Decode the signature bytes from Base64.
  2. Encrypt string content with sha256 and verify signature base PKCS1V15,1024.