Beispiel #1
3
 /**
  * @group github602
  */
 public function testEmptyContextTag()
 {
     $asn1 = new ASN1();
     $decoded = $asn1->decodeBER("�");
     $this->assertInternalType('array', $decoded);
     $this->assertCount(0, $decoded[0]['content']);
 }
Beispiel #2
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 = '')
 {
     if (!is_string($key)) {
         return false;
     }
     $components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false];
     $key = parent::load($key, $password);
     if ($key === false) {
         return false;
     }
     $asn1 = new ASN1();
     $decoded = $asn1->decodeBER($key);
     if (empty($decoded)) {
         return false;
     }
     $key = $asn1->asn1map($decoded[0], RSAPrivateKey);
     if (is_array($key)) {
         $components += ['modulus' => $key['modulus'], 'publicExponent' => $key['publicExponent'], 'privateExponent' => $key['privateExponent'], 'primes' => [1 => $key['prime1'], $key['prime2']], 'exponents' => [1 => $key['exponent1'], $key['exponent2']], 'coefficients' => [2 => $key['coefficient']]];
         if ($key['version'] == 'multi') {
             foreach ($key['otherPrimeInfos'] as $primeInfo) {
                 $components['primes'][] = $primeInfo['prime'];
                 $components['exponents'][] = $primeInfo['exponent'];
                 $components['coefficients'][] = $primeInfo['coefficient'];
             }
         }
         return $components;
     }
     $key = $asn1->asn1map($decoded[0], RSAPublicKey);
     return is_array($key) ? $components + $key : false;
 }
Beispiel #3
0
 /**
  * {@inheritdoc}
  */
 protected function supportsKey($key)
 {
     if (false === parent::supportsKey($key)) {
         return false;
     }
     // openssl_sign with EC keys was introduced in this PHP release
     $minVersions = array('5.4' => '5.4.26', '5.5' => '5.5.10', '5.6' => '5.6.0');
     if (isset($minVersions[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION]) && version_compare(PHP_VERSION, $minVersions[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION], '<')) {
         return false;
     }
     $keyDetails = openssl_pkey_get_details($key);
     if (0 === preg_match('/-----BEGIN PUBLIC KEY-----([^-]+)-----END PUBLIC KEY-----/', $keyDetails['key'], $matches)) {
         return false;
     }
     $publicKey = trim($matches[1]);
     $asn1 = new ASN1();
     /*
      * http://tools.ietf.org/html/rfc3279#section-2.2.3
      * AlgorithmIdentifier ::= SEQUENCE {
      *     algorithm OBJECT IDENTIFIER,
      *     parameters ANY DEFINED BY algorithm OPTIONAL
      * }
      * For ECDSA Signature Algorithm:
      * algorithm: ansi-X9-62 => 1.2.840.10045.2.1
      * parameters: id-ecSigType => 1.2.840.10045.x.y.z
      *
      */
     $asnAlgorithmIdentifier = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('ansi-X9-62' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'id-ecSigType' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER)));
     /*
      * http://tools.ietf.org/html/rfc5280#section-4.1
      * SubjectPublicKeyInfo ::= SEQUENCE {
      *     algorithm AlgorithmIdentifier,
      *     subjectPublicKey BIT STRING
      * }
      */
     $asnSubjectPublicKeyInfo = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('algorithm' => $asnAlgorithmIdentifier, 'subjectPublicKey' => array('type' => ASN1::TYPE_BIT_STRING)));
     $decoded = $asn1->decodeBER(base64_decode($publicKey));
     $mappedDetails = $asn1->asn1map($decoded[0], $asnSubjectPublicKeyInfo);
     return isset($mappedDetails['algorithm']['id-ecSigType']) ? $this->getSupportedECDSACurve() === $mappedDetails['algorithm']['id-ecSigType'] : false;
 }
Beispiel #4
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 = '')
 {
     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;
 }
