Complex classes like Encrypter 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 Encrypter, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 34 | final class Encrypter implements EncrypterInterface |
||
| 35 | { |
||
| 36 | use HasKeyChecker; |
||
| 37 | use HasJWAManager; |
||
| 38 | use HasCompressionManager; |
||
| 39 | |||
| 40 | /** |
||
| 41 | * Encrypter constructor. |
||
| 42 | * |
||
| 43 | * @param \Jose\Algorithm\JWAManagerInterface $jwa_manager |
||
| 44 | * @param \Jose\Compression\CompressionManagerInterface $compression_manager |
||
| 45 | */ |
||
| 46 | public function __construct( |
||
| 53 | |||
| 54 | /** |
||
| 55 | * @param array|\Jose\Object\JWKInterface|\Jose\Object\JWKSetInterface|\Jose\Object\JWTInterface|string $input |
||
| 56 | * @param array $instructions |
||
| 57 | * @param array $shared_protected_header |
||
| 58 | * @param array $shared_unprotected_header |
||
| 59 | * @param string $serialization |
||
| 60 | * @param null $aad |
||
| 61 | * |
||
| 62 | * @return string |
||
| 63 | */ |
||
| 64 | /*public function encrypt($input, array $instructions, $serialization, array $shared_protected_header = [], array $shared_unprotected_header = [], $aad = null) |
||
| 65 | { |
||
| 66 | $additional_header = []; |
||
| 67 | $this->checkSerializationMode($serialization); |
||
| 68 | $input = $this->getPayloadConverter()->convertPayloadToString($additional_header, $input); |
||
| 69 | $this->checkInstructions($instructions, $serialization); |
||
| 70 | if (!empty($shared_unprotected_header) && JSONSerializationModes::JSON_COMPACT_SERIALIZATION === $serialization) { |
||
| 71 | throw new \InvalidArgumentException('Cannot create Compact Json Serialization representation: shared unprotected header cannot be kept'); |
||
| 72 | } |
||
| 73 | if (!empty($aad) && JSONSerializationModes::JSON_COMPACT_SERIALIZATION === $serialization) { |
||
| 74 | throw new \InvalidArgumentException('Cannot create Compact Json Serialization representation: AAD cannot be kept'); |
||
| 75 | } |
||
| 76 | |||
| 77 | $protected_header = array_merge($shared_protected_header, $additional_header); |
||
| 78 | |||
| 79 | // We check if key management mode is OK |
||
| 80 | $key_management_mode = $this->getKeyManagementMode($instructions, $protected_header, $shared_unprotected_header); |
||
| 81 | |||
| 82 | // We get the content encryption algorithm |
||
| 83 | $content_encryption_algorithm = $this->getContentEncryptionAlgorithm($instructions, $protected_header, $shared_unprotected_header); |
||
| 84 | |||
| 85 | // CEK |
||
| 86 | $cek = $this->determineCEK($key_management_mode, $instructions, $protected_header, $shared_unprotected_header, $content_encryption_algorithm->getCEKSize()); |
||
| 87 | |||
| 88 | $recipients = ['recipients' => []]; |
||
| 89 | foreach ($instructions as $instruction) { |
||
| 90 | $recipients['recipients'][] = $this->computeRecipient($instruction, $protected_header, $shared_unprotected_header, $cek, $content_encryption_algorithm->getCEKSize(), $serialization); |
||
| 91 | } |
||
| 92 | |||
| 93 | // We prepare the payload and compress it if required |
||
| 94 | $compression_method = $this->findCompressionMethod($instructions, $protected_header, $shared_unprotected_header); |
||
| 95 | $this->compressPayload($input, $compression_method); |
||
| 96 | |||
| 97 | // We compute the initialization vector |
||
| 98 | $iv = null; |
||
| 99 | if (null !== $iv_size = $content_encryption_algorithm->getIVSize()) { |
||
| 100 | $iv = $this->createIV($iv_size); |
||
| 101 | } |
||
| 102 | |||
| 103 | // JWT Shared protected header |
||
| 104 | $jwt_shared_protected_header = Base64Url::encode(json_encode($protected_header)); |
||
| 105 | |||
| 106 | // We encrypt the payload and get the tag |
||
| 107 | $tag = null; |
||
| 108 | $ciphertext = $content_encryption_algorithm->encryptContent($input, $cek, $iv, $aad, $jwt_shared_protected_header, $tag); |
||
| 109 | |||
| 110 | // JWT Ciphertext |
||
| 111 | $jwt_ciphertext = Base64Url::encode($ciphertext); |
||
| 112 | |||
| 113 | // JWT AAD |
||
| 114 | $jwt_aad = null === $aad ? null : Base64Url::encode($aad); |
||
| 115 | |||
| 116 | // JWT Tag |
||
| 117 | $jwt_tag = null === $tag ? null : Base64Url::encode($tag); |
||
| 118 | |||
| 119 | // JWT IV |
||
| 120 | $jwt_iv = null === $iv ? '' : Base64Url::encode($iv); |
||
| 121 | |||
| 122 | $values = [ |
||
| 123 | 'ciphertext' => $jwt_ciphertext, |
||
| 124 | 'protected' => $jwt_shared_protected_header, |
||
| 125 | 'unprotected' => $shared_unprotected_header, |
||
| 126 | 'iv' => $jwt_iv, |
||
| 127 | 'tag' => $jwt_tag, |
||
| 128 | 'aad' => $jwt_aad, |
||
| 129 | ]; |
||
| 130 | foreach ($values as $key => $value) { |
||
| 131 | if (!empty($value)) { |
||
| 132 | $recipients[$key] = $value; |
||
| 133 | } |
||
| 134 | } |
||
| 135 | |||
| 136 | return Converter::convert($recipients, $serialization); |
||
| 137 | }*/ |
||
| 138 | |||
| 139 | /** |
||
| 140 | * @param \Jose\Object\EncryptionInstructionInterface $instruction |
||
| 141 | * @param $protected_header |
||
| 142 | * @param $unprotected_header |
||
| 143 | * @param string $cek |
||
| 144 | * @param int $cek_size |
||
| 145 | * @param string $serialization |
||
| 146 | * |
||
| 147 | * @throws \Exception |
||
| 148 | * |
||
| 149 | * @return array |
||
| 150 | */ |
||
| 151 | /*private function computeRecipient(EncryptionInstructionInterface $instruction, &$protected_header, $unprotected_header, $cek, $cek_size, $serialization) |
||
| 152 | { |
||
| 153 | if (!$this->checkKeyUsage($instruction->getRecipientKey(), 'encryption')) { |
||
| 154 | throw new \InvalidArgumentException('Key cannot be used to encrypt'); |
||
| 155 | } |
||
| 156 | |||
| 157 | $recipient_header = $instruction->getRecipientUnprotectedHeader(); |
||
| 158 | $complete_header = array_merge($protected_header, $unprotected_header, $recipient_header); |
||
| 159 | |||
| 160 | $key_encryption_algorithm = $this->getKeyEncryptionAlgorithm($complete_header); |
||
| 161 | |||
| 162 | if (!$this->checkKeyAlgorithm($instruction->getRecipientKey(), $key_encryption_algorithm->getAlgorithmName())) { |
||
| 163 | throw new \InvalidArgumentException(sprintf('Key is only allowed for algorithm "%s".', $instruction->getRecipientKey()->get('alg'))); |
||
| 164 | } |
||
| 165 | |||
| 166 | $jwt_cek = null; |
||
| 167 | if ($key_encryption_algorithm instanceof KeyEncryptionInterface) { |
||
| 168 | $jwt_cek = Base64Url::encode($key_encryption_algorithm->encryptKey($instruction->getRecipientKey(), $cek, $protected_header)); |
||
| 169 | } elseif ($key_encryption_algorithm instanceof KeyAgreementWrappingInterface) { |
||
| 170 | if (null === $instruction->getSenderKey()) { |
||
| 171 | throw new \RuntimeException('The sender key must be set using Key Agreement or Key Agreement with Wrapping algorithms.'); |
||
| 172 | } |
||
| 173 | $additional_header_values = []; |
||
| 174 | $jwt_cek = Base64Url::encode($key_encryption_algorithm->wrapAgreementKey($instruction->getSenderKey(), $instruction->getRecipientKey(), $cek, $cek_size, $complete_header, $additional_header_values)); |
||
| 175 | $this->updateHeader($additional_header_values, $protected_header, $recipient_header, $serialization); |
||
| 176 | } elseif ($key_encryption_algorithm instanceof KeyAgreementInterface) { |
||
| 177 | $additional_header_values = []; |
||
| 178 | $jwt_cek = Base64Url::encode($key_encryption_algorithm->getAgreementKey($cek_size, $instruction->getSenderKey(), $instruction->getRecipientKey(), $complete_header, $additional_header_values)); |
||
| 179 | $this->updateHeader($additional_header_values, $protected_header, $recipient_header, $serialization); |
||
| 180 | } |
||
| 181 | |||
| 182 | $result = []; |
||
| 183 | if (null !== $jwt_cek) { |
||
| 184 | $result['encrypted_key'] = $jwt_cek; |
||
| 185 | } |
||
| 186 | if (!empty($recipient_header)) { |
||
| 187 | $result['header'] = $recipient_header; |
||
| 188 | } |
||
| 189 | |||
| 190 | return $result; |
||
| 191 | }*/ |
||
| 192 | |||
| 193 | /** |
||
| 194 | * @param array $additional_header_values |
||
| 195 | * @param array $protected_header |
||
| 196 | * @param array $recipient_header |
||
| 197 | * @param string $serialization |
||
| 198 | */ |
||
| 199 | /*private function updateHeader(array $additional_header_values, array &$protected_header, array &$recipient_header, $serialization) |
||
| 200 | { |
||
| 201 | if (JSONSerializationModes::JSON_COMPACT_SERIALIZATION === $serialization) { |
||
| 202 | $protected_header = array_merge($protected_header, $additional_header_values); |
||
| 203 | } else { |
||
| 204 | $recipient_header = array_merge($recipient_header, $additional_header_values); |
||
| 205 | } |
||
| 206 | }*/ |
||
| 207 | |||
| 208 | /** |
||
| 209 | * @param \Jose\Object\EncryptionInstructionInterface[] $instructions |
||
| 210 | * @param array $protected_header |
||
| 211 | * @param array $unprotected_header |
||
| 212 | * |
||
| 213 | * @return string |
||
| 214 | */ |
||
| 215 | /*private function getKeyManagementMode(array $instructions, $protected_header, $unprotected_header) |
||
| 216 | { |
||
| 217 | $mode = null; |
||
| 218 | foreach ($instructions as $instruction) { |
||
| 219 | $recipient_header = $instruction->getRecipientUnprotectedHeader(); |
||
| 220 | $complete_header = array_merge($protected_header, $unprotected_header, $recipient_header); |
||
| 221 | |||
| 222 | $temp = $this->getKeyManagementMode2($complete_header); |
||
| 223 | if (null === $mode) { |
||
| 224 | $mode = $temp; |
||
| 225 | } else { |
||
| 226 | if (!$this->areKeyManagementModeAuthorized($mode, $temp)) { |
||
| 227 | throw new \RuntimeException('Foreign key management mode forbidden.'); |
||
| 228 | } |
||
| 229 | } |
||
| 230 | } |
||
| 231 | |||
| 232 | return $mode; |
||
| 233 | }*/ |
||
| 234 | |||
| 235 | /** |
||
| 236 | * @param string $mode1 |
||
| 237 | * @param string $mode2 |
||
| 238 | * |
||
| 239 | * @return bool |
||
| 240 | */ |
||
| 241 | /*private function areKeyManagementModeAuthorized($mode1, $mode2) |
||
| 242 | { |
||
| 243 | switch ($mode1.$mode2) { |
||
| 244 | case 'encenc': |
||
| 245 | case 'encwrap': |
||
| 246 | case 'wrapenc': |
||
| 247 | case 'wrapwrap': |
||
| 248 | case 'agreeagree': |
||
| 249 | case 'dirdir': |
||
| 250 | return true; |
||
| 251 | default: |
||
| 252 | return false; |
||
| 253 | } |
||
| 254 | }*/ |
||
| 255 | |||
| 256 | /** |
||
| 257 | * @param array $complete_header |
||
| 258 | * |
||
| 259 | * @return string |
||
| 260 | */ |
||
| 261 | /*private function getKeyManagementMode2($complete_header) |
||
| 262 | { |
||
| 263 | $key_encryption_algorithm = $this->getKeyEncryptionAlgorithm($complete_header); |
||
| 264 | |||
| 265 | if ($key_encryption_algorithm instanceof KeyEncryptionInterface) { |
||
| 266 | return 'enc'; |
||
| 267 | } elseif ($key_encryption_algorithm instanceof KeyAgreementWrappingInterface) { |
||
| 268 | return 'wrap'; |
||
| 269 | } elseif ($key_encryption_algorithm instanceof KeyAgreementInterface) { |
||
| 270 | return 'agree'; |
||
| 271 | } elseif ($key_encryption_algorithm instanceof DirectEncryptionInterface) { |
||
| 272 | return 'dir'; |
||
| 273 | } else { |
||
| 274 | throw new \RuntimeException('Unable to get key management mode.'); |
||
| 275 | } |
||
| 276 | }*/ |
||
| 277 | |||
| 278 | /** |
||
| 279 | * @param string $key_management_mode |
||
| 280 | * @param \Jose\Object\EncryptionInstructionInterface[] $instructions |
||
| 281 | * @param array $protected_header |
||
| 282 | * @param array $unprotected_header |
||
| 283 | * @param int $cek_size |
||
| 284 | * |
||
| 285 | * @return string |
||
| 286 | */ |
||
| 287 | /*private function determineCEK($key_management_mode, array $instructions, $protected_header, $unprotected_header, $cek_size) |
||
| 288 | { |
||
| 289 | switch ($key_management_mode) { |
||
| 290 | case 'enc': |
||
| 291 | case 'wrap': |
||
| 292 | return $this->createCEK($cek_size); |
||
| 293 | case 'dir': |
||
| 294 | return $this->getDirectKey($instructions, $protected_header, $unprotected_header); |
||
| 295 | case 'agree': |
||
| 296 | return $this->getAgreementKey($instructions, $protected_header, $unprotected_header, $cek_size); |
||
| 297 | default: |
||
| 298 | throw new \RuntimeException('Unable to get CEK (unsupported key management mode).'); |
||
| 299 | } |
||
| 300 | }*/ |
||
| 301 | |||
| 302 | /** |
||
| 303 | * @param \Jose\Object\EncryptionInstructionInterface[] $instructions |
||
| 304 | * @param array $protected_header |
||
| 305 | * @param array $unprotected_header |
||
| 306 | * |
||
| 307 | * @return string |
||
| 308 | */ |
||
| 309 | /*private function getDirectKey(array $instructions, $protected_header, $unprotected_header) |
||
| 310 | { |
||
| 311 | $cek = null; |
||
| 312 | foreach ($instructions as $instruction) { |
||
| 313 | $recipient_header = $instruction->getRecipientUnprotectedHeader(); |
||
| 314 | $complete_header = array_merge($protected_header, $unprotected_header, $recipient_header); |
||
| 315 | |||
| 316 | $key_encryption_algorithm = $this->getKeyEncryptionAlgorithm($complete_header); |
||
| 317 | if (!$key_encryption_algorithm instanceof DirectEncryptionInterface) { |
||
| 318 | throw new \RuntimeException('The key encryption algorithm is not an instance of DirectEncryptionInterface'); |
||
| 319 | } |
||
| 320 | |||
| 321 | $temp = $key_encryption_algorithm->getCEK($instruction->getRecipientKey(), $complete_header); |
||
| 322 | if (null === $cek) { |
||
| 323 | $cek = $temp; |
||
| 324 | } else { |
||
| 325 | if ($cek !== $temp) { |
||
| 326 | throw new \RuntimeException('Foreign CEK forbidden using direct key.'); |
||
| 327 | } |
||
| 328 | } |
||
| 329 | } |
||
| 330 | |||
| 331 | return $cek; |
||
| 332 | }*/ |
||
| 333 | |||
| 334 | /** |
||
| 335 | * @param \Jose\Object\EncryptionInstructionInterface[] $instructions |
||
| 336 | * @param array $protected_header |
||
| 337 | * @param array $unprotected_header |
||
| 338 | * @param int $cek_size |
||
| 339 | * |
||
| 340 | * @return string |
||
| 341 | */ |
||
| 342 | /*private function getAgreementKey(array $instructions, $protected_header, $unprotected_header, $cek_size) |
||
| 343 | { |
||
| 344 | $cek = null; |
||
| 345 | foreach ($instructions as $instruction) { |
||
| 346 | $recipient_header = $instruction->getRecipientUnprotectedHeader(); |
||
| 347 | $complete_header = array_merge($protected_header, $unprotected_header, $recipient_header); |
||
| 348 | |||
| 349 | $key_encryption_algorithm = $this->getKeyEncryptionAlgorithm($complete_header); |
||
| 350 | |||
| 351 | if (!$key_encryption_algorithm instanceof KeyAgreementInterface) { |
||
| 352 | throw new \RuntimeException('The key encryption algorithm is not an instance of KeyAgreementInterface'); |
||
| 353 | } |
||
| 354 | |||
| 355 | if (null === $instruction->getSenderKey()) { |
||
| 356 | throw new \RuntimeException('The sender key must be set using Key Agreement or Key Agreement with Wrapping algorithms.'); |
||
| 357 | } |
||
| 358 | $additional_header_values = []; |
||
| 359 | $temp = $key_encryption_algorithm->getAgreementKey($cek_size, $instruction->getSenderKey(), $instruction->getRecipientKey(), $complete_header, $additional_header_values); |
||
| 360 | if (null === $cek) { |
||
| 361 | $cek = $temp; |
||
| 362 | } else { |
||
| 363 | if ($cek !== $temp) { |
||
| 364 | throw new \RuntimeException('Foreign CEK forbidden using direct key agreement.'); |
||
| 365 | } |
||
| 366 | } |
||
| 367 | } |
||
| 368 | |||
| 369 | return $cek; |
||
| 370 | }*/ |
||
| 371 | |||
| 372 | /** |
||
| 373 | * @param string $payload |
||
| 374 | * @param array $complete_headers |
||
| 375 | */ |
||
| 376 | private function compressPayload(&$payload, array $complete_headers) |
||
| 391 | |||
| 392 | /** |
||
| 393 | * @param array $complete_headers |
||
| 394 | * |
||
| 395 | * @return string|null |
||
| 396 | */ |
||
| 397 | private function findCompressionMethod($complete_headers) |
||
| 405 | |||
| 406 | /** |
||
| 407 | * @param string $method |
||
| 408 | * |
||
| 409 | * @return \Jose\Compression\CompressionInterface |
||
| 410 | */ |
||
| 411 | private function getCompressionMethod($method) |
||
| 420 | |||
| 421 | /** |
||
| 422 | * @param array $complete_header |
||
| 423 | * |
||
| 424 | * @return \Jose\Algorithm\KeyEncryption\DirectEncryptionInterface|\Jose\Algorithm\KeyEncryption\KeyEncryptionInterface|\Jose\Algorithm\KeyEncryption\KeyAgreementInterface|\Jose\Algorithm\KeyEncryption\KeyAgreementWrappingInterface |
||
| 425 | */ |
||
| 426 | private function getKeyEncryptionAlgorithm($complete_header) |
||
| 444 | |||
| 445 | /** |
||
| 446 | * @param \Jose\Object\EncryptionInstructionInterface[] $instructions |
||
| 447 | * @param array $protected_header |
||
| 448 | * @param array $unprotected_header |
||
| 449 | * |
||
| 450 | * @return \Jose\Algorithm\ContentEncryption\ContentEncryptionInterface |
||
| 451 | */ |
||
| 452 | private function getContentEncryptionAlgorithm(array $instructions, array $protected_header = [], array $unprotected_header = []) |
||
| 477 | |||
| 478 | /** |
||
| 479 | * @param int $size |
||
| 480 | * |
||
| 481 | * @return string |
||
| 482 | */ |
||
| 483 | private function createCEK($size) |
||
| 487 | |||
| 488 | /** |
||
| 489 | * @param int $size |
||
| 490 | * |
||
| 491 | * @return string |
||
| 492 | */ |
||
| 493 | private function createIV($size) |
||
| 497 | |||
| 498 | /** |
||
| 499 | * @param int $length |
||
| 500 | * |
||
| 501 | * @return string |
||
| 502 | */ |
||
| 503 | private function generateRandomString($length) |
||
| 511 | |||
| 512 | /** |
||
| 513 | * @param \Jose\Object\JWEInterface $jwe |
||
| 514 | * @param \Jose\Object\JWKInterface $recipient_key |
||
| 515 | * @param \Jose\Object\JWKInterface|null $sender_key |
||
| 516 | * @param array $recipient_headers |
||
| 517 | * |
||
| 518 | * @return \Jose\Object\JWEInterface |
||
| 519 | */ |
||
| 520 | public function addRecipient(JWEInterface $jwe, JWKInterface $recipient_key, JWKInterface $sender_key = null, array $recipient_headers = []) |
||
| 581 | |||
| 582 | private function getEncryptedKey($cek, $key_encryption_algorithm, JWKInterface $recipient_key, JWKInterface $sender_key = null) |
||
| 599 | |||
| 600 | private function getCEK($key_management_mode, array $complete_headers, KeyEncryptionAlgorithmInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, JWKInterface $recipient_key, JWKInterface $sender_key = null) |
||
| 614 | |||
| 615 | private function getKeyManagementModeOfAlgorithm(EncryptionInterface $algorithm) |
||
| 629 | |||
| 630 | /** |
||
| 631 | * @param array $complete_headers |
||
| 632 | * |
||
| 633 | * @return \Jose\Algorithm\KeyEncryptionAlgorithmInterface |
||
| 634 | */ |
||
| 635 | private function findKeyEncryptionAlgorithm(array $complete_headers) |
||
| 646 | |||
| 647 | /** |
||
| 648 | * @param array $complete_headers |
||
| 649 | * |
||
| 650 | * @return \Jose\Algorithm\ContentEncryptionAlgorithmInterface |
||
| 651 | */ |
||
| 652 | private function findContentEncryptionAlgorithm(array $complete_headers) |
||
| 665 | |||
| 666 | /** |
||
| 667 | * @param array $complete_headers |
||
| 668 | * @param \Jose\Algorithm\KeyEncryptionAlgorithmInterface $key_encryption_algorithm |
||
| 669 | * @param \Jose\Algorithm\ContentEncryptionAlgorithmInterface $content_encryption_algorithm |
||
| 670 | * @param \Jose\Object\JWKInterface $recipient_key |
||
| 671 | * @param \Jose\Object\JWKInterface|null $sender_key |
||
| 672 | * |
||
| 673 | * @return string |
||
| 674 | */ |
||
| 675 | private function calculateAgreementKey(array $complete_headers, KeyEncryptionAlgorithmInterface $key_encryption_algorithm, ContentEncryptionAlgorithmInterface $content_encryption_algorithm, JWKInterface $recipient_key, JWKInterface $sender_key = null) |
||
| 685 | } |
||
| 686 |