Failed Conditions
Push — v7 ( 84f8b9...722dd5 )
by Florent
02:35
created

JWEBuilder   F

Complexity

Total Complexity 72

Size/Duplication

Total Lines 534
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 0
Metric Value
wmc 72
lcom 1
cbo 15
dl 0
loc 534
rs 2.5423
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A getSupportedKeyEncryptionAlgorithms() 0 4 1
A getSupportedContentEncryptionAlgorithms() 0 4 1
A getSupportedCompressionMethods() 0 4 1
A withPayload() 0 11 3
A withAAD() 0 7 1
A withSharedProtectedHeaders() 0 11 2
A withSharedHeaders() 0 11 2
C addRecipient() 0 36 8
B build() 0 26 6
A checkAndSetContentEncryptionAlgorithm() 0 9 3
A processRecipient() 0 14 3
A encryptJWE() 0 11 2
B preparePayload() 0 17 5
B getEncryptedKey() 0 16 6
A getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm() 0 4 1
A getEncryptedKeyFromKeyEncryptionAlgorithm() 0 4 1
A getEncryptedKeyFromKeyWrappingAlgorithm() 0 4 1
A checkKey() 0 9 2
C determineCEK() 0 32 8
A getCompressionMethod() 0 8 2
A areKeyManagementModesCompatible() 0 14 2
A createCEK() 0 4 1
A createIV() 0 4 1
A getKeyEncryptionAlgorithm() 0 12 3
A getContentEncryptionAlgorithm() 0 12 3
A checkDuplicatedHeaderParameters() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like JWEBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use JWEBuilder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\Encryption;
15
16
use Base64Url\Base64Url;
17
use Jose\Component\Core\Encoder\PayloadEncoderInterface;
18
use Jose\Component\Core\JWAManager;
19
use Jose\Component\Core\JWK;
20
use Jose\Component\Core\Util\KeyChecker;
21
use Jose\Component\Encryption\Algorithm\ContentEncryptionAlgorithmInterface;
22
use Jose\Component\Encryption\Algorithm\KeyEncryption\DirectEncryptionInterface;
23
use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyAgreementInterface;
24
use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyAgreementWrappingInterface;
25
use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyEncryptionInterface;
26
use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyWrappingInterface;
27
use Jose\Component\Encryption\Algorithm\KeyEncryptionAlgorithmInterface;
28
use Jose\Component\Encryption\Compression\CompressionMethodInterface;
29
use Jose\Component\Encryption\Compression\CompressionMethodManager;
30
31
final class JWEBuilder
32
{
33
    /**
34
     * @var PayloadEncoderInterface
35
     */
36
    private $payloadEncoder;
37
    /**
38
     * @var mixed
39
     */
40
    private $payload;
41
42
    /**
43
     * @var string|null
44
     */
45
    private $aad;
46
47
    /**
48
     * @var array
49
     */
50
    private $recipients = [];
51
52
    /**
53
     * @var JWAManager
54
     */
55
    private $keyEncryptionAlgorithmManager;
56
57
    /**
58
     * @var JWAManager
59
     */
60
    private $contentEncryptionAlgorithmManager;
61
62
    /**
63
     * @var CompressionMethodManager
64
     */
65
    private $compressionManager;
66
67
    /**
68
     * @var array
69
     */
70
    private $sharedProtectedHeaders = [];
71
72
    /**
73
     * @var array
74
     */
75
    private $sharedHeaders = [];
76
77
    /**
78
     * @var null|CompressionMethodInterface
79
     */
80
    private $compressionMethod = null;
81
82
    /**
83
     * @var null|ContentEncryptionAlgorithmInterface
84
     */
85
    private $contentEncryptionAlgorithm = null;
86
87
    /**
88
     * @var null|string
89
     */
90
    private $keyManagementMode = null;
91
92
    /**
93
     * JWEBuilder constructor.
94
     * @param PayloadEncoderInterface  $payloadEncoder
95
     * @param JWAManager               $keyEncryptionAlgorithmManager
96
     * @param JWAManager               $contentEncryptionAlgorithmManager
97
     * @param CompressionMethodManager $compressionManager
98
     */
99
    public function __construct(PayloadEncoderInterface $payloadEncoder, JWAManager $keyEncryptionAlgorithmManager, JWAManager $contentEncryptionAlgorithmManager, CompressionMethodManager $compressionManager)
100
    {
101
        $this->payloadEncoder = $payloadEncoder;
102
        $this->keyEncryptionAlgorithmManager = $keyEncryptionAlgorithmManager;
103
        $this->contentEncryptionAlgorithmManager = $contentEncryptionAlgorithmManager;
104
        $this->compressionManager = $compressionManager;
105
    }
106
107
    /**
108
     * @return string[]
109
     */
110
    public function getSupportedKeyEncryptionAlgorithms(): array
111
    {
112
        return $this->keyEncryptionAlgorithmManager->list();
113
    }
114
115
    /**
116
     * @return string[]
117
     */
118
    public function getSupportedContentEncryptionAlgorithms(): array
119
    {
120
        return $this->contentEncryptionAlgorithmManager->list();
121
    }
122
123
    /**
124
     * @return string[]
125
     */
126
    public function getSupportedCompressionMethods(): array
127
    {
128
        return $this->compressionManager->list();
129
    }
130
131
    /**
132
     * @param mixed $payload
133
     *
134
     * @return JWEBuilder
135
     */
136
    public function withPayload($payload): JWEBuilder
137
    {
138
        $payload = is_string($payload) ? $payload : $this->payloadEncoder->encode($payload);
139
        if (false === mb_detect_encoding($payload, 'UTF-8', true)) {
140
            throw new \InvalidArgumentException('The payload must be encoded in UTF-8');
141
        }
142
        $clone = clone $this;
143
        $clone->payload = $payload;
144
145
        return $clone;
146
    }
147
148
    /**
149
     * @param string|null $aad
150
     *
151
     * @return JWEBuilder
152
     */
153
    public function withAAD(?string $aad): JWEBuilder
154
    {
155
        $clone = clone $this;
156
        $clone->aad = $aad;
157
158
        return $clone;
159
    }
160
161
    /**
162
     * @param array $sharedProtectedHeaders
163
     *
164
     * @return JWEBuilder
165
     */
166
    public function withSharedProtectedHeaders(array $sharedProtectedHeaders): JWEBuilder
167
    {
168
        $this->checkDuplicatedHeaderParameters($sharedProtectedHeaders, $this->sharedHeaders);
169
        foreach ($this->recipients as $recipient) {
170
            $this->checkDuplicatedHeaderParameters($sharedProtectedHeaders, $recipient->getHeaders());
171
        }
172
        $clone = clone $this;
173
        $clone->sharedProtectedHeaders = $sharedProtectedHeaders;
174
175
        return $clone;
176
    }
177
178
    /**
179
     * @param array $sharedHeaders
180
     *
181
     * @return JWEBuilder
182
     */
183
    public function withSharedHeaders(array $sharedHeaders): JWEBuilder
184
    {
185
        $this->checkDuplicatedHeaderParameters($this->sharedProtectedHeaders, $sharedHeaders);
186
        foreach ($this->recipients as $recipient) {
187
            $this->checkDuplicatedHeaderParameters($sharedHeaders, $recipient->getHeaders());
188
        }
189
        $clone = clone $this;
190
        $clone->sharedHeaders = $sharedHeaders;
191
192
        return $clone;
193
    }
194
195
    /**
196
     * @param JWK   $recipientKey
197
     * @param array $recipientHeaders
198
     *
199
     * @return JWEBuilder
200
     */
201
    public function addRecipient(JWK $recipientKey, array $recipientHeaders = []): JWEBuilder
202
    {
203
        $this->checkDuplicatedHeaderParameters($this->sharedProtectedHeaders, $recipientHeaders);
204
        $this->checkDuplicatedHeaderParameters($this->sharedHeaders, $recipientHeaders);
205
        $clone = clone $this;
206
        $completeHeaders = array_merge($clone->sharedHeaders, $recipientHeaders, $clone->sharedProtectedHeaders);
207
        $clone->checkAndSetContentEncryptionAlgorithm($completeHeaders);
208
        $keyEncryptionAlgorithm = $clone->getKeyEncryptionAlgorithm($completeHeaders);
209
        if (null === $clone->keyManagementMode) {
210
            $clone->keyManagementMode = $keyEncryptionAlgorithm->getKeyManagementMode();
211
        } else {
212
            if (!$clone->areKeyManagementModesCompatible($clone->keyManagementMode, $keyEncryptionAlgorithm->getKeyManagementMode())) {
213
                throw new \InvalidArgumentException('Foreign key management mode forbidden.');
214
            }
215
        }
216
217
        $compressionMethod = $clone->getCompressionMethod($completeHeaders);
218
        if (null !== $compressionMethod) {
219
            if (null === $clone->compressionMethod) {
220
                $clone->compressionMethod = $compressionMethod;
221
            } elseif ($clone->compressionMethod->name() !== $compressionMethod->name()) {
222
                throw new \InvalidArgumentException('Incompatible compression method.');
223
            }
224
        }
225
        if (null === $compressionMethod && null !== $clone->compressionMethod) {
226
            throw new \InvalidArgumentException('Inconsistent compression method.');
227
        }
228
        $clone->checkKey($keyEncryptionAlgorithm, $recipientKey);
229
        $clone->recipients[] = [
230
            'key' => $recipientKey,
231
            'headers' => $recipientHeaders,
232
            'key_encryption_algorithm' => $keyEncryptionAlgorithm,
233
        ];
234
235
        return $clone;
236
    }
237
238
    /**
239
     * @return JWE
240
     */
241
    public function build(): JWE
242
    {
243
        if (0 === count($this->recipients)) {
244
            throw new \LogicException('No recipient.');
245
        }
246
247
        $additionalHeaders = [];
248
        $cek = $this->determineCEK($additionalHeaders);
249
250
        $recipients = [];
251
        foreach ($this->recipients as $recipient) {
252
            $recipient = $this->processRecipient($recipient, $cek, $additionalHeaders);
253
            $recipients[] = $recipient;
254
        }
255
256
        if (!empty($additionalHeaders) && 1 === count($this->recipients)) {
257
            $sharedProtectedHeaders = array_merge($additionalHeaders, $this->sharedProtectedHeaders);
258
        } else {
259
            $sharedProtectedHeaders = $this->sharedProtectedHeaders;
260
        }
261
        $encodedSharedProtectedHeaders = empty($sharedProtectedHeaders) ? '' : Base64Url::encode(json_encode($sharedProtectedHeaders));
262
263
        list($ciphertext, $iv, $tag) = $this->encryptJWE($cek, $encodedSharedProtectedHeaders);
264
265
        return JWE::create($ciphertext, $iv, $this->aad, $tag, $this->sharedHeaders, $sharedProtectedHeaders, $encodedSharedProtectedHeaders, $recipients);
266
    }
267
268
    /**
269
     * @param array $completeHeaders
270
     */
271
    private function checkAndSetContentEncryptionAlgorithm(array $completeHeaders): void
272
    {
273
        $contentEncryptionAlgorithm = $this->getContentEncryptionAlgorithm($completeHeaders);
274
        if (null === $this->contentEncryptionAlgorithm) {
275
            $this->contentEncryptionAlgorithm = $contentEncryptionAlgorithm;
276
        } elseif ($contentEncryptionAlgorithm->name() !== $this->contentEncryptionAlgorithm->name()) {
277
            throw new \InvalidArgumentException('Inconsistent content encryption algorithm');
278
        }
279
    }
280
281
    /**
282
     * @param array  $recipient
283
     * @param string $cek
284
     * @param array  $additionalHeaders
285
     *
286
     * @return Recipient
287
     */
288
    private function processRecipient(array $recipient, string $cek, array &$additionalHeaders): Recipient
289
    {
290
        $completeHeaders = array_merge($this->sharedHeaders, $recipient['headers'], $this->sharedProtectedHeaders);
291
        /** @var KeyEncryptionAlgorithmInterface $keyEncryptionAlgorithm */
292
        $keyEncryptionAlgorithm = $recipient['key_encryption_algorithm'];
293
        $encryptedContentEncryptionKey = $this->getEncryptedKey($completeHeaders, $cek, $keyEncryptionAlgorithm, $additionalHeaders, $recipient['key']);
294
        $recipientHeaders = $recipient['headers'];
295
        if (!empty($additionalHeaders) && 1 !== count($this->recipients)) {
296
            $recipientHeaders = array_merge($recipientHeaders, $additionalHeaders);
297
            $additionalHeaders = [];
298
        }
299
300
        return Recipient::create($recipientHeaders, $encryptedContentEncryptionKey);
301
    }
302
303
    /**
304
     * @param string $cek
305
     * @param string $encodedSharedProtectedHeaders
306
     *
307
     * @return array
308
     */
309
    private function encryptJWE(string $cek, string $encodedSharedProtectedHeaders): array
310
    {
311
        $tag = null;
312
        $iv_size = $this->contentEncryptionAlgorithm->getIVSize();
313
        $iv = $this->createIV($iv_size);
314
        $payload = $this->preparePayload();
315
        $aad = $this->aad ? Base64Url::encode($this->aad) : null;
316
        $ciphertext = $this->contentEncryptionAlgorithm->encryptContent($payload, $cek, $iv, $aad, $encodedSharedProtectedHeaders, $tag);
317
318
        return [$ciphertext, $iv, $tag];
319
    }
320
321
    /**
322
     * @return string
323
     */
324
    private function preparePayload(): ?string
325
    {
326
        $prepared = is_string($this->payload) ? $this->payload : json_encode($this->payload);
327
        if (null === $prepared) {
328
            throw new \RuntimeException('The payload is empty or cannot encoded into JSON.');
329
        }
330
331
        if (null === $this->compressionMethod) {
332
            return $prepared;
333
        }
334
        $compressedPayload = $this->compressionMethod->compress($prepared);
335
        if (null === $compressedPayload) {
336
            throw new \RuntimeException('The payload cannot be compressed.');
337
        }
338
339
        return $compressedPayload;
340
    }
341
342
    /**
343
     * @param array                           $completeHeaders
344
     * @param string                          $cek
345
     * @param KeyEncryptionAlgorithmInterface $keyEncryptionAlgorithm
346
     * @param JWK                             $recipientKey
347
     * @param array                           $additionalHeaders
348
     *
349
     * @return string|null
350
     */
351
    private function getEncryptedKey(array $completeHeaders, string $cek, KeyEncryptionAlgorithmInterface $keyEncryptionAlgorithm, array &$additionalHeaders, JWK $recipientKey): ?string
352
    {
353
        if ($keyEncryptionAlgorithm instanceof KeyEncryptionInterface) {
354
            return $this->getEncryptedKeyFromKeyEncryptionAlgorithm($completeHeaders, $cek, $keyEncryptionAlgorithm, $recipientKey, $additionalHeaders);
355
        } elseif ($keyEncryptionAlgorithm instanceof KeyWrappingInterface) {
356
            return $this->getEncryptedKeyFromKeyWrappingAlgorithm($completeHeaders, $cek, $keyEncryptionAlgorithm, $recipientKey, $additionalHeaders);
357
        } elseif ($keyEncryptionAlgorithm instanceof KeyAgreementWrappingInterface) {
358
            return $this->getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm($completeHeaders, $cek, $keyEncryptionAlgorithm, $additionalHeaders, $recipientKey);
359
        } elseif ($keyEncryptionAlgorithm instanceof KeyAgreementInterface) {
360
            return null;
361
        } elseif ($keyEncryptionAlgorithm instanceof DirectEncryptionInterface) {
362
            return null;
363
        }
364
365
        throw new \InvalidArgumentException('Unsupported key encryption algorithm.');
366
    }
367
368
    /**
369
     * @param array                         $completeHeaders
370
     * @param string                        $cek
371
     * @param KeyAgreementWrappingInterface $keyEncryptionAlgorithm
372
     * @param array                         $additionalHeaders
373
     * @param JWK                           $recipientKey
374
     *
375
     * @return string
376
     */
377
    private function getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm(array $completeHeaders, string $cek, KeyAgreementWrappingInterface $keyEncryptionAlgorithm, array &$additionalHeaders, JWK $recipientKey): string
378
    {
379
        return $keyEncryptionAlgorithm->wrapAgreementKey($recipientKey, $cek, $this->contentEncryptionAlgorithm->getCEKSize(), $completeHeaders, $additionalHeaders);
380
    }
381
382
    /**
383
     * @param array                  $completeHeaders
384
     * @param string                 $cek
385
     * @param KeyEncryptionInterface $keyEncryptionAlgorithm
386
     * @param JWK                    $recipientKey
387
     * @param array                  $additionalHeaders
388
     *
389
     * @return string
390
     */
391
    private function getEncryptedKeyFromKeyEncryptionAlgorithm(array $completeHeaders, string $cek, KeyEncryptionInterface $keyEncryptionAlgorithm, JWK $recipientKey, array &$additionalHeaders): string
392
    {
393
        return $keyEncryptionAlgorithm->encryptKey($recipientKey, $cek, $completeHeaders, $additionalHeaders);
394
    }
395
396
    /**
397
     * @param array                $completeHeaders
398
     * @param string               $cek
399
     * @param KeyWrappingInterface $keyEncryptionAlgorithm
400
     * @param JWK                  $recipientKey
401
     * @param array                $additionalHeaders
402
     *
403
     * @return string
404
     */
405
    private function getEncryptedKeyFromKeyWrappingAlgorithm(array $completeHeaders, string $cek, KeyWrappingInterface $keyEncryptionAlgorithm, JWK $recipientKey, array &$additionalHeaders): string
406
    {
407
        return $keyEncryptionAlgorithm->wrapKey($recipientKey, $cek, $completeHeaders, $additionalHeaders);
408
    }
409
410
    /**
411
     * @param KeyEncryptionAlgorithmInterface $keyEncryptionAlgorithm
412
     * @param JWK                             $recipientKey
413
     */
414
    private function checkKey(KeyEncryptionAlgorithmInterface $keyEncryptionAlgorithm, JWK $recipientKey)
415
    {
416
        KeyChecker::checkKeyUsage($recipientKey, 'encryption');
417
        if ('dir' !== $keyEncryptionAlgorithm->name()) {
418
            KeyChecker::checkKeyAlgorithm($recipientKey, $keyEncryptionAlgorithm->name());
419
        } else {
420
            KeyChecker::checkKeyAlgorithm($recipientKey, $this->contentEncryptionAlgorithm->name());
421
        }
422
    }
423
424
    /**
425
     * @param array $additionalHeaders
426
     *
427
     * @return string
428
     */
429
    private function determineCEK(array &$additionalHeaders): string
430
    {
431
        switch ($this->keyManagementMode) {
432
            case KeyEncryptionInterface::MODE_ENCRYPT:
433
            case KeyEncryptionInterface::MODE_WRAP:
434
                return $this->createCEK($this->contentEncryptionAlgorithm->getCEKSize());
435
            case KeyEncryptionInterface::MODE_AGREEMENT:
436
                if (1 !== count($this->recipients)) {
437
                    throw new \LogicException('Unable to encrypt for multiple recipients using key agreement algorithms.');
438
                }
439
                /** @var JWK $key */
440
                $key = $this->recipients[0]['key'];
441
                /** @var KeyAgreementInterface $algorithm */
442
                $algorithm = $this->recipients[0]['key_encryption_algorithm'];
443
                $completeHeaders = array_merge($this->sharedHeaders, $this->recipients[0]['headers'], $this->sharedProtectedHeaders);
444
445
                return $algorithm->getAgreementKey($this->contentEncryptionAlgorithm->getCEKSize(), $this->contentEncryptionAlgorithm->name(), $key, $completeHeaders, $additionalHeaders);
446
            case KeyEncryptionInterface::MODE_DIRECT:
447
                if (1 !== count($this->recipients)) {
448
                    throw new \LogicException('Unable to encrypt for multiple recipients using key agreement algorithms.');
449
                }
450
                /** @var JWK $key */
451
                $key = $this->recipients[0]['key'];
452
                if ('oct' !== $key->get('kty')) {
453
                    throw new \RuntimeException('Wrong key type.');
454
                }
455
456
                return Base64Url::decode($key->get('k'));
457
            default:
458
                throw new \InvalidArgumentException(sprintf('Unsupported key management mode "%s".', $this->keyManagementMode));
459
        }
460
    }
461
462
    /**
463
     * @param array $completeHeaders
464
     *
465
     * @return CompressionMethodInterface|null
466
     */
467
    private function getCompressionMethod(array $completeHeaders): ?CompressionMethodInterface
468
    {
469
        if (!array_key_exists('zip', $completeHeaders)) {
470
            return null;
471
        }
472
473
        return $this->compressionManager->get($completeHeaders['zip']);
474
    }
475
476
    /**
477
     * @param string $current
478
     * @param string $new
479
     *
480
     * @return bool
481
     */
482
    private function areKeyManagementModesCompatible(string $current, string $new): bool
483
    {
484
        $agree = KeyEncryptionAlgorithmInterface::MODE_AGREEMENT;
485
        $dir = KeyEncryptionAlgorithmInterface::MODE_DIRECT;
486
        $enc = KeyEncryptionAlgorithmInterface::MODE_ENCRYPT;
487
        $wrap = KeyEncryptionAlgorithmInterface::MODE_WRAP;
488
        $supportedKeyManagementModeCombinations = [$enc.$enc => true, $enc.$wrap => true, $wrap.$enc => true, $wrap.$wrap => true, $agree.$agree => false, $agree.$dir => false, $agree.$enc => false, $agree.$wrap => false, $dir.$agree => false, $dir.$dir => false, $dir.$enc => false, $dir.$wrap => false, $enc.$agree => false, $enc.$dir => false, $wrap.$agree => false, $wrap.$dir => false];
489
490
        if (array_key_exists($current.$new, $supportedKeyManagementModeCombinations)) {
491
            return $supportedKeyManagementModeCombinations[$current.$new];
492
        }
493
494
        return false;
495
    }
496
497
    /**
498
     * @param int $size
499
     *
500
     * @return string
501
     */
502
    private function createCEK(int $size): string
503
    {
504
        return random_bytes($size / 8);
505
    }
506
507
    /**
508
     * @param int $size
509
     *
510
     * @return string
511
     */
512
    private function createIV(int $size): string
513
    {
514
        return random_bytes($size / 8);
515
    }
516
517
    /**
518
     * @param array $completeHeaders
519
     *
520
     * @return KeyEncryptionAlgorithmInterface
521
     */
522
    private function getKeyEncryptionAlgorithm(array $completeHeaders): KeyEncryptionAlgorithmInterface
523
    {
524
        if (!array_key_exists('alg', $completeHeaders)) {
525
            throw new \InvalidArgumentException('Parameter "alg" is missing.');
526
        }
527
        $keyEncryptionAlgorithm = $this->keyEncryptionAlgorithmManager->get($completeHeaders['alg']);
528
        if (!$keyEncryptionAlgorithm instanceof KeyEncryptionAlgorithmInterface) {
529
            throw new \InvalidArgumentException(sprintf('The key encryption algorithm "%s" is not supported or not a key encryption algorithm instance.', $completeHeaders['alg']));
530
        }
531
532
        return $keyEncryptionAlgorithm;
533
    }
534
535
    /**
536
     * @param array $completeHeaders
537
     *
538
     * @return ContentEncryptionAlgorithmInterface
539
     */
540
    private function getContentEncryptionAlgorithm(array $completeHeaders): ContentEncryptionAlgorithmInterface
541
    {
542
        if (!array_key_exists('enc', $completeHeaders)) {
543
            throw new \InvalidArgumentException('Parameter "enc" is missing.');
544
        }
545
        $contentEncryptionAlgorithm = $this->contentEncryptionAlgorithmManager->get($completeHeaders['enc']);
546
        if (!$contentEncryptionAlgorithm instanceof ContentEncryptionAlgorithmInterface) {
547
            throw new \InvalidArgumentException(sprintf('The content encryption algorithm "%s" is not supported or not a content encryption algorithm instance.', $completeHeaders['alg']));
548
        }
549
550
        return $contentEncryptionAlgorithm;
551
    }
552
553
    /**
554
     * @param array $header1
555
     * @param array $header2
556
     */
557
    private function checkDuplicatedHeaderParameters(array $header1, array $header2)
558
    {
559
        $inter = array_intersect_key($header1, $header2);
560
        if (!empty($inter)) {
561
            throw new \InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', json_encode(array_keys($inter))));
562
        }
563
    }
564
}
565