Complex classes like ECKey often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ECKey, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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 | /** |
||
73 | * @throws InvalidArgumentException if the key cannot be loaded |
||
74 | * @throws ParserException if the key cannot be loaded |
||
75 | */ |
||
76 | private static function loadPEM(string $data): array |
||
77 | { |
||
78 | $data = base64_decode(preg_replace('#-.*-|\r|\n#', '', $data), true); |
||
79 | $asnObject = ASNObject::fromBinary($data); |
||
80 | if (!$asnObject instanceof Sequence) { |
||
81 | throw new InvalidArgumentException('Unable to load the key.'); |
||
82 | } |
||
83 | $children = $asnObject->getChildren(); |
||
84 | if (self::isPKCS8($children)) { |
||
85 | $children = self::loadPKCS8($children); |
||
86 | } |
||
87 | |||
88 | if (4 === \count($children)) { |
||
89 | return self::loadPrivatePEM($children); |
||
90 | } |
||
91 | if (2 === \count($children)) { |
||
92 | return self::loadPublicPEM($children); |
||
93 | } |
||
94 | |||
95 | throw new InvalidArgumentException('Unable to load the key.'); |
||
96 | } |
||
97 | |||
98 | /** |
||
99 | * @param ASNObject[] $children |
||
100 | * |
||
101 | * @throws InvalidArgumentException if the key cannot be loaded |
||
102 | * @throws ParserException if the key cannot be loaded |
||
103 | */ |
||
104 | private static function loadPKCS8(array $children): array |
||
105 | { |
||
106 | $binary = hex2bin($children[2]->getContent()); |
||
107 | $asnObject = ASNObject::fromBinary($binary); |
||
108 | if (!$asnObject instanceof Sequence) { |
||
109 | throw new InvalidArgumentException('Unable to load the key.'); |
||
110 | } |
||
111 | |||
112 | return $asnObject->getChildren(); |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * @throws InvalidArgumentException if the key cannot be loaded |
||
117 | */ |
||
118 | private static function loadPublicPEM(array $children): array |
||
119 | { |
||
120 | if (!$children[0] instanceof Sequence) { |
||
121 | throw new InvalidArgumentException('Unsupported key type.'); |
||
122 | } |
||
123 | |||
124 | $sub = $children[0]->getChildren(); |
||
125 | if (!$sub[0] instanceof ObjectIdentifier) { |
||
126 | throw new InvalidArgumentException('Unsupported key type.'); |
||
127 | } |
||
128 | if ('1.2.840.10045.2.1' !== $sub[0]->getContent()) { |
||
129 | throw new InvalidArgumentException('Unsupported key type.'); |
||
130 | } |
||
131 | if (!$sub[1] instanceof ObjectIdentifier) { |
||
132 | throw new InvalidArgumentException('Unsupported key type.'); |
||
133 | } |
||
134 | if (!$children[1] instanceof BitString) { |
||
135 | throw new InvalidArgumentException('Unable to load the key.'); |
||
136 | } |
||
137 | |||
138 | $bits = $children[1]->getContent(); |
||
139 | $bits_length = mb_strlen($bits, '8bit'); |
||
140 | if (0 !== mb_strpos($bits, '04', 0, '8bit')) { |
||
141 | throw new InvalidArgumentException('Unsupported key type'); |
||
142 | } |
||
143 | |||
144 | $values = ['kty' => 'EC']; |
||
145 | $values['crv'] = self::getCurve($sub[1]->getContent()); |
||
146 | $values['x'] = Base64Url::encode(hex2bin(mb_substr($bits, 2, ($bits_length - 2) / 2, '8bit'))); |
||
147 | $values['y'] = Base64Url::encode(hex2bin(mb_substr($bits, (int) (($bits_length - 2) / 2 + 2), ($bits_length - 2) / 2, '8bit'))); |
||
148 | |||
149 | return $values; |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * @throws InvalidArgumentException if the OID is not supported |
||
154 | */ |
||
155 | private static function getCurve(string $oid): string |
||
156 | { |
||
157 | $curves = self::getSupportedCurves(); |
||
158 | $curve = array_search($oid, $curves, true); |
||
159 | if (!\is_string($curve)) { |
||
160 | throw new InvalidArgumentException('Unsupported OID.'); |
||
161 | } |
||
162 | |||
163 | return $curve; |
||
164 | } |
||
165 | |||
166 | private static function getSupportedCurves(): array |
||
167 | { |
||
168 | return [ |
||
169 | 'P-256' => '1.2.840.10045.3.1.7', |
||
170 | 'P-384' => '1.3.132.0.34', |
||
171 | 'P-521' => '1.3.132.0.35', |
||
172 | ]; |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * @throws InvalidArgumentException if the key cannot be loaded |
||
177 | */ |
||
178 | private static function verifyVersion(ASNObject $children): void |
||
179 | { |
||
180 | if (!$children instanceof Integer || '1' !== $children->getContent()) { |
||
|
|||
181 | throw new InvalidArgumentException('Unable to load the key.'); |
||
182 | } |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * @throws InvalidArgumentException if the key cannot be loaded |
||
187 | */ |
||
188 | private static function getXAndY(ASNObject $children, string &$x, string &$y): void |
||
189 | { |
||
190 | if (!$children instanceof ExplicitlyTaggedObject || !\is_array($children->getContent())) { |
||
191 | throw new InvalidArgumentException('Unable to load the key.'); |
||
192 | } |
||
193 | if (!$children->getContent()[0] instanceof BitString) { |
||
194 | throw new InvalidArgumentException('Unable to load the key.'); |
||
195 | } |
||
196 | |||
197 | $bits = $children->getContent()[0]->getContent(); |
||
198 | $bits_length = mb_strlen($bits, '8bit'); |
||
199 | |||
200 | if (0 !== mb_strpos($bits, '04', 0, '8bit')) { |
||
201 | throw new InvalidArgumentException('Unsupported key type'); |
||
202 | } |
||
203 | |||
204 | $x = mb_substr($bits, 2, (int) (($bits_length - 2) / 2), '8bit'); |
||
205 | $y = mb_substr($bits, (int) (($bits_length - 2) / 2 + 2), (int) (($bits_length - 2) / 2), '8bit'); |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * @throws InvalidArgumentException if the key cannot be loaded |
||
210 | */ |
||
211 | private static function getD(ASNObject $children): string |
||
212 | { |
||
213 | if (!$children instanceof OctetString) { |
||
214 | throw new InvalidArgumentException('Unable to load the key.'); |
||
215 | } |
||
216 | |||
217 | return $children->getContent(); |
||
218 | } |
||
219 | |||
220 | /** |
||
221 | * @throws InvalidArgumentException if the key cannot be loaded |
||
222 | */ |
||
223 | private static function loadPrivatePEM(array $children): array |
||
248 | |||
249 | /** |
||
250 | * @param ASNObject[] $children |
||
251 | */ |
||
252 | private static function isPKCS8(array $children): bool |
||
253 | { |
||
254 | if (3 !== \count($children)) { |
||
255 | return false; |
||
256 | } |
||
257 | |||
258 | $classes = [0 => Integer::class, 1 => Sequence::class, 2 => OctetString::class]; |
||
259 | foreach ($classes as $k => $class) { |
||
260 | if (!$children[$k] instanceof $class) { |
||
267 | |||
268 | /** |
||
269 | * @throws InvalidArgumentException if the key is invalid |
||
270 | */ |
||
271 | private function loadJWK(array $jwk): void |
||
290 | } |
||
291 |