Skip to content

Commit d9f438f

Browse files
Merge pull request #12 from Mastercard/feature/add-jwe-support
Adding JWE support
2 parents d65a71c + ee47135 commit d9f438f

17 files changed

+774
-83
lines changed

README.md

+173-29
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
- [Usage](#usage)
1717
* [Prerequisites](#prerequisites)
1818
* [Adding the Library to Your Project](#adding-the-library-to-your-project)
19-
* [Performing Field Level Encryption and Decryption](#performing-field-level-encryption-and-decryption)
19+
* [Performing Payload Encryption and Decryption](#performing-payload-encryption-and-decryption)
20+
* [JWE Encryption and Decryption](#jwe-encryption-and-decryption)
21+
* [Mastercard Encryption and Decryption](#mastercard-encryption-and-decryption)
2022
* [Integrating with OpenAPI Generator API Client Libraries](#integrating-with-openapi-generator-api-client-libraries)
2123

2224

@@ -27,28 +29,22 @@ This is the Python version of the Mastercard compliant payload encryption/decryp
2729
Python 3.6+
2830

2931
### References <a name="references"></a>
30-
31-
<img src="https://user-images.githubusercontent.com/3964455/55345820-c520a280-54a8-11e9-8235-407199fa1d97.png" alt="Encryption of sensitive data" width="75%" height="75%"/>
32+
* [JSON Web Encryption (JWE)](https://datatracker.ietf.org/doc/html/rfc7516)
33+
* [Securing Sensitive Data Using Payload Encryption](https://developer.mastercard.com/platform/documentation/security-and-authentication/securing-sensitive-data-using-payload-encryption/)
3234

3335
## Usage <a name="usage"></a>
34-
3536
### Prerequisites <a name="prerequisites"></a>
36-
3737
Before using this library, you will need to set up a project in the [Mastercard Developers Portal](https://developer.mastercard.com).
3838

3939
As part of this set up, you'll receive:
40-
4140
- A public request encryption certificate (aka _Client Encryption Keys_)
4241
- A private response decryption key (aka _Mastercard Encryption Keys_)
4342

4443
### Installation <a name="adding-the-libraries-to-your-project"></a>
45-
4644
If you want to use **mastercard-client-encryption** with [Python](https://www.python.org/), it is available through `PyPI`:
47-
4845
- [https://pypi.org/project/mastercard-client-encryption](https://pypi.org/project/mastercard-client-encryption)
4946

5047
**Adding the library to your project**
51-
5248
Install the library by pip:
5349

5450
```bash
@@ -70,18 +66,168 @@ $ python3 setup.py install
7066
You can then use it as a regular module:
7167

7268
```python
69+
# Mastercard Encryption/Decryption
7370
from client_encryption.field_level_encryption_config import FieldLevelEncryptionConfig
7471
from client_encryption.field_level_encryption import encrypt_payload, decrypt_payload
7572
```
7673

77-
### Performing Field Level Encryption and Decryption <a name="performing-field-level-encryption-and-decryption"></a>
74+
```python
75+
# JWE Encryption/Decryption
76+
from client_encryption.jwe_encryption_config import JweEncryptionConfig
77+
from client_encryption.jwe_encryption import encrypt_payload, decrypt_payload
78+
```
7879

79-
- [Introduction](#introduction)
80-
- [Configuring the Field Level Encryption](#configuring-the-field-level-encryption)
81-
- [Performing Encryption](#performing-encryption)
82-
- [Performing Decryption](#performing-decryption)
80+
### Performing Payload Encryption and Decryption <a name="performing-payload-encryption-and-decryption"></a>
8381

84-
#### Introduction <a name="introduction"></a>
82+
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.
83+
84+
+ [JWE Encryption and Decryption](#jwe-encryption-and-decryption)
85+
+ [Mastercard Encryption and Decryption](#mastercard-encryption-and-decryption)
86+
87+
#### JWE Encryption and Decryption <a name="jwe-encryption-and-decryption"></a>
88+
89+
+ [Introduction](#jwe-introduction)
90+
+ [Configuring the JWE Encryption](#configuring-the-jwe-encryption)
91+
+ [Performing JWE Encryption](#performing-jwe-encryption)
92+
+ [Performing JWE Decryption](#performing-jwe-decryption)
93+
94+
##### Introduction <a name="jwe-introduction"></a>
95+
96+
This library uses [JWE compact serialization](https://datatracker.ietf.org/doc/html/rfc7516#section-7.1) for the encryption of sensitive data.
97+
The core methods responsible for payload encryption and decryption are `encrypt_payload` and `decrypt_payload` in the `jwe_encryption` module.
98+
99+
- `encrypt_payload()` usage:
100+
101+
```python
102+
config = JweEncryptionConfig(config_dictionary)
103+
encrypted_request_payload = encrypt_payload(body, config)
104+
```
105+
106+
- `decrypt_payload()` usage:
107+
108+
```python
109+
config = JweEncryptionConfig(config_dictionary)
110+
decrypted_response_payload = decrypt_payload(body, config)
111+
```
112+
113+
##### Configuring the JWE Encryption <a name="configuring-the-jwe-encryption"></a>
114+
115+
`jwe_encryption` needs a config dictionary to instruct how to decrypt/decrypt the payloads. Example:
116+
117+
```json
118+
{
119+
"paths": {
120+
"$": {
121+
"toEncrypt": {
122+
"path.to.foo": "path.to.encryptedFoo"
123+
},
124+
"toDecrypt": {
125+
"path.to.encryptedFoo": "path.to.foo"
126+
}
127+
}
128+
},
129+
"encryptedValueFieldName": "encryptedData",
130+
"encryptionCertificate": "./path/to/public.cert",
131+
"decryptionKey": "./path/to/your/private.key",
132+
}
133+
```
134+
135+
The above can be either stored to a file or passed to 'JweEncryptionConfig' as dictionary:
136+
```python
137+
config_dictionary = {
138+
"paths": {…},
139+
140+
"decryptionKey": "./path/to/your/private.key"
141+
}
142+
143+
config = JweEncryptionConfig(config_dictionary)
144+
145+
config_file_path = "./config.json"
146+
config = JweEncryptionConfig(config_file_path)
147+
```
148+
149+
##### Performing JWE Encryption <a name="performing-jwe-encryption"></a>
150+
151+
Call `jwe_encryption.encrypt_payload()` with a JSON (dict) request payload, and optional `params` object.
152+
153+
Example using the configuration [above](#configuring-the-jwe-encryption):
154+
155+
```python
156+
from client_encryption.session_key_params import SessionKeyParams
157+
158+
payload = {
159+
"path": {
160+
"to": {
161+
"foo": {
162+
"sensitiveField1": "sensitiveValue1",
163+
"sensitiveField2": "sensitiveValue2"
164+
}
165+
}
166+
}
167+
}
168+
169+
params = SessionKeyParams.generate(conf) # optional
170+
request_payload = encrypt_payload(payload, config, params)
171+
```
172+
173+
Output:
174+
175+
```json
176+
{
177+
"path": {
178+
"to": {
179+
"encryptedFoo": {
180+
"encryptedValue": "eyJraWQiOiI3NjFiMDAzYzFlYWRlM(...)==.Y+oPYKZEMTKyYcSIVEgtQw=="
181+
}
182+
}
183+
}
184+
}
185+
```
186+
187+
##### Performing JWE Decryption <a name="performing-jwe-decryption"></a>
188+
189+
Call `jwe_encryption.decrypt_payload()` with a JSON (dict) encrypted response payload.
190+
191+
Example using the configuration [above](#configuring-the-jwe-encryption):
192+
193+
```python
194+
response = {
195+
"path": {
196+
"to": {
197+
"encryptedFoo": {
198+
"encryptedValue": "eyJraWQiOiI3NjFiMDAzYzFlYWRlM(...)==.Y+oPYKZEMTKyYcSIVEgtQw=="
199+
}
200+
}
201+
}
202+
}
203+
204+
response_payload = decrypt_payload(response, config)
205+
206+
```
207+
208+
Output:
209+
210+
```json
211+
{
212+
"path": {
213+
"to": {
214+
"foo": {
215+
"sensitiveField1": "sensitiveValue1",
216+
"sensitiveField2": "sensitiveValue2"
217+
}
218+
}
219+
}
220+
}
221+
```
222+
223+
#### Mastercard Encryption and Decryption <a name="mastercard-encryption-and-decryption"></a>
224+
225+
+ [Introduction](#mastercard-introduction)
226+
+ [Configuring the Mastercard Encryption](#configuring-the-mastercard-encryption)
227+
+ [Performing Mastercard Encryption](#performing-mastercard-encryption)
228+
+ [Performing Mastercard Decryption](#performing-mastercard-decryption)
229+
230+
##### Introduction <a name="introduction"></a>
85231

86232
The core methods responsible for payload encryption and decryption are `encrypt_payload` and `decrypt_payload` in the `field_level_encryption` module.
87233

@@ -99,7 +245,7 @@ config = FieldLevelEncryptionConfig(config_dictionary)
99245
decrypted_response_payload = decrypt_payload(body, config)
100246
```
101247

102-
#### Configuring the Field Level Encryption <a name="configuring-the-field-level-encryption"></a>
248+
##### Configuring the Mastercard Encryption <a name="configuring-the-mastercard-encryption"></a>
103249

104250
`field_level_encryption` needs a config dictionary to instruct how to decrypt/decrypt the payloads. Example:
105251

@@ -150,7 +296,7 @@ We have a predefined set of configurations to use with Mastercard services:
150296

151297

152298

153-
#### Performing Encryption <a name="performing-encryption"></a>
299+
##### Performing Mastercard Encryption <a name="performing-mastercard-encryption"></a>
154300

155301
Call `field_level_encryption.encrypt_payload()` with a JSON (dict) request payload, and optional `params` object.
156302

@@ -192,7 +338,7 @@ Output:
192338
}
193339
```
194340

195-
#### Performing Decryption <a name="performing-decryption"></a>
341+
##### Performing Mastercard Decryption <a name="performing-mastercard-decryption"></a>
196342

197343
Call `field_level_encryption.decrypt_payload()` with a JSON (dict) encrypted response payload.
198344

@@ -247,10 +393,9 @@ config = {
247393
248394
}
249395
},
250-
"ivFieldName": "iv",
251-
"encryptedKeyFieldName": "encryptedKey",
396+
"encryptionCertificate": "path/to/cert.pem",
252397
253-
"oaepPaddingDigestAlgorithm": "SHA256"
398+
"decryptionKey": "path/to/to/key.pem"
254399
}
255400

256401
add_encryption_layer(api_client, config)
@@ -263,7 +408,7 @@ from client_encryption.api_encryption import add_encryption_layer
263408
add_encryption_layer(api_client, "path/to/my/config.json")
264409
```
265410

266-
This method will add the field level 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.
411+
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.
267412

268413
##### OpenAPI Generator <a name="openapi-generator"></a>
269414

@@ -273,7 +418,7 @@ OpenAPI client can be generated, starting from your OpenAPI Spec using the follo
273418
openapi-generator-cli generate -i openapi-spec.yaml -l python -o out
274419
```
275420

276-
Client library will be generated in the `out` folder.
421+
The client library will be generated in the `out` folder.
277422

278423
See also:
279424

@@ -292,16 +437,16 @@ To use it:
292437
from openapi_client.api_client import ApiClient # import generated OpenAPI client
293438
```
294439

295-
3. Add the field level encryption layer to the generated client:
440+
3. Add the encryption layer to the generated client:
296441

297442
```python
298443
# Create a new instance of the generated client
299444
api_client = ApiClient()
300-
# Enable field level encryption
445+
# Enable encryption
301446
add_encryption_layer(api_client, "path/to/my/config.json")
302447
```
303448

304-
4. Use the `ApiClient` instance with the Field Level Encryption enabled:
449+
4. Use the `ApiClient` instance with Encryption enabled:
305450

306451
Example:
307452

@@ -340,14 +485,13 @@ According to the above the signing layer must be applied first in order to work
340485
add_signing_layer(api_client, key_file, key_password, consumer_key)
341486
```
342487
343-
4. Then add the field level encryption layer:
488+
4. Then add the encryption layer:
344489
```python
345490
add_encryption_layer(api_client, "path/to/my/config.json")
346491
```
347492

348-
5. Use the `ApiClient` instance with Authentication and Field Level Encryption both enabled:
493+
5. Use the `ApiClient` instance with Authentication and Encryption both enabled:
349494
```python
350495
response = MyServiceApi(api_client).do_some_action_post(body=request_body)
351496
decrypted = response.json()
352-
353497
```

0 commit comments

Comments
 (0)