Payload Encryption (Optional)

Payload encryption enables secure, tamper-proof communication between clients and Socure’s RiskOS™ using industry-standard JWE (JSON Web Encryption) and JWS (JSON Web Signature) protocols.

This feature ensures confidentiality, integrity, and compliance when transmitting sensitive identity data.

Supported endpoints

Payload encryption is supported on the following RiskOS™ endpoints.

Service Supported Endpoints
Evaluation POST /api/evaluation
POST /api/evaluation/:version
PATCH /api/evaluation/:eval_id
DocV POST /api/docv/upload
PATCH /api/docv/upload/:docv_transaction_token

Required headers

Encryption is triggered by the X-Payload-Encrypted: true header.

HTTP

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Accept: application/json
X-Payload-Encrypted: true

If the X-Payload-Encrypted header is omitted, the endpoint expects a standard JSON payload.

Request format

All supported endpoints require the same encrypted request structure:

JSON

{
  "encrypted_request": "<JWE_STRING>"
}

The encrypted_request value must be a properly constructed JWE compact serialization string.

Response format

Evaluation API response

JSON

{
  "eval_id": "fd2a9785-554c-46be-a36f-e9443eefa380",
  "encrypted_response": "<JWE_STRING>"
}

DocV API response

JSON

{
  "docv_previous_reference_id": "fd2a9785-554c-46be-a36f-e9443eefa380",
  "encrypted_response": "<JWE_STRING>"
}

The encrypted_response field contains a signed and encrypted JWE payload.

Supported cryptographic standards

Payload encryption uses a dual-layer model:

  1. Digital Signature (JWS) -- Ensures integrity and authenticity.
  2. Encryption (JWE) -- Ensures confidentiality.
Layer Algorithm
Signing RS256 (RSA + SHA256)
Key Encryption RSA_OAEP_256
Content Encryption A256GCM (AES-GCM 256-bit)

Encryption flow

Customer → Socure (request)

  1. Sign the payload using your private RSA key with RS256.
  2. Generate a unique 256-bit AES Content Encryption Key (CEK).
  3. Encrypt the signed payload using A256GCM.
  4. Encrypt the CEK using Socure's public RSA key with RSA_OAEP_256.
  5. Serialize the components into a JWE compact string.
  6. Send the JWE string in the request body:

JSON

{
  "encrypted_request": "<JWE_STRING>"
}

Socure then:

Socure → Customer (response)

  1. Socure signs the response payload using its private RSA key.
  2. Generates a unique 256-bit CEK.
  3. Encrypts the signed payload using A256GCM.
  4. Encrypts the CEK using your public RSA key.
  5. Returns:

JSON

{
  "eval_id": "<uuid or docv reference>",
  "encrypted_response": "<JWE_STRING>"
}

You must:

Key management requirements

Encryption keys from Socure ID+ cannot be reused. A dedicated X.509 certificate exchange must be completed specifically for your RiskOS™ account.

RiskOS™ does not support multiple key pairs for different use cases within the same account.

Encryption is enforced only when X-Payload-Encrypted: true is present.

Prerequisites for payload encryption

Ensure you meet the following requirements before enabling payload encryption:

API responses

Success — HTTP 200:

Indicates successful processing of the encrypted payload.

JSON

{
  "eval_id": "123456-abcdef-123456",
  "encrypted_response": "<JWE_String>"
}

Error — HTTP 400:

Indicates encryption or signature validation failure.

JSON

{
  "eval_id": "123456-abcdef-123456",
  "error": "Payload encryption is not supported for this api-key"
}

Common error messages:

Error — HTTP 500:

The payload was successfully verified, but Socure failed during response encryption.

JSON

{
  "eval_id": "123456-abcdef-123456",
  "error": "The encrypted payload is verified with success, however, server has internal problems to handle response encryption"
}

Reference implementation (Java example)

This example demonstrates signing and encryption using the Nimbus JOSE + JWT library. Any standards-compliant JWE/JWS implementation may be used.

