19
19
use Jose \Component \Core \Util \Ecc \Curve ;
20
20
use Jose \Component \Core \Util \Ecc \NistCurve ;
21
21
use RuntimeException ;
22
+ use Throwable ;
22
23
23
24
/**
24
25
* @internal
@@ -29,9 +30,9 @@ public static function convertToPEM(JWK $jwk): string
29
30
{
30
31
if ($ jwk ->has ('d ' )) {
31
32
return self ::convertPrivateKeyToPEM ($ jwk );
32
- } else {
33
- return self ::convertPublicKeyToPEM ($ jwk );
34
33
}
34
+
35
+ return self ::convertPublicKeyToPEM ($ jwk );
35
36
}
36
37
37
38
public static function convertPublicKeyToPEM (JWK $ jwk ): string
@@ -50,11 +51,11 @@ public static function convertPublicKeyToPEM(JWK $jwk): string
50
51
51
52
break ;
52
53
default :
53
- throw new \ InvalidArgumentException ('Unsupported curve. ' );
54
+ throw new InvalidArgumentException ('Unsupported curve. ' );
54
55
}
55
56
$ der .= self ::getKey ($ jwk );
56
57
$ 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 );
58
59
$ pem .= '-----END PUBLIC KEY----- ' .PHP_EOL ;
59
60
60
61
return $ pem ;
@@ -76,11 +77,11 @@ public static function convertPrivateKeyToPEM(JWK $jwk): string
76
77
77
78
break ;
78
79
default :
79
- throw new \ InvalidArgumentException ('Unsupported curve. ' );
80
+ throw new InvalidArgumentException ('Unsupported curve. ' );
80
81
}
81
82
$ der .= self ::getKey ($ jwk );
82
83
$ 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 );
84
85
$ pem .= '-----END EC PRIVATE KEY----- ' .PHP_EOL ;
85
86
86
87
return $ pem ;
@@ -96,27 +97,40 @@ public static function createECKey(string $curve, array $values = []): JWK
96
97
{
97
98
try {
98
99
$ jwk = self ::createECKeyUsingOpenSSL ($ curve );
99
- } catch (\ Exception $ e ) {
100
+ } catch (Throwable $ e ) {
100
101
$ jwk = self ::createECKeyUsingPurePhp ($ curve );
101
102
}
102
- $ values = \ array_merge ($ values , $ jwk );
103
+ $ values = array_merge ($ values , $ jwk );
103
104
104
105
return new JWK ($ values );
105
106
}
106
107
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
+
107
122
private static function createECKeyUsingPurePhp (string $ curve ): array
108
123
{
109
- $ nistCurve = static ::getCurve ($ curve );
110
- $ componentSize = (int ) ceil ($ nistCurve ->getSize () / 8 );
124
+ $ nistCurve = self ::getNistCurve ($ curve );
111
125
$ privateKey = $ nistCurve ->createPrivateKey ();
112
126
$ publicKey = $ nistCurve ->createPublicKey ($ privateKey );
113
127
114
128
return [
115
129
'kty ' => 'EC ' ,
116
130
'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 )),
120
134
];
121
135
}
122
136
@@ -126,40 +140,29 @@ private static function createECKeyUsingOpenSSL(string $curve): array
126
140
'curve_name ' => self ::getOpensslCurveName ($ curve ),
127
141
'private_key_type ' => OPENSSL_KEYTYPE_EC ,
128
142
]);
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 ) {
131
148
throw new RuntimeException ('Unable to create the key ' );
132
149
}
133
150
$ res = openssl_pkey_get_private ($ out );
134
-
151
+ if (false === $ res ) {
152
+ throw new RuntimeException ('Unable to create the key ' );
153
+ }
135
154
$ details = openssl_pkey_get_details ($ res );
136
-
137
- $ nistCurve = static ::getCurve ($ curve );
138
- $ componentSize = (int ) ceil ($ nistCurve ->getSize () / 8 );
155
+ $ nistCurve = self ::getNistCurve ($ curve );
139
156
140
157
return [
141
158
'kty ' => 'EC ' ,
142
159
'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 )),
146
163
];
147
164
}
148
165
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
-
163
166
private static function getOpensslCurveName (string $ curve ): string
164
167
{
165
168
switch ($ curve ) {
@@ -170,114 +173,120 @@ private static function getOpensslCurveName(string $curve): string
170
173
case 'P-521 ' :
171
174
return 'secp521r1 ' ;
172
175
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 ));
174
177
}
175
178
}
176
179
177
180
private static function p256PublicKey (): string
178
181
{
179
- return \pack ('H* ' ,
182
+ return pack (
183
+ 'H* ' ,
180
184
'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
188
192
);
189
193
}
190
194
191
195
private static function p384PublicKey (): string
192
196
{
193
- return \pack ('H* ' ,
197
+ return pack (
198
+ 'H* ' ,
194
199
'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
202
207
);
203
208
}
204
209
205
210
private static function p521PublicKey (): string
206
211
{
207
- return \pack ('H* ' ,
212
+ return pack (
213
+ 'H* ' ,
208
214
'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
216
222
);
217
223
}
218
224
219
225
private static function p256PrivateKey (JWK $ jwk ): string
220
226
{
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
235
241
);
236
242
}
237
243
238
244
private static function p384PrivateKey (JWK $ jwk ): string
239
245
{
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
254
260
);
255
261
}
256
262
257
263
private static function p521PrivateKey (JWK $ jwk ): string
258
264
{
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
273
279
);
274
280
}
275
281
276
282
private static function getKey (JWK $ jwk ): string
277
283
{
284
+ $ nistCurve = self ::getNistCurve ($ jwk ->get ('crv ' ));
285
+ $ length = (int ) ceil ($ nistCurve ->getSize () / 8 );
286
+
278
287
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 );
282
291
}
283
292
}
0 commit comments