Rev 846 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
827 | daniel-mar | 1 | <?php |
2 | |||
3 | /** |
||
4 | * Miccrosoft BLOB Formatted RSA Key Handler |
||
5 | * |
||
6 | * More info: |
||
7 | * |
||
8 | * https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx |
||
9 | * |
||
10 | * PHP version 5 |
||
11 | * |
||
874 | daniel-mar | 12 | * @category Crypt |
13 | * @package RSA |
||
827 | daniel-mar | 14 | * @author Jim Wigginton <terrafrost@php.net> |
15 | * @copyright 2015 Jim Wigginton |
||
16 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
||
17 | * @link http://phpseclib.sourceforge.net |
||
18 | */ |
||
19 | |||
20 | namespace phpseclib3\Crypt\RSA\Formats\Keys; |
||
21 | |||
22 | use ParagonIE\ConstantTime\Base64; |
||
23 | use phpseclib3\Common\Functions\Strings; |
||
24 | use phpseclib3\Exception\UnsupportedFormatException; |
||
25 | use phpseclib3\Math\BigInteger; |
||
26 | |||
27 | /** |
||
28 | * Microsoft BLOB Formatted RSA Key Handler |
||
29 | * |
||
874 | daniel-mar | 30 | * @package RSA |
827 | daniel-mar | 31 | * @author Jim Wigginton <terrafrost@php.net> |
874 | daniel-mar | 32 | * @access public |
827 | daniel-mar | 33 | */ |
34 | abstract class MSBLOB |
||
35 | { |
||
36 | /** |
||
37 | * Public/Private Key Pair |
||
38 | * |
||
874 | daniel-mar | 39 | * @access private |
827 | daniel-mar | 40 | */ |
41 | const PRIVATEKEYBLOB = 0x7; |
||
42 | /** |
||
43 | * Public Key |
||
44 | * |
||
874 | daniel-mar | 45 | * @access private |
827 | daniel-mar | 46 | */ |
47 | const PUBLICKEYBLOB = 0x6; |
||
48 | /** |
||
49 | * Public Key |
||
50 | * |
||
874 | daniel-mar | 51 | * @access private |
827 | daniel-mar | 52 | */ |
53 | const PUBLICKEYBLOBEX = 0xA; |
||
54 | /** |
||
55 | * RSA public key exchange algorithm |
||
56 | * |
||
874 | daniel-mar | 57 | * @access private |
827 | daniel-mar | 58 | */ |
59 | const CALG_RSA_KEYX = 0x0000A400; |
||
60 | /** |
||
61 | * RSA public key exchange algorithm |
||
62 | * |
||
874 | daniel-mar | 63 | * @access private |
827 | daniel-mar | 64 | */ |
65 | const CALG_RSA_SIGN = 0x00002400; |
||
66 | /** |
||
67 | * Public Key |
||
68 | * |
||
874 | daniel-mar | 69 | * @access private |
827 | daniel-mar | 70 | */ |
71 | const RSA1 = 0x31415352; |
||
72 | /** |
||
73 | * Private Key |
||
74 | * |
||
874 | daniel-mar | 75 | * @access private |
827 | daniel-mar | 76 | */ |
77 | const RSA2 = 0x32415352; |
||
78 | |||
79 | /** |
||
80 | * Break a public or private key down into its constituent components |
||
81 | * |
||
874 | daniel-mar | 82 | * @access public |
827 | daniel-mar | 83 | * @param string $key |
84 | * @param string $password optional |
||
85 | * @return array |
||
86 | */ |
||
87 | public static function load($key, $password = '') |
||
88 | { |
||
89 | if (!Strings::is_stringable($key)) { |
||
90 | throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); |
||
91 | } |
||
92 | |||
93 | $key = Base64::decode($key); |
||
94 | |||
95 | if (!is_string($key)) { |
||
96 | throw new \UnexpectedValueException('Base64 decoding produced an error'); |
||
97 | } |
||
98 | if (strlen($key) < 20) { |
||
99 | throw new \UnexpectedValueException('Key appears to be malformed'); |
||
100 | } |
||
101 | |||
102 | // PUBLICKEYSTRUC publickeystruc |
||
103 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx |
||
104 | extract(unpack('atype/aversion/vreserved/Valgo', Strings::shift($key, 8))); |
||
105 | /** |
||
106 | * @var string $type |
||
107 | * @var string $version |
||
108 | * @var integer $reserved |
||
109 | * @var integer $algo |
||
110 | */ |
||
111 | switch (ord($type)) { |
||
112 | case self::PUBLICKEYBLOB: |
||
113 | case self::PUBLICKEYBLOBEX: |
||
114 | $publickey = true; |
||
115 | break; |
||
116 | case self::PRIVATEKEYBLOB: |
||
117 | $publickey = false; |
||
118 | break; |
||
119 | default: |
||
120 | throw new \UnexpectedValueException('Key appears to be malformed'); |
||
121 | } |
||
122 | |||
123 | $components = ['isPublicKey' => $publickey]; |
||
124 | |||
125 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx |
||
126 | switch ($algo) { |
||
127 | case self::CALG_RSA_KEYX: |
||
128 | case self::CALG_RSA_SIGN: |
||
129 | break; |
||
130 | default: |
||
131 | throw new \UnexpectedValueException('Key appears to be malformed'); |
||
132 | } |
||
133 | |||
134 | // RSAPUBKEY rsapubkey |
||
135 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx |
||
136 | // could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit |
||
137 | extract(unpack('Vmagic/Vbitlen/a4pubexp', Strings::shift($key, 12))); |
||
138 | /** |
||
139 | * @var integer $magic |
||
140 | * @var integer $bitlen |
||
141 | * @var string $pubexp |
||
142 | */ |
||
143 | switch ($magic) { |
||
144 | case self::RSA2: |
||
145 | $components['isPublicKey'] = false; |
||
146 | // fall-through |
||
147 | case self::RSA1: |
||
148 | break; |
||
149 | default: |
||
150 | throw new \UnexpectedValueException('Key appears to be malformed'); |
||
151 | } |
||
152 | |||
153 | $baseLength = $bitlen / 16; |
||
154 | if (strlen($key) != 2 * $baseLength && strlen($key) != 9 * $baseLength) { |
||
155 | throw new \UnexpectedValueException('Key appears to be malformed'); |
||
156 | } |
||
157 | |||
158 | $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(strrev($pubexp), 256); |
||
159 | // BYTE modulus[rsapubkey.bitlen/8] |
||
160 | $components['modulus'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256); |
||
161 | |||
162 | if ($publickey) { |
||
163 | return $components; |
||
164 | } |
||
165 | |||
166 | $components['isPublicKey'] = false; |
||
167 | |||
168 | // BYTE prime1[rsapubkey.bitlen/16] |
||
169 | $components['primes'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)]; |
||
170 | // BYTE prime2[rsapubkey.bitlen/16] |
||
171 | $components['primes'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256); |
||
172 | // BYTE exponent1[rsapubkey.bitlen/16] |
||
173 | $components['exponents'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)]; |
||
174 | // BYTE exponent2[rsapubkey.bitlen/16] |
||
175 | $components['exponents'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256); |
||
176 | // BYTE coefficient[rsapubkey.bitlen/16] |
||
177 | $components['coefficients'] = [2 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)]; |
||
178 | if (isset($components['privateExponent'])) { |
||
179 | $components['publicExponent'] = $components['privateExponent']; |
||
180 | } |
||
181 | // BYTE privateExponent[rsapubkey.bitlen/8] |
||
182 | $components['privateExponent'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256); |
||
183 | |||
184 | return $components; |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * Convert a private key to the appropriate format. |
||
189 | * |
||
874 | daniel-mar | 190 | * @access public |
827 | daniel-mar | 191 | * @param \phpseclib3\Math\BigInteger $n |
192 | * @param \phpseclib3\Math\BigInteger $e |
||
193 | * @param \phpseclib3\Math\BigInteger $d |
||
194 | * @param array $primes |
||
195 | * @param array $exponents |
||
196 | * @param array $coefficients |
||
197 | * @param string $password optional |
||
198 | * @return string |
||
199 | */ |
||
200 | public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '') |
||
201 | { |
||
202 | if (count($primes) != 2) { |
||
203 | throw new \InvalidArgumentException('MSBLOB does not support multi-prime RSA keys'); |
||
204 | } |
||
205 | |||
206 | if (!empty($password) && is_string($password)) { |
||
207 | throw new UnsupportedFormatException('MSBLOB private keys do not support encryption'); |
||
208 | } |
||
209 | |||
210 | $n = strrev($n->toBytes()); |
||
211 | $e = str_pad(strrev($e->toBytes()), 4, "\0"); |
||
212 | $key = pack('aavV', chr(self::PRIVATEKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX); |
||
213 | $key .= pack('VVa*', self::RSA2, 8 * strlen($n), $e); |
||
214 | $key .= $n; |
||
215 | $key .= strrev($primes[1]->toBytes()); |
||
216 | $key .= strrev($primes[2]->toBytes()); |
||
217 | $key .= strrev($exponents[1]->toBytes()); |
||
218 | $key .= strrev($exponents[2]->toBytes()); |
||
219 | $key .= strrev($coefficients[2]->toBytes()); |
||
220 | $key .= strrev($d->toBytes()); |
||
221 | |||
222 | return Base64::encode($key); |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Convert a public key to the appropriate format |
||
227 | * |
||
874 | daniel-mar | 228 | * @access public |
827 | daniel-mar | 229 | * @param \phpseclib3\Math\BigInteger $n |
230 | * @param \phpseclib3\Math\BigInteger $e |
||
231 | * @return string |
||
232 | */ |
||
233 | public static function savePublicKey(BigInteger $n, BigInteger $e) |
||
234 | { |
||
235 | $n = strrev($n->toBytes()); |
||
236 | $e = str_pad(strrev($e->toBytes()), 4, "\0"); |
||
237 | $key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX); |
||
238 | $key .= pack('VVa*', self::RSA1, 8 * strlen($n), $e); |
||
239 | $key .= $n; |
||
240 | |||
241 | return Base64::encode($key); |
||
242 | } |
||
243 | } |