Spiral PG is using RSA digital signature for API authentication.

Key Pair Generation

Spiral will provide a public key to verify the message sending by spiral. Vice versa, merchants are needed to provide the public key to Spiral portal first and signed the request message by its private key.

As time and merchant reference is used as the payload of the signing, a signature can only be used for a single transaction and be valid for a short period of time.

You can follow below steps to generate key pair by OpenSSL:

1. Execute `openssl genrsa -out clientId_private_key.pem 2048` to get merchant’s private key. (Private should be used by the merchant’s web system)

2. Execute `openssl rsa -in clientId_private_key.pem -pubout > clientId_public_key.pem` to get merchant’s public key. (Public key to be shared to EFT and import to Spiral portal)

Signing Message

The signing message is formatted by below 3 data elements:

Date element

Description

clientId

Client ID, assigned by Spiral

MerchantRef

Merchant Reference, the value is unique for every
transaction, it can be constructed by invoice number + the number of payment
attempts

Datetime

The UTC time in %Y-%m-%d’T’%H:%M:%S’Z’ format
when the request/response is composed, the UTC time has 8 hours difference with HKT. e.g.  2021-01-17T11:39:51+08:00 (Hong Kong Time) should be using 2021-01-17T03:39:51Z

How to sign

To create a Spiral-Client-Signature:

  1. Construct a payload by concatenating the following items in order: client ID, merchant reference and Spiral-Request-Datetime
  2. Use the client’s private key to create a signature for the payload using SHA256withRSA algorithm
  3. Base64 encode the signature bytes to get the signature string

Payload Example:

Client ID: 000000000000001

Merchant reference: 123456789012

Spiral-Request-Datetime: 2020-08-01T10:22:34Z
Resulting Payload: 0000000000000011234567890122020-08-01T10:22:34Z

 

Signature creation sample Code:

define(“ClientPrivateKey”, “file://./Cert/custpri.pem”);

public function signing($cliendId, $merchantRef){
  $now = new Datetime(“now”);
  $now->setTimeZone(new DateTimeZone(‘UTC’));
  $request_datetime = $now->format(‘Y-m-d\TH:i:s\Z’);
  $payload = $cliendId . $merchantRef . $request_datetime;

  $pkeyid = openssl_pkey_get_private(ClientPrivateKey);
  openssl_sign($payload, $signature, $pkeyid, OPENSSL_ALGO_SHA256);

  return $signature;
}

var clientPrivateKey = ‘/Cert/custpri.pem’;

function signing(clientId, merchantRef) {
  const crypto = require(‘crypto’);
  const sign = crypto.createSign(‘SHA256’);
  const fs = require(‘fs’);

  var isoTime = d.toISOString();
  isoTime = isoTime.replace(/\.\d+/, “”);
  sign.write(clientId + merchantRef + isoTime);
  sign.end();

  const key = fs.readFileSync(clientPrivateKey);
  return sign.sign(key, ‘base64’);
}

const String clientPrivateKey = @”C:\cert\custpri.xml”;

static String RSA_Sha256_Signature(String clientId, String merchantRef)
{
  // Using private key in XML format, there are online tool available to convert pem into xml format
  // e.g.: https://the-x.cn/en-us/certificate/PemToXml.aspx
  RSACryptoServiceProvider rsa = new  RSACryptoServiceProvider();
  rsa.FromXmlString(File.ReadAllText(clientPrivateKey));

  ASCIIEncoding encoder = new ASCIIEncoding();
  byte[] binData = encoder.GetBytes(clientId + merchantRef + isoTime);
  byte[] binSignature = rsa.SignData(binData, CryptoConfig.CreateFromName(“SHA256”));
return Convert.ToBase64String(binSignature);
}

How to verify

  1. Construct a payload by concatenating the following items in order: client ID, merchant reference and Spiral-Request-Datetime

  2. Base64 decode the signature string to get the signature bytes

  3. Verify the signature against the payload, decoded signature and Spiral PG’s public key using SHA256withRSA algorithm

Signature verification sample code:

define(“SpiralPublicKey”, “file://./Cert/spiralpub.pem”);

// check server signature
public function verify($clientId, $merchantRef, $requestDateTime, $signature)
{
  $pubkeyid = openssl_pkey_get_public(SpiralPublicKey);
  $server_sig_payload = $clientId .   $merchantRef . $requestDateTime;
  $ok = openssl_verify($server_sig_payload, base64_decode($signature), $pubkeyid, OPENSSL_ALGO_SHA256);
  return ($ok == 1);
}

var spiralPublicKey = ‘/Cert/spiralpub(UAT).pem’;

function verifying(clientId, merchantRef, dateTime, signature) {
  const crypto = require(‘crypto’);
  const verify = crypto.createVerify(‘SHA256’);
  const fs = require(‘fs’);

  verify.write(clientId + merchantRef + dateTime);
  verify.end();

  const key = fs.readFileSync(spiralPublicKey);
  return verify.verify(key, signature, ‘base64’);
}

const String clientPrivateKey = @”C:\cert\spiralpub.xml”;

static bool RSA_Sha256_Verify(String clientId, String merchantRef, String isoTime, String signature)
{
  // Using XML format, there are online tool available to convert pem into xml format
  // e.g.: https://the-x.cn/en-us/certificate/PemToXml.aspx
  RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
  rsa.FromXmlString(File.ReadAllText(fileName));

  ASCIIEncoding encoder = new ASCIIEncoding();
  byte[] binData = encoder.GetBytes(clientId + merchantRef + isoTime);
  byte[] binSignature = Convert.FromBase64String(signature);
  return rsa.VerifyData(binData, CryptoConfig.CreateFromName(“SHA256”), binSignature);
}

You can verify the result of the signature signing using below tool.

 

Signature Calculation Checking Tool

Client ID:
(The value is given during account creation.)

Merchant Refernce:
(This value is unique for every transaction, sometimes it is constructed by invoice number + the number of payment attempts.)

Time:
(In ISO format, UTC+0)

Private Key:

Signature: