This is the Python version of the Mastercard compliant payload encryption/decryption.
Python 3.8+
Before using this library, you will need to set up a project in the Mastercard Developers Portal.
As part of this set up, you'll receive:
If you want to use mastercard-client-encryption with Python, it is available through PyPI
:
Adding the library to your project Install the library by pip:
pip install mastercard-client-encryption
Or clone it from git:
git clone https://github.com/Mastercard/client-encryption-python.git
and then execute from the repo folder:
python3 setup.py install
You can then use it as a regular module:
# Mastercard Encryption/Decryption
from client_encryption.field_level_encryption_config import FieldLevelEncryptionConfig
from client_encryption.field_level_encryption import encrypt_payload, decrypt_payload
# JWE Encryption/Decryption
from client_encryption.jwe_encryption_config import JweEncryptionConfig
from client_encryption.jwe_encryption import encrypt_payload, decrypt_payload
This library supports two types of encryption/decryption, both of which support field level and entire payload encryption: JWE encryption and what the library refers to as Field Level Encryption (Mastercard encryption), a scheme used by many services hosted on Mastercard Developers before the library added support for JWE.
This library uses JWE compact serialization for the encryption of sensitive data.
The core methods responsible for payload encryption and decryption are encrypt_payload
and decrypt_payload
in the jwe_encryption
module.
encrypt_payload()
usage:config = JweEncryptionConfig(config_dictionary)
encrypted_request_payload = encrypt_payload(body, config)
decrypt_payload()
usage:config = JweEncryptionConfig(config_dictionary)
decrypted_response_payload = decrypt_payload(body, config)
jwe_encryption
needs a config dictionary to instruct how to decrypt/decrypt the payloads. Example:
{
"paths": {
"$": {
"toEncrypt": {
"path.to.foo": "path.to.encryptedFoo"
},
"toDecrypt": {
"path.to.encryptedFoo": "path.to.foo"
}
}
},
"encryptedValueFieldName": "encryptedData",
"encryptionCertificate": "./path/to/public.cert",
"decryptionKey": "./path/to/your/private.key",
}
The above can be either stored to a file or passed to 'JweEncryptionConfig' as dictionary:
config_dictionary = {
"paths": {…},
…
"decryptionKey": "./path/to/your/private.key"
}
config = JweEncryptionConfig(config_dictionary)
config_file_path = "./config.json"
config = JweEncryptionConfig(config_file_path)
Call jwe_encryption.encrypt_payload()
with a JSON (dict) request payload, and optional params
object.
Example using the configuration above:
from client_encryption.session_key_params import SessionKeyParams
payload = {
"path": {
"to": {
"foo": {
"sensitiveField1": "sensitiveValue1",
"sensitiveField2": "sensitiveValue2"
}
}
}
}
params = SessionKeyParams.generate(conf) # optional
request_payload = encrypt_payload(payload, config, params)
Output:
{
"path": {
"to": {
"encryptedFoo": {
"encryptedValue": "eyJraWQiOiI3NjFiMDAzYzFlYWRlM(...)==.Y+oPYKZEMTKyYcSIVEgtQw=="
}
}
}
}
Call jwe_encryption.decrypt_payload()
with a JSON (dict) encrypted response payload.
Example using the configuration above:
response = {
"path": {
"to": {
"encryptedFoo": {
"encryptedValue": "eyJraWQiOiI3NjFiMDAzYzFlYWRlM(...)==.Y+oPYKZEMTKyYcSIVEgtQw=="
}
}
}
}
response_payload = decrypt_payload(response, config)
Output:
{
"path": {
"to": {
"foo": {
"sensitiveField1": "sensitiveValue1",
"sensitiveField2": "sensitiveValue2"
}
}
}
}
The core methods responsible for payload encryption and decryption are encrypt_payload
and decrypt_payload
in the field_level_encryption
module.
encrypt_payload()
usage:config = FieldLevelEncryptionConfig(config_dictionary)
encrypted_request_payload = encrypt_payload(body, config)
decrypt_payload()
usage:config = FieldLevelEncryptionConfig(config_dictionary)
decrypted_response_payload = decrypt_payload(body, config)
field_level_encryption
needs a config dictionary to instruct how to decrypt/decrypt the payloads. Example:
{
"paths": {
"$": {
"toEncrypt": {
"path.to.foo": "path.to.encryptedFoo"
},
"toDecrypt": {
"path.to.encryptedFoo": "path.to.foo"
}
}
},
"ivFieldName": "iv",
"encryptedKeyFieldName": "encryptedKey",
"encryptedValueFieldName": "encryptedData",
"dataEncoding": "hex",
"encryptionCertificate": "./path/to/public.cert",
"decryptionKey": "./path/to/your/private.key",
"oaepPaddingDigestAlgorithm": "SHA256"
}
The above can be either stored to a file or passed to 'FieldLevelEncryptionConfig' as dictionary:
config_dictionary = {
"paths": {…},
…
"decryptionKey": "./path/to/your/private.key",
"oaepPaddingDigestAlgorithm": "SHA256"
}
config = FieldLevelEncryptionConfig(config_dictionary)
config_file_path = "./config.json"
config = FieldLevelEncryptionConfig(config_file_path)
Call field_level_encryption.encrypt_payload()
with a JSON (dict) request payload, and optional params
object.
Example using the configuration above:
from client_encryption.session_key_params import SessionKeyParams
payload = {
"path": {
"to": {
"foo": {
"sensitiveField1": "sensitiveValue1",
"sensitiveField2": "sensitiveValue2"
}
}
}
}
params = SessionKeyParams.generate(conf) # optional
request_payload = encrypt_payload(payload, config, params)
Output:
{
"path": {
"to": {
"encryptedFoo": {
"iv": "7f1105fb0c684864a189fb3709ce3d28",
"encryptedKey": "67f467d1b653d98411a0c6d3c…ffd4c09dd42f713a51bff2b48f937c8",
"encryptedData": "b73aabd267517fc09ed72455c2…dffb5fa04bf6e6ce9ade1ff514ed6141",
"publicKeyFingerprint": "80810fc13a8319fcf0e2e…82cc3ce671176343cfe8160c2279",
"oaepHashingAlgorithm": "SHA256"
}
}
}
}
Call field_level_encryption.decrypt_payload()
with a JSON (dict) encrypted response payload.
Example using the configuration above:
response = {
"path": {
"to": {
"encryptedFoo": {
"iv": "e5d313c056c411170bf07ac82ede78c9",
"encryptedKey": "e3a56746c0f9109d18b3a2652b76…f16d8afeff36b2479652f5c24ae7bd",
"encryptedData": "809a09d78257af5379df0c454dcdf…353ed59fe72fd4a7735c69da4080e74f",
"oaepHashingAlgorithm": "SHA256",
"publicKeyFingerprint": "80810fc13a8319fcf0e2e…3ce671176343cfe8160c2279"
}
}
}
}
response_payload = decrypt_payload(response, config)
Output:
{
"path": {
"to": {
"foo": {
"sensitiveField1": "sensitiveValue1",
"sensitiveField2": "sensitiveValue2"
}
}
}
}
OpenAPI Generator generates API client libraries from OpenAPI Specs. It provides generators and library templates for supporting multiple languages and frameworks.
The client-encryption-python library provides a method you can use to integrate the OpenAPI generated client with this library:
from client_encryption.api_encryption import add_encryption_layer
config = {
"paths": {
"$": {
…
}
},
"encryptionCertificate": "path/to/cert.pem",
…
"decryptionKey": "path/to/to/key.pem"
}
add_encryption_layer(api_client, config)
Alternatively you can pass the configuration by a json file:
from client_encryption.api_encryption import add_encryption_layer
add_encryption_layer(api_client, "path/to/my/config.json")
This method will add the Mastercard/JWE encryption in the generated OpenApi client, taking care of encrypting request and decrypting response payloads, but also of updating HTTP headers when needed, automatically, without manually calling encrypt_payload()
/decrypt_payload()
functions for each API request or response.
OpenAPI client can be generated, starting from your OpenAPI Spec using the following command:
openapi-generator-cli generate -i openapi-spec.yaml -l python -o out
The client library will be generated in the out
folder.
See also:
api_encryption.add_encryption_layer
:To use it:
Generate the OpenAPI client
Import the mastercard-client-encryption module and the generated OpenAPI client
from client_encryption.api_encryption import add_encryption_layer
from openapi_client.api_client import ApiClient # import generated OpenAPI client
Add the encryption layer to the generated client:
# Create a new instance of the generated client
api_client = ApiClient()
# Enable encryption
add_encryption_layer(api_client, "path/to/my/config.json")
Use the ApiClient
instance with Encryption enabled:
Example:
request_body = {…}
response = MyServiceApi(api_client).do_some_action_post(body=request_body)
# requests and responses will be automatically encrypted and decrypted
# accordingly with the configuration object used
# … use the (decrypted) response object here …
decrypted = response.json()
mastercard-client-encryption
module:In order to use both signing and encryption layers, a defined order is required as signing library should calculate the hash of the encrypted payload. According to the above the signing layer must be applied first in order to work as inner layer. The outer layer - encryption - will be executed first, providing the signing layer the encrypted payload to sign.
Generate the OpenAPI client
Import both mastercard-client-encryption and mastercard-client-encryption modules and the generated OpenAPI client
from oauth1.signer_interceptor import add_signing_layer
from client_encryption.api_encryption import add_encryption_layer
from openapi_client.api_client import ApiClient # import generated OpenAPI client
Add the authentication layer to the generated client:
# Create a new instance of the generated client
api_client = ApiClient()
# Enable authentication
add_signing_layer(api_client, key_file, key_password, consumer_key)
Then add the encryption layer:
add_encryption_layer(api_client, "path/to/my/config.json")
Use the ApiClient
instance with Authentication and Encryption both enabled:
response = MyServiceApi(api_client).do_some_action_post(body=request_body)
decrypted = response.json()