Encrypt / decrypt payload

Java

import java.security.interfaces.*;
import javax.crypto.*;

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;

public class Cipher {

public String encrypt(String message, RSAPublicKey rsaPublicKey) throws Exception {

// Assign the default JWE alg and enc.

JWEAlgorithm alg = JWEAlgorithm.RSA_OAEP_256;
       EncryptionMethod enc = EncryptionMethod.A256GCM;

// Generate the Content Encryption Key (CEK).

KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
       keyGenerator.init(enc.cekBitLength());
       SecretKey cek = keyGenerator.generateKey();

// Encrypt the JWE with the RSA public key + specified AES CEK.

JWEObject jwe = new JWEObject(
               new JWEHeader(alg, enc),
               new Payload(message));
       jwe.encrypt(new RSAEncrypter(rsaPublicKey, cek));
       return jwe.serialize();
   }

public String decrypt(String jweString, RSAPrivateKey rsaPrivateKey) throws Exception {

// Decrypt the JWE with the RSA private key.

JWEObject jwe = JWEObject.parse(jweString);
       jwe.decrypt(new RSADecrypter(rsaPrivateKey));

return jwe.getPayload().toString();
   }
}

Sign / verify signature

Java

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;

public class Signature {

public String sign(String message, RSAKey rsaJWK) throws Exception {

// Create RSA-signer with the private key

JWSSigner signer = new RSASSASigner(rsaJWK);

// Prepare JWS object with simple string as payload

JWSObject jwsObject = new JWSObject(
               new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(rsaJWK.getKeyID()).build(),
               new Payload(message));

// Compute the RSA signature

jwsObject.sign(signer);

// Serialize to compact form.

return jwsObject.serialize();
   }

public boolean verify(String jwsToken, RSAKey rsaPublicKey) throws Exception {

// To parse the JWS and verify it.

JWSObject jwsObject = JWSObject.parse(jwsToken);
       JWSVerifier verifier = new RSASSAVerifier(rsaPublicKey);

return jwsObject.verify(verifier);
   }

}

Message sender

Java

import com.nimbusds.jose.jwk.RSAKey;

public class Sender {
   RSAKey rsaKey;
   RSAKey receiverPublicKey;

public Sender(RSAKey rsaKey, RSAKey receiverPublicKey) {
       this.rsaKey = rsaKey;
       this.receiverPublicKey = receiverPublicKey;
   }

public String packageMessage(String message) throws Exception {
       System.out.println("Message encryption starts with " + message);

// Create a signature with a designated RSA2048 key pair.

Signature signature = new Signature();
       String jwsToken = signature.sign(message, rsaKey);

Cipher cipher = new Cipher();
       String jweToken = cipher.encrypt(jwsToken, receiverPublicKey.toRSAPublicKey());

System.out.println("Success with encrypted payload: " + jweToken);

return jweToken;
   }

}

Message receiver

Java

import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.jwk.RSAKey;

public class Receiver {
   RSAKey rsaKey;
   RSAKey senderPublicKey;

public Receiver(RSAKey rsaKey, RSAKey senderPublicKey) {
       this.rsaKey = rsaKey;
       this.senderPublicKey = senderPublicKey;
   }

public String unpackageMessage(String encryptedMessage) throws Exception {
       System.out.println("Message decryption starts with: " + encryptedMessage);

Signature signature = new Signature();
       Cipher cipher = new Cipher();
       String decryptedJwsToken = cipher.decrypt(encryptedMessage, rsaKey.toRSAPrivateKey());

if (!signature.verify(decryptedJwsToken, senderPublicKey)) {
           throw new Exception("Error: the JKS token can not be accepted due to sender's signature verification failure.");
       }

System.out.println("Success: The sender's JKS token is verified with the sender's signature.");
       JWSObject jwsObject = JWSObject.parse(decryptedJwsToken);
       return jwsObject.getPayload().toString();
   }

}