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.

ServiceSupported Endpoints
EvaluationPOST /api/evaluation
POST /api/evaluation/:version
PATCH /api/evaluation/:eval_id
DocVPOST /api/docv/upload
PATCH /api/docv/upload/:docv_transaction_token

Required headers

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

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:

{
  "encrypted_request": "<JWE_STRING>"
}

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


Response format

Evaluation API response

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

DocV API response

{
  "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.
LayerAlgorithm
SigningRS256 (RSA + SHA256)
Key EncryptionRSA_OAEP_256
Content EncryptionA256GCM (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:
{
  "encrypted_request": "<JWE_STRING>"
}

Socure then:

  • Decrypts the CEK using its private key
  • Decrypts the payload using the CEK
  • Verifies the signature using your public key

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:
{
  "eval_id": "<uuid or docv reference>",
  "encrypted_response": "<JWE_STRING>"
}

You must:

  • Decrypt the CEK with your private key.
  • Decrypt the payload.
  • Verify Socure's signature using Socure's public key.

Key management requirements

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

  • Single Key Pair per Account
    RiskOS™ does not support multiple key pairs for different use cases within the same account.

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


Prerequisites for payload encryption

Ensure the following requirements are met before enabling payload encryption:

Have completed the X.509 certificate exchange with Socure for your RiskOS™ account.
Possess a valid RiskOS™ API key.
Use TLS 1.2 or higher (TLS 1.3 recommended).

Encryption will not function until the certificate exchange is completed.


API responses

Success — HTTP 200:

Indicates successful processing of the encrypted payload.

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

Error — HTTP 400:

Indicates encryption or signature validation failure.

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

Troubleshooting:

If you receive the error "Payload encryption is not supported for this api-key", verify that you are not using keys associated with a different Socure product (like ID+). Keys must be exchanged specifically for your RiskOS™ account.

Common error messages:

  • Payload encryption is not supported for this API key
  • Payload format is not supported
  • Encrypted payload is not in JWE format
  • Signed payload is not in JWS format
  • Payload decryption failed
  • Payload signature could not be verified

Error — HTTP 500:

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

{
  "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

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

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

import com.nimbusds.jose.jwk.RSAKey;

//This class is to simulate customer's actions below:
// 1. Use customer-generated RSA2048 private key to sign;
// 2. Use the Socure-generated RSA2048 public key to wrap AES key.

public class Sender {
   RSAKey rsaKey;
   RSAKey receiverPublicKey;

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


//This function demonstrates how customer will sign and encrypt the payload.

   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

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

//  This class is to simulate Socure's actions below:
//  1. Use Socure-generated RSA2048 private key to unwrap the AES key;
//  2. Use customer-generated RSA2048 public key to verify the customer's signature.
  
public class Receiver {
   RSAKey rsaKey;
   RSAKey senderPublicKey;

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

// This function demonstrates how Socure will decrypt the JWE token and verify the signature of customer.

   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();
   }

}