[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/web-token/jwt-core/Util/ -> ECKey.php (source)

   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  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer