Open BankingB2B SuiteERP Integration
GuidesAPI ReferenceChangelog
Menu

Categories

Open BankingB2B SuiteERP Integration

  • GENERAL INFORMATION

    • Documentation guide
    • SingleView B2B Core APIs
    • Platform architecture
  • GETTING STARTED

    • Integration guide
    • Become a user
    • Prerequisites for Sandbox
    • Establishing secure connection
    • Authenticating your request
    • Making your first test api call
    • Prerequisites for production
    • Moving to production
  • AUTHENTICATION

    • Header parameters
    • Obtain access token
    • Generate signature
  • Data APIs

    • Account statement
    • Balance enquiry
    • IBAN verification
    • POS transactions
    • Raw statement
  • Payment APIs

  • SADAD

  • RESOURCES

    • POR Details
    • Error codes
  • API Archives


Generate signature

Digitally sign your API requests to ensure authenticity and data integrity

Every request sent to the SingleView B2B API Suite must include a digital signature. The signature verifies that the request originated from an authorized client and that the request payload has not been modified during transmission.

The signature is generated by hashing the request payload using the SHA-256 algorithm and encrypting the hash with the client's RSA private key. When the request is received, the SingleView platform validates the signature using the corresponding public key associated with the client's certificate.

How signature generation works

The signature generation process consists of the following steps:

  1. Extract the Message object from the request payload.
  2. Convert the JSON object into its canonical representation.
  3. Encode the payload using UTF-8.
  4. Generate a SHA-256 hash of the payload.
  5. Encrypt the hash using your RSA private key.
  6. Encode the encrypted signature as a Base64 string.
  7. Include the generated signature in the Signature field of the request payload.

Only requests with a valid signature are processed by the SingleView platform.

Important information

Property

Value

Algorithm

SHA256withRSA

Character Encoding

UTF-8

Signature Format

Base64

Signed Content

Message object only

Keystore Format

.p12 or .pfx containing a valid RSA private key and certificate

Verification

Performed using the corresponding public key from the certificate

Note

Only the contents of the Message object should be digitally signed. Do not sign the entire request payload or include the Signature field when generating the signature.

Example Request

The following example shows a request before the signature has been generated.

{
"Message": {
"OSVBeneficiaryBankDetailsRequest": {
"OSVBeneficiaryBankDetails": [
{
"NationalId": "1231512323",
"IBANAccount": "SA2012345678972123456789"
}
]
}
},
"Signature": ""
}

In this example, only the Message object is used to generate the signature.

Sample implementation algorithm

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Enumeration;
import java.util.Locale;
import java.util.logging.Logger;


// INPUTS
String jsonPayload = """
    {
            "OSVBeneficiaryBankDetailsRequest": {
                "OSVBeneficiaryBankDetails": [
                    {
                        "NationalId": "1231512323",
                        "IBANAccount": "SA2012345678972123456789"
                    }
                ]
            }
        }
  """; // Pass payload as a string

String keyPath = "path of file"; // .p12 | .pfx | .key | .pem
String password = "password"; // only for p12/pfx

// VALIDATION
if (jsonPayload == null || jsonPayload.isBlank()) {
  throw new IllegalArgumentException("jsonPayload must not be empty");
}
if (keyPath == null || keyPath.isBlank()) {
  throw new IllegalArgumentException("keyPath must not be empty");
}

// JSON PARSE + CANONICALIZE
ObjectMapper mapper = new ObjectMapper();
Object payloadObject = mapper.readValue(jsonPayload, Object.class);
ObjectMapper canonicalMapper = new ObjectMapper();
canonicalMapper.configure(
  SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,
  true
);
String dataToSign = canonicalMapper.writeValueAsString(payloadObject);

// KEY LOADER (AUTO DETECT)
PrivateKey privateKey;
String lowerPath = keyPath.toLowerCase(Locale.ROOT);
if (lowerPath.endsWith(".p12") || lowerPath.endsWith(".pfx")) {
  if (password == null || password.isBlank()) {
    throw new IllegalArgumentException("password is required for p12/pfx");
  }

  KeyStore keyStore = KeyStore.getInstance("PKCS12");
  char[] passwordChars = password.toCharArray();
  try (FileInputStream fis = new FileInputStream(keyPath)) {
    keyStore.load(fis, passwordChars);
  }

  privateKey = null;
  Enumeration<String> aliases = keyStore.aliases();
  while (aliases.hasMoreElements()) {
    String alias = aliases.nextElement();
    if (keyStore.isKeyEntry(alias)) {
      privateKey = (PrivateKey) keyStore.getKey(alias, passwordChars);
      break;
    }
  }

  if (privateKey == null) {
    throw new IllegalArgumentException("No private key found in keystore");
  }
} else if (lowerPath.endsWith(".key") || lowerPath.endsWith(".pem")) {
  String pem = Files.readString(Path.of(keyPath), StandardCharsets.UTF_8);

  // Optional: PEM may include CERTIFICATE block(s)
  boolean hasCert = pem.contains("-----BEGIN CERTIFICATE-----");
  boolean hasPkcs8Key = pem.contains("-----BEGIN PRIVATE KEY-----");
  boolean hasPkcs1Key = pem.contains("-----BEGIN RSA PRIVATE KEY-----");

  if (hasCert && !hasPkcs8Key && !hasPkcs1Key) {
    throw new IllegalArgumentException(
      "PEM contains CERTIFICATE only. Signing requires a PRIVATE KEY."
    );
  }

  if (hasPkcs1Key) {
    throw new IllegalArgumentException(
      "BEGIN RSA PRIVATE KEY (PKCS#1) is not supported by this code. Convert to PKCS#8: BEGIN PRIVATE KEY."
    );
  }

  if (!hasPkcs8Key) {
    throw new IllegalArgumentException("No supported private key block found in PEM");
  }

  String key = pem
    .replace("-----BEGIN PRIVATE KEY-----", "")
    .replace("-----END PRIVATE KEY-----", "")
    // remove certificate blocks if present
    .replaceAll("-----BEGIN CERTIFICATE-----[\\s\\S]*?-----END CERTIFICATE-----", "")
    .replaceAll("\\s", "");

  byte[] decoded = Base64.getDecoder().decode(key);
  PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
  privateKey = KeyFactory.getInstance("RSA").generatePrivate(spec);
} else {
  throw new IllegalArgumentException("Unsupported key format");
}

// SIGN
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(privateKey);
signer.update(dataToSign.getBytes(StandardCharsets.UTF_8));
String signature = Base64.getEncoder().encodeToString(signer.sign());

Include the signature in the request

After generating the signature, include it in the request payload before sending the request.

Sample representation of including signature in request
{
"Message": {
...
},
"Signature": "<Base64 Encoded Signature>"
}

Signature verification

When the request reaches the SingleView platform, the following validation steps are performed:

  • The Message object is extracted.
  • A SHA-256 hash is generated from the received payload.
  • The submitted signature is decrypted using the public key associated with the configured certificate.
  • The calculated hash is compared with the decrypted hash.
  • If both values match, the request is authenticated and processed.

If signature validation fails, the request is rejected.

Best Practices

  • Protect your private key and never expose it in source code or client applications.
  • Use only the private key associated with the certificate configured during onboarding.
  • Ensure all payloads are encoded using UTF-8 before signing.
  • Sign only the Message object.
  • Store keystore files securely and restrict access to authorized users.
  • Rotate certificates and keys in accordance with your organization's security policies.
Updated June 25, 2026

PreviousObtain access tokenNextAccount statement
Was this helpful?