1<?php
2
3/**
4 * PKCS#1 Formatted RSA Key Handler
5 *
6 * PHP version 5
7 *
8 * Used by File/X509.php
9 *
10 * Processes keys with the following headers:
11 *
12 * -----BEGIN RSA PRIVATE KEY-----
13 * -----BEGIN RSA PUBLIC KEY-----
14 *
15 * Analogous to ssh-keygen's pem format (as specified by -m)
16 *
17 * @category  Crypt
18 * @package   RSA
19 * @author    Jim Wigginton <terrafrost@php.net>
20 * @copyright 2015 Jim Wigginton
21 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
22 * @link      http://phpseclib.sourceforge.net
23 */
24
25namespace phpseclib3\Crypt\RSA\Formats\Keys;
26
27use phpseclib3\Common\Functions\Strings;
28use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
29use phpseclib3\File\ASN1;
30use phpseclib3\File\ASN1\Maps;
31use phpseclib3\Math\BigInteger;
32
33/**
34 * PKCS#1 Formatted RSA Key Handler
35 *
36 * @package RSA
37 * @author  Jim Wigginton <terrafrost@php.net>
38 * @access  public
39 */
40abstract class PKCS1 extends Progenitor
41{
42    /**
43     * Break a public or private key down into its constituent components
44     *
45     * @access public
46     * @param string $key
47     * @param string $password optional
48     * @return array
49     */
50    public static function load($key, $password = '')
51    {
52        if (!Strings::is_stringable($key)) {
53            throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
54        }
55
56        if (strpos($key, 'PUBLIC') !== false) {
57            $components = ['isPublicKey' => true];
58        } elseif (strpos($key, 'PRIVATE') !== false) {
59            $components = ['isPublicKey' => false];
60        } else {
61            $components = [];
62        }
63
64        $key = parent::load($key, $password);
65
66        $decoded = ASN1::decodeBER($key);
67        if (empty($decoded)) {
68            throw new \RuntimeException('Unable to decode BER');
69        }
70
71        $key = ASN1::asn1map($decoded[0], Maps\RSAPrivateKey::MAP);
72        if (is_array($key)) {
73            $components += [
74                'modulus' => $key['modulus'],
75                'publicExponent' => $key['publicExponent'],
76                'privateExponent' => $key['privateExponent'],
77                'primes' => [1 => $key['prime1'], $key['prime2']],
78                'exponents' => [1 => $key['exponent1'], $key['exponent2']],
79                'coefficients' => [2 => $key['coefficient']]
80            ];
81            if ($key['version'] == 'multi') {
82                foreach ($key['otherPrimeInfos'] as $primeInfo) {
83                    $components['primes'][] = $primeInfo['prime'];
84                    $components['exponents'][] = $primeInfo['exponent'];
85                    $components['coefficients'][] = $primeInfo['coefficient'];
86                }
87            }
88            if (!isset($components['isPublicKey'])) {
89                $components['isPublicKey'] = false;
90            }
91            return $components;
92        }
93
94        $key = ASN1::asn1map($decoded[0], Maps\RSAPublicKey::MAP);
95
96        if (!is_array($key)) {
97            throw new \RuntimeException('Unable to perform ASN1 mapping');
98        }
99
100        if (!isset($components['isPublicKey'])) {
101            $components['isPublicKey'] = true;
102        }
103
104        return $components + $key;
105    }
106
107    /**
108     * Convert a private key to the appropriate format.
109     *
110     * @access public
111     * @param \phpseclib3\Math\BigInteger $n
112     * @param \phpseclib3\Math\BigInteger $e
113     * @param \phpseclib3\Math\BigInteger $d
114     * @param array $primes
115     * @param array $exponents
116     * @param array $coefficients
117     * @param string $password optional
118     * @param array $options optional
119     * @return string
120     */
121    public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
122    {
123        $num_primes = count($primes);
124        $key = [
125            'version' => $num_primes == 2 ? 'two-prime' : 'multi',
126            'modulus' => $n,
127            'publicExponent' => $e,
128            'privateExponent' => $d,
129            'prime1' => $primes[1],
130            'prime2' => $primes[2],
131            'exponent1' => $exponents[1],
132            'exponent2' => $exponents[2],
133            'coefficient' => $coefficients[2]
134        ];
135        for ($i = 3; $i <= $num_primes; $i++) {
136            $key['otherPrimeInfos'][] = [
137                'prime' => $primes[$i],
138                'exponent' => $exponents[$i],
139                'coefficient' => $coefficients[$i]
140            ];
141        }
142
143        $key = ASN1::encodeDER($key, Maps\RSAPrivateKey::MAP);
144
145        return self::wrapPrivateKey($key, 'RSA', $password, $options);
146    }
147
148    /**
149     * Convert a public key to the appropriate format
150     *
151     * @access public
152     * @param \phpseclib3\Math\BigInteger $n
153     * @param \phpseclib3\Math\BigInteger $e
154     * @return string
155     */
156    public static function savePublicKey(BigInteger $n, BigInteger $e)
157    {
158        $key = [
159            'modulus' => $n,
160            'publicExponent' => $e
161        ];
162
163        $key = ASN1::encodeDER($key, Maps\RSAPublicKey::MAP);
164
165        return self::wrapPublicKey($key, 'RSA');
166    }
167}
168