Beispiel #5
0
    /**
     * Compute a public key identifier.
     *
     * Although key identifiers may be set to any unique value, this function
     * computes key identifiers from public key according to the two
     * recommended methods (4.2.1.2 RFC 3280).
     * Highly polymorphic: try to accept all possible forms of key:
     * - Key object
     * - \phpseclib\File\X509 object with public or private key defined
     * - Certificate or CSR array
     * - \phpseclib\File\ASN1\Element object
     * - PEM or DER string
     *
     * @param mixed $key optional
     * @param int $method optional
     * @access public
     * @return string binary key identifier
     */
    function computeKeyIdentifier($key = null, $method = 1)
    {
        if (is_null($key)) {
            $key = $this;
        }

        switch (true) {
            case is_string($key):
                break;
            case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
                return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method);
            case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
                return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method);
            case !is_object($key):
                return false;
            case $key instanceof Element:
                // Assume the element is a bitstring-packed key.
                $asn1 = new ASN1();
                $decoded = $asn1->decodeBER($key->element);
                if (empty($decoded)) {
                    return false;
                }
                $raw = $asn1->asn1map($decoded[0], array('type' => ASN1::TYPE_BIT_STRING));
                if (empty($raw)) {
                    return false;
                }
                $raw = base64_decode($raw);
                // If the key is private, compute identifier from its corresponding public key.
                $key = new RSA();
                if (!$key->loadKey($raw)) {
                    return false;   // Not an unencrypted RSA key.
                }
                if ($key->getPrivateKey() !== false) {  // If private.
                    return $this->computeKeyIdentifier($key, $method);
                }
                $key = $raw;    // Is a public key.
                break;
            case $key instanceof X509:
                if (isset($key->publicKey)) {
                    return $this->computeKeyIdentifier($key->publicKey, $method);
                }
                if (isset($key->privateKey)) {
                    return $this->computeKeyIdentifier($key->privateKey, $method);
                }
                if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) {
                    return $this->computeKeyIdentifier($key->currentCert, $method);
                }
                return false;
            default: // Should be a key object (i.e.: \phpseclib\Crypt\RSA).
                $key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
                break;
        }

        // If in PEM format, convert to binary.
        $key = $this->_extractBER($key);

        // Now we have the key string: compute its sha-1 sum.
        $hash = new Hash('sha1');
        $hash = $hash->hash($key);

        if ($method == 2) {
            $hash = substr($hash, -8);
            $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
        }

        return $hash;
    }
Beispiel #6
0
 /**
  * Decodes and maps the BasicOCSPResponse part of the response.
  *
  * @return array
  */
 private function getResponseMapped()
 {
     $parser = new Asn1Parser();
     $responseBasicDecoded = $parser->decodeBER(base64_decode($this->response));
     return $parser->asn1map($responseBasicDecoded[0], $this->asn1->BasicOCSPResponse);
 }
Beispiel #7
0
 /**
  * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching)
  *
  * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5
  * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified.
  * This means that under rare conditions you can have a perfectly valid v1.5 signature
  * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends
  * that if you're going to validate these types of signatures you "should indicate
  * whether the underlying BER encoding is a DER encoding and hence whether the signature
  * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do
  * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of
  * RSA::PADDING_PKCS1... that means BER encoding was used.
  *
  * @access private
  * @param string $m
  * @param string $s
  * @return bool
  */
 function _rsassa_pkcs1_v1_5_relaxed_verify($m, $s)
 {
     // Length checking
     if (strlen($s) != $this->k) {
         return false;
     }
     // RSA verification
     $s = $this->_os2ip($s);
     $m2 = $this->_rsavp1($s);
     if ($m2 === false) {
         return false;
     }
     $em = $this->_i2osp($m2, $this->k);
     if ($em === false) {
         return false;
     }
     if ($this->_string_shift($em, 2) != "") {
         return false;
     }
     $em = ltrim($em, "ÿ");
     if ($this->_string_shift($em) != "") {
         return false;
     }
     $asn1 = new ASN1();
     $decoded = $asn1->decodeBER($em);
     if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) {
         return false;
     }
     $AlgorithmIdentifier = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'parameters' => array('type' => ASN1::TYPE_ANY, 'optional' => true)));
     $DigestInfo = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('digestAlgorithm' => $AlgorithmIdentifier, 'digest' => array('type' => ASN1::TYPE_OCTET_STRING)));
     $oids = array('1.2.840.113549.2.2' => 'md2', '1.2.840.113549.2.4' => 'md4', '1.2.840.113549.2.5' => 'md5', '1.3.14.3.2.26' => 'sha1', '2.16.840.1.101.3.4.2.1' => 'sha256', '2.16.840.1.101.3.4.2.2' => 'sha384', '2.16.840.1.101.3.4.2.3' => 'sha512');
     $asn1->loadOIDs($oids);
     $decoded = $asn1->asn1map($decoded[0], $DigestInfo);
     if (!isset($decoded) || $decoded === false) {
         return false;
     }
     if (!in_array($decoded['digestAlgorithm']['algorithm'], $oids)) {
         return false;
     }
     $hash = new Hash($decoded['digestAlgorithm']['algorithm']);
     $em = $hash->hash($m);
     $em2 = Base64::decode($decoded['digest']);
     return $this->_equals($em, $em2);
 }
Beispiel #8
0
 /**
  * @param $publicKey
  *
  * @return array|bool
  */
 protected static function loadPublicKey($publicKey)
 {
     $asn1 = new ASN1();
     $asnAlgorithmIdentifier = ['type' => ASN1::TYPE_SEQUENCE, 'children' => ['ansi-X9-62' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], 'id-ecSigType' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER]]];
     $asnSubjectPublicKeyInfo = ['type' => ASN1::TYPE_SEQUENCE, 'children' => ['algorithm' => $asnAlgorithmIdentifier, 'subjectPublicKey' => ['type' => ASN1::TYPE_BIT_STRING]]];
     $decoded = $asn1->decodeBER(base64_decode($publicKey));
     $mappedDetails = $asn1->asn1map($decoded[0], $asnSubjectPublicKeyInfo);
     if (null === $mappedDetails) {
         return false;
     }
     $details = Base64Url::decode($mappedDetails['subjectPublicKey']);
     if (!self::isAlgorithmSupported($mappedDetails['algorithm']['id-ecSigType'])) {
         return false;
     }
     if (substr($details, 0, 1) !== "") {
         return false;
     }
     if (substr($details, 1, 1) !== "") {
         return false;
     }
     $X = substr($details, 2, (strlen($details) - 2) / 2);
     $Y = substr($details, (strlen($details) - 2) / 2 + 2, (strlen($details) - 2) / 2);
     return ['x' => Base64Url::encode($X), 'y' => Base64Url::encode($Y)];
 }
Beispiel #9
0
 /**
  * Load a Certificate Revocation List
  *
  * @param String $crl
  *
  * @access public
  * @return Mixed
  */
 function loadCRL($crl)
 {
     if (is_array($crl) && isset($crl['tbsCertList'])) {
         $this->currentCert = $crl;
         unset($this->signatureSubject);
         return $crl;
     }
     $asn1 = new ASN1();
     $crl = $this->_extractBER($crl);
     $orig = $crl;
     if ($crl === false) {
         $this->currentCert = false;
         return false;
     }
     $asn1->loadOIDs($this->oids);
     $decoded = $asn1->decodeBER($crl);
     if (empty($decoded)) {
         $this->currentCert = false;
         return false;
     }
     $crl = $asn1->asn1map($decoded[0], $this->CertificateList);
     if (!isset($crl) || $crl === false) {
         $this->currentCert = false;
         return false;
     }
     $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
     $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
     $rclist =& $this->_subArray($crl, 'tbsCertList/revokedCertificates');
     if (is_array($rclist)) {
         foreach ($rclist as $i => $extension) {
             $this->_mapInExtensions($rclist, "{$i}/crlEntryExtensions", $asn1);
         }
     }
     $this->currentKeyIdentifier = null;
     $this->currentCert = $crl;
     return $crl;
 }