22import ssl
33import datetime
44from ipaddress import ip_address
5+ from typing import Literal , Optional
56
67# Importing cryptography modules for X509 certificates, private key generation,
78# and serialization, including RSA key generation and PEM encoding without encryption.
@@ -241,7 +242,9 @@ def ca_certificate_path(self, path: str) -> None:
241242 """
242243 self .ca_cert_path_ = path
243244
244- def sign_csr (self , csr_data : bytes , is_server : bool | None = None ) -> bytes :
245+ def sign_csr (
246+ self , csr_data : bytes , role : Optional [Literal ["server" , "client" ]] = None
247+ ) -> bytes :
245248 """
246249 Sign a Certificate Signing Request (CSR) with the Certificate Authority (CA) key.
247250
@@ -252,9 +255,9 @@ def sign_csr(self, csr_data: bytes, is_server: bool | None = None) -> bytes:
252255 ----------
253256 csr_data : bytes
254257 The certificate signing request data in PEM format.
255- is_server : bool , optional
256- If True, indicates this is a server certificate . If False, indicates this is a client certificate.
257- If None, will be inferred from the Common Name (CN) in the CSR by checking if it contains "server" .
258+ role : Optional[Literal["server", "client"]] , optional
259+ The role of the certificate, either " server" or "client" . If not provided,
260+ it will be inferred from the Common Name in the CSR.
258261
259262 Returns
260263 -------
@@ -282,10 +285,15 @@ def sign_csr(self, csr_data: bytes, is_server: bool | None = None) -> bytes:
282285 # Load client CSR
283286 csr = x509 .load_pem_x509_csr (csr_data )
284287
285- # infer by CN if not provided
286- if is_server is None :
287- cn = csr .subject .get_attributes_for_oid (NameOID .COMMON_NAME )[0 ].value
288- is_server = "server" in cn .lower ()
288+ # Heuristic if role not explicitly provided
289+ if role is None :
290+ try :
291+ cn = csr .subject .get_attributes_for_oid (NameOID .COMMON_NAME )[
292+ 0
293+ ].value .lower ()
294+ role = "server" if "server" in cn else "client"
295+ except Exception :
296+ role = "client"
289297
290298 ku = x509 .KeyUsage (
291299 digital_signature = True ,
@@ -300,12 +308,12 @@ def sign_csr(self, csr_data: bytes, is_server: bool | None = None) -> bytes:
300308 )
301309 eku = x509 .ExtendedKeyUsage (
302310 [ExtendedKeyUsageOID .SERVER_AUTH ]
303- if is_server
311+ if role == "server"
304312 else [ExtendedKeyUsageOID .CLIENT_AUTH ]
305313 )
306314
307315 # Generate client certificate
308- certificate = (
316+ builder = (
309317 x509 .CertificateBuilder ()
310318 # Set the subject name for the certificate to the subject specified in the CSR (Certificate Signing Request)
311319 .subject_name (csr .subject )
@@ -332,16 +340,6 @@ def sign_csr(self, csr_data: bytes, is_server: bool | None = None) -> bytes:
332340 # Add the subject alternative name extension with the IP address
333341 .add_extension (ku , critical = True )
334342 .add_extension (eku , critical = False )
335- .add_extension (
336- (
337- x509 .SubjectAlternativeName (
338- [x509 .IPAddress (ip_address (self .ip_address ))]
339- )
340- if is_server
341- else x509 .SubjectAlternativeName ([])
342- ), # no SAN-IP en client por defecto
343- critical = False ,
344- )
345343 .add_extension (
346344 x509 .SubjectKeyIdentifier .from_public_key (csr .public_key ()),
347345 critical = False ,
@@ -350,10 +348,19 @@ def sign_csr(self, csr_data: bytes, is_server: bool | None = None) -> bytes:
350348 x509 .AuthorityKeyIdentifier .from_issuer_public_key (ca_key .public_key ()),
351349 critical = False ,
352350 )
353- # Sign the certificate using the CA's private key with SHA256 as the hashing algorithm
354- .sign (ca_key , hashes .SHA256 ())
355351 )
356352
353+ if role == "server" :
354+ builder = builder .add_extension (
355+ x509 .SubjectAlternativeName (
356+ [x509 .IPAddress (ip_address (self .ip_address ))]
357+ ),
358+ critical = False ,
359+ )
360+
361+ # Sign the certificate using the CA's private key with SHA256 as the hashing algorithm
362+ certificate = builder .sign (ca_key , hashes .SHA256 ())
363+
357364 return certificate .public_bytes (serialization .Encoding .PEM )
358365
359366 def _key_and_csr (self , name = "client" ) -> tuple :
@@ -683,7 +690,7 @@ def get_context(self) -> tuple[ssl.SSLContext, ssl.SSLContext]:
683690
684691 return ssl_context_client , ssl_context_server
685692
686- def sign_and_store (self , name : str ) -> str :
693+ def sign_and_store (self , name : Literal [ "server" , "client" ] ) -> str :
687694 """
688695 Sign a Certificate Signing Request (CSR) and store the resulting certificate.
689696
@@ -709,6 +716,7 @@ def sign_and_store(self, name: str) -> str:
709716 """
710717 csr_path = self .certificate_paths [name ]
711718 cert_path = self .certificate_signed_paths [name ]
712- cert_pem = self .sign_csr (self .load_certificate (csr_path ))
719+ cert_pem = self .sign_csr (self .load_certificate (csr_path ), role = name )
713720 self .write_certificate (cert_path , cert_pem )
721+ os .chmod (cert_path , 0o644 )
714722 return cert_path
0 commit comments