This specification defines an approach for encrypting sensitive payment method response data. It relies on JSON Web Encryption [[!RFC7516]] for algorithms and message structure.

The working group maintains a list of all bug reports that the group has not yet addressed. Pull requests with proposed specification text for outstanding issues are strongly encouraged.

Introduction

This specification defines a way to encrypt payment method response data in conjunction with the PaymentRequest API [[!payment-request]].

End-to-end encryption from payment handler to processor can help address several use cases:

This document describes a standardized approach, that leverages a limited profile of JSON Web Encryption, to be used by a variety of payment method specifications. At a high level, it works as follows:

The profile defined in this specification limits the set of allowed algorithms. This is to prevent the use of algorithms that have been shown, since [[!RFC7518]] was published, to have security vulnerabilities.

Examples

The following examples illustrate how data from the Tokenized Card Payment Specification [[tokenized-card]] would be encrypted using this specification.

For simplicity, the sample code uses the open source [[!nimbus-jose-jwt]], however the use of any similar library that is conformant with [[!RFC7516]] will work.

Key Generation Example

This Java code sample shows how to generate a new 2048 bit RSA key pair using the default JVM security provider. This is not a JSON Web Token specific function and should be repeatable using any standard encryption library that conforms with [[!RFC3447]].

          // Key generation
          KeyPairGenerator keyGenerator = null;
          try {
              keyGenerator = KeyPairGenerator.getInstance("RSA");
          } catch (NoSuchAlgorithmException e) {
              e.printStackTrace();
          }
          keyGenerator.initialize(2048);

          KeyPair kp = keyGenerator.genKeyPair();
          RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic();
          RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate();
        

The output of this process, which will be performed by the merchant or their processor before creating a PaymentRequest, is a pair of RSA keys (one public and one private).

This public key is the "key encryption key" and is referenced from the request data of the PaymentRequest. The payment handler uses it to create the encrypted JSON Web Token.

The private key is retained by the merchant or processor and used to decrypt the encrypted JSON Web Token.

Encryption Example

This example shows how to encrypt a JSON Web Token (JWT) claim set using an RSA public key (like the one generated above). This code is based on the example at [[!jwe-with-rsa-example]].

          //Create an empty claims set
          JWTClaimsSet claimsSet = new JWTClaimsSet();

          //Add claims to the claims set
          claimsSet.setCustomClaim("cardNumber", "5413339000001513");
          claimsSet.setCustomClaim("expiryMonth", "12");
          claimsSet.setCustomClaim("expiryYear", "20");
          claimsSet.setCustomClaim("cryptogram", "AlhlvxmN2ZKuAAESNFZ4GoABFA==");
          claimsSet.setCustomClaim("typeOfCryptogram", "UCAF");
          claimsSet.setCustomClaim("trid", "9812345678");
          claimsSet.setCustomClaim("eci", "242");

          //Print JSON representation of claims set  
          System.out.println(claimsSet.toJSONObject());

          // Request that JWE is created RSA-OAEP-256 and 128-bit AES/GCM
          JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM);

          // Create the JWT object (not yet encrypted)
          EncryptedJWT jwt = new EncryptedJWT(header, claimsSet);

          // Create an encrypter with the public RSA key
          RSAEncrypter encrypter = new RSAEncrypter(publicKey);

          // Do the encryption
          try {
              //This library will generate a random content encryption key during this step
              //Other libararies may require that both the RSA key and the CEK are provided
              jwt.encrypt(encrypter);
          } catch (JOSEException e) {
              e.printStackTrace();
          }

          // Serialise to JWT compact form
          String jwtString = jwt.serialize();

          System.out.println(jwtString);
        

The output of this process is a new JSON Web Token where the content is encrypted using AES GCM and a content encryption key that is encrypted using RSA OAEP 256 and the public key provided.

Decryption Example

