On This Page

{#jumplink-list}  
[Markdown](/docs/vas/en-us/platform/developer/all/rest/rest-getting-started/restgs-jwt-shared-secret-intro/restgs-mle-shared-secret-intro.md)  
Filter  
FILTER BY TAG

MLE Request + Response

Enable Message-Level Encryption {#restgs-mle-shared-secret-intro}
=================================================================

IMPORTANT There are additional tasks you must complete before you can enable message-level encryption. See the Prerequisites for MLE section below.  
Message-Level Encryption (MLE) enables you to store information or communicate with other parties while helping to prevent uninvolved parties from understanding the stored information. Enabling MLE requires you to create a *REST-Certificate* for encrypting your requests and a *REST -- API Response MLE* key for decrypting received responses. If your organization is using a meta key, the portfolio account or merchant account that created the meta key must also create the REST -- API Response MLE key.  
MLE provides enhanced security for message payload by using an asymmetric encryption technique (public-key cryptography). The message encryption is implemented with symmetric encryption using Advanced Encryption Standard (AES), Galois Counter Mode (GCM) with 256-bit key size. The encryption of keys is supported using RSA Optimal Asymmetric Encryption Padding (OAEP) with 2048-bit key size. The encryption service is based on JSON Web Encryption (JWE), works on top of SSL and requires separate key-pairs for request and response legs of the transaction.  
MLE is required for APIs that primarily deal with sensitive transaction data, both financial and non-financial. These are the types of sensitive transaction data:

* Personal identification information (PII)

* Personal account number (PAN)

* Personal account information (PAI)
  {#restgs-mle-shared-secret-intro_d44e45}  
  MLE is supported when using JSON web tokens.  
  Each of these authentication schemes uses an encrypted payload, called the *JWE* . A JWE token has these five components, with each component separated by a period (.):

* JOSE header containing four elements:

  ```
  "alg": "RSA-OAEP-256", // The algorithm used to encrypt the CEK.
  "enc": "A256GCM", // The algorithm used to encrypt the message.
  "iat": "1702493653", // The current timestamp in milliseconds.
  "kid": "keyId" // The serial number of shared public cert for encryption of CEK.
  ```
* JWE encrypted key

* JWE initialization vector

* JWE additional authentication data (AAD)

* JWE ciphertext and authentication tag

Prerequisites for MLE {#restgs-mle-shared-secret-prereq}
========================================================

Before you can enable message-level encryption (MLE), you must complete these requirements:

* Verify that the `Visa Acceptance Solutions` products you are integrating support MLE.
* Ensure that your system is configured to read the public key and encrypt the API payload.

Create or Submit a REST---API Response MLE Key {#restgs-security-mle-shared-secret-reply}
=========================================================================================

Before you can enable your system to support MLE, you must create or upload a *REST---API response MLE* certificate. After creating or uploading the certificate, you can extract the certificate's key to begin enabling MLE.  
Follow these steps to create or submit an API Response MLE certificate in the `Business Center`:

1. Log in to the `Business Center`:

   * **Test:** [`https://businesscentertest.visaacceptance.com`](https://businesscentertest.visaacceptance.com/ebc2/ "")
   * **Production:** [`https://businesscenter.visaacceptance.com`](https://businesscenter.visaacceptance.com/ebc2/ "")
   2. On the left navigation panel, choose ![](/content/dam/documentation/cybs/en-us/common/images/ebc/ebc-icon-pymt-config.svg/jcr:content/renditions/original) Payment Configuration \&gt; Key Management.  
      ![](/content/dam/documentation/cybs/en-us/topics/platform/rest/getting-started/images/left-navigation.png/jcr:content/renditions/original)
   3. Click + Generate key on the Key Management page.  
      ![](/content/dam/documentation/cybs/en-us/topics/payments-processing/payment-services/sec-keys/images/generate-key.png/jcr:content/renditions/original)
2. Under REST APIs, choose **REST -- API Response MLE** , and then click **Generate key** .  
   ![](/content/dam/documentation/cybs/en-us/topics/platform/rest/getting-started/images/restgs-mle-reply.png/jcr:content/renditions/original) {#restgs-security-mle-shared-secret-reply_d42e35}
   {#restgs-security-mle-shared-secret-reply_d42e35}

3. Choose one of these options to download your key:

   * To create a new API response MLE certificate, click **Download key** ![](/content/dam/documentation/cybs/en-us/common/images/ebc/ebc-bttn-download.svg/jcr:content/renditions/original) .
   * To upload your own certificate, enter your public PEM-formatted certificate in the text box, and then click **Download key** ![](/content/dam/documentation/cybs/en-us/common/images/ebc/ebc-bttn-download.svg/jcr:content/renditions/original) . The *.pem* file downloads to your desktop. If prompted by your system, approve the location to which the file downloads.

   ![](/content/dam/documentation/cybs/en-us/topics/platform/rest/getting-started/images/restgs-mle-reply-submit.png/jcr:content/renditions/original) {#restgs-security-mle-shared-secret-reply_d42e45}
   {#restgs-security-mle-shared-secret-reply_d42e45}
   6. If you are creating a certificate, the Set a Password window appears. Create a password for the certificate by entering the password into the New Password and Confirm Password fields, and then click Generate key.  
   The *.p12* file downloads to your desktop. If prompted by your system, approve the location to which the key downloads.  
   ![](/content/dam/documentation/cybs/en-us/topics/platform/rest/getting-started/images/restgs-set-pass.png/jcr:content/renditions/original)  
   To create or upload another key, click **Generate another key**. To view all of your created keys, go to the Key Management page.

   > IMPORTANT
   > Securely store the *.p12* file and password in your system. These credentials are required in order to implement certain products, and you must be able to access them.
   > {#restgs-security-mle-shared-secret-reply_d42e72}
   > {#restgs-security-mle-shared-secret-reply_d42e72}

4. Click **Cancel** .  
   The Key Management page appears. {#restgs-security-mle-shared-secret-reply_d42e100}
   {#restgs-security-mle-shared-secret-reply_d42e100}

5. Click the **Key Type** filter and choose **REST-API Response MLE**.{#restgs-security-mle-shared-secret-reply_d42e108}
   {#restgs-security-mle-shared-secret-reply_d42e108}

6. Click the **Expires At** filter and choose **All Dates**.{#restgs-security-mle-shared-secret-reply_d42e117}
   {#restgs-security-mle-shared-secret-reply_d42e117}

7. Click **Search**.{#restgs-security-mle-shared-secret-reply_d42e126}
   {#restgs-security-mle-shared-secret-reply_d42e126}

8. Find the REST--API Response key that you created in the Search Results table and save its key ID.  
   The key ID is needed to test and configure your system to use MLE.  
   ![](/content/dam/documentation/cybs/en-us/topics/platform/rest/getting-started/images/restgs-mle-ebc-key-mgmt.png/jcr:content/renditions/original) {#restgs-security-mle-shared-secret-reply_d42e132}
   {#restgs-security-mle-shared-secret-reply_d42e132}

{#restgs-security-mle-shared-secret-reply_d42e27}

**Test Your REST--API Response MLE Key**
:
To test your REST--API Response key, see [Test Your REST---API Response MLE Key](/docs/vas/en-us/platform/developer/all/rest/rest-getting-started/restgs-jwt-message-intro/restgs-jwt-message-test.md "").

Overview of MLE Setup Tasks {#restgs-mle-shared-secret-overview}
================================================================

Use the information in this section to configure your system with a custom MLE using JWTs.  
If you do not want to set up a custom MLE, you can use the REST Client SDK instead. For more information, see the REST Client SDKs in [GitHub](https://github.com/CyberSource/ "").

#### Figure:

Overview of MLE Set-Up Tasks  
![](/content/dam/documentation/cybs/en-us/topics/platform/rest/getting-started/images/restgs-mle-overview-shared-secret-vap-700x1200.svg/jcr:content/renditions/original)

1. Import the required programming libraries for your system.

2. Import these three credentials:

   * Shared secret key pair (REST API key)
   * MLE request certificate (SJC public certificate)
   * MLE response certificate (REST -- API Response MLE)
3. Encrypt the JSON request message using a JSON Web Encryption (JWE) that uses the SJC public certificate.

4. Create the HTTP body in this format: `{"encryptedRequest": "`*JWE-with-SJC*`"}`.

5. Create the JSON Web Signature (JWS) payload with these JWT payload fields and your signing certificate's private key. For descriptions of these fields, see the Headers table and Message Body Fields table in [Construct Messages Using JSON Web Tokens](/docs/vas/en-us/platform/developer/all/rest/rest-getting-started/restgs-jwt-message-intro/restgs-jwt-const-intro.md "").

   **Header Fields**
   :
   * alg
   * kid
   * type

   **Message Body Fields**
   :
   * digestAlgorithm
   * digest of the HTTP body
   * exp
   * iat
   * iss
   * jti
   * request-host
   * request-method
   * request-resource-path
   * v-c-jwt-version
   * v-c-merchant-id
   * v-c-response-mle-kid from the MLE response certificate.

6. Sign the JWS with your private key from the shared secret key pair and send it as `Authorization: Bearer`.

7. Receive an encrypted response and decrypt it with the MLE private key. You will receive the response in this format: `{"encryptedResponse": "`*JWE-with-ResponseMLECertificate*`"}`  
   The JWE contains a JOSE header containing these four default elements:

   ```
   "alg": "RSA-OAEP-256", // The algorithm used to encrypt the CEK.
   "enc": "A256GCM", // The algorithm used to encrypt the message.
   "iat": "1702493653", // The current timestamp in milliseconds.
   "kid": "keyId" // The serial number of the v-c-api-response-mle-kid from the authentication JWS in step 5.
   ```

Java Example: Enabling MLE Using JWTs {#restgs-mle-shared-secret-jwt}
=====================================================================

This example describes a possible way in which to configure your system to create a custom MLE with JWTs. These steps use the Java programming language.

1. Import your preferred libraries to support MLE. In this step, the configuration uses Java leveraging the open source Nimbus JOSE and Bouncy Castle libraries.

   ```
   // Nimbus JOSE + JWT
   import com.nimbusds.jose.JWEAlgorithm;
   import com.nimbusds.jose.JWEHeader;
   import com.nimbusds.jose.JWEObject;
   import com.nimbusds.jose.JWSAlgorithm;
   import com.nimbusds.jose.JWSHeader;
   import com.nimbusds.jose.JWSObject;
   import com.nimbusds.jose.JOSEObjectType;
   import com.nimbusds.jose.EncryptionMethod;
   import com.nimbusds.jose.Payload;
   import com.nimbusds.jose.crypto.RSADecrypter;
   import com.nimbusds.jose.crypto.RSAEncrypter;
   import com.nimbusds.jose.crypto.MACSigner;

   // BouncyCastle (PEM parsing + cert conversion)
   import org.bouncycastle.cert.X509CertificateHolder;
   import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
   import org.bouncycastle.openssl.PEMKeyPair;
   import org.bouncycastle.openssl.PEMParser;
   import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
   ```
2. Import the signing, MLE, and SJC certificates. The P12 certificate as the signing certificate.

   ```
   public final class CryptoMaterialHmac {
   public final byte[] sharedSecret;
   public final String keyId;
   public final KeyPairMaterial responseCert;
   public final X509Certificate sjcCert;

   public final class CryptoMaterialDual {
   // Merchant: JWS (REST – Certificate)
   public final KeyPairMaterial signingCert;
   // Merchant: Response decryption (API Response MLE)
   public final KeyPairMaterial responseCert;
   // Platform encryption cert (SJC)
   public final X509Certificate sjcCert;

   public CryptoMaterialHmac(byte[] sharedSecret, String keyId, KeyPairMaterial responseCert, X509Certificate sjcCert) {
   this.sharedSecret = sharedSecret;
   this.keyId = keyId;
   this.responseCert = responseCert;
   this.sjcCert = sjcCert;
   }
   }
   ```
3. Unpack your imported certificates into a usable format for your system.  
   Create this method for your system to read your *.p12* file, if you are using the P12 certificate.

   ```
   static KeyPairMaterial loadKeyPairFromP12(Path p12Path, char[] password, String keyAlias) throws Exception {
   KeyStore ks = KeyStore.getInstance("PKCS12");
   try (InputStream in = Files.newInputStream(p12Path)) {
   ks.load( in , password);
   }
   KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) ks.getEntry(
   keyAlias, new KeyStore.PasswordProtection(password));
   return new KeyPairMaterial(entry.getPrivateKey(), (X509Certificate) entry.getCertificate());
   }
   ```

   Create this method for your system to read the PEM chain and private key.

   ```
   static KeyPairMaterial loadKeyPairFromPem(Path certificateChainPem, String privateKeyPem) throws Exception {
   X509Certificate leaf = readPemCerts(certificateChainPem).get(0);
   PrivateKey key = readPkcs8PrivateKey(privateKeyPem);
   return new KeyPairMaterial(key, leaf);
   }
   ```

   Create this method for your system to read the SJC from the *.p12* file or PEM chain.

   ```
   static X509Certificate loadSjcFromP12(Path p12Path, char[] password, String sjcAlias) throws Exception {
   KeyStore ks = KeyStore.getInstance("PKCS12");
   try (InputStream in = Files.newInputStream(p12Path)) {
   ks.load( in , password);
   }
   return (X509Certificate) ks.getCertificate(sjcAlias);
   }

   static X509Certificate loadSjcFromPem(Path sjcCertPem) throws Exception {
   return readPemCerts(sjcCertPem).get(0);
   }
   ```

   Create this method to include PEM helper functions.

   ```
   static List &lt; X509Certificate &gt; readPemCerts(Path pemPath) throws Exception {
   try (Reader r = Files.newBufferedReader(pemPath); org.bouncycastle.openssl.PEMParser p = new org.bouncycastle.openssl.PEMParser(r)) {
   var xconv = new org.bouncycastle.cert.jcajce.JcaX509CertificateConverter().setProvider("BC");
   List &lt; X509Certificate &gt; certs = new ArrayList &lt; &gt; ();
   Object o;
   while ((o = p.readObject()) != null) {
   if (o instanceof org.bouncycastle.cert.X509CertificateHolder h) certs.add(xconv.getCertificate(h));
   }
   return certs;
   }
   }

   static PrivateKey readPkcs8PrivateKey(String pem) throws Exception {
   try (var parser = new org.bouncycastle.openssl.PEMParser(new StringReader(pem))) {
   Object o = parser.readObject();
   var conv = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter().setProvider("BC");
   if (o instanceof org.bouncycastle.asn1.pkcs.PrivateKeyInfo pki) return conv.getPrivateKey(pki);
   if (o instanceof org.bouncycastle.openssl.PEMKeyPair kp) return conv.getPrivateKey(kp.getPrivateKeyInfo());
   throw new IllegalArgumentException("Expect PKCS#8 private key PEM");
   }
   }
   ```
4. Create these methods as helpers for encrypting and signing.

   ```
   static String kidFromCert(X509Certificate cert) {
   String dn = cert.getSubjectDN().getName().toUpperCase();
   int i = dn.indexOf("SERIALNUMBER=");
   if (i &gt;= 0) {
   int j = dn.indexOf(",", i);
   if (j &lt; 0) j = dn.length();
   return dn.substring(i + "SERIALNUMBER=".length(), j).trim();
   }
   return cert.getSerialNumber().toString();
   }

   static String encryptToJwe(String json, X509Certificate sjcCert) throws Exception {
   var header = new com.nimbusds.jose.JWEHeader.Builder(
   com.nimbusds.jose.JWEAlgorithm.RSA_OAEP,
   com.nimbusds.jose.EncryptionMethod.A256GCM)
   .contentType("JWT")
   .keyID(kidFromCert(sjcCert))
   .build();
   var jwe = new com.nimbusds.jose.JWEObject(header, new com.nimbusds.jose.Payload(json));
   jwe.encrypt(new com.nimbusds.jose.crypto.RSAEncrypter((RSAPublicKey) sjcCert.getPublicKey()));
   return jwe.serialize();
   }

   static String sha256Base64(String body) throws Exception {
   MessageDigest md = MessageDigest.getInstance("SHA-256");
   return Base64.getEncoder().encodeToString(md.digest(body.getBytes(StandardCharsets.UTF_8)));
   }


   static String signAsJwsHmac(String payload, byte[] sharedSecret, String keyId) throws Exception {
   var headerBuilder = new com.nimbusds.jose.JWSHeader.Builder(com.nimbusds.jose.JWSAlgorithm.HS256)
   .type(com.nimbusds.jose.JOSEObjectType.JWT);

   // CHANGED: Add optional keyId (not extracted from certificate)
   if (keyId != null && !keyId.isEmpty()) {
   headerBuilder.keyID(keyId);
   }

   var header = headerBuilder.build();
   var jws = new com.nimbusds.jose.JWSObject(header, new com.nimbusds.jose.Payload(payload));
   jws.sign(new com.nimbusds.jose.crypto.MACSigner(sharedSecret));
   return jws.serialize();
   }

   static String decryptJwe(String compactJwe, KeyPairMaterial responseCert) throws Exception {
   var jwe = com.nimbusds.jose.JWEObject.parse(compactJwe);
   jwe.decrypt(new com.nimbusds.jose.crypto.RSADecrypter((RSAPrivateKey) responseCert.privateKey));
   return jwe.getPayload().toString();
   }
   ```
5. Create a class that uses the methods described in the above steps to encrypt and decrypt your payloads with MLE using JWTs.

   ```
   // - REST – Shared Secret (base64-encoded)
   // - API Response MLE from PEM
   // - SJC from PEM

   // Load and decode your base64-encoded shared secret
   String base64Secret = "YOUR_BASE64_ENCODED_SECRET";// Get from your secure configuration
   byte[] sharedSecret = java.util.Base64.getDecoder().decode(base64Secret);
   String keyId = "1234567890";// Optional: Your Key ID

   KeyPairMaterial responseCert = loadKeyPairFromPem(
   Paths.get("api_response_mle_chain.pem"), Files.readString(Paths.get("api_response_mle_private_key.pem")));
   X509Certificate sjc = loadSjcFromPem(Paths.get("sjc_certificate.pem"));

   CryptoMaterialHmac mat = new CryptoMaterialHmac(sharedSecret, keyId, responseCert, sjc);

   // 1) Build your request JSON
   String requestJson = new org.json.JSONObject()
   .put("amount", "10.00")
   .put("currency", "USD")
   .put("reference", "ORDER-12345")
   .toString();

   // 2) Encrypt request body to JWE using SJC public cert
   String encryptedJwe = encryptToJwe(requestJson, mat.sjcCert);

   // 3) Build the HTTP body (this is what you'll hash for the digest)
   String httpBody = new org.json.JSONObject()
   .put("encryptedRequest", encryptedJwe)
   .toString();

   // 4) Build JWS payload: include iat, response kid, digestAlgorithm, and digest of httpBody
   String digestB64 = sha256Base64(httpBody);
   String jwsPayload = new org.json.JSONObject()
   .put("iat", java.time.Instant.now().getEpochSecond())
   .put("v-c-api-response-mle-kid", kidFromCert(mat.responseCert.cert))// instruct server to encrypt to your API Response MLE key
   .put("digestAlgorithm", "SHA-256")
   .put("digest", digestB64)
   .toString();

   // 5) Sign the JWS with the shared secret using HMAC
   String signedJws = signAsJwsHmac(jwsPayload, mat.sharedSecret, mat.keyId);

   // 6) Send the HTTP request
   // POST /your/api
   // Content-Type: application/json
   // Authorization: Bearer &lt;signedJws&gt;
   /*
   Body:
   { "encryptedRequest": "&lt;encryptedJwe&gt;" }
   */

   // 7) Handle the response (decrypt if needed with API Response MLE private key)
   String apiResponse = /* http call result as string */;
   org.json.JSONObject resp = new org.json.JSONObject(apiResponse);
   String finalPayload = resp.has("encryptedResponse")
   ? decryptJwe(resp.getString("encryptedResponse"), mat.responseCert)
   : apiResponse;
   ```

RELATED TO THIS PAGE

* <>
* [Getting Started with REST](https://developer.visaacceptance.com/docs/vas/en-us/platform/developer/all/rest/rest-getting-started/restgs-intro.md)
* [Response Codes](https://developer.visaacceptance.com/api/reference/response-codes.md)
* [API Reference Sandbox](https://developer.visaacceptance.com/api-reference-assets/index.md#static-home-section)
* [Business Center Test](https://businesscentertest.visaacceptance.com/ebc2/)
* [Business Center Production](https://businesscenter.visaacceptance.com/ebc2/)
* [Customer Support](https://developer.visaacceptance.com/support/contact-us.md)

