[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * PKCS1 Formatted Key Handler 5 * 6 * PHP version 5 7 * 8 * @author Jim Wigginton <terrafrost@php.net> 9 * @copyright 2015 Jim Wigginton 10 * @license http://www.opensource.org/licenses/mit-license.html MIT License 11 * @link http://phpseclib.sourceforge.net 12 */ 13 14 namespace phpseclib3\Crypt\Common\Formats\Keys; 15 16 use phpseclib3\Common\Functions\Strings; 17 use phpseclib3\Crypt\AES; 18 use phpseclib3\Crypt\DES; 19 use phpseclib3\Crypt\Random; 20 use phpseclib3\Crypt\TripleDES; 21 use phpseclib3\Exception\UnsupportedAlgorithmException; 22 use phpseclib3\File\ASN1; 23 24 /** 25 * PKCS1 Formatted Key Handler 26 * 27 * @author Jim Wigginton <terrafrost@php.net> 28 */ 29 abstract class PKCS1 extends PKCS 30 { 31 /** 32 * Default encryption algorithm 33 * 34 * @var string 35 */ 36 private static $defaultEncryptionAlgorithm = 'AES-128-CBC'; 37 38 /** 39 * Sets the default encryption algorithm 40 * 41 * @param string $algo 42 */ 43 public static function setEncryptionAlgorithm($algo) 44 { 45 self::$defaultEncryptionAlgorithm = $algo; 46 } 47 48 /** 49 * Returns the mode constant corresponding to the mode string 50 * 51 * @param string $mode 52 * @return int 53 * @throws \UnexpectedValueException if the block cipher mode is unsupported 54 */ 55 private static function getEncryptionMode($mode) 56 { 57 switch ($mode) { 58 case 'CBC': 59 case 'ECB': 60 case 'CFB': 61 case 'OFB': 62 case 'CTR': 63 return $mode; 64 } 65 throw new \UnexpectedValueException('Unsupported block cipher mode of operation'); 66 } 67 68 /** 69 * Returns a cipher object corresponding to a string 70 * 71 * @param string $algo 72 * @return string 73 * @throws \UnexpectedValueException if the encryption algorithm is unsupported 74 */ 75 private static function getEncryptionObject($algo) 76 { 77 $modes = '(CBC|ECB|CFB|OFB|CTR)'; 78 switch (true) { 79 case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches): 80 $cipher = new AES(self::getEncryptionMode($matches[2])); 81 $cipher->setKeyLength($matches[1]); 82 return $cipher; 83 case preg_match("#^DES-EDE3-$modes$#", $algo, $matches): 84 return new TripleDES(self::getEncryptionMode($matches[1])); 85 case preg_match("#^DES-$modes$#", $algo, $matches): 86 return new DES(self::getEncryptionMode($matches[1])); 87 default: 88 throw new UnsupportedAlgorithmException($algo . ' is not a supported algorithm'); 89 } 90 } 91 92 /** 93 * Generate a symmetric key for PKCS#1 keys 94 * 95 * @param string $password 96 * @param string $iv 97 * @param int $length 98 * @return string 99 */ 100 private static function generateSymmetricKey($password, $iv, $length) 101 { 102 $symkey = ''; 103 $iv = substr($iv, 0, 8); 104 while (strlen($symkey) < $length) { 105 $symkey .= md5($symkey . $password . $iv, true); 106 } 107 return substr($symkey, 0, $length); 108 } 109 110 /** 111 * Break a public or private key down into its constituent components 112 * 113 * @param string $key 114 * @param string $password optional 115 * @return array 116 */ 117 protected static function load($key, $password) 118 { 119 if (!Strings::is_stringable($key)) { 120 throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); 121 } 122 123 /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is 124 "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to 125 protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding 126 two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: 127 128 http://tools.ietf.org/html/rfc1421#section-4.6.1.1 129 http://tools.ietf.org/html/rfc1421#section-4.6.1.3 130 131 DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. 132 DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation 133 function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's 134 own implementation. ie. the implementation *is* the standard and any bugs that may exist in that 135 implementation are part of the standard, as well. 136 137 * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ 138 if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { 139 $iv = Strings::hex2bin(trim($matches[2])); 140 // remove the Proc-Type / DEK-Info sections as they're no longer needed 141 $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); 142 $ciphertext = ASN1::extractBER($key); 143 if ($ciphertext === false) { 144 $ciphertext = $key; 145 } 146 $crypto = self::getEncryptionObject($matches[1]); 147 $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3)); 148 $crypto->setIV($iv); 149 $key = $crypto->decrypt($ciphertext); 150 } else { 151 if (self::$format != self::MODE_DER) { 152 $decoded = ASN1::extractBER($key); 153 if ($decoded !== false) { 154 $key = $decoded; 155 } elseif (self::$format == self::MODE_PEM) { 156 throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text'); 157 } 158 } 159 } 160 161 return $key; 162 } 163 164 /** 165 * Wrap a private key appropriately 166 * 167 * @param string $key 168 * @param string $type 169 * @param string $password 170 * @param array $options optional 171 * @return string 172 */ 173 protected static function wrapPrivateKey($key, $type, $password, array $options = []) 174 { 175 if (empty($password) || !is_string($password)) { 176 return "-----BEGIN $type PRIVATE KEY-----\r\n" . 177 chunk_split(Strings::base64_encode($key), 64) . 178 "-----END $type PRIVATE KEY-----"; 179 } 180 181 $encryptionAlgorithm = isset($options['encryptionAlgorithm']) ? $options['encryptionAlgorithm'] : self::$defaultEncryptionAlgorithm; 182 183 $cipher = self::getEncryptionObject($encryptionAlgorithm); 184 $iv = Random::string($cipher->getBlockLength() >> 3); 185 $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3)); 186 $cipher->setIV($iv); 187 $iv = strtoupper(Strings::bin2hex($iv)); 188 return "-----BEGIN $type PRIVATE KEY-----\r\n" . 189 "Proc-Type: 4,ENCRYPTED\r\n" . 190 "DEK-Info: " . $encryptionAlgorithm . ",$iv\r\n" . 191 "\r\n" . 192 chunk_split(Strings::base64_encode($cipher->encrypt($key)), 64) . 193 "-----END $type PRIVATE KEY-----"; 194 } 195 196 /** 197 * Wrap a public key appropriately 198 * 199 * @param string $key 200 * @param string $type 201 * @return string 202 */ 203 protected static function wrapPublicKey($key, $type) 204 { 205 return "-----BEGIN $type PUBLIC KEY-----\r\n" . 206 chunk_split(Strings::base64_encode($key), 64) . 207 "-----END $type PUBLIC KEY-----"; 208 } 209 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body