This sample shows how to decrypt an encrypted JSON Web Token (JWT) using an RSA private key (like the one generated above). This would be performed by the merchant or their processor upon receiving the PaymentResponse.

          // Parse the JWT from serialized string format
          jwt = EncryptedJWT.parse(jwtString);

          //Verify the JWT uses safe algorithms
          if(!jwt.getHeader().getAlgorithm().equals(JWEAlgorithm.RSA-OAEP-256))
            throw new Exception("Invalid 'alg' header, only RSA-OAEP-256 is allowed.")
          
          if(!jwt.getHeader().getEncryptionMethod().equals(EncryptionMethod.A128GCM))
            throw new Exception("Invalid 'enc' header, only A128GCM is allowed.")
          
          // Create a decrypter with the specified private RSA key
          RSADecrypter decrypter = new RSADecrypter(privateKey);

          //Decrypt JWT
          jwt.decrypt(decrypter);

          // Retrieve JWT claims
          String cardNumber = jwt.getJWTClaimsSet().getCustomClaim("cardNumber");
          String expiryMonth = jwt.getJWTClaimsSet().getCustomClaim("expiryMonth");
          String expiryYear = jwt.getJWTClaimsSet().getCustomClaim("expiryYear");
          String cryptogram = jwt.getJWTClaimsSet().getCustomClaim("cryptogram");
          String typeOfCryptogram = jwt.getJWTClaimsSet().getCustomClaim("typeOfCryptogram");
          String trid = jwt.getJWTClaimsSet().getCustomClaim("trid");
          String eci = jwt.getJWTClaimsSet().getCustomClaim("eci");
        

The output of this process is a decrypted JWT containing the claims set that was originally encrypted by the payment handler.

Encryption

Key Encryption key

The key encryption key MUST be a 2048-bit RSA public key shared via an X.509 certificate in a file encoded according to [[!RFC7468]].

How a payment handler acquires the key encryption key is outside the scope of this specification. See the section on how to use this specification within a payment method specification.

Message Structure

This specification leverages JSON Web Encryption to create an encrypted message structure. The result of using this specification is a JWE Compact Serialization with the following structure:

(header).(encrypted key).(initialization vector).(ciphertext).(authentication tag)
        

Each component is BASE64URL encoded. The components are concatenated and separated by a period (".").

Header

The header is a JOSE Header, which describes the encryption to be applied during plaintext encryption. Implementations of this specification MUST use the following parameters and values:

Encrypted Key

During this process, the payment handler generates and uses a content encryption key (CEK) to produce the ciphertext. The CEK is then encrypted using the RSA OAEP 256 algorithm and the resulting JWE Encrypted Key becomes part of the message structure.

Initialization Vector

The initialization vector is a randomly generated object used during encryption, and is shared for the decryption of the cipher text. It is a JWE initialization vector.

Ciphertext

The ciphertext is the result of plaintext encryption of the sensitive data of the payment method, using the algorithm named in the header, the content encryption key, and the initialization vector.

Authentication TAG

The authentication tag allows the receiver to detect whether the message has been altered. It is a JWE Authentication TAG and is generated during the encryption operation.

Decryption

@@Todo: Describe how to decrypt the encrypted data and the JSON form it takes.

How to Use this Specification within a Payment Method Specification

@@Todo: Describe how to reference this specification from within a payment method specification.@@

Plaintext Encryption

Each payment method definition identifies which part of the PaymentResponse is encrypted. One member of the PaymentResponse should hold the encrypted message structure.

A payment method may take different approaches to adding data fields to the JWT claims set, including:

Access to the Key Encryption Key

Each payment method definition describes how the payment handler acquires the key encryption key. For example, the payment handler may retrieve the key encryption key via a URL provided in a PaymentRequest.

Dependencies

This specification relies on several other underlying specifications.

Payment Request
The PaymentRequest and PaymentResponse interfaces are defined in [[!payment-handler]].
Payment Handler
The term payment handler is defined in [[!payment-handler]].
X.509
The term X.509 certificate is defined by [[!X509]].
JSON Web Encryption
The terms content encryption key, JWE Compact Serialization, JSON Web Encryption, JWE Encrypted Key, JOSE Header, JWE Ciphertext, JWE Authentication Tag and JWE Initialization Vector are defined by [[!RFC7516]].
JSON Web Algorithms
The terms AES GCM and RSA OAEP 256 are defined by [[!RFC7518]].
JSON Web Token
The terms JSON Web Token and JWT claims set are defined by [[!RFC7519]].
Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1
The term RSA public key is defined by [[!RFC3447]].
The Base16, Base32, and Base64 Data Encodings
The term BASE64URL encoded is defined in [[!RFC4648]].