Beispiel #1
0
 /**
  * Convert a public key to the appropriate format
  *
  * @access public
  * @param \phpseclib\Math\BigInteger $n
  * @param \phpseclib\Math\BigInteger $e
  * @return string
  */
 static function savePublicKey(BigInteger $n, BigInteger $e)
 {
     $key = PKCS1::savePublicKey($n, $e);
     $key = ASN1::extractBER($key);
     return self::wrapPublicKey($key, '1.2.840.113549.1.1.1');
 }
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;
     }
     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 #3
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;
     }
     /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
                "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
                protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
                two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
     
                http://tools.ietf.org/html/rfc1421#section-4.6.1.1
                http://tools.ietf.org/html/rfc1421#section-4.6.1.3
     
                DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
                DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
                function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
                own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
                implementation are part of the standard, as well.
     
                * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
     if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
         $iv = Hex::decode(trim($matches[2]));
         // remove the Proc-Type / DEK-Info sections as they're no longer needed
         $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
         $ciphertext = ASN1::extractBER($key);
         if ($ciphertext === false) {
             $ciphertext = $key;
         }
         $crypto = self::getEncryptionObject($matches[1]);
         $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
         $crypto->setIV($iv);
         $key = $crypto->decrypt($ciphertext);
     } else {
         if (self::$format != self::MODE_DER) {
             $decoded = ASN1::extractBER($key);
             if ($decoded !== false) {
                 $key = $decoded;
             } elseif (self::$format == self::MODE_PEM) {
                 return false;
             }
         }
     }
     return $key;
 }