33from ecdsa .util import sigdecode_string , sigencode_string , sigdecode_der , sigencode_der
44
55from jose .backends .base import Key
6- from jose .utils import base64_to_long
6+ from jose .utils import base64_to_long , long_to_base64
77from jose .constants import ALGORITHMS
88from jose .exceptions import JWKError
99
@@ -68,19 +68,26 @@ def _process_jwk(self, jwk_dict):
6868 if not jwk_dict .get ('kty' ) == 'EC' :
6969 raise JWKError ("Incorrect key type. Expected: 'EC', Recieved: %s" % jwk_dict .get ('kty' ))
7070
71+ if not all (k in jwk_dict for k in ['x' , 'y' , 'crv' ]):
72+ raise JWKError ('Mandatory parameters are missing' )
73+
7174 x = base64_to_long (jwk_dict .get ('x' ))
7275 y = base64_to_long (jwk_dict .get ('y' ))
73-
7476 curve = {
7577 'P-256' : ec .SECP256R1 ,
7678 'P-384' : ec .SECP384R1 ,
7779 'P-521' : ec .SECP521R1 ,
7880 }[jwk_dict ['crv' ]]
7981
80- ec_pn = ec .EllipticCurvePublicNumbers (x , y , curve ())
81- verifying_key = ec_pn .public_key (self .cryptography_backend ())
82+ public = ec .EllipticCurvePublicNumbers (x , y , curve ())
83+
84+ if 'd' in jwk_dict :
85+ d = base64_to_long (jwk_dict .get ('d' ))
86+ private = ec .EllipticCurvePrivateNumbers (d , public )
8287
83- return verifying_key
88+ return private .private_key (self .cryptography_backend ())
89+ else :
90+ return public .public_key (self .cryptography_backend ())
8491
8592 def sign (self , msg ):
8693 if self .hash_alg .digest_size * 8 > self .prepared_key .curve .key_size :
@@ -97,17 +104,21 @@ def verify(self, msg, sig):
97104 verifier = self .prepared_key .verifier (signature , ec .ECDSA (self .hash_alg ()))
98105 verifier .update (msg )
99106 try :
100- return verifier .verify ()
107+ verifier .verify ()
108+ return True
101109 except :
102110 return False
103111
112+ def is_public (self ):
113+ return hasattr (self .prepared_key , 'public_bytes' )
114+
104115 def public_key (self ):
105- if hasattr ( self .prepared_key , 'public_bytes' ):
116+ if self .is_public ( ):
106117 return self
107118 return self .__class__ (self .prepared_key .public_key (), self ._algorithm )
108119
109120 def to_pem (self ):
110- if hasattr ( self .prepared_key , 'public_bytes' ):
121+ if self .is_public ( ):
111122 pem = self .prepared_key .public_bytes (
112123 encoding = serialization .Encoding .PEM ,
113124 format = serialization .PublicFormat .SubjectPublicKeyInfo
@@ -120,6 +131,39 @@ def to_pem(self):
120131 )
121132 return pem
122133
134+ def to_dict (self ):
135+ if not self .is_public ():
136+ public_key = self .prepared_key .public_key ()
137+ else :
138+ public_key = self .prepared_key
139+
140+ crv = {
141+ 'secp256r1' : 'P-256' ,
142+ 'secp384r1' : 'P-384' ,
143+ 'secp521r1' : 'P-521' ,
144+ }[self .prepared_key .curve .name ]
145+
146+ # Calculate the key size in bytes. Section 6.2.1.2 and 6.2.1.3 of
147+ # RFC7518 prescribes that the 'x', 'y' and 'd' parameters of the curve
148+ # points must be encoded as octed-strings of this length.
149+ key_size = (self .prepared_key .curve .key_size + 7 ) // 8
150+
151+ data = {
152+ 'alg' : self ._algorithm ,
153+ 'kty' : 'EC' ,
154+ 'crv' : crv ,
155+ 'x' : long_to_base64 (public_key .public_numbers ().x , size = key_size ),
156+ 'y' : long_to_base64 (public_key .public_numbers ().y , size = key_size ),
157+ }
158+
159+ if not self .is_public ():
160+ data ['d' ] = long_to_base64 (
161+ self .prepared_key .private_numbers ().private_value ,
162+ size = key_size
163+ )
164+
165+ return data
166+
123167
124168class CryptographyRSAKey (Key ):
125169 SHA256 = hashes .SHA256
@@ -170,17 +214,51 @@ def _process_jwk(self, jwk_dict):
170214
171215 e = base64_to_long (jwk_dict .get ('e' , 256 ))
172216 n = base64_to_long (jwk_dict .get ('n' ))
173-
174- verifying_key = rsa .RSAPublicNumbers (e , n ).public_key (self .cryptography_backend ())
175- return verifying_key
217+ public = rsa .RSAPublicNumbers (e , n )
218+
219+ if 'd' not in jwk_dict :
220+ return public .public_key (self .cryptography_backend ())
221+ else :
222+ # This is a private key.
223+ d = base64_to_long (jwk_dict .get ('d' ))
224+
225+ extra_params = ['p' , 'q' , 'dp' , 'dq' , 'qi' ]
226+
227+ if any (k in jwk_dict for k in extra_params ):
228+ # Precomputed private key parameters are available.
229+ if not all (k in jwk_dict for k in extra_params ):
230+ # These values must be present when 'p' is according to
231+ # Section 6.3.2 of RFC7518, so if they are not we raise
232+ # an error.
233+ raise JWKError ('Precomputed private key parameters are incomplete.' )
234+
235+ p = base64_to_long (jwk_dict ['p' ])
236+ q = base64_to_long (jwk_dict ['q' ])
237+ dp = base64_to_long (jwk_dict ['dp' ])
238+ dq = base64_to_long (jwk_dict ['dq' ])
239+ qi = base64_to_long (jwk_dict ['qi' ])
240+ else :
241+ # The precomputed private key parameters are not available,
242+ # so we use cryptography's API to fill them in.
243+ p , q = rsa .rsa_recover_prime_factors (n , e , d )
244+ dp = rsa .rsa_crt_dmp1 (d , p )
245+ dq = rsa .rsa_crt_dmq1 (d , q )
246+ qi = rsa .rsa_crt_iqmp (p , q )
247+
248+ private = rsa .RSAPrivateNumbers (p , q , d , dp , dq , qi , public )
249+
250+ return private .private_key (self .cryptography_backend ())
176251
177252 def sign (self , msg ):
178- signer = self .prepared_key .signer (
179- padding .PKCS1v15 (),
180- self .hash_alg ()
181- )
182- signer .update (msg )
183- signature = signer .finalize ()
253+ try :
254+ signer = self .prepared_key .signer (
255+ padding .PKCS1v15 (),
256+ self .hash_alg ()
257+ )
258+ signer .update (msg )
259+ signature = signer .finalize ()
260+ except Exception as e :
261+ raise JWKError (e )
184262 return signature
185263
186264 def verify (self , msg , sig ):
@@ -196,13 +274,16 @@ def verify(self, msg, sig):
196274 except InvalidSignature :
197275 return False
198276
277+ def is_public (self ):
278+ return hasattr (self .prepared_key , 'public_bytes' )
279+
199280 def public_key (self ):
200- if hasattr ( self .prepared_key , 'public_bytes' ):
281+ if self .is_public ( ):
201282 return self
202283 return self .__class__ (self .prepared_key .public_key (), self ._algorithm )
203284
204285 def to_pem (self ):
205- if hasattr ( self .prepared_key , 'public_bytes' ):
286+ if self .is_public ( ):
206287 return self .prepared_key .public_bytes (
207288 encoding = serialization .Encoding .PEM ,
208289 format = serialization .PublicFormat .SubjectPublicKeyInfo
@@ -213,3 +294,28 @@ def to_pem(self):
213294 format = serialization .PrivateFormat .TraditionalOpenSSL ,
214295 encryption_algorithm = serialization .NoEncryption ()
215296 )
297+
298+ def to_dict (self ):
299+ if not self .is_public ():
300+ public_key = self .prepared_key .public_key ()
301+ else :
302+ public_key = self .prepared_key
303+
304+ data = {
305+ 'alg' : self ._algorithm ,
306+ 'kty' : 'RSA' ,
307+ 'n' : long_to_base64 (public_key .public_numbers ().n ),
308+ 'e' : long_to_base64 (public_key .public_numbers ().e ),
309+ }
310+
311+ if not self .is_public ():
312+ data .update ({
313+ 'd' : long_to_base64 (self .prepared_key .private_numbers ().d ),
314+ 'p' : long_to_base64 (self .prepared_key .private_numbers ().p ),
315+ 'q' : long_to_base64 (self .prepared_key .private_numbers ().q ),
316+ 'dp' : long_to_base64 (self .prepared_key .private_numbers ().dmp1 ),
317+ 'dq' : long_to_base64 (self .prepared_key .private_numbers ().dmq1 ),
318+ 'qi' : long_to_base64 (self .prepared_key .private_numbers ().iqmp ),
319+ })
320+
321+ return data
0 commit comments