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

```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

```json
{
  "encrypted_request": "<JWE_STRING>"
}
```

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

## Response format

### Evaluation API response

JSON

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

### DocV API response

JSON

```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

```json
{
  "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:

JSON

```json
{
  "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 you meet the following requirements 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 does not function until the certificate exchange is complete.

## API responses

### Success — HTTP `200`:

Indicates successful processing of the encrypted payload.

JSON

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

### Error — HTTP `400`:

Indicates encryption or signature validation failure.

JSON

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

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.

JSON

```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

```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

```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

```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

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

}
```
