Spomky-Labs /
jose
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | /* |
||
| 4 | * The MIT License (MIT) |
||
| 5 | * |
||
| 6 | * Copyright (c) 2014-2016 Spomky-Labs |
||
| 7 | * |
||
| 8 | * This software may be modified and distributed under the terms |
||
| 9 | * of the MIT license. See the LICENSE file for details. |
||
| 10 | */ |
||
| 11 | |||
| 12 | namespace Jose; |
||
| 13 | |||
| 14 | use Base64Url\Base64Url; |
||
| 15 | use Jose\Algorithm\JWAManager; |
||
| 16 | use Jose\Algorithm\SignatureAlgorithmInterface; |
||
| 17 | use Jose\Factory\AlgorithmManagerFactory; |
||
| 18 | use Jose\Object\JWKInterface; |
||
| 19 | use Jose\Object\JWSInterface; |
||
| 20 | use Jose\Object\Signature; |
||
| 21 | use Jose\Object\SignatureInterface; |
||
| 22 | |||
| 23 | final class Signer |
||
| 24 | { |
||
| 25 | /** |
||
| 26 | * @var JWAManager |
||
| 27 | */ |
||
| 28 | private $jwaManager; |
||
| 29 | |||
| 30 | use Behaviour\HasKeyChecker; |
||
| 31 | use Behaviour\CommonSigningMethods; |
||
| 32 | |||
| 33 | /** |
||
| 34 | * Signer constructor. |
||
| 35 | * |
||
| 36 | * @param string[]|SignatureAlgorithmInterface[] $signature_algorithms |
||
| 37 | */ |
||
| 38 | public function __construct(array $signature_algorithms) |
||
| 39 | { |
||
| 40 | $this->setSignatureAlgorithms($signature_algorithms); |
||
| 41 | $this->jwaManager = AlgorithmManagerFactory::createAlgorithmManager($signature_algorithms); |
||
| 42 | } |
||
| 43 | |||
| 44 | /** |
||
| 45 | * @param array $signature_algorithms |
||
| 46 | * |
||
| 47 | * @return Signer |
||
| 48 | */ |
||
| 49 | public static function createSigner(array $signature_algorithms): Signer |
||
| 50 | { |
||
| 51 | $signer = new self($signature_algorithms); |
||
| 52 | |||
| 53 | return $signer; |
||
| 54 | } |
||
| 55 | |||
| 56 | /** |
||
| 57 | * @param JWSInterface $jws |
||
| 58 | */ |
||
| 59 | public function sign(JWSInterface &$jws) |
||
| 60 | { |
||
| 61 | $nb_signatures = $jws->countSignatures(); |
||
| 62 | |||
| 63 | for ($i = 0; $i < $nb_signatures; $i++) { |
||
| 64 | $this->computeSignature($jws, $jws->getSignature($i)); |
||
| 65 | } |
||
| 66 | } |
||
| 67 | |||
| 68 | /** |
||
| 69 | * @param JWSInterface $jws |
||
| 70 | * @param SignatureInterface $signature |
||
| 71 | */ |
||
| 72 | private function computeSignature(JWSInterface $jws, SignatureInterface &$signature) |
||
| 73 | { |
||
| 74 | if (null === $signature->getSignatureKey()) { |
||
| 75 | return; |
||
| 76 | } |
||
| 77 | $this->checkKeyUsage($signature->getSignatureKey(), 'signature'); |
||
| 78 | |||
| 79 | $signature_algorithm = $this->getSignatureAlgorithm($signature->getAllHeaders(), $signature->getSignatureKey()); |
||
| 80 | |||
| 81 | $input = $this->getInputToSign($jws, $signature); |
||
| 82 | |||
| 83 | $value = $signature_algorithm->sign( |
||
| 84 | $signature->getSignatureKey(), |
||
| 85 | $input |
||
| 86 | ); |
||
| 87 | |||
| 88 | $signature = Signature::createSignatureFromLoadedData( |
||
| 89 | $value, |
||
| 90 | $signature->getEncodedProtectedHeaders(), |
||
| 91 | $signature->getHeaders() |
||
| 92 | ); |
||
| 93 | } |
||
| 94 | |||
| 95 | /** |
||
| 96 | * @param JWSInterface $jws |
||
| 97 | * @param SignatureInterface $signature |
||
| 98 | * |
||
| 99 | * @return string |
||
| 100 | */ |
||
| 101 | private function getInputToSign(JWSInterface $jws, SignatureInterface $signature): string |
||
| 102 | { |
||
| 103 | $this->checkB64HeaderAndCrit($signature); |
||
| 104 | $encoded_protected_headers = $signature->getEncodedProtectedHeaders(); |
||
| 105 | $payload = $jws->getPayload(); |
||
| 106 | if (!$signature->hasProtectedHeader('b64') || true === $signature->getProtectedHeader('b64')) { |
||
| 107 | $encoded_payload = Base64Url::encode(is_string($payload) ? $payload : json_encode($payload)); |
||
| 108 | |||
| 109 | return sprintf('%s.%s', $encoded_protected_headers, $encoded_payload); |
||
| 110 | } |
||
| 111 | |||
| 112 | return sprintf('%s.%s', $encoded_protected_headers, $payload); |
||
| 113 | } |
||
| 114 | |||
| 115 | /** |
||
| 116 | * @param SignatureInterface $signature |
||
| 117 | * |
||
| 118 | * @throws \InvalidArgumentException |
||
| 119 | */ |
||
| 120 | private function checkB64HeaderAndCrit(SignatureInterface $signature) |
||
| 121 | { |
||
| 122 | if (!$signature->hasProtectedHeader('b64')) { |
||
| 123 | return; |
||
| 124 | } |
||
| 125 | |||
| 126 | if (false == $signature->hasProtectedHeader('crit')) { |
||
|
0 ignored issues
–
show
|
|||
| 127 | throw new \InvalidArgumentException('The protected header parameter "crit" is mandatory when protected header parameter "b64" is set.'); |
||
| 128 | } |
||
| 129 | $critHeader = $signature->getProtectedHeader('crit'); |
||
| 130 | if (!is_array($critHeader)) { |
||
| 131 | throw new \InvalidArgumentException('The protected header parameter "crit" must be an array.'); |
||
| 132 | } |
||
| 133 | if (!in_array('b64', $critHeader)) { |
||
| 134 | throw new \InvalidArgumentException('The protected header parameter "crit" must contain "b64" when protected header parameter "b64" is set.'); |
||
| 135 | } |
||
| 136 | } |
||
| 137 | |||
| 138 | /** |
||
| 139 | * @param array $complete_headers The complete headers |
||
| 140 | * @param JWKInterface $key |
||
| 141 | * |
||
| 142 | * @return SignatureAlgorithmInterface |
||
| 143 | */ |
||
| 144 | private function getSignatureAlgorithm(array $complete_headers, JWKInterface $key): SignatureAlgorithmInterface |
||
| 145 | { |
||
| 146 | if (!array_key_exists('alg', $complete_headers)) { |
||
| 147 | throw new \InvalidArgumentException('No "alg" parameter set in the headers.'); |
||
| 148 | } |
||
| 149 | if ($key->has('alg') && $key->get('alg') !== $complete_headers['alg']) { |
||
| 150 | throw new \InvalidArgumentException(sprintf('The algorithm "%s" is not allowed with this key.', $complete_headers['alg'])); |
||
| 151 | } |
||
| 152 | |||
| 153 | $algorithm = $this->jwaManager->getAlgorithm($complete_headers['alg']); |
||
| 154 | if (!$algorithm instanceof SignatureAlgorithmInterface) { |
||
| 155 | throw new \InvalidArgumentException(sprintf('The algorithm "%s" is not supported or is not a signature algorithm.', $complete_headers['alg'])); |
||
| 156 | } |
||
| 157 | |||
| 158 | return $algorithm; |
||
| 159 | } |
||
| 160 | } |
||
| 161 |
When comparing two booleans, it is generally considered safer to use the strict comparison operator.