[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 declare(strict_types=1); 4 5 /* 6 * The MIT License (MIT) 7 * 8 * Copyright (c) 2014-2020 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\Signature; 15 16 use function array_key_exists; 17 use Base64Url\Base64Url; 18 use function count; 19 use function in_array; 20 use InvalidArgumentException; 21 use function is_array; 22 use Jose\Component\Core\Algorithm; 23 use Jose\Component\Core\AlgorithmManager; 24 use Jose\Component\Core\JWK; 25 use Jose\Component\Core\Util\JsonConverter; 26 use Jose\Component\Core\Util\KeyChecker; 27 use Jose\Component\Signature\Algorithm\MacAlgorithm; 28 use Jose\Component\Signature\Algorithm\SignatureAlgorithm; 29 use LogicException; 30 use RuntimeException; 31 32 class JWSBuilder 33 { 34 /** 35 * @var null|string 36 */ 37 protected $payload; 38 39 /** 40 * @var bool 41 */ 42 protected $isPayloadDetached; 43 44 /** 45 * @var array 46 */ 47 protected $signatures = []; 48 49 /** 50 * @var null|bool 51 */ 52 protected $isPayloadEncoded; 53 54 /** 55 * @var AlgorithmManager 56 */ 57 private $signatureAlgorithmManager; 58 59 public function __construct(AlgorithmManager $signatureAlgorithmManager) 60 { 61 $this->signatureAlgorithmManager = $signatureAlgorithmManager; 62 } 63 64 /** 65 * Returns the algorithm manager associated to the builder. 66 */ 67 public function getSignatureAlgorithmManager(): AlgorithmManager 68 { 69 return $this->signatureAlgorithmManager; 70 } 71 72 /** 73 * Reset the current data. 74 * 75 * @return JWSBuilder 76 */ 77 public function create(): self 78 { 79 $this->payload = null; 80 $this->isPayloadDetached = false; 81 $this->signatures = []; 82 $this->isPayloadEncoded = null; 83 84 return $this; 85 } 86 87 /** 88 * Set the payload. 89 * This method will return a new JWSBuilder object. 90 * 91 * @throws InvalidArgumentException if the payload is not UTF-8 encoded 92 * 93 * @return JWSBuilder 94 */ 95 public function withPayload(string $payload, bool $isPayloadDetached = false): self 96 { 97 if (false === mb_detect_encoding($payload, 'UTF-8', true)) { 98 throw new InvalidArgumentException('The payload must be encoded in UTF-8'); 99 } 100 $clone = clone $this; 101 $clone->payload = $payload; 102 $clone->isPayloadDetached = $isPayloadDetached; 103 104 return $clone; 105 } 106 107 /** 108 * Adds the information needed to compute the signature. 109 * This method will return a new JWSBuilder object. 110 * 111 * @throws InvalidArgumentException if the payload encoding is inconsistent 112 * 113 * @return JWSBuilder 114 */ 115 public function addSignature(JWK $signatureKey, array $protectedHeader, array $header = []): self 116 { 117 $this->checkB64AndCriticalHeader($protectedHeader); 118 $isPayloadEncoded = $this->checkIfPayloadIsEncoded($protectedHeader); 119 if (null === $this->isPayloadEncoded) { 120 $this->isPayloadEncoded = $isPayloadEncoded; 121 } elseif ($this->isPayloadEncoded !== $isPayloadEncoded) { 122 throw new InvalidArgumentException('Foreign payload encoding detected.'); 123 } 124 $this->checkDuplicatedHeaderParameters($protectedHeader, $header); 125 KeyChecker::checkKeyUsage($signatureKey, 'signature'); 126 $algorithm = $this->findSignatureAlgorithm($signatureKey, $protectedHeader, $header); 127 KeyChecker::checkKeyAlgorithm($signatureKey, $algorithm->name()); 128 $clone = clone $this; 129 $clone->signatures[] = [ 130 'signature_algorithm' => $algorithm, 131 'signature_key' => $signatureKey, 132 'protected_header' => $protectedHeader, 133 'header' => $header, 134 ]; 135 136 return $clone; 137 } 138 139 /** 140 * Computes all signatures and return the expected JWS object. 141 * 142 * @throws RuntimeException if the payload is not set 143 * @throws RuntimeException if no signature is defined 144 */ 145 public function build(): JWS 146 { 147 if (null === $this->payload) { 148 throw new RuntimeException('The payload is not set.'); 149 } 150 if (0 === count($this->signatures)) { 151 throw new RuntimeException('At least one signature must be set.'); 152 } 153 154 $encodedPayload = false === $this->isPayloadEncoded ? $this->payload : Base64Url::encode($this->payload); 155 $jws = new JWS($this->payload, $encodedPayload, $this->isPayloadDetached); 156 foreach ($this->signatures as $signature) { 157 /** @var MacAlgorithm|SignatureAlgorithm $algorithm */ 158 $algorithm = $signature['signature_algorithm']; 159 /** @var JWK $signatureKey */ 160 $signatureKey = $signature['signature_key']; 161 /** @var array $protectedHeader */ 162 $protectedHeader = $signature['protected_header']; 163 /** @var array $header */ 164 $header = $signature['header']; 165 $encodedProtectedHeader = 0 === count($protectedHeader) ? null : Base64Url::encode(JsonConverter::encode($protectedHeader)); 166 $input = sprintf('%s.%s', $encodedProtectedHeader, $encodedPayload); 167 if ($algorithm instanceof SignatureAlgorithm) { 168 $s = $algorithm->sign($signatureKey, $input); 169 } else { 170 $s = $algorithm->hash($signatureKey, $input); 171 } 172 $jws = $jws->addSignature($s, $protectedHeader, $encodedProtectedHeader, $header); 173 } 174 175 return $jws; 176 } 177 178 private function checkIfPayloadIsEncoded(array $protectedHeader): bool 179 { 180 return !array_key_exists('b64', $protectedHeader) || true === $protectedHeader['b64']; 181 } 182 183 /** 184 * @throws LogicException if the header parameter "crit" is missing, invalid or does not contain "b64" when "b64" is set 185 */ 186 private function checkB64AndCriticalHeader(array $protectedHeader): void 187 { 188 if (!array_key_exists('b64', $protectedHeader)) { 189 return; 190 } 191 if (!array_key_exists('crit', $protectedHeader)) { 192 throw new LogicException('The protected header parameter "crit" is mandatory when protected header parameter "b64" is set.'); 193 } 194 if (!is_array($protectedHeader['crit'])) { 195 throw new LogicException('The protected header parameter "crit" must be an array.'); 196 } 197 if (!in_array('b64', $protectedHeader['crit'], true)) { 198 throw new LogicException('The protected header parameter "crit" must contain "b64" when protected header parameter "b64" is set.'); 199 } 200 } 201 202 /** 203 * @throws InvalidArgumentException if the header parameter "alg" is missing or the algorithm is not allowed/not supported 204 * 205 * @return MacAlgorithm|SignatureAlgorithm 206 */ 207 private function findSignatureAlgorithm(JWK $key, array $protectedHeader, array $header): Algorithm 208 { 209 $completeHeader = array_merge($header, $protectedHeader); 210 if (!array_key_exists('alg', $completeHeader)) { 211 throw new InvalidArgumentException('No "alg" parameter set in the header.'); 212 } 213 if ($key->has('alg') && $key->get('alg') !== $completeHeader['alg']) { 214 throw new InvalidArgumentException(sprintf('The algorithm "%s" is not allowed with this key.', $completeHeader['alg'])); 215 } 216 217 $algorithm = $this->signatureAlgorithmManager->get($completeHeader['alg']); 218 if (!$algorithm instanceof SignatureAlgorithm && !$algorithm instanceof MacAlgorithm) { 219 throw new InvalidArgumentException(sprintf('The algorithm "%s" is not supported.', $completeHeader['alg'])); 220 } 221 222 return $algorithm; 223 } 224 225 /** 226 * @throws InvalidArgumentException if the header contains duplicated entries 227 */ 228 private function checkDuplicatedHeaderParameters(array $header1, array $header2): void 229 { 230 $inter = array_intersect_key($header1, $header2); 231 if (0 !== count($inter)) { 232 throw new InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', implode(', ', array_keys($inter)))); 233 } 234 } 235 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |