Rev 1042 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
827 | daniel-mar | 1 | <?php |
2 | |||
3 | /** |
||
4 | * PKCS#8 Formatted EC Key Handler |
||
5 | * |
||
6 | * PHP version 5 |
||
7 | * |
||
8 | * Processes keys with the following headers: |
||
9 | * |
||
10 | * -----BEGIN ENCRYPTED PRIVATE KEY----- |
||
11 | * -----BEGIN PRIVATE KEY----- |
||
12 | * -----BEGIN PUBLIC KEY----- |
||
13 | * |
||
14 | * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 |
||
15 | * is specific to private keys it's basically creating a DER-encoded wrapper |
||
16 | * for keys. This just extends that same concept to public keys (much like ssh-keygen) |
||
17 | * |
||
18 | * @author Jim Wigginton <terrafrost@php.net> |
||
19 | * @copyright 2015 Jim Wigginton |
||
20 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
||
21 | * @link http://phpseclib.sourceforge.net |
||
22 | */ |
||
23 | |||
24 | namespace phpseclib3\Crypt\EC\Formats\Keys; |
||
25 | |||
26 | use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; |
||
27 | use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; |
||
28 | use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; |
||
29 | use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; |
||
30 | use phpseclib3\Crypt\EC\Curves\Ed25519; |
||
31 | use phpseclib3\Crypt\EC\Curves\Ed448; |
||
32 | use phpseclib3\Exception\UnsupportedCurveException; |
||
33 | use phpseclib3\File\ASN1; |
||
34 | use phpseclib3\File\ASN1\Maps; |
||
35 | use phpseclib3\Math\BigInteger; |
||
36 | |||
37 | /** |
||
38 | * PKCS#8 Formatted EC Key Handler |
||
39 | * |
||
40 | * @author Jim Wigginton <terrafrost@php.net> |
||
41 | */ |
||
42 | abstract class PKCS8 extends Progenitor |
||
43 | { |
||
44 | use Common; |
||
45 | |||
46 | /** |
||
47 | * OID Name |
||
48 | * |
||
49 | * @var array |
||
50 | */ |
||
51 | const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448']; |
||
52 | |||
53 | /** |
||
54 | * OID Value |
||
55 | * |
||
56 | * @var string |
||
57 | */ |
||
58 | const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113']; |
||
59 | |||
60 | /** |
||
61 | * Break a public or private key down into its constituent components |
||
62 | * |
||
63 | * @param string $key |
||
64 | * @param string $password optional |
||
65 | * @return array |
||
66 | */ |
||
67 | public static function load($key, $password = '') |
||
68 | { |
||
69 | // initialize_static_variables() is defined in both the trait and the parent class |
||
70 | // when it's defined in two places it's the traits one that's called |
||
71 | // the parent one is needed, as well, but the parent one is called by other methods |
||
72 | // in the parent class as needed and in the context of the parent it's the parent |
||
73 | // one that's called |
||
74 | self::initialize_static_variables(); |
||
75 | |||
76 | $key = parent::load($key, $password); |
||
77 | |||
78 | $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; |
||
79 | |||
80 | switch ($key[$type . 'Algorithm']['algorithm']) { |
||
81 | case 'id-Ed25519': |
||
82 | case 'id-Ed448': |
||
83 | return self::loadEdDSA($key); |
||
84 | } |
||
85 | |||
86 | $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); |
||
1042 | daniel-mar | 87 | if (!$decoded) { |
88 | throw new \RuntimeException('Unable to decode BER'); |
||
89 | } |
||
827 | daniel-mar | 90 | $params = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP); |
91 | if (!$params) { |
||
92 | throw new \RuntimeException('Unable to decode the parameters using Maps\ECParameters'); |
||
93 | } |
||
94 | |||
95 | $components = []; |
||
96 | $components['curve'] = self::loadCurveByParam($params); |
||
97 | |||
1114 | daniel-mar | 98 | if ($type == 'publicKey') { |
827 | daniel-mar | 99 | $components['QA'] = self::extractPoint("\0" . $key['publicKey'], $components['curve']); |
100 | |||
101 | return $components; |
||
102 | } |
||
103 | |||
104 | $decoded = ASN1::decodeBER($key['privateKey']); |
||
1042 | daniel-mar | 105 | if (!$decoded) { |
106 | throw new \RuntimeException('Unable to decode BER'); |
||
107 | } |
||
827 | daniel-mar | 108 | $key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP); |
109 | if (isset($key['parameters']) && $params != $key['parameters']) { |
||
110 | throw new \RuntimeException('The PKCS8 parameter field does not match the private key parameter field'); |
||
111 | } |
||
112 | |||
113 | $components['dA'] = new BigInteger($key['privateKey'], 256); |
||
114 | $components['curve']->rangeCheck($components['dA']); |
||
115 | $components['QA'] = isset($key['publicKey']) ? |
||
116 | self::extractPoint($key['publicKey'], $components['curve']) : |
||
117 | $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); |
||
118 | |||
119 | return $components; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * Break a public or private EdDSA key down into its constituent components |
||
124 | * |
||
125 | * @return array |
||
126 | */ |
||
127 | private static function loadEdDSA(array $key) |
||
128 | { |
||
129 | $components = []; |
||
130 | |||
131 | if (isset($key['privateKey'])) { |
||
132 | $components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448(); |
||
133 | |||
134 | // 0x04 == octet string |
||
135 | // 0x20 == length (32 bytes) |
||
136 | if (substr($key['privateKey'], 0, 2) != "\x04\x20") { |
||
137 | throw new \RuntimeException('The first two bytes of the private key field should be 0x0420'); |
||
138 | } |
||
1042 | daniel-mar | 139 | $arr = $components['curve']->extractSecret(substr($key['privateKey'], 2)); |
140 | $components['dA'] = $arr['dA']; |
||
141 | $components['secret'] = $arr['secret']; |
||
827 | daniel-mar | 142 | } |
143 | |||
144 | if (isset($key['publicKey'])) { |
||
145 | if (!isset($components['curve'])) { |
||
146 | $components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448(); |
||
147 | } |
||
148 | |||
149 | $components['QA'] = self::extractPoint($key['publicKey'], $components['curve']); |
||
150 | } |
||
151 | |||
152 | if (isset($key['privateKey']) && !isset($components['QA'])) { |
||
153 | $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); |
||
154 | } |
||
155 | |||
156 | return $components; |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * Convert an EC public key to the appropriate format |
||
161 | * |
||
162 | * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve |
||
163 | * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey |
||
164 | * @param array $options optional |
||
165 | * @return string |
||
166 | */ |
||
167 | public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []) |
||
168 | { |
||
169 | self::initialize_static_variables(); |
||
170 | |||
171 | if ($curve instanceof MontgomeryCurve) { |
||
172 | throw new UnsupportedCurveException('Montgomery Curves are not supported'); |
||
173 | } |
||
174 | |||
175 | if ($curve instanceof TwistedEdwardsCurve) { |
||
176 | return self::wrapPublicKey( |
||
177 | $curve->encodePoint($publicKey), |
||
178 | null, |
||
179 | $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448' |
||
180 | ); |
||
181 | } |
||
182 | |||
183 | $params = new ASN1\Element(self::encodeParameters($curve, false, $options)); |
||
184 | |||
185 | $key = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); |
||
186 | |||
187 | return self::wrapPublicKey($key, $params, 'id-ecPublicKey'); |
||
188 | } |
||
189 | |||
190 | /** |
||
191 | * Convert a private key to the appropriate format. |
||
192 | * |
||
193 | * @param \phpseclib3\Math\BigInteger $privateKey |
||
194 | * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve |
||
195 | * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey |
||
1042 | daniel-mar | 196 | * @param string $secret optional |
827 | daniel-mar | 197 | * @param string $password optional |
198 | * @param array $options optional |
||
199 | * @return string |
||
200 | */ |
||
1042 | daniel-mar | 201 | public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = []) |
827 | daniel-mar | 202 | { |
203 | self::initialize_static_variables(); |
||
204 | |||
205 | if ($curve instanceof MontgomeryCurve) { |
||
206 | throw new UnsupportedCurveException('Montgomery Curves are not supported'); |
||
207 | } |
||
208 | |||
209 | if ($curve instanceof TwistedEdwardsCurve) { |
||
210 | return self::wrapPrivateKey( |
||
1042 | daniel-mar | 211 | "\x04\x20" . $secret, |
827 | daniel-mar | 212 | [], |
213 | null, |
||
214 | $password, |
||
215 | $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448' |
||
216 | ); |
||
217 | } |
||
218 | |||
219 | $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); |
||
220 | |||
221 | $params = new ASN1\Element(self::encodeParameters($curve, false, $options)); |
||
222 | |||
223 | $key = [ |
||
224 | 'version' => 'ecPrivkeyVer1', |
||
225 | 'privateKey' => $privateKey->toBytes(), |
||
226 | //'parameters' => $params, |
||
227 | 'publicKey' => "\0" . $publicKey |
||
228 | ]; |
||
229 | |||
230 | $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP); |
||
231 | |||
232 | return self::wrapPrivateKey($key, [], $params, $password, 'id-ecPublicKey', '', $options); |
||
233 | } |
||
234 | } |