JWEBuilder   F
last analyzed

Complexity

Total Complexity 71

Size/Duplication

Total Lines 552
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 0
Metric Value
wmc 71
lcom 1
cbo 15
dl 0
loc 552
rs 2.6315
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A create() 0 13 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
A preparePayload() 0 14 3
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
A getKeyEncryptionAlgorithmManager() 0 4 1
A getContentEncryptionAlgorithmManager() 0 4 1
A getCompressionManager() 0 4 1

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