[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * PKCS#8 Formatted Key Handler 5 * 6 * PHP version 5 7 * 8 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) 9 * 10 * Processes keys with the following headers: 11 * 12 * -----BEGIN ENCRYPTED PRIVATE KEY----- 13 * -----BEGIN PRIVATE KEY----- 14 * -----BEGIN PUBLIC KEY----- 15 * 16 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 17 * is specific to private keys it's basically creating a DER-encoded wrapper 18 * for keys. This just extends that same concept to public keys (much like ssh-keygen) 19 * 20 * @author Jim Wigginton <terrafrost@php.net> 21 * @copyright 2015 Jim Wigginton 22 * @license http://www.opensource.org/licenses/mit-license.html MIT License 23 * @link http://phpseclib.sourceforge.net 24 */ 25 26 namespace phpseclib3\Crypt\Common\Formats\Keys; 27 28 use phpseclib3\Common\Functions\Strings; 29 use phpseclib3\Crypt\AES; 30 use phpseclib3\Crypt\DES; 31 use phpseclib3\Crypt\Random; 32 use phpseclib3\Crypt\RC2; 33 use phpseclib3\Crypt\RC4; 34 use phpseclib3\Crypt\TripleDES; 35 use phpseclib3\Exception\InsufficientSetupException; 36 use phpseclib3\Exception\UnsupportedAlgorithmException; 37 use phpseclib3\File\ASN1; 38 use phpseclib3\File\ASN1\Maps; 39 40 /** 41 * PKCS#8 Formatted Key Handler 42 * 43 * @author Jim Wigginton <terrafrost@php.net> 44 */ 45 abstract class PKCS8 extends PKCS 46 { 47 /** 48 * Default encryption algorithm 49 * 50 * @var string 51 */ 52 private static $defaultEncryptionAlgorithm = 'id-PBES2'; 53 54 /** 55 * Default encryption scheme 56 * 57 * Only used when defaultEncryptionAlgorithm is id-PBES2 58 * 59 * @var string 60 */ 61 private static $defaultEncryptionScheme = 'aes128-CBC-PAD'; 62 63 /** 64 * Default PRF 65 * 66 * Only used when defaultEncryptionAlgorithm is id-PBES2 67 * 68 * @var string 69 */ 70 private static $defaultPRF = 'id-hmacWithSHA256'; 71 72 /** 73 * Default Iteration Count 74 * 75 * @var int 76 */ 77 private static $defaultIterationCount = 2048; 78 79 /** 80 * OIDs loaded 81 * 82 * @var bool 83 */ 84 private static $oidsLoaded = false; 85 86 /** 87 * Sets the default encryption algorithm 88 * 89 * @param string $algo 90 */ 91 public static function setEncryptionAlgorithm($algo) 92 { 93 self::$defaultEncryptionAlgorithm = $algo; 94 } 95 96 /** 97 * Sets the default encryption algorithm for PBES2 98 * 99 * @param string $algo 100 */ 101 public static function setEncryptionScheme($algo) 102 { 103 self::$defaultEncryptionScheme = $algo; 104 } 105 106 /** 107 * Sets the iteration count 108 * 109 * @param int $count 110 */ 111 public static function setIterationCount($count) 112 { 113 self::$defaultIterationCount = $count; 114 } 115 116 /** 117 * Sets the PRF for PBES2 118 * 119 * @param string $algo 120 */ 121 public static function setPRF($algo) 122 { 123 self::$defaultPRF = $algo; 124 } 125 126 /** 127 * Returns a SymmetricKey object based on a PBES1 $algo 128 * 129 * @return \phpseclib3\Crypt\Common\SymmetricKey 130 * @param string $algo 131 */ 132 private static function getPBES1EncryptionObject($algo) 133 { 134 $algo = preg_match('#^pbeWith(?:MD2|MD5|SHA1|SHA)And(.*?)-CBC$#', $algo, $matches) ? 135 $matches[1] : 136 substr($algo, 13); // strlen('pbeWithSHAAnd') == 13 137 138 switch ($algo) { 139 case 'DES': 140 $cipher = new DES('cbc'); 141 break; 142 case 'RC2': 143 $cipher = new RC2('cbc'); 144 $cipher->setKeyLength(64); 145 break; 146 case '3-KeyTripleDES': 147 $cipher = new TripleDES('cbc'); 148 break; 149 case '2-KeyTripleDES': 150 $cipher = new TripleDES('cbc'); 151 $cipher->setKeyLength(128); 152 break; 153 case '128BitRC2': 154 $cipher = new RC2('cbc'); 155 $cipher->setKeyLength(128); 156 break; 157 case '40BitRC2': 158 $cipher = new RC2('cbc'); 159 $cipher->setKeyLength(40); 160 break; 161 case '128BitRC4': 162 $cipher = new RC4(); 163 $cipher->setKeyLength(128); 164 break; 165 case '40BitRC4': 166 $cipher = new RC4(); 167 $cipher->setKeyLength(40); 168 break; 169 default: 170 throw new UnsupportedAlgorithmException("$algo is not a supported algorithm"); 171 } 172 173 return $cipher; 174 } 175 176 /** 177 * Returns a hash based on a PBES1 $algo 178 * 179 * @return string 180 * @param string $algo 181 */ 182 private static function getPBES1Hash($algo) 183 { 184 if (preg_match('#^pbeWith(MD2|MD5|SHA1|SHA)And.*?-CBC$#', $algo, $matches)) { 185 return $matches[1] == 'SHA' ? 'sha1' : $matches[1]; 186 } 187 188 return 'sha1'; 189 } 190 191 /** 192 * Returns a KDF baesd on a PBES1 $algo 193 * 194 * @return string 195 * @param string $algo 196 */ 197 private static function getPBES1KDF($algo) 198 { 199 switch ($algo) { 200 case 'pbeWithMD2AndDES-CBC': 201 case 'pbeWithMD2AndRC2-CBC': 202 case 'pbeWithMD5AndDES-CBC': 203 case 'pbeWithMD5AndRC2-CBC': 204 case 'pbeWithSHA1AndDES-CBC': 205 case 'pbeWithSHA1AndRC2-CBC': 206 return 'pbkdf1'; 207 } 208 209 return 'pkcs12'; 210 } 211 212 /** 213 * Returns a SymmetricKey object baesd on a PBES2 $algo 214 * 215 * @return SymmetricKey 216 * @param string $algo 217 */ 218 private static function getPBES2EncryptionObject($algo) 219 { 220 switch ($algo) { 221 case 'desCBC': 222 $cipher = new DES('cbc'); 223 break; 224 case 'des-EDE3-CBC': 225 $cipher = new TripleDES('cbc'); 226 break; 227 case 'rc2CBC': 228 $cipher = new RC2('cbc'); 229 // in theory this can be changed 230 $cipher->setKeyLength(128); 231 break; 232 case 'rc5-CBC-PAD': 233 throw new UnsupportedAlgorithmException('rc5-CBC-PAD is not supported for PBES2 PKCS#8 keys'); 234 case 'aes128-CBC-PAD': 235 case 'aes192-CBC-PAD': 236 case 'aes256-CBC-PAD': 237 $cipher = new AES('cbc'); 238 $cipher->setKeyLength(substr($algo, 3, 3)); 239 break; 240 default: 241 throw new UnsupportedAlgorithmException("$algo is not supported"); 242 } 243 244 return $cipher; 245 } 246 247 /** 248 * Initialize static variables 249 * 250 */ 251 private static function initialize_static_variables() 252 { 253 if (!isset(static::$childOIDsLoaded)) { 254 throw new InsufficientSetupException('This class should not be called directly'); 255 } 256 257 if (!static::$childOIDsLoaded) { 258 ASN1::loadOIDs(is_array(static::OID_NAME) ? 259 array_combine(static::OID_NAME, static::OID_VALUE) : 260 [static::OID_NAME => static::OID_VALUE]); 261 static::$childOIDsLoaded = true; 262 } 263 if (!self::$oidsLoaded) { 264 // from https://tools.ietf.org/html/rfc2898 265 ASN1::loadOIDs([ 266 // PBES1 encryption schemes 267 'pbeWithMD2AndDES-CBC' => '1.2.840.113549.1.5.1', 268 'pbeWithMD2AndRC2-CBC' => '1.2.840.113549.1.5.4', 269 'pbeWithMD5AndDES-CBC' => '1.2.840.113549.1.5.3', 270 'pbeWithMD5AndRC2-CBC' => '1.2.840.113549.1.5.6', 271 'pbeWithSHA1AndDES-CBC' => '1.2.840.113549.1.5.10', 272 'pbeWithSHA1AndRC2-CBC' => '1.2.840.113549.1.5.11', 273 274 // from PKCS#12: 275 // https://tools.ietf.org/html/rfc7292 276 'pbeWithSHAAnd128BitRC4' => '1.2.840.113549.1.12.1.1', 277 'pbeWithSHAAnd40BitRC4' => '1.2.840.113549.1.12.1.2', 278 'pbeWithSHAAnd3-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.3', 279 'pbeWithSHAAnd2-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.4', 280 'pbeWithSHAAnd128BitRC2-CBC' => '1.2.840.113549.1.12.1.5', 281 'pbeWithSHAAnd40BitRC2-CBC' => '1.2.840.113549.1.12.1.6', 282 283 'id-PBKDF2' => '1.2.840.113549.1.5.12', 284 'id-PBES2' => '1.2.840.113549.1.5.13', 285 'id-PBMAC1' => '1.2.840.113549.1.5.14', 286 287 // from PKCS#5 v2.1: 288 // http://www.rsa.com/rsalabs/pkcs/files/h11302-wp-pkcs5v2-1-password-based-cryptography-standard.pdf 289 'id-hmacWithSHA1' => '1.2.840.113549.2.7', 290 'id-hmacWithSHA224' => '1.2.840.113549.2.8', 291 'id-hmacWithSHA256' => '1.2.840.113549.2.9', 292 'id-hmacWithSHA384' => '1.2.840.113549.2.10', 293 'id-hmacWithSHA512' => '1.2.840.113549.2.11', 294 'id-hmacWithSHA512-224' => '1.2.840.113549.2.12', 295 'id-hmacWithSHA512-256' => '1.2.840.113549.2.13', 296 297 'desCBC' => '1.3.14.3.2.7', 298 'des-EDE3-CBC' => '1.2.840.113549.3.7', 299 'rc2CBC' => '1.2.840.113549.3.2', 300 'rc5-CBC-PAD' => '1.2.840.113549.3.9', 301 302 'aes128-CBC-PAD' => '2.16.840.1.101.3.4.1.2', 303 'aes192-CBC-PAD' => '2.16.840.1.101.3.4.1.22', 304 'aes256-CBC-PAD' => '2.16.840.1.101.3.4.1.42' 305 ]); 306 self::$oidsLoaded = true; 307 } 308 } 309 310 /** 311 * Break a public or private key down into its constituent components 312 * 313 * @param string $key 314 * @param string $password optional 315 * @return array 316 */ 317 protected static function load($key, $password = '') 318 { 319 if (!Strings::is_stringable($key)) { 320 throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); 321 } 322 323 $isPublic = strpos($key, 'PUBLIC') !== false; 324 $isPrivate = strpos($key, 'PRIVATE') !== false; 325 326 $decoded = self::preParse($key); 327 328 $meta = []; 329 330 $decrypted = ASN1::asn1map($decoded[0], Maps\EncryptedPrivateKeyInfo::MAP); 331 if (strlen($password) && is_array($decrypted)) { 332 $algorithm = $decrypted['encryptionAlgorithm']['algorithm']; 333 switch ($algorithm) { 334 // PBES1 335 case 'pbeWithMD2AndDES-CBC': 336 case 'pbeWithMD2AndRC2-CBC': 337 case 'pbeWithMD5AndDES-CBC': 338 case 'pbeWithMD5AndRC2-CBC': 339 case 'pbeWithSHA1AndDES-CBC': 340 case 'pbeWithSHA1AndRC2-CBC': 341 case 'pbeWithSHAAnd3-KeyTripleDES-CBC': 342 case 'pbeWithSHAAnd2-KeyTripleDES-CBC': 343 case 'pbeWithSHAAnd128BitRC2-CBC': 344 case 'pbeWithSHAAnd40BitRC2-CBC': 345 case 'pbeWithSHAAnd128BitRC4': 346 case 'pbeWithSHAAnd40BitRC4': 347 $cipher = self::getPBES1EncryptionObject($algorithm); 348 $hash = self::getPBES1Hash($algorithm); 349 $kdf = self::getPBES1KDF($algorithm); 350 351 $meta['meta']['algorithm'] = $algorithm; 352 353 $temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); 354 if (!$temp) { 355 throw new \RuntimeException('Unable to decode BER'); 356 } 357 extract(ASN1::asn1map($temp[0], Maps\PBEParameter::MAP)); 358 $iterationCount = (int) $iterationCount->toString(); 359 $cipher->setPassword($password, $kdf, $hash, $salt, $iterationCount); 360 $key = $cipher->decrypt($decrypted['encryptedData']); 361 $decoded = ASN1::decodeBER($key); 362 if (!$decoded) { 363 throw new \RuntimeException('Unable to decode BER 2'); 364 } 365 366 break; 367 case 'id-PBES2': 368 $meta['meta']['algorithm'] = $algorithm; 369 370 $temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); 371 if (!$temp) { 372 throw new \RuntimeException('Unable to decode BER'); 373 } 374 $temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP); 375 extract($temp); 376 377 $cipher = self::getPBES2EncryptionObject($encryptionScheme['algorithm']); 378 $meta['meta']['cipher'] = $encryptionScheme['algorithm']; 379 380 $temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); 381 if (!$temp) { 382 throw new \RuntimeException('Unable to decode BER'); 383 } 384 $temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP); 385 extract($temp); 386 387 if (!$cipher instanceof RC2) { 388 $cipher->setIV($encryptionScheme['parameters']['octetString']); 389 } else { 390 $temp = ASN1::decodeBER($encryptionScheme['parameters']); 391 if (!$temp) { 392 throw new \RuntimeException('Unable to decode BER'); 393 } 394 extract(ASN1::asn1map($temp[0], Maps\RC2CBCParameter::MAP)); 395 $effectiveKeyLength = (int) $rc2ParametersVersion->toString(); 396 switch ($effectiveKeyLength) { 397 case 160: 398 $effectiveKeyLength = 40; 399 break; 400 case 120: 401 $effectiveKeyLength = 64; 402 break; 403 case 58: 404 $effectiveKeyLength = 128; 405 break; 406 //default: // should be >= 256 407 } 408 $cipher->setIV($iv); 409 $cipher->setKeyLength($effectiveKeyLength); 410 } 411 412 $meta['meta']['keyDerivationFunc'] = $keyDerivationFunc['algorithm']; 413 switch ($keyDerivationFunc['algorithm']) { 414 case 'id-PBKDF2': 415 $temp = ASN1::decodeBER($keyDerivationFunc['parameters']); 416 if (!$temp) { 417 throw new \RuntimeException('Unable to decode BER'); 418 } 419 $prf = ['algorithm' => 'id-hmacWithSHA1']; 420 $params = ASN1::asn1map($temp[0], Maps\PBKDF2params::MAP); 421 extract($params); 422 $meta['meta']['prf'] = $prf['algorithm']; 423 $hash = str_replace('-', '/', substr($prf['algorithm'], 11)); 424 $params = [ 425 $password, 426 'pbkdf2', 427 $hash, 428 $salt, 429 (int) $iterationCount->toString() 430 ]; 431 if (isset($keyLength)) { 432 $params[] = (int) $keyLength->toString(); 433 } 434 $cipher->setPassword(...$params); 435 $key = $cipher->decrypt($decrypted['encryptedData']); 436 $decoded = ASN1::decodeBER($key); 437 if (!$decoded) { 438 throw new \RuntimeException('Unable to decode BER 3'); 439 } 440 break; 441 default: 442 throw new UnsupportedAlgorithmException('Only PBKDF2 is supported for PBES2 PKCS#8 keys'); 443 } 444 break; 445 case 'id-PBMAC1': 446 //$temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); 447 //$value = ASN1::asn1map($temp[0], Maps\PBMAC1params::MAP); 448 // since i can't find any implementation that does PBMAC1 it is unsupported 449 throw new UnsupportedAlgorithmException('Only PBES1 and PBES2 PKCS#8 keys are supported.'); 450 // at this point we'll assume that the key conforms to PublicKeyInfo 451 } 452 } 453 454 $private = ASN1::asn1map($decoded[0], Maps\OneAsymmetricKey::MAP); 455 if (is_array($private)) { 456 if ($isPublic) { 457 throw new \UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key'); 458 } 459 460 if (isset($private['privateKeyAlgorithm']['parameters']) && !$private['privateKeyAlgorithm']['parameters'] instanceof ASN1\Element && isset($decoded[0]['content'][1]['content'][1])) { 461 $temp = $decoded[0]['content'][1]['content'][1]; 462 $private['privateKeyAlgorithm']['parameters'] = new ASN1\Element(substr($key, $temp['start'], $temp['length'])); 463 } 464 if (is_array(static::OID_NAME)) { 465 if (!in_array($private['privateKeyAlgorithm']['algorithm'], static::OID_NAME)) { 466 throw new UnsupportedAlgorithmException($private['privateKeyAlgorithm']['algorithm'] . ' is not a supported key type'); 467 } 468 } else { 469 if ($private['privateKeyAlgorithm']['algorithm'] != static::OID_NAME) { 470 throw new UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $private['privateKeyAlgorithm']['algorithm'] . ' key'); 471 } 472 } 473 if (isset($private['publicKey'])) { 474 if ($private['publicKey'][0] != "\0") { 475 throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($private['publicKey'][0])); 476 } 477 $private['publicKey'] = substr($private['publicKey'], 1); 478 } 479 return $private + $meta; 480 } 481 482 // EncryptedPrivateKeyInfo and PublicKeyInfo have largely identical "signatures". the only difference 483 // is that the former has an octet string and the later has a bit string. the first byte of a bit 484 // string represents the number of bits in the last byte that are to be ignored but, currently, 485 // bit strings wanting a non-zero amount of bits trimmed are not supported 486 $public = ASN1::asn1map($decoded[0], Maps\PublicKeyInfo::MAP); 487 488 if (is_array($public)) { 489 if ($isPrivate) { 490 throw new \UnexpectedValueException('Human readable string claims private key but DER encoded string claims public key'); 491 } 492 493 if ($public['publicKey'][0] != "\0") { 494 throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($public['publicKey'][0])); 495 } 496 if (is_array(static::OID_NAME)) { 497 if (!in_array($public['publicKeyAlgorithm']['algorithm'], static::OID_NAME)) { 498 throw new UnsupportedAlgorithmException($public['publicKeyAlgorithm']['algorithm'] . ' is not a supported key type'); 499 } 500 } else { 501 if ($public['publicKeyAlgorithm']['algorithm'] != static::OID_NAME) { 502 throw new UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $public['publicKeyAlgorithm']['algorithm'] . ' key'); 503 } 504 } 505 if (isset($public['publicKeyAlgorithm']['parameters']) && !$public['publicKeyAlgorithm']['parameters'] instanceof ASN1\Element && isset($decoded[0]['content'][0]['content'][1])) { 506 $temp = $decoded[0]['content'][0]['content'][1]; 507 $public['publicKeyAlgorithm']['parameters'] = new ASN1\Element(substr($key, $temp['start'], $temp['length'])); 508 } 509 $public['publicKey'] = substr($public['publicKey'], 1); 510 return $public; 511 } 512 513 throw new \RuntimeException('Unable to parse using either OneAsymmetricKey or PublicKeyInfo ASN1 maps'); 514 } 515 516 /** 517 * Wrap a private key appropriately 518 * 519 * @param string $key 520 * @param string $attr 521 * @param mixed $params 522 * @param string $password 523 * @param string $oid optional 524 * @param string $publicKey optional 525 * @param array $options optional 526 * @return string 527 */ 528 protected static function wrapPrivateKey($key, $attr, $params, $password, $oid = null, $publicKey = '', array $options = []) 529 { 530 self::initialize_static_variables(); 531 532 $key = [ 533 'version' => 'v1', 534 'privateKeyAlgorithm' => [ 535 'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid 536 ], 537 'privateKey' => $key 538 ]; 539 if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') { 540 $key['privateKeyAlgorithm']['parameters'] = $params; 541 } 542 if (!empty($attr)) { 543 $key['attributes'] = $attr; 544 } 545 if (!empty($publicKey)) { 546 $key['version'] = 'v2'; 547 $key['publicKey'] = $publicKey; 548 } 549 $key = ASN1::encodeDER($key, Maps\OneAsymmetricKey::MAP); 550 if (!empty($password) && is_string($password)) { 551 $salt = Random::string(8); 552 553 $iterationCount = isset($options['iterationCount']) ? $options['iterationCount'] : self::$defaultIterationCount; 554 $encryptionAlgorithm = isset($options['encryptionAlgorithm']) ? $options['encryptionAlgorithm'] : self::$defaultEncryptionAlgorithm; 555 $encryptionScheme = isset($options['encryptionScheme']) ? $options['encryptionScheme'] : self::$defaultEncryptionScheme; 556 $prf = isset($options['PRF']) ? $options['PRF'] : self::$defaultPRF; 557 558 if ($encryptionAlgorithm == 'id-PBES2') { 559 $crypto = self::getPBES2EncryptionObject($encryptionScheme); 560 $hash = str_replace('-', '/', substr($prf, 11)); 561 $kdf = 'pbkdf2'; 562 $iv = Random::string($crypto->getBlockLength() >> 3); 563 564 $PBKDF2params = [ 565 'salt' => $salt, 566 'iterationCount' => $iterationCount, 567 'prf' => ['algorithm' => $prf, 'parameters' => null] 568 ]; 569 $PBKDF2params = ASN1::encodeDER($PBKDF2params, Maps\PBKDF2params::MAP); 570 571 if (!$crypto instanceof RC2) { 572 $params = ['octetString' => $iv]; 573 } else { 574 $params = [ 575 'rc2ParametersVersion' => 58, 576 'iv' => $iv 577 ]; 578 $params = ASN1::encodeDER($params, Maps\RC2CBCParameter::MAP); 579 $params = new ASN1\Element($params); 580 } 581 582 $params = [ 583 'keyDerivationFunc' => [ 584 'algorithm' => 'id-PBKDF2', 585 'parameters' => new ASN1\Element($PBKDF2params) 586 ], 587 'encryptionScheme' => [ 588 'algorithm' => $encryptionScheme, 589 'parameters' => $params 590 ] 591 ]; 592 $params = ASN1::encodeDER($params, Maps\PBES2params::MAP); 593 594 $crypto->setIV($iv); 595 } else { 596 $crypto = self::getPBES1EncryptionObject($encryptionAlgorithm); 597 $hash = self::getPBES1Hash($encryptionAlgorithm); 598 $kdf = self::getPBES1KDF($encryptionAlgorithm); 599 600 $params = [ 601 'salt' => $salt, 602 'iterationCount' => $iterationCount 603 ]; 604 $params = ASN1::encodeDER($params, Maps\PBEParameter::MAP); 605 } 606 $crypto->setPassword($password, $kdf, $hash, $salt, $iterationCount); 607 $key = $crypto->encrypt($key); 608 609 $key = [ 610 'encryptionAlgorithm' => [ 611 'algorithm' => $encryptionAlgorithm, 612 'parameters' => new ASN1\Element($params) 613 ], 614 'encryptedData' => $key 615 ]; 616 617 $key = ASN1::encodeDER($key, Maps\EncryptedPrivateKeyInfo::MAP); 618 619 return "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . 620 chunk_split(Strings::base64_encode($key), 64) . 621 "-----END ENCRYPTED PRIVATE KEY-----"; 622 } 623 624 return "-----BEGIN PRIVATE KEY-----\r\n" . 625 chunk_split(Strings::base64_encode($key), 64) . 626 "-----END PRIVATE KEY-----"; 627 } 628 629 /** 630 * Wrap a public key appropriately 631 * 632 * @param string $key 633 * @param mixed $params 634 * @param string $oid 635 * @return string 636 */ 637 protected static function wrapPublicKey($key, $params, $oid = null) 638 { 639 self::initialize_static_variables(); 640 641 $key = [ 642 'publicKeyAlgorithm' => [ 643 'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid 644 ], 645 'publicKey' => "\0" . $key 646 ]; 647 648 if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') { 649 $key['publicKeyAlgorithm']['parameters'] = $params; 650 } 651 652 $key = ASN1::encodeDER($key, Maps\PublicKeyInfo::MAP); 653 654 return "-----BEGIN PUBLIC KEY-----\r\n" . 655 chunk_split(Strings::base64_encode($key), 64) . 656 "-----END PUBLIC KEY-----"; 657 } 658 659 /** 660 * Perform some preliminary parsing of the key 661 * 662 * @param string $key 663 * @return array 664 */ 665 private static function preParse(&$key) 666 { 667 self::initialize_static_variables(); 668 669 if (self::$format != self::MODE_DER) { 670 $decoded = ASN1::extractBER($key); 671 if ($decoded !== false) { 672 $key = $decoded; 673 } elseif (self::$format == self::MODE_PEM) { 674 throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text'); 675 } 676 } 677 678 $decoded = ASN1::decodeBER($key); 679 if (!$decoded) { 680 throw new \RuntimeException('Unable to decode BER'); 681 } 682 683 return $decoded; 684 } 685 686 /** 687 * Returns the encryption parameters used by the key 688 * 689 * @param string $key 690 * @return array 691 */ 692 public static function extractEncryptionAlgorithm($key) 693 { 694 if (!Strings::is_stringable($key)) { 695 throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); 696 } 697 698 $decoded = self::preParse($key); 699 700 $r = ASN1::asn1map($decoded[0], ASN1\Maps\EncryptedPrivateKeyInfo::MAP); 701 if (!is_array($r)) { 702 throw new \RuntimeException('Unable to parse using EncryptedPrivateKeyInfo map'); 703 } 704 705 if ($r['encryptionAlgorithm']['algorithm'] == 'id-PBES2') { 706 $decoded = ASN1::decodeBER($r['encryptionAlgorithm']['parameters']->element); 707 if (!$decoded) { 708 throw new \RuntimeException('Unable to decode BER'); 709 } 710 $r['encryptionAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], ASN1\Maps\PBES2params::MAP); 711 712 $kdf = &$r['encryptionAlgorithm']['parameters']['keyDerivationFunc']; 713 switch ($kdf['algorithm']) { 714 case 'id-PBKDF2': 715 $decoded = ASN1::decodeBER($kdf['parameters']->element); 716 if (!$decoded) { 717 throw new \RuntimeException('Unable to decode BER'); 718 } 719 $kdf['parameters'] = ASN1::asn1map($decoded[0], Maps\PBKDF2params::MAP); 720 } 721 } 722 723 return $r['encryptionAlgorithm']; 724 } 725 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body