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