Skip to content
This repository was archived by the owner on Feb 28, 2024. It is now read-only.

Commit 033f614

Browse files
authored
Merge pull request #22 from rfeelin/feature/jwe-encryption
Adding JWE support
2 parents 5a973ce + 1cd724c commit 033f614

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2406
-716
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
vendor
33
tests.xml
44
coverage.xml
5-
.scannerwork
5+
.scannerwork
6+
.phpunit.result.cache
7+
composer.lock

README.md

Lines changed: 213 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* [Loading the Encryption Certificate](#loading-the-encryption-certificate)
1818
* [Loading the Decryption Key](#loading-the-decryption-key)
1919
* [Performing Field Level Encryption and Decryption](#performing-field-level-encryption-and-decryption)
20+
* [Performing JWE Encryption and Decryption](#performing-JWE-encryption-and-decryption)
2021
* [Integrating with OpenAPI Generator API Client Libraries](#integrating-with-openapi-generator-api-client-libraries)
2122

2223
## Overview <a name="overview"></a>
@@ -387,6 +388,191 @@ Output:
387388
}
388389
```
389390

391+
### Performing JWE Encryption and Decryption <a name="performing-JWE-encryption-and-decryption"></a>
392+
393+
+ [Introduction](#introduction-jwe)
394+
+ [Configuring the JWE Encryption](#configuring-the-JWE-encryption)
395+
+ [Performing Encryption](#performing-encryption-jwe)
396+
+ [Performing Decryption](#performing-decryption-jwe)
397+
+ [Encrypting Entire Payloads](#encrypting-entire-payloads-jwe)
398+
+ [Decrypting Entire Payloads](#decrypting-entire-payloads-jwe)
399+
400+
#### Introduction <a name="introduction-jwe"></a>
401+
402+
The core methods responsible for payload encryption and decryption are `encryptPayload` and `decryptPayload` in the `JWEEncryption` class.
403+
404+
* `encryptPayload` usage:
405+
```php
406+
use Mastercard\Developer\Encryption;
407+
// …
408+
$encryptedRequestPayload = JWEEncryption::encryptPayload($requestPayload, $config);
409+
```
410+
411+
* `decryptPayload` usage:
412+
```php
413+
use Mastercard\Developer\Encryption;
414+
// …
415+
$responsePayload = JWEEncryption::decryptPayload($encryptedResponsePayload, $config);
416+
```
417+
418+
#### Configuring the JWE Encryption <a name="configuring-the-JWE-encryption"></a>
419+
Use the `JWEEncryptionConfigBuilder` to create `JWEEncryptionConfig` instances. Example:
420+
```php
421+
use Mastercard\Developer\Encryption;
422+
// …
423+
$config = FieldLevelEncryptionConfigBuilder::aFieldLevelEncryptionConfig()
424+
->withEncryptionCertificate($encryptionCertificate)
425+
->withDecryptionKey($decryptionKey)
426+
->withEncryptionPath('$.path.to.foo', '$.path.to.encryptedFoo')
427+
->withDecryptionPath('$.path.to.encryptedFoo', '$.path.to.foo')
428+
->withEncryptedValueFieldName('encryptedValue')
429+
->build();
430+
```
431+
Note: If `withEncryptedValueFieldName` is left blank, the value will default to `encryptedData`
432+
433+
See also:
434+
* [JWEEncryptionConfig.php](https://github.com/Mastercard/client-encryption-php/blob/master/src/Developer/Encryption/JWEEncryptionConfig.php) for all config options
435+
* [Service Configurations for Client Encryption PHP](https://github.com/Mastercard/client-encryption-php/wiki/Service-Configurations-for-Client-Encryption-PHP)
436+
437+
#### Performing Encryption <a name="performing-encryption-jwe"></a>
438+
439+
Call `JWEEncryption::encryptPayload` with a JSON request payload and a `JWEEncryptionConfig` instance.
440+
441+
Example using the configuration [above](#configuring-the-JWE-encryption):
442+
```php
443+
use Mastercard\Developer\Encryption;
444+
// …
445+
$payload = '{
446+
"path": {
447+
"to": {
448+
"foo": {
449+
"sensitiveField1": "sensitiveValue1",
450+
"sensitiveField2": "sensitiveValue2"
451+
}
452+
}
453+
}
454+
}';
455+
$encryptedPayload = JWEEncryption::encryptPayload($payload, $config);
456+
echo (json_encode(json_decode($encryptedPayload), JSON_PRETTY_PRINT));
457+
```
458+
459+
Output:
460+
461+
```json
462+
{
463+
"path": {
464+
"to": {
465+
"encryptedFoo": "809a09d78257af5379df0c454dcdf…353ed59fe72fd4a7735c69da4080e74f"
466+
}
467+
}
468+
}
469+
```
470+
471+
#### Performing Decryption <a name="performing-decryption-jwe"></a>
472+
473+
Call `JWEEncryption::decryptPayload` with a JSON response payload and a `JWEEncryptionConfig` instance.
474+
475+
Example using the configuration [above](#configuring-the-JWE-encryption):
476+
```php
477+
use Mastercard\Developer\Encryption;
478+
// …
479+
$encryptedPayload = '{
480+
"path": {
481+
"to": {
482+
"encryptedFoo": {
483+
"encryptedValue": "809a09d78257af5379df0c454dcdf…353ed59fe72fd4a7735c69da4080e74f"
484+
}
485+
}
486+
}
487+
}';
488+
$payload = JWEEncryption::decryptPayload($encryptedPayload, $config);
489+
echo (json_encode(json_decode($payload), JSON_PRETTY_PRINT));
490+
```
491+
492+
Output:
493+
```json
494+
{
495+
"path": {
496+
"to": {
497+
"foo": {
498+
"sensitiveField1": "sensitiveValue1",
499+
"sensitiveField2": "sensitiveValue2"
500+
}
501+
}
502+
}
503+
}
504+
```
505+
506+
#### Encrypting Entire Payloads <a name="encrypting-entire-payloads-jwe"></a>
507+
508+
Entire payloads can be encrypted using the '$' operator as encryption path:
509+
510+
```php
511+
use Mastercard\Developer\Encryption;
512+
// …
513+
$config = JWEConfigBuilder::aFieldLevelEncryptionConfig()
514+
->withEncryptionCertificate(encryptionCertificate)
515+
->withEncryptionPath('$', '$')
516+
->withEncryptedValueFieldName("encryptedValue")
517+
// …
518+
->build();
519+
```
520+
521+
Example:
522+
```php
523+
use Mastercard\Developer\Encryption;
524+
// …
525+
$payload = '{
526+
"sensitiveField1": "sensitiveValue1",
527+
"sensitiveField2": "sensitiveValue2"
528+
}';
529+
$encryptedPayload = JWEEncryption::encryptPayload($payload, $config);
530+
echo (json_encode(json_decode($encryptedPayload), JSON_PRETTY_PRINT));
531+
```
532+
533+
Output:
534+
```json
535+
{
536+
"encryptedValue": "e5e9340f4d2618d27f8955828c86…379b13901a3b1e2efed616b6750a90fd379515"
537+
}
538+
```
539+
540+
#### Decrypting Entire Payloads <a name="decrypting-entire-payloads-jwe"></a>
541+
542+
Entire payloads can be decrypted using the '$' operator as decryption path:
543+
544+
```php
545+
use Mastercard\Developer\Encryption;
546+
// …
547+
$config = JWEEncryptionConfigBuilder::aFieldLevelEncryptionConfig()
548+
->withDecryptionKey(decryptionKey)
549+
->withDecryptionPath('$', '$')
550+
->withEncryptedValueFieldName("encryptedValue")
551+
// …
552+
->build();
553+
```
554+
555+
Example:
556+
```php
557+
use Mastercard\Developer\Encryption;
558+
// …
559+
$encryptedPayload = '{
560+
"encryptedValue": "e5e9340f4d2618d27f8955828c86…379b13901a3b1e2efed616b6750a90fd379515"
561+
}';
562+
$payload = FieldLevelEncryption::decryptPayload($encryptedPayload, $config);
563+
echo (json_encode(json_decode($payload), JSON_PRETTY_PRINT));
564+
```
565+
566+
Output:
567+
```json
568+
{
569+
"sensitiveField1": "sensitiveValue1",
570+
"sensitiveField2": "sensitiveValue2"
571+
}
572+
```
573+
574+
575+
390576
### Integrating with OpenAPI Generator API Client Libraries <a name="integrating-with-openapi-generator-api-client-libraries"></a>
391577

392578
[OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator) generates API client libraries from [OpenAPI Specs](https://github.com/OAI/OpenAPI-Specification).
@@ -410,7 +596,7 @@ See also:
410596
* [OpenAPI Generator CLI Installation](https://openapi-generator.tech/docs/installation/)
411597
* [CONFIG OPTIONS for php](https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/php.md)
412598

413-
##### Usage of the `PsrHttpMessageEncryptionInterceptor`
599+
##### Usage of the `PsrHttpMessageEncryptionInterceptor` for Field Level Encryption
414600

415601
```php
416602
use GuzzleHttp;
@@ -436,3 +622,29 @@ $config->setHost('https://sandbox.api.mastercard.com');
436622
$serviceApi = new ServiceApi($client, $config);
437623
// …
438624
```
625+
##### Usage of the `PsrHttpMessageEncryptionInterceptor` for JWE Encryption
626+
627+
```php
628+
use GuzzleHttp;
629+
use OpenAPI\Client\Api\ServiceApi;
630+
use OpenAPI\Client\Configuration
631+
use Mastercard\Developer\Signers\PsrHttpMessageSigner;
632+
use Mastercard\Developer\Interceptors\PsrHttpMessageEncryptionInterceptor;
633+
// …
634+
635+
$stack = new GuzzleHttp\HandlerStack();
636+
$stack->setHandler(new GuzzleHttp\Handler\CurlHandler());
637+
$jweEncryptionConfig = JWEEncryptionConfigBuilder::aJWEEncryptionConfig()
638+
// …
639+
->build();
640+
$jweEncryptionInterceptor = new PsrHttpMessageEncryptionInterceptor($jweEncryptionConfig);
641+
$stack->push(GuzzleHttp\Middleware::mapRequest([$jweEncryptionInterceptor, 'interceptRequest']));
642+
$stack->push(GuzzleHttp\Middleware::mapResponse([$jweEncryptionInterceptor, 'interceptResponse']));
643+
$stack->push(GuzzleHttp\Middleware::mapRequest([new PsrHttpMessageSigner($consumerKey, $signingKey), 'sign']));
644+
$options = ['handler' => $stack];
645+
$client = new GuzzleHttp\Client($options);
646+
$config = new Configuration();
647+
$config->setHost('https://sandbox.api.mastercard.com');
648+
$serviceApi = new ServiceApi($client, $config);
649+
// …
650+
```

composer.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
}
1919
],
2020
"require": {
21-
"phpseclib/phpseclib": "^2.0",
22-
"symfony/polyfill-php70": "^1.19"
21+
"phpseclib/phpseclib": "~3.0",
22+
"symfony/polyfill-php70": "^1.19",
23+
"galbar/jsonpath": "^2.0"
2324
},
2425
"require-dev": {
2526
"guzzlehttp/guzzle": "^6.2",
26-
"yoast/phpunit-polyfills": "^1.0"
27+
"yoast/phpunit-polyfills": "^1.0",
28+
"phake/phake": "*"
2729
},
2830
"suggest": {
2931
"psr/http-message": "Allow usage of the PsrHttpMessageEncryptionInterceptor class"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace Mastercard\Developer\Encryption\AES;
4+
5+
use Mastercard\Developer\Encryption\EncryptionException;
6+
use phpseclib3\Crypt\AES;
7+
8+
class AESCBC
9+
{
10+
private function __construct(){
11+
// This class can't be instantiated
12+
}
13+
14+
/**
15+
* @param string $iv
16+
* @param string $key
17+
* @param string $bytes
18+
* @throws EncryptionException
19+
* @return string
20+
*/
21+
public static function encrypt($iv, $key, $bytes) {
22+
$aes = new AES('cbc');
23+
$aes->setKey($key);
24+
$aes->setIV($iv);
25+
$encryptedBytes = $aes->encrypt($bytes);
26+
if (false === $encryptedBytes) {
27+
throw new EncryptionException('Failed to encrypt bytes!');
28+
}
29+
return $encryptedBytes;
30+
}
31+
32+
/**
33+
* @param string $iv
34+
* @param string $key
35+
* @param string $encryptedBytes
36+
* @throws EncryptionException
37+
* @return string
38+
*/
39+
public static function decrypt($iv, $key, $encryptedBytes) {
40+
$aes = new AES('cbc');
41+
$aes->setKey($key);
42+
$aes->setIV($iv);
43+
$bytes = $aes->decrypt($encryptedBytes);
44+
if (false === $bytes) {
45+
throw new EncryptionException('Failed to decrypt bytes with the provided key and IV!');
46+
}
47+
return $bytes;
48+
}
49+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Mastercard\Developer\Encryption\AES;
4+
5+
class AESEncryption
6+
{
7+
private function __construct()
8+
{
9+
// Nothing to do here
10+
}
11+
12+
13+
/**
14+
* @param string $cipher_algo
15+
* @return string
16+
*/
17+
public static function generateIv($cipher_algo = 'AES-128-CBC')
18+
{
19+
$ivLength = openssl_cipher_iv_length($cipher_algo);
20+
$iv = openssl_random_pseudo_bytes($ivLength);
21+
22+
return $iv;
23+
}
24+
25+
/**
26+
* @param int $bitLength
27+
* @return string
28+
*/
29+
public static function generateCek($bitLength)
30+
{
31+
return openssl_random_pseudo_bytes($bitLength / 8);
32+
}
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Mastercard\Developer\Encryption\AES;
4+
5+
use phpseclib3\Crypt\AES;
6+
7+
class AESGCM
8+
{
9+
private function __construct()
10+
{
11+
}
12+
13+
/**
14+
* @param string $iv
15+
* @param string $key
16+
* @param string $authTag
17+
* @param string $aad
18+
* @param string $cipherText
19+
* @return string
20+
*/
21+
public static function decrypt($iv, $key, $authTag, $aad, $cipherText)
22+
{
23+
$cipher = new AES('gcm');
24+
$cipher->setNonce($iv);
25+
$cipher->setKey($key);
26+
$cipher->disablePadding();
27+
$cipher->setTag($authTag);
28+
$cipher->setAAD($aad);
29+
return $cipher->decrypt($cipherText);
30+
}
31+
}

0 commit comments

Comments
 (0)