1919use Jose \Component \Core \Util \Ecc \Curve ;
2020use Jose \Component \Core \Util \Ecc \NistCurve ;
2121use RuntimeException ;
22+ use Throwable ;
2223
2324/**
2425 * @internal
@@ -29,9 +30,9 @@ public static function convertToPEM(JWK $jwk): string
2930 {
3031 if ($ jwk ->has ('d ' )) {
3132 return self ::convertPrivateKeyToPEM ($ jwk );
32- } else {
33- return self ::convertPublicKeyToPEM ($ jwk );
3433 }
34+
35+ return self ::convertPublicKeyToPEM ($ jwk );
3536 }
3637
3738 public static function convertPublicKeyToPEM (JWK $ jwk ): string
@@ -50,11 +51,11 @@ public static function convertPublicKeyToPEM(JWK $jwk): string
5051
5152 break ;
5253 default :
53- throw new \ InvalidArgumentException ('Unsupported curve. ' );
54+ throw new InvalidArgumentException ('Unsupported curve. ' );
5455 }
5556 $ der .= self ::getKey ($ jwk );
5657 $ pem = '-----BEGIN PUBLIC KEY----- ' .PHP_EOL ;
57- $ pem .= \ chunk_split (\ base64_encode ($ der ), 64 , PHP_EOL );
58+ $ pem .= chunk_split (base64_encode ($ der ), 64 , PHP_EOL );
5859 $ pem .= '-----END PUBLIC KEY----- ' .PHP_EOL ;
5960
6061 return $ pem ;
@@ -76,11 +77,11 @@ public static function convertPrivateKeyToPEM(JWK $jwk): string
7677
7778 break ;
7879 default :
79- throw new \ InvalidArgumentException ('Unsupported curve. ' );
80+ throw new InvalidArgumentException ('Unsupported curve. ' );
8081 }
8182 $ der .= self ::getKey ($ jwk );
8283 $ pem = '-----BEGIN EC PRIVATE KEY----- ' .PHP_EOL ;
83- $ pem .= \ chunk_split (\ base64_encode ($ der ), 64 , PHP_EOL );
84+ $ pem .= chunk_split (base64_encode ($ der ), 64 , PHP_EOL );
8485 $ pem .= '-----END EC PRIVATE KEY----- ' .PHP_EOL ;
8586
8687 return $ pem ;
@@ -96,27 +97,40 @@ public static function createECKey(string $curve, array $values = []): JWK
9697 {
9798 try {
9899 $ jwk = self ::createECKeyUsingOpenSSL ($ curve );
99- } catch (\ Exception $ e ) {
100+ } catch (Throwable $ e ) {
100101 $ jwk = self ::createECKeyUsingPurePhp ($ curve );
101102 }
102- $ values = \ array_merge ($ values , $ jwk );
103+ $ values = array_merge ($ values , $ jwk );
103104
104105 return new JWK ($ values );
105106 }
106107
108+ private static function getNistCurve (string $ curve ): Curve
109+ {
110+ switch ($ curve ) {
111+ case 'P-256 ' :
112+ return NistCurve::curve256 ();
113+ case 'P-384 ' :
114+ return NistCurve::curve384 ();
115+ case 'P-521 ' :
116+ return NistCurve::curve521 ();
117+ default :
118+ throw new InvalidArgumentException (sprintf ('The curve "%s" is not supported. ' , $ curve ));
119+ }
120+ }
121+
107122 private static function createECKeyUsingPurePhp (string $ curve ): array
108123 {
109- $ nistCurve = static ::getCurve ($ curve );
110- $ componentSize = (int ) ceil ($ nistCurve ->getSize () / 8 );
124+ $ nistCurve = self ::getNistCurve ($ curve );
111125 $ privateKey = $ nistCurve ->createPrivateKey ();
112126 $ publicKey = $ nistCurve ->createPublicKey ($ privateKey );
113127
114128 return [
115129 'kty ' => 'EC ' ,
116130 'crv ' => $ curve ,
117- 'd ' => Base64Url::encode (str_pad (gmp_export ($ privateKey -> getSecret ()), $ componentSize , "\0" , STR_PAD_LEFT )),
118- 'x ' => Base64Url::encode (str_pad (gmp_export ($ publicKey ->getPoint ()->getX ()), $ componentSize , "\0" , STR_PAD_LEFT )),
119- 'y ' => Base64Url::encode (str_pad (gmp_export ($ publicKey -> getPoint ()-> getY ()), $ componentSize , "\0" , STR_PAD_LEFT )),
131+ 'x ' => Base64Url::encode (str_pad (gmp_export ($ publicKey -> getPoint ()-> getX ()), ( int ) ceil ( $ nistCurve -> getSize () / 8 ) , "\0" , STR_PAD_LEFT )),
132+ 'y ' => Base64Url::encode (str_pad (gmp_export ($ publicKey ->getPoint ()->getY ()), ( int ) ceil ( $ nistCurve -> getSize () / 8 ) , "\0" , STR_PAD_LEFT )),
133+ 'd ' => Base64Url::encode (str_pad (gmp_export ($ privateKey -> getSecret ()), ( int ) ceil ( $ nistCurve -> getSize () / 8 ) , "\0" , STR_PAD_LEFT )),
120134 ];
121135 }
122136
@@ -126,40 +140,29 @@ private static function createECKeyUsingOpenSSL(string $curve): array
126140 'curve_name ' => self ::getOpensslCurveName ($ curve ),
127141 'private_key_type ' => OPENSSL_KEYTYPE_EC ,
128142 ]);
129- $ res = openssl_pkey_export ($ key , $ out );
130- if (false === $ res ) {
143+ if (false === $ key ) {
144+ throw new RuntimeException ('Unable to create the key ' );
145+ }
146+ $ result = openssl_pkey_export ($ key , $ out );
147+ if (false === $ result ) {
131148 throw new RuntimeException ('Unable to create the key ' );
132149 }
133150 $ res = openssl_pkey_get_private ($ out );
134-
151+ if (false === $ res ) {
152+ throw new RuntimeException ('Unable to create the key ' );
153+ }
135154 $ details = openssl_pkey_get_details ($ res );
136-
137- $ nistCurve = static ::getCurve ($ curve );
138- $ componentSize = (int ) ceil ($ nistCurve ->getSize () / 8 );
155+ $ nistCurve = self ::getNistCurve ($ curve );
139156
140157 return [
141158 'kty ' => 'EC ' ,
142159 'crv ' => $ curve ,
143- 'x ' => Base64Url::encode (str_pad ($ details ['ec ' ]['x ' ], $ componentSize , "\0" , STR_PAD_LEFT )),
144- 'y ' => Base64Url::encode (str_pad ($ details ['ec ' ]['y ' ], $ componentSize , "\0" , STR_PAD_LEFT )),
145- 'd ' => Base64Url::encode (str_pad ($ details ['ec ' ]['d ' ], $ componentSize , "\0" , STR_PAD_LEFT )),
160+ 'd ' => Base64Url::encode (str_pad ($ details ['ec ' ]['d ' ], ( int ) ceil ( $ nistCurve -> getSize () / 8 ) , "\0" , STR_PAD_LEFT )),
161+ 'x ' => Base64Url::encode (str_pad ($ details ['ec ' ]['x ' ], ( int ) ceil ( $ nistCurve -> getSize () / 8 ) , "\0" , STR_PAD_LEFT )),
162+ 'y ' => Base64Url::encode (str_pad ($ details ['ec ' ]['y ' ], ( int ) ceil ( $ nistCurve -> getSize () / 8 ) , "\0" , STR_PAD_LEFT )),
146163 ];
147164 }
148165
149- private static function getCurve (string $ curve ): Curve
150- {
151- switch ($ curve ) {
152- case 'P-256 ' :
153- return NistCurve::curve256 ();
154- case 'P-384 ' :
155- return NistCurve::curve384 ();
156- case 'P-521 ' :
157- return NistCurve::curve521 ();
158- default :
159- throw new InvalidArgumentException (\sprintf ('The curve "%s" is not supported. ' , $ curve ));
160- }
161- }
162-
163166 private static function getOpensslCurveName (string $ curve ): string
164167 {
165168 switch ($ curve ) {
@@ -170,114 +173,120 @@ private static function getOpensslCurveName(string $curve): string
170173 case 'P-521 ' :
171174 return 'secp521r1 ' ;
172175 default :
173- throw new InvalidArgumentException (\ sprintf ('The curve "%s" is not supported. ' , $ curve ));
176+ throw new InvalidArgumentException (sprintf ('The curve "%s" is not supported. ' , $ curve ));
174177 }
175178 }
176179
177180 private static function p256PublicKey (): string
178181 {
179- return \pack ('H* ' ,
182+ return pack (
183+ 'H* ' ,
180184 '3059 ' // SEQUENCE, length 89
181- .'3013 ' // SEQUENCE, length 19
182- .'0607 ' // OID, length 7
183- .'2a8648ce3d0201 ' // 1.2.840.10045.2.1 = EC Public Key
184- .'0608 ' // OID, length 8
185- .'2a8648ce3d030107 ' // 1.2.840.10045.3.1.7 = P-256 Curve
186- .'0342 ' // BIT STRING, length 66
187- .'00 ' // prepend with NUL - pubkey will follow
185+ .'3013 ' // SEQUENCE, length 19
186+ .'0607 ' // OID, length 7
187+ .'2a8648ce3d0201 ' // 1.2.840.10045.2.1 = EC Public Key
188+ .'0608 ' // OID, length 8
189+ .'2a8648ce3d030107 ' // 1.2.840.10045.3.1.7 = P-256 Curve
190+ .'0342 ' // BIT STRING, length 66
191+ .'00 ' // prepend with NUL - pubkey will follow
188192 );
189193 }
190194
191195 private static function p384PublicKey (): string
192196 {
193- return \pack ('H* ' ,
197+ return pack (
198+ 'H* ' ,
194199 '3076 ' // SEQUENCE, length 118
195- .'3010 ' // SEQUENCE, length 16
196- .'0607 ' // OID, length 7
197- .'2a8648ce3d0201 ' // 1.2.840.10045.2.1 = EC Public Key
198- .'0605 ' // OID, length 5
199- .'2b81040022 ' // 1.3.132.0.34 = P-384 Curve
200- .'0362 ' // BIT STRING, length 98
201- .'00 ' // prepend with NUL - pubkey will follow
200+ .'3010 ' // SEQUENCE, length 16
201+ .'0607 ' // OID, length 7
202+ .'2a8648ce3d0201 ' // 1.2.840.10045.2.1 = EC Public Key
203+ .'0605 ' // OID, length 5
204+ .'2b81040022 ' // 1.3.132.0.34 = P-384 Curve
205+ .'0362 ' // BIT STRING, length 98
206+ .'00 ' // prepend with NUL - pubkey will follow
202207 );
203208 }
204209
205210 private static function p521PublicKey (): string
206211 {
207- return \pack ('H* ' ,
212+ return pack (
213+ 'H* ' ,
208214 '30819b ' // SEQUENCE, length 154
209- .'3010 ' // SEQUENCE, length 16
210- .'0607 ' // OID, length 7
211- .'2a8648ce3d0201 ' // 1.2.840.10045.2.1 = EC Public Key
212- .'0605 ' // OID, length 5
213- .'2b81040023 ' // 1.3.132.0.35 = P-521 Curve
214- .'038186 ' // BIT STRING, length 134
215- .'00 ' // prepend with NUL - pubkey will follow
215+ .'3010 ' // SEQUENCE, length 16
216+ .'0607 ' // OID, length 7
217+ .'2a8648ce3d0201 ' // 1.2.840.10045.2.1 = EC Public Key
218+ .'0605 ' // OID, length 5
219+ .'2b81040023 ' // 1.3.132.0.35 = P-521 Curve
220+ .'038186 ' // BIT STRING, length 134
221+ .'00 ' // prepend with NUL - pubkey will follow
216222 );
217223 }
218224
219225 private static function p256PrivateKey (JWK $ jwk ): string
220226 {
221- $ d = \ unpack ('H* ' , Base64Url::decode ($ jwk ->get ('d ' )))[1 ];
222- $ dl = \mb_strlen ( $ d , ' 8bit ' ) / 2 ;
223-
224- return \pack ( 'H* ' ,
225- '30 ' . \dechex ( 87 + $ dl ) // SEQUENCE, length 87+length($d)
226- .'020101 ' // INTEGER, 1
227- . ' 04 ' . \dechex ( $ dl ) // OCTET STRING, length($d)
228- .$ d
229- .'a00a ' // TAGGED OBJECT #0, length 10
230- .'0608 ' // OID, length 8
231- .'2a8648ce3d030107 ' // 1.3.132.0.34 = P-384 Curve
232- .'a144 ' // TAGGED OBJECT #1, length 68
233- .'0342 ' // BIT STRING, length 66
234- .'00 ' // prepend with NUL - pubkey will follow
227+ $ d = unpack ('H* ' , str_pad ( Base64Url::decode ($ jwk ->get ('d ' )), 32 , "\0" , STR_PAD_LEFT ))[1 ];
228+
229+ return pack (
230+ 'H* ' ,
231+ '3077 ' // SEQUENCE, length 87+length($d)=32
232+ .'020101 ' // INTEGER, 1
233+ . ' 0420 ' // OCTET STRING, length($d) = 32
234+ .$ d
235+ .'a00a ' // TAGGED OBJECT #0, length 10
236+ .'0608 ' // OID, length 8
237+ .'2a8648ce3d030107 ' // 1.3.132.0.34 = P-384 Curve
238+ .'a144 ' // TAGGED OBJECT #1, length 68
239+ .'0342 ' // BIT STRING, length 66
240+ .'00 ' // prepend with NUL - pubkey will follow
235241 );
236242 }
237243
238244 private static function p384PrivateKey (JWK $ jwk ): string
239245 {
240- $ d = \ unpack ('H* ' , Base64Url::decode ($ jwk ->get ('d ' )))[1 ];
241- $ dl = \mb_strlen ( $ d , ' 8bit ' ) / 2 ;
242-
243- return \pack ( 'H* ' ,
244- '3081 ' . \dechex ( 116 + $ dl ) // SEQUENCE, length 116 + length($d)
245- .'020101 ' // INTEGER, 1
246- . ' 04 ' . \dechex ( $ dl ) // OCTET STRING, length($d)
247- .$ d
248- .'a007 ' // TAGGED OBJECT #0, length 7
249- .'0605 ' // OID, length 5
250- .'2b81040022 ' // 1.3.132.0.34 = P-384 Curve
251- .'a164 ' // TAGGED OBJECT #1, length 100
252- .'0362 ' // BIT STRING, length 98
253- .'00 ' // prepend with NUL - pubkey will follow
246+ $ d = unpack ('H* ' , str_pad ( Base64Url::decode ($ jwk ->get ('d ' )), 48 , "\0" , STR_PAD_LEFT ))[1 ];
247+
248+ return pack (
249+ 'H* ' ,
250+ '3081a4 ' // SEQUENCE, length 116 + length($d)=48
251+ .'020101 ' // INTEGER, 1
252+ . ' 0430 ' // OCTET STRING, length($d) = 30
253+ .$ d
254+ .'a007 ' // TAGGED OBJECT #0, length 7
255+ .'0605 ' // OID, length 5
256+ .'2b81040022 ' // 1.3.132.0.34 = P-384 Curve
257+ .'a164 ' // TAGGED OBJECT #1, length 100
258+ .'0362 ' // BIT STRING, length 98
259+ .'00 ' // prepend with NUL - pubkey will follow
254260 );
255261 }
256262
257263 private static function p521PrivateKey (JWK $ jwk ): string
258264 {
259- $ d = \ unpack ('H* ' , Base64Url::decode ($ jwk ->get ('d ' )))[1 ];
260- $ dl = \mb_strlen ( $ d , ' 8bit ' ) / 2 ;
261-
262- return \pack ( 'H* ' ,
263- '3081 ' . \dechex ( 154 + $ dl ) // SEQUENCE, length 154+ length(d)
264- .'020101 ' // INTEGER, 1
265- . ' 04 ' . \dechex ( $ dl ) // OCTET STRING, length(d)
266- .$ d
267- .'a007 ' // TAGGED OBJECT #0, length 7
268- .'0605 ' // OID, length 5
269- .'2b81040023 ' // 1.3.132.0.35 = P-521 Curve
270- .'a18189 ' // TAGGED OBJECT #1, length 137
271- .'038186 ' // BIT STRING, length 134
272- .'00 ' // prepend with NUL - pubkey will follow
265+ $ d = unpack ('H* ' , str_pad ( Base64Url::decode ($ jwk ->get ('d ' )), 66 , "\0" , STR_PAD_LEFT ))[1 ];
266+
267+ return pack (
268+ 'H* ' ,
269+ '3081dc ' // SEQUENCE, length 154 + length($d)=66
270+ .'020101 ' // INTEGER, 1
271+ . ' 0442 ' // OCTET STRING, length(d) = 66
272+ .$ d
273+ .'a007 ' // TAGGED OBJECT #0, length 7
274+ .'0605 ' // OID, length 5
275+ .'2b81040023 ' // 1.3.132.0.35 = P-521 Curve
276+ .'a18189 ' // TAGGED OBJECT #1, length 137
277+ .'038186 ' // BIT STRING, length 134
278+ .'00 ' // prepend with NUL - pubkey will follow
273279 );
274280 }
275281
276282 private static function getKey (JWK $ jwk ): string
277283 {
284+ $ nistCurve = self ::getNistCurve ($ jwk ->get ('crv ' ));
285+ $ length = (int ) ceil ($ nistCurve ->getSize () / 8 );
286+
278287 return
279- \pack ( ' H* ' , ' 04 ' )
280- .Base64Url::decode ($ jwk ->get ('x ' ))
281- .Base64Url::decode ($ jwk ->get ('y ' ));
288+ "\04"
289+ .str_pad ( Base64Url::decode ($ jwk ->get ('x ' )), $ length , "\0" , STR_PAD_LEFT )
290+ .str_pad ( Base64Url::decode ($ jwk ->get ('y ' )), $ length , "\0" , STR_PAD_LEFT );
282291 }
283292}
0 commit comments