phpseclib\Crypt\Common\PKCS8::load PHP Method

load() static public method

Break a public or private key down into its constituent components
static public load ( string $key, string $password = '' ) : array
$key string
$password string optional
return array
    static function load($key, $password = '')
    {
        if (!is_string($key)) {
            return false;
        }
        if (self::$format != self::MODE_DER) {
            $decoded = ASN1::extractBER($key);
            if ($decoded !== false) {
                $key = $decoded;
            } elseif (self::$format == self::MODE_PEM) {
                return false;
            }
        }
        $asn1 = new ASN1();
        $decoded = $asn1->decodeBER($key);
        if (empty($decoded)) {
            return false;
        }
        $meta = [];
        $asn1->loadOIDs(oids);
        $decrypted = $asn1->asn1map($decoded[0], EncryptedPrivateKeyInfo);
        if (strlen($password) && is_array($decrypted)) {
            $algorithm = $decrypted['encryptionAlgorithm']['algorithm'];
            switch ($algorithm) {
                // PBES1
                case 'pbeWithMD2AndDES-CBC':
                case 'pbeWithMD2AndRC2-CBC':
                case 'pbeWithMD5AndDES-CBC':
                case 'pbeWithMD5AndRC2-CBC':
                case 'pbeWithSHA1AndDES-CBC':
                case 'pbeWithSHA1AndRC2-CBC':
                case 'pbeWithSHAAnd3-KeyTripleDES-CBC':
                case 'pbeWithSHAAnd2-KeyTripleDES-CBC':
                case 'pbeWithSHAAnd128BitRC2-CBC':
                case 'pbeWithSHAAnd40BitRC2-CBC':
                case 'pbeWithSHAAnd128BitRC4':
                case 'pbeWithSHAAnd40BitRC4':
                    $cipher = self::getPBES1EncryptionObject($algorithm);
                    $hash = self::getPBES1Hash($algorithm);
                    $kdf = self::getPBES1KDF($algorithm);
                    $meta['meta']['algorithm'] = $algorithm;
                    $temp = $asn1->decodeBER($decrypted['encryptionAlgorithm']['parameters']);
                    extract($asn1->asn1map($temp[0], PBEParameter));
                    $iterationCount = (int) $iterationCount->toString();
                    $cipher->setPassword($password, $kdf, $hash, Base64::decode($salt), $iterationCount);
                    $key = $cipher->decrypt(Base64::decode($decrypted['encryptedData']));
                    $decoded = $asn1->decodeBER($key);
                    if (empty($decoded)) {
                        return false;
                    }
                    break;
                case 'id-PBES2':
                    $meta['meta']['algorithm'] = $algorithm;
                    $temp = $asn1->decodeBER($decrypted['encryptionAlgorithm']['parameters']);
                    $temp = $asn1->asn1map($temp[0], PBES2params);
                    extract($temp);
                    $cipher = self::getPBES2EncryptionObject($encryptionScheme['algorithm']);
                    $meta['meta']['cipher'] = $encryptionScheme['algorithm'];
                    $temp = $asn1->decodeBER($decrypted['encryptionAlgorithm']['parameters']);
                    $temp = $asn1->asn1map($temp[0], PBES2params);
                    extract($temp);
                    if (!$cipher instanceof RC2) {
                        $cipher->setIV(Base64::decode($encryptionScheme['parameters']['octetString']));
                    } else {
                        $temp = $asn1->decodeBER($encryptionScheme['parameters']);
                        extract($asn1->asn1map($temp[0], RC2CBCParameter));
                        $effectiveKeyLength = (int) $rc2ParametersVersion->toString();
                        switch ($effectiveKeyLength) {
                            case 160:
                                $effectiveKeyLength = 40;
                                break;
                            case 120:
                                $effectiveKeyLength = 64;
                                break;
                            case 58:
                                $effectiveKeyLength = 128;
                                break;
                                //default: // should be >= 256
                        }
                        $cipher->setIV(Base64::decode($iv));
                        $cipher->setKeyLength($effectiveKeyLength);
                    }
                    $meta['meta']['keyDerivationFunc'] = $keyDerivationFunc['algorithm'];
                    switch ($keyDerivationFunc['algorithm']) {
                        case 'id-PBKDF2':
                            $temp = $asn1->decodeBER($keyDerivationFunc['parameters']);
                            $prf = ['algorithm' => 'id-hmacWithSHA1'];
                            $params = $asn1->asn1map($temp[0], PBKDF2params);
                            extract($params);
                            $meta['meta']['prf'] = $prf['algorithm'];
                            $hash = str_replace('-', '/', substr($prf['algorithm'], 11));
                            $params = [$password, 'pbkdf2', $hash, Base64::decode($salt), (int) $iterationCount->toString()];
                            if (isset($keyLength)) {
                                $params[] = (int) $keyLength->toString();
                            }
                            call_user_func_array([$cipher, 'setPassword'], $params);
                            $key = $cipher->decrypt(Base64::decode($decrypted['encryptedData']));
                            $decoded = $asn1->decodeBER($key);
                            if (empty($decoded)) {
                                return false;
                            }
                            break;
                        default:
                            throw new UnsupportedAlgorithmException('Only PBKDF2 is supported for PBES2 PKCS#8 keys');
                    }
                    break;
                case 'id-PBMAC1':
                    //$temp = $asn1->decodeBER($decrypted['encryptionAlgorithm']['parameters']);
                    //$value = $asn1->asn1map($temp[0], PBMAC1params);
                    // since i can't find any implementation that does PBMAC1 it is unsupported
                    throw new UnsupportedAlgorithmException('Only PBES1 and PBES2 PKCS#8 keys are supported.');
                    // at this point we'll assume that the key conforms to PublicKeyInfo
            }
        }
        $private = $asn1->asn1map($decoded[0], PrivateKeyInfo);
        if (is_array($private)) {
            return $private + $meta;
        }
        // EncryptedPrivateKeyInfo and PublicKeyInfo have largely identical "signatures". the only difference
        // is that the former has an octet string and the later has a bit string. the first byte of a bit
        // string represents the number of bits in the last byte that are to be ignored but, currently,
        // bit strings wanting a non-zero amount of bits trimmed are not supported
        $public = $asn1->asn1map($decoded[0], PublicKeyInfo);
        if (is_array($public)) {
            $public['publicKey'] = base64_decode($public['publicKey']);
            if ($public['publicKey'][0] != "") {
                return false;
            }
            $public['publicKey'] = base64_encode(substr($public['publicKey'], 1));
            return $public;
        }
        return false;
    }

Usage Example

Example #1
0
 /**
  * Break a public or private key down into its constituent components
  *
  * @access public
  * @param string $key
  * @param string $password optional
  * @return array
  */
 static function load($key, $password = '')
 {
     $components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false];
     $key = parent::load($key, $password);
     if ($key === false) {
         return false;
     }
     $type = isset($key['privateKey']) ? 'private' : 'public';
     if ($key[$type . 'KeyAlgorithm']['algorithm'] != '1.2.840.113549.1.1.1') {
         return false;
     }
     $result = $components + PKCS1::load($key[$type . 'Key']);
     if (isset($key['meta'])) {
         $result['meta'] = $key['meta'];
     }
     return $result;
 }