66from datetime import datetime
77from datetime import timezone
88
9- from OpenSSL import crypto
9+ from cryptography import x509
10+ from cryptography .exceptions import InvalidSignature
11+ from cryptography .hazmat .primitives import hashes , serialization
12+ from cryptography .hazmat .primitives .asymmetric import rsa
13+ from cryptography .x509 .oid import NameOID
1014import dateutil .parser
1115
1216import saml2 .cryptography .pki
@@ -36,7 +40,6 @@ def create_certificate(
3640 valid_to = 315360000 ,
3741 sn = 1 ,
3842 key_length = 1024 ,
39- hash_alg = "sha256" ,
4043 write_to_file = False ,
4144 cert_dir = "" ,
4245 cipher_passphrase = None ,
@@ -87,8 +90,6 @@ def create_certificate(
8790 is 1.
8891 :param key_length: Length of the key to be generated. Defaults
8992 to 1024.
90- :param hash_alg: Hash algorithm to use for the key. Default
91- is sha256.
9293 :param write_to_file: True if you want to write the certificate
9394 to a file. The method will then return
9495 a tuple with path to certificate file and
@@ -131,49 +132,68 @@ def create_certificate(
131132 k_f = join (cert_dir , key_file )
132133
133134 # create a key pair
134- k = crypto .PKey ()
135- k .generate_key (crypto .TYPE_RSA , key_length )
135+ k = rsa .generate_private_key (
136+ public_exponent = 65537 ,
137+ key_size = key_length ,
138+ )
136139
137140 # create a self-signed cert
138- cert = crypto . X509 ()
141+ builder = x509 . CertificateBuilder ()
139142
140143 if request :
141- cert = crypto . X509Req ()
144+ builder = x509 . CertificateSigningRequestBuilder ()
142145
143146 if len (cert_info ["country_code" ]) != 2 :
144147 raise WrongInput ("Country code must be two letters!" )
145- cert .get_subject ().C = cert_info ["country_code" ]
146- cert .get_subject ().ST = cert_info ["state" ]
147- cert .get_subject ().L = cert_info ["city" ]
148- cert .get_subject ().O = cert_info ["organization" ] # noqa: E741
149- cert .get_subject ().OU = cert_info ["organization_unit" ]
150- cert .get_subject ().CN = cn
148+ subject_name = x509 .Name ([
149+ x509 .NameAttribute (NameOID .COUNTRY_NAME ,
150+ cert_info ["country_code" ]),
151+ x509 .NameAttribute (NameOID .STATE_OR_PROVINCE_NAME ,
152+ cert_info ["state" ]),
153+ x509 .NameAttribute (NameOID .LOCALITY_NAME ,
154+ cert_info ["city" ]),
155+ x509 .NameAttribute (NameOID .ORGANIZATION_NAME ,
156+ cert_info ["organization" ]),
157+ x509 .NameAttribute (NameOID .ORGANIZATIONAL_UNIT_NAME ,
158+ cert_info ["organization_unit" ]),
159+ x509 .NameAttribute (NameOID .COMMON_NAME , cn ),
160+ ])
161+ builder = builder .subject_name (subject_name )
151162 if not request :
152- cert .set_serial_number (sn )
153- cert .gmtime_adj_notBefore (valid_from ) # Valid before present time
154- cert .gmtime_adj_notAfter (valid_to ) # 3 650 days
155- cert .set_issuer (cert .get_subject ())
156- cert .set_pubkey (k )
157- cert .sign (k , hash_alg )
163+ now = datetime .datetime .now (datetime .UTC )
164+ builder = builder .serial_number (
165+ sn ,
166+ ).not_valid_before (
167+ now + datetime .timedelta (seconds = valid_from ),
168+ ).not_valid_after (
169+ now + datetime .timedelta (seconds = valid_to ),
170+ ).issuer_name (
171+ subject_name ,
172+ ).public_key (
173+ k .public_key (),
174+ )
175+ cert = builder .sign (k , hashes .SHA256 ())
158176
159177 try :
160- if request :
161- tmp_cert = crypto .dump_certificate_request (crypto .FILETYPE_PEM , cert )
162- else :
163- tmp_cert = crypto .dump_certificate (crypto .FILETYPE_PEM , cert )
164- tmp_key = None
178+ tmp_cert = cert .public_bytes (serialization .Encoding .PEM )
179+ key_encryption = None
165180 if cipher_passphrase is not None :
166181 passphrase = cipher_passphrase ["passphrase" ]
167182 if isinstance (cipher_passphrase ["passphrase" ], str ):
168183 passphrase = passphrase .encode ("utf-8" )
169- tmp_key = crypto . dump_privatekey ( crypto . FILETYPE_PEM , k , cipher_passphrase [ "cipher" ], passphrase )
184+ key_encryption = serialization . BestAvailableEncryption ( passphrase )
170185 else :
171- tmp_key = crypto .dump_privatekey (crypto .FILETYPE_PEM , k )
186+ key_encryption = serialization .NoEncryption ()
187+ tmp_key = k .private_bytes (
188+ encoding = serialization .Encoding .PEM ,
189+ format = serialization .PrivateFormat .TraditionalOpenSSL ,
190+ encryption_algorithm = key_encryption ,
191+ )
172192 if write_to_file :
173- with open (c_f , "w " ) as fc :
174- fc .write (tmp_cert . decode ( "utf-8" ) )
175- with open (k_f , "w " ) as fk :
176- fk .write (tmp_key . decode ( "utf-8" ) )
193+ with open (c_f , "wb " ) as fc :
194+ fc .write (tmp_cert )
195+ with open (k_f , "wb " ) as fk :
196+ fk .write (tmp_key )
177197 return c_f , k_f
178198 return tmp_cert , tmp_key
179199 except Exception as ex :
@@ -198,7 +218,6 @@ def create_cert_signed_certificate(
198218 sign_cert_str ,
199219 sign_key_str ,
200220 request_cert_str ,
201- hash_alg = "sha256" ,
202221 valid_from = 0 ,
203222 valid_to = 315360000 ,
204223 sn = 1 ,
@@ -222,8 +241,6 @@ def create_cert_signed_certificate(
222241 the requested certificate. If you only have
223242 a file use the method read_str_from_file
224243 to get a string representation.
225- :param hash_alg: Hash algorithm to use for the key. Default
226- is sha256.
227244 :param valid_from: When the certificate starts to be valid.
228245 Amount of seconds from when the
229246 certificate is generated.
@@ -237,27 +254,29 @@ def create_cert_signed_certificate(
237254 :return: String representation of the signed
238255 certificate.
239256 """
240- ca_cert = crypto .load_certificate (crypto .FILETYPE_PEM , sign_cert_str )
241- ca_key = None
242- if passphrase is not None :
243- ca_key = crypto .load_privatekey (crypto .FILETYPE_PEM , sign_key_str , passphrase )
244- else :
245- ca_key = crypto .load_privatekey (crypto .FILETYPE_PEM , sign_key_str )
246- req_cert = crypto .load_certificate_request (crypto .FILETYPE_PEM , request_cert_str )
247-
248- cert = crypto .X509 ()
249- cert .set_subject (req_cert .get_subject ())
250- cert .set_serial_number (sn )
251- cert .gmtime_adj_notBefore (valid_from )
252- cert .gmtime_adj_notAfter (valid_to )
253- cert .set_issuer (ca_cert .get_subject ())
254- cert .set_pubkey (req_cert .get_pubkey ())
255- cert .sign (ca_key , hash_alg )
256-
257- cert_dump = crypto .dump_certificate (crypto .FILETYPE_PEM , cert )
258- if isinstance (cert_dump , str ):
259- return cert_dump
260- return cert_dump .decode ("utf-8" )
257+ if isinstance (sign_cert_str , str ):
258+ sign_cert_str = sign_cert_str .encode ("utf-8" )
259+ ca_cert = x509 .load_pem_x509_certificate (sign_cert_str )
260+ ca_key = serialization .load_pem_private_key (
261+ sign_key_str , password = passphrase )
262+ req_cert = x509 .load_pem_x509_csr (request_cert_str )
263+
264+ now = datetime .datetime .now (datetime .UTC )
265+ cert = x509 .CertificateBuilder ().subject_name (
266+ req_cert .subject ,
267+ ).serial_number (
268+ sn ,
269+ ).not_valid_before (
270+ now + datetime .timedelta (seconds = valid_from ),
271+ ).not_valid_after (
272+ now + datetime .timedelta (seconds = valid_to ),
273+ ).issuer_name (
274+ ca_cert .subject ,
275+ ).public_key (
276+ req_cert .public_key (),
277+ ).sign (ca_key , hashes .SHA256 ())
278+
279+ return cert .public_bytes (serialization .Encoding .PEM ).decode ("utf-8" )
261280
262281 def verify_chain (self , cert_chain_str_list , cert_str ):
263282 """
@@ -276,13 +295,6 @@ def verify_chain(self, cert_chain_str_list, cert_str):
276295 cert_str = tmp_cert_str
277296 return (True , "Signed certificate is valid and correctly signed by CA " "certificate." )
278297
279- def certificate_not_valid_yet (self , cert ):
280- starts_to_be_valid = dateutil .parser .parse (cert .get_notBefore ())
281- now = datetime .now (timezone .utc )
282- if starts_to_be_valid < now :
283- return False
284- return True
285-
286298 def verify (self , signing_cert_str , cert_str ):
287299 """
288300 Verifies if a certificate is valid and signed by a given certificate.
@@ -303,34 +315,34 @@ def verify(self, signing_cert_str, cert_str):
303315 Message = Why the validation failed.
304316 """
305317 try :
306- ca_cert = crypto .load_certificate (crypto .FILETYPE_PEM , signing_cert_str )
307- cert = crypto .load_certificate (crypto .FILETYPE_PEM , cert_str )
308-
309- if self .certificate_not_valid_yet (ca_cert ):
318+ if isinstance (signing_cert_str , str ):
319+ signing_cert_str = signing_cert_str .encode ("utf-8" )
320+ if isinstance (cert_str , str ):
321+ cert_str = cert_str .encode ("utf-8" )
322+ ca_cert = x509 .load_pem_x509_certificate (signing_cert_str )
323+ cert = x509 .load_pem_x509_certificate (cert_str )
324+ now = datetime .datetime .now (datetime .UTC )
325+
326+ if ca_cert .not_valid_before_utc >= now :
310327 return False , "CA certificate is not valid yet."
311328
312- if ca_cert .has_expired () == 1 :
329+ if ca_cert .not_valid_after_utc < now :
313330 return False , "CA certificate is expired."
314331
315- if cert .has_expired () == 1 :
332+ if cert .not_valid_after_utc < now :
316333 return False , "The signed certificate is expired."
317334
318- if self . certificate_not_valid_yet ( cert ) :
335+ if cert . not_valid_before_utc >= now :
319336 return False , "The signed certificate is not valid yet."
320337
321- if ca_cert .get_subject ().CN == cert .get_subject ().CN :
338+ if ca_cert .subject .get_attributes_for_oid (NameOID .COMMON_NAME ) == \
339+ cert .subject .get_attributes_for_oid (NameOID .COMMON_NAME ):
322340 return False , ("CN may not be equal for CA certificate and the " "signed certificate." )
323341
324- cert_algorithm = cert .get_signature_algorithm ()
325- cert_algorithm = cert_algorithm .decode ("ascii" )
326- cert_str = cert_str .encode ("ascii" )
327-
328- cert_crypto = saml2 .cryptography .pki .load_pem_x509_certificate (cert_str )
329-
330342 try :
331- crypto . verify (ca_cert , cert_crypto . signature , cert_crypto . tbs_certificate_bytes , cert_algorithm )
343+ cert . verify_directly_issued_by (ca_cert )
332344 return True , "Signed certificate is valid and correctly signed by CA certificate."
333- except crypto . Error as e :
345+ except ( ValueError , TypeError , InvalidSignature ) as e :
334346 return False , f"Certificate is incorrectly signed: { str (e )} "
335347 except Exception as e :
336348 return False , f"Certificate is not valid for an unknown reason. { str (e )} "
@@ -352,8 +364,14 @@ def read_cert_from_file(cert_file, cert_type="pem"):
352364 data = fp .read ()
353365
354366 try :
355- cert = saml2 .cryptography .pki .load_x509_certificate (data , cert_type )
356- pem_data = saml2 .cryptography .pki .get_public_bytes_from_cert (cert )
367+ cert = None
368+ if cert_type == "pem" :
369+ cert = x509 .load_pem_x509_certificate (data )
370+ elif cert_type == "der" :
371+ cert = x509 .load_der_x509_certificate (data )
372+ else :
373+ raise ValueError (f"cert-type { cert_type } not supported" )
374+ pem_data = cert .public_bytes (serialization .Encoding .PEM ).decode ("utf-8" )
357375 except Exception as e :
358376 raise CertificateError (e )
359377
0 commit comments