[ 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\Core\Util; 15 16 use Base64Url\Base64Url; 17 use function extension_loaded; 18 use InvalidArgumentException; 19 use function is_array; 20 use Jose\Component\Core\JWK; 21 use RuntimeException; 22 23 /** 24 * @internal 25 */ 26 class ECKey 27 { 28 public static function convertToPEM(JWK $jwk): string 29 { 30 if ($jwk->has('d')) { 31 return self::convertPrivateKeyToPEM($jwk); 32 } 33 34 return self::convertPublicKeyToPEM($jwk); 35 } 36 37 /** 38 * @throws InvalidArgumentException if the curve is not supported 39 */ 40 public static function convertPublicKeyToPEM(JWK $jwk): string 41 { 42 switch ($jwk->get('crv')) { 43 case 'P-256': 44 $der = self::p256PublicKey(); 45 46 break; 47 48 case 'secp256k1': 49 $der = self::p256KPublicKey(); 50 51 break; 52 53 case 'P-384': 54 $der = self::p384PublicKey(); 55 56 break; 57 58 case 'P-521': 59 $der = self::p521PublicKey(); 60 61 break; 62 63 default: 64 throw new InvalidArgumentException('Unsupported curve.'); 65 } 66 $der .= self::getKey($jwk); 67 $pem = '-----BEGIN PUBLIC KEY-----'.PHP_EOL; 68 $pem .= chunk_split(base64_encode($der), 64, PHP_EOL); 69 $pem .= '-----END PUBLIC KEY-----'.PHP_EOL; 70 71 return $pem; 72 } 73 74 /** 75 * @throws InvalidArgumentException if the curve is not supported 76 */ 77 public static function convertPrivateKeyToPEM(JWK $jwk): string 78 { 79 switch ($jwk->get('crv')) { 80 case 'P-256': 81 $der = self::p256PrivateKey($jwk); 82 83 break; 84 85 case 'secp256k1': 86 $der = self::p256KPrivateKey($jwk); 87 88 break; 89 90 case 'P-384': 91 $der = self::p384PrivateKey($jwk); 92 93 break; 94 95 case 'P-521': 96 $der = self::p521PrivateKey($jwk); 97 98 break; 99 100 default: 101 throw new InvalidArgumentException('Unsupported curve.'); 102 } 103 $der .= self::getKey($jwk); 104 $pem = '-----BEGIN EC PRIVATE KEY-----'.PHP_EOL; 105 $pem .= chunk_split(base64_encode($der), 64, PHP_EOL); 106 $pem .= '-----END EC PRIVATE KEY-----'.PHP_EOL; 107 108 return $pem; 109 } 110 111 /** 112 * Creates a EC key with the given curve and additional values. 113 * 114 * @param string $curve The curve 115 * @param array $values values to configure the key 116 */ 117 public static function createECKey(string $curve, array $values = []): JWK 118 { 119 $jwk = self::createECKeyUsingOpenSSL($curve); 120 $values = array_merge($values, $jwk); 121 122 return new JWK($values); 123 } 124 125 /** 126 * @throws InvalidArgumentException if the curve is not supported 127 */ 128 private static function getNistCurveSize(string $curve): int 129 { 130 switch ($curve) { 131 case 'P-256': 132 case 'secp256k1': 133 return 256; 134 135 case 'P-384': 136 return 384; 137 138 case 'P-521': 139 return 521; 140 141 default: 142 throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve)); 143 } 144 } 145 146 /** 147 * @throws RuntimeException if the extension OpenSSL is not available 148 * @throws RuntimeException if the key cannot be created 149 */ 150 private static function createECKeyUsingOpenSSL(string $curve): array 151 { 152 if (!extension_loaded('openssl')) { 153 throw new RuntimeException('Please install the OpenSSL extension'); 154 } 155 $key = openssl_pkey_new([ 156 'curve_name' => self::getOpensslCurveName($curve), 157 'private_key_type' => OPENSSL_KEYTYPE_EC, 158 ]); 159 if (false === $key) { 160 throw new RuntimeException('Unable to create the key'); 161 } 162 $result = openssl_pkey_export($key, $out); 163 if (false === $result) { 164 throw new RuntimeException('Unable to create the key'); 165 } 166 $res = openssl_pkey_get_private($out); 167 if (false === $res) { 168 throw new RuntimeException('Unable to create the key'); 169 } 170 $details = openssl_pkey_get_details($res); 171 if (false === $details) { 172 throw new InvalidArgumentException('Unable to get the key details'); 173 } 174 $nistCurveSize = self::getNistCurveSize($curve); 175 176 return [ 177 'kty' => 'EC', 178 'crv' => $curve, 179 'd' => Base64Url::encode(str_pad($details['ec']['d'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)), 180 'x' => Base64Url::encode(str_pad($details['ec']['x'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)), 181 'y' => Base64Url::encode(str_pad($details['ec']['y'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)), 182 ]; 183 } 184 185 /** 186 * @throws InvalidArgumentException if the curve is not supported 187 */ 188 private static function getOpensslCurveName(string $curve): string 189 { 190 switch ($curve) { 191 case 'P-256': 192 return 'prime256v1'; 193 194 case 'secp256k1': 195 return 'secp256k1'; 196 197 case 'P-384': 198 return 'secp384r1'; 199 200 case 'P-521': 201 return 'secp521r1'; 202 203 default: 204 throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve)); 205 } 206 } 207 208 private static function p256PublicKey(): string 209 { 210 return pack( 211 'H*', 212 '3059' // SEQUENCE, length 89 213 .'3013' // SEQUENCE, length 19 214 .'0607' // OID, length 7 215 .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key 216 .'0608' // OID, length 8 217 .'2a8648ce3d030107' // 1.2.840.10045.3.1.7 = P-256 Curve 218 .'0342' // BIT STRING, length 66 219 .'00' // prepend with NUL - pubkey will follow 220 ); 221 } 222 223 private static function p256KPublicKey(): string 224 { 225 return pack( 226 'H*', 227 '3056' // SEQUENCE, length 86 228 .'3010' // SEQUENCE, length 16 229 .'0607' // OID, length 7 230 .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key 231 .'0605' // OID, length 8 232 .'2B8104000A' // 1.3.132.0.10 secp256k1 233 .'0342' // BIT STRING, length 66 234 .'00' // prepend with NUL - pubkey will follow 235 ); 236 } 237 238 private static function p384PublicKey(): string 239 { 240 return pack( 241 'H*', 242 '3076' // SEQUENCE, length 118 243 .'3010' // SEQUENCE, length 16 244 .'0607' // OID, length 7 245 .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key 246 .'0605' // OID, length 5 247 .'2b81040022' // 1.3.132.0.34 = P-384 Curve 248 .'0362' // BIT STRING, length 98 249 .'00' // prepend with NUL - pubkey will follow 250 ); 251 } 252 253 private static function p521PublicKey(): string 254 { 255 return pack( 256 'H*', 257 '30819b' // SEQUENCE, length 154 258 .'3010' // SEQUENCE, length 16 259 .'0607' // OID, length 7 260 .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key 261 .'0605' // OID, length 5 262 .'2b81040023' // 1.3.132.0.35 = P-521 Curve 263 .'038186' // BIT STRING, length 134 264 .'00' // prepend with NUL - pubkey will follow 265 ); 266 } 267 268 private static function p256PrivateKey(JWK $jwk): string 269 { 270 $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 32, "\0", STR_PAD_LEFT)); 271 if (!is_array($d) || !isset($d[1])) { 272 throw new InvalidArgumentException('Unable to get the private key'); 273 } 274 275 return pack( 276 'H*', 277 '3077' // SEQUENCE, length 87+length($d)=32 278 .'020101' // INTEGER, 1 279 .'0420' // OCTET STRING, length($d) = 32 280 .$d[1] 281 .'a00a' // TAGGED OBJECT #0, length 10 282 .'0608' // OID, length 8 283 .'2a8648ce3d030107' // 1.3.132.0.34 = P-256 Curve 284 .'a144' // TAGGED OBJECT #1, length 68 285 .'0342' // BIT STRING, length 66 286 .'00' // prepend with NUL - pubkey will follow 287 ); 288 } 289 290 private static function p256KPrivateKey(JWK $jwk): string 291 { 292 $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 32, "\0", STR_PAD_LEFT)); 293 if (!is_array($d) || !isset($d[1])) { 294 throw new InvalidArgumentException('Unable to get the private key'); 295 } 296 297 return pack( 298 'H*', 299 '3074' // SEQUENCE, length 84+length($d)=32 300 .'020101' // INTEGER, 1 301 .'0420' // OCTET STRING, length($d) = 32 302 .$d[1] 303 .'a007' // TAGGED OBJECT #0, length 7 304 .'0605' // OID, length 5 305 .'2b8104000a' // 1.3.132.0.10 secp256k1 306 .'a144' // TAGGED OBJECT #1, length 68 307 .'0342' // BIT STRING, length 66 308 .'00' // prepend with NUL - pubkey will follow 309 ); 310 } 311 312 private static function p384PrivateKey(JWK $jwk): string 313 { 314 $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 48, "\0", STR_PAD_LEFT)); 315 if (!is_array($d) || !isset($d[1])) { 316 throw new InvalidArgumentException('Unable to get the private key'); 317 } 318 319 return pack( 320 'H*', 321 '3081a4' // SEQUENCE, length 116 + length($d)=48 322 .'020101' // INTEGER, 1 323 .'0430' // OCTET STRING, length($d) = 30 324 .$d[1] 325 .'a007' // TAGGED OBJECT #0, length 7 326 .'0605' // OID, length 5 327 .'2b81040022' // 1.3.132.0.34 = P-384 Curve 328 .'a164' // TAGGED OBJECT #1, length 100 329 .'0362' // BIT STRING, length 98 330 .'00' // prepend with NUL - pubkey will follow 331 ); 332 } 333 334 private static function p521PrivateKey(JWK $jwk): string 335 { 336 $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 66, "\0", STR_PAD_LEFT)); 337 if (!is_array($d) || !isset($d[1])) { 338 throw new InvalidArgumentException('Unable to get the private key'); 339 } 340 341 return pack( 342 'H*', 343 '3081dc' // SEQUENCE, length 154 + length($d)=66 344 .'020101' // INTEGER, 1 345 .'0442' // OCTET STRING, length(d) = 66 346 .$d[1] 347 .'a007' // TAGGED OBJECT #0, length 7 348 .'0605' // OID, length 5 349 .'2b81040023' // 1.3.132.0.35 = P-521 Curve 350 .'a18189' // TAGGED OBJECT #1, length 137 351 .'038186' // BIT STRING, length 134 352 .'00' // prepend with NUL - pubkey will follow 353 ); 354 } 355 356 private static function getKey(JWK $jwk): string 357 { 358 $nistCurveSize = self::getNistCurveSize($jwk->get('crv')); 359 $length = (int) ceil($nistCurveSize / 8); 360 361 return 362 "\04" 363 .str_pad(Base64Url::decode($jwk->get('x')), $length, "\0", STR_PAD_LEFT) 364 .str_pad(Base64Url::decode($jwk->get('y')), $length, "\0", STR_PAD_LEFT); 365 } 366 }
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 |