These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | /* |
||
6 | * The MIT License (MIT) |
||
7 | * |
||
8 | * Copyright (c) 2014-2019 Spomky-Labs |
||
9 | * |
||
10 | * This software may be modified and distributed under the terms |
||
11 | * of the MIT license. See the LICENSE file for details. |
||
12 | */ |
||
13 | |||
14 | namespace Jose\Component\KeyManagement\KeyConverter; |
||
15 | |||
16 | use Base64Url\Base64Url; |
||
17 | use Exception; |
||
18 | use FG\ASN1\ASNObject; |
||
19 | use FG\ASN1\ExplicitlyTaggedObject; |
||
20 | use FG\ASN1\Universal\BitString; |
||
21 | use FG\ASN1\Universal\Integer; |
||
22 | use FG\ASN1\Universal\ObjectIdentifier; |
||
23 | use FG\ASN1\Universal\OctetString; |
||
24 | use FG\ASN1\Universal\Sequence; |
||
25 | use InvalidArgumentException; |
||
26 | |||
27 | /** |
||
28 | * @internal |
||
29 | */ |
||
30 | class ECKey |
||
31 | { |
||
32 | /** |
||
33 | * @var array |
||
34 | */ |
||
35 | private $values = []; |
||
36 | |||
37 | private function __construct(array $data) |
||
38 | { |
||
39 | $this->loadJWK($data); |
||
40 | } |
||
41 | |||
42 | public static function createFromPEM(string $pem): self |
||
43 | { |
||
44 | $data = self::loadPEM($pem); |
||
45 | |||
46 | return new self($data); |
||
47 | } |
||
48 | |||
49 | /** |
||
50 | * @param ECKey $private |
||
51 | * |
||
52 | * @return ECKey |
||
53 | */ |
||
54 | public static function toPublic(self $private): self |
||
55 | { |
||
56 | $data = $private->toArray(); |
||
57 | if (\array_key_exists('d', $data)) { |
||
58 | unset($data['d']); |
||
59 | } |
||
60 | |||
61 | return new self($data); |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * @return array |
||
66 | */ |
||
67 | public function toArray() |
||
68 | { |
||
69 | return $this->values; |
||
70 | } |
||
71 | |||
72 | private static function loadPEM(string $data): array |
||
73 | { |
||
74 | $data = base64_decode(preg_replace('#-.*-|\r|\n#', '', $data), true); |
||
75 | $asnObject = ASNObject::fromBinary($data); |
||
76 | if (!$asnObject instanceof Sequence) { |
||
0 ignored issues
–
show
|
|||
77 | throw new InvalidArgumentException('Unable to load the key.'); |
||
78 | } |
||
79 | $children = $asnObject->getChildren(); |
||
80 | if (self::isPKCS8($children)) { |
||
81 | $children = self::loadPKCS8($children); |
||
82 | } |
||
83 | |||
84 | if (4 === \count($children)) { |
||
85 | return self::loadPrivatePEM($children); |
||
86 | } |
||
87 | if (2 === \count($children)) { |
||
88 | return self::loadPublicPEM($children); |
||
89 | } |
||
90 | |||
91 | throw new Exception('Unable to load the key.'); |
||
92 | } |
||
93 | |||
94 | /** |
||
95 | * @param ASNObject[] $children |
||
96 | */ |
||
97 | private static function loadPKCS8(array $children): array |
||
98 | { |
||
99 | $binary = hex2bin($children[2]->getContent()); |
||
100 | $asnObject = ASNObject::fromBinary($binary); |
||
101 | if (!$asnObject instanceof Sequence) { |
||
0 ignored issues
–
show
The class
FG\ASN1\Universal\Sequence does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
102 | throw new InvalidArgumentException('Unable to load the key.'); |
||
103 | } |
||
104 | |||
105 | return $asnObject->getChildren(); |
||
106 | } |
||
107 | |||
108 | private static function loadPublicPEM(array $children): array |
||
109 | { |
||
110 | if (!$children[0] instanceof Sequence) { |
||
0 ignored issues
–
show
The class
FG\ASN1\Universal\Sequence does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
111 | throw new InvalidArgumentException('Unsupported key type.'); |
||
112 | } |
||
113 | |||
114 | $sub = $children[0]->getChildren(); |
||
115 | if (!$sub[0] instanceof ObjectIdentifier) { |
||
0 ignored issues
–
show
The class
FG\ASN1\Universal\ObjectIdentifier does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
116 | throw new InvalidArgumentException('Unsupported key type.'); |
||
117 | } |
||
118 | if ('1.2.840.10045.2.1' !== $sub[0]->getContent()) { |
||
119 | throw new InvalidArgumentException('Unsupported key type.'); |
||
120 | } |
||
121 | if (!$sub[1] instanceof ObjectIdentifier) { |
||
0 ignored issues
–
show
The class
FG\ASN1\Universal\ObjectIdentifier does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
122 | throw new InvalidArgumentException('Unsupported key type.'); |
||
123 | } |
||
124 | if (!$children[1] instanceof BitString) { |
||
0 ignored issues
–
show
The class
FG\ASN1\Universal\BitString does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
125 | throw new InvalidArgumentException('Unable to load the key.'); |
||
126 | } |
||
127 | |||
128 | $bits = $children[1]->getContent(); |
||
129 | $bits_length = mb_strlen($bits, '8bit'); |
||
130 | if ('04' !== mb_substr($bits, 0, 2, '8bit')) { |
||
131 | throw new InvalidArgumentException('Unsupported key type'); |
||
132 | } |
||
133 | |||
134 | $values = ['kty' => 'EC']; |
||
135 | $values['crv'] = self::getCurve($sub[1]->getContent()); |
||
136 | $values['x'] = Base64Url::encode(hex2bin(mb_substr($bits, 2, ($bits_length - 2) / 2, '8bit'))); |
||
137 | $values['y'] = Base64Url::encode(hex2bin(mb_substr($bits, (int) (($bits_length - 2) / 2 + 2), ($bits_length - 2) / 2, '8bit'))); |
||
138 | |||
139 | return $values; |
||
140 | } |
||
141 | |||
142 | private static function getCurve(string $oid): string |
||
143 | { |
||
144 | $curves = self::getSupportedCurves(); |
||
145 | $curve = array_search($oid, $curves, true); |
||
146 | if (!\is_string($curve)) { |
||
147 | throw new InvalidArgumentException('Unsupported OID.'); |
||
148 | } |
||
149 | |||
150 | return $curve; |
||
151 | } |
||
152 | |||
153 | private static function getSupportedCurves(): array |
||
154 | { |
||
155 | return [ |
||
156 | 'P-256' => '1.2.840.10045.3.1.7', |
||
157 | 'P-384' => '1.3.132.0.34', |
||
158 | 'P-521' => '1.3.132.0.35', |
||
159 | ]; |
||
160 | } |
||
161 | |||
162 | private static function verifyVersion(ASNObject $children): void |
||
163 | { |
||
164 | if (!$children instanceof Integer || '1' !== $children->getContent()) { |
||
0 ignored issues
–
show
The class
FG\ASN1\Universal\Integer does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
165 | throw new InvalidArgumentException('Unable to load the key.'); |
||
166 | } |
||
167 | } |
||
168 | |||
169 | private static function getXAndY(ASNObject $children, string &$x, string &$y): void |
||
170 | { |
||
171 | if (!$children instanceof ExplicitlyTaggedObject || !\is_array($children->getContent())) { |
||
0 ignored issues
–
show
The class
FG\ASN1\ExplicitlyTaggedObject does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
172 | throw new InvalidArgumentException('Unable to load the key.'); |
||
173 | } |
||
174 | if (!$children->getContent()[0] instanceof BitString) { |
||
0 ignored issues
–
show
The class
FG\ASN1\Universal\BitString does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
175 | throw new InvalidArgumentException('Unable to load the key.'); |
||
176 | } |
||
177 | |||
178 | $bits = $children->getContent()[0]->getContent(); |
||
179 | $bits_length = mb_strlen($bits, '8bit'); |
||
180 | |||
181 | if ('04' !== mb_substr($bits, 0, 2, '8bit')) { |
||
182 | throw new InvalidArgumentException('Unsupported key type'); |
||
183 | } |
||
184 | |||
185 | $x = mb_substr($bits, 2, (int) (($bits_length - 2) / 2), '8bit'); |
||
186 | $y = mb_substr($bits, (int) (($bits_length - 2) / 2 + 2), (int) (($bits_length - 2) / 2), '8bit'); |
||
187 | } |
||
188 | |||
189 | private static function getD(ASNObject $children): string |
||
190 | { |
||
191 | if (!$children instanceof OctetString) { |
||
0 ignored issues
–
show
The class
FG\ASN1\Universal\OctetString does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
192 | throw new InvalidArgumentException('Unable to load the key.'); |
||
193 | } |
||
194 | |||
195 | return $children->getContent(); |
||
196 | } |
||
197 | |||
198 | private static function loadPrivatePEM(array $children): array |
||
199 | { |
||
200 | self::verifyVersion($children[0]); |
||
201 | $x = ''; |
||
202 | $y = ''; |
||
203 | $d = self::getD($children[1]); |
||
204 | self::getXAndY($children[3], $x, $y); |
||
205 | |||
206 | if (!$children[2] instanceof ExplicitlyTaggedObject || !\is_array($children[2]->getContent())) { |
||
0 ignored issues
–
show
The class
FG\ASN1\ExplicitlyTaggedObject does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
207 | throw new InvalidArgumentException('Unable to load the key.'); |
||
208 | } |
||
209 | if (!$children[2]->getContent()[0] instanceof ObjectIdentifier) { |
||
0 ignored issues
–
show
The class
FG\ASN1\Universal\ObjectIdentifier does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
210 | throw new InvalidArgumentException('Unable to load the key.'); |
||
211 | } |
||
212 | |||
213 | $curve = $children[2]->getContent()[0]->getContent(); |
||
214 | |||
215 | $values = ['kty' => 'EC']; |
||
216 | $values['crv'] = self::getCurve($curve); |
||
217 | $values['d'] = Base64Url::encode(hex2bin($d)); |
||
218 | $values['x'] = Base64Url::encode(hex2bin($x)); |
||
219 | $values['y'] = Base64Url::encode(hex2bin($y)); |
||
220 | |||
221 | return $values; |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * @param ASNObject[] $children |
||
226 | */ |
||
227 | private static function isPKCS8(array $children): bool |
||
228 | { |
||
229 | if (3 !== \count($children)) { |
||
230 | return false; |
||
231 | } |
||
232 | |||
233 | $classes = [0 => Integer::class, 1 => Sequence::class, 2 => OctetString::class]; |
||
234 | foreach ($classes as $k => $class) { |
||
235 | if (!$children[$k] instanceof $class) { |
||
236 | return false; |
||
237 | } |
||
238 | } |
||
239 | |||
240 | return true; |
||
241 | } |
||
242 | |||
243 | private function loadJWK(array $jwk): void |
||
244 | { |
||
245 | $keys = [ |
||
246 | 'kty' => 'The key parameter "kty" is missing.', |
||
247 | 'crv' => 'Curve parameter is missing', |
||
248 | 'x' => 'Point parameters are missing.', |
||
249 | 'y' => 'Point parameters are missing.', |
||
250 | ]; |
||
251 | foreach ($keys as $k => $v) { |
||
252 | if (!\array_key_exists($k, $jwk)) { |
||
253 | throw new InvalidArgumentException($v); |
||
254 | } |
||
255 | } |
||
256 | |||
257 | if ('EC' !== $jwk['kty']) { |
||
258 | throw new InvalidArgumentException('JWK is not an Elliptic Curve key.'); |
||
259 | } |
||
260 | $this->values = $jwk; |
||
261 | } |
||
262 | } |
||
263 |
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.