Subversion Repositories oidplus

Rev

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
}