def from_certificate(cls, cert: Certificate) -> "SrkItemRSA": """Create SRKItemRSA from certificate.""" assert isinstance(cert, Certificate) flag = 0 try: key_usage = cert.extensions.get_extension_for_class( KeyUsage) # type:ignore assert isinstance(key_usage.value, KeyUsage) if key_usage.value.key_cert_sign: flag = 0x80 except ExtensionNotFound: pass if isinstance(cert.public_key(), rsa.RSAPublicKey): public_key = cert.public_key() assert isinstance(public_key, rsa.RSAPublicKey) pub_key_numbers = public_key.public_numbers() assert isinstance(pub_key_numbers, rsa.RSAPublicNumbers) # get modulus and exponent of public key since we are RSA modulus_len = math.ceil(pub_key_numbers.n.bit_length() / 8) exponent_len = math.ceil(pub_key_numbers.e.bit_length() / 8) modulus = pub_key_numbers.n.to_bytes(modulus_len, "big") exponent = pub_key_numbers.e.to_bytes(exponent_len, "big") return cls(modulus, exponent, flag) raise NotImplementedSRKCertificate()
def verify(issuer: x509.Certificate, cert: x509.Certificate): """ Verify certificate with its issuer """ h = cert.signature_hash_algorithm issuer.public_key().verify(cert.signature, cert.tbs_certificate_bytes, padding.PKCS1v15(), h)
def _get_basic_certificate_text(cls, certificate: Certificate) -> List[str]: text_output = [ cls._format_field( "SHA1 Fingerprint:", binascii.hexlify(certificate.fingerprint( hashes.SHA1())).decode("ascii")), cls._format_field("Common Name:", _get_name_as_short_text(certificate.subject)), cls._format_field("Issuer:", _get_name_as_short_text(certificate.issuer)), cls._format_field("Serial Number:", str(certificate.serial_number)), cls._format_field("Not Before:", certificate.not_valid_before.date().isoformat()), cls._format_field("Not After:", certificate.not_valid_after.date().isoformat()), cls._format_field("Public Key Algorithm:", certificate.public_key().__class__.__name__), ] if certificate.signature_hash_algorithm: # The signature_hash_algorithm can be None if signature did not use separate hash (ED25519, ED448) # https://cryptography.io/en/latest/x509/reference/#cryptography.x509.Certificate.signature_hash_algorithm text_output.append( cls._format_field("Signature Algorithm:", certificate.signature_hash_algorithm.name)) public_key = certificate.public_key() if isinstance(public_key, EllipticCurvePublicKey): text_output.append( cls._format_field("Key Size:", str(public_key.curve.key_size))) text_output.append( cls._format_field("Curve:", str(public_key.curve.name))) elif isinstance(public_key, RSAPublicKey): text_output.append( cls._format_field("Key Size:", str(public_key.key_size))) text_output.append( cls._format_field( "Exponent:", str(public_key.public_numbers().e))) # type: ignore else: # DSA Key? https://github.com/nabla-c0d3/sslyze/issues/314 pass try: # Print the SAN extension if there's one text_output.append( cls._format_field( "DNS Subject Alternative Names:", str(extract_dns_subject_alternative_names(certificate)))) except KeyError: pass return text_output
def certificate_to_context( certificate: x509.Certificate) -> Common.Certificate: """ certificate_to_context function Translates an X509 certificate into a Common.Certificate object :type certificate: ``x509.Certificate`` :param oid: Certificate Extension OID :return: Certificate represented as a Common.Certificate object :rtype: ``Common.Certificate`` """ spkisha256 = hashes.Hash(hashes.SHA256(), backends.default_backend()) spkisha256.update(certificate.public_key().public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo)) extensions_contexts: List[Common.CertificateExtension] = [] for extension in certificate.extensions: extension_oid = cast(oid.ObjectIdentifier, extension.oid) extensions_contexts.append( extension_context(oid=extension_oid.dotted_string, extension_name=extension_oid._name, critical=extension.critical, extension_value=extension.value)) indicator = certificate.fingerprint(hashes.SHA256()).hex() cert = Common.Certificate( subject_dn=certificate.subject.rfc4514_string(), issuer_dn=certificate.issuer.rfc4514_string(), serial_number=str(certificate.serial_number), validity_not_before=certificate.not_valid_before.strftime( "%Y-%m-%dT%H:%M:%S.000Z"), validity_not_after=certificate.not_valid_after.strftime( "%Y-%m-%dT%H:%M:%S.000Z"), sha256=certificate.fingerprint(hashes.SHA256()).hex(), sha1=certificate.fingerprint(hashes.SHA1()).hex(), md5=certificate.fingerprint(hashes.MD5()).hex(), spki_sha256=spkisha256.finalize().hex(), extensions=extensions_contexts, signature_algorithm=certificate.signature_hash_algorithm. name, # type: ignore[union-attr] signature=certificate.signature.hex(), publickey=public_key_context(certificate.public_key()), # type: ignore dbot_score=Common.DBotScore(indicator=indicator, indicator_type=DBotScoreType.CERTIFICATE, integration_name="X509Certificate", score=Common.DBotScore.NONE), pem=certificate.public_bytes( serialization.Encoding.PEM).decode('ascii')) return cert
def key_type_and_size(cert: x509.Certificate) -> List[Tuple[bool, str]]: checks = [] # get the public key pk = cert.public_key() # check the keypair type exp_pk_type = 'RSA' pk_type = 'RSA' if isinstance(pk, rsa.RSAPublicKey) else 'NOT ALLOWED' msg = 'The keypair must be %s' % exp_pk_type res = FAILURE if pk_type != exp_pk_type else SUCCESS checks.append((res, msg)) # check the key size min_size = 2048 size = pk.key_size msg = ('The key size must be greater than or equal to %d (now: %d)' % (min_size, size)) res = FAILURE if size < min_size else SUCCESS checks.append((res, msg)) msg = ('The key size must be one of %s (now: %d)' % (ALLOWED_SIZES, size)) res = FAILURE if size not in ALLOWED_SIZES else SUCCESS checks.append((res, msg)) return checks
def _validate(crl: x509.CertificateRevocationList, issuer: x509.Certificate): """Validates a crl against a issuer certificate""" if crl.next_update is None: # rfc5280: Conforming CRL issuers MUST # include the nextUpdate field in all CRLs. raise CouldNotGetValidCRLError("CRL is missing next update field") if not (crl.next_update > datetime.utcnow() and crl.last_update < datetime.utcnow()): raise CouldNotGetValidCRLError( f"CRL failed date validation. " f"Last update: '{crl.last_update}' Next Update: '{crl.next_update}'" ) if not crl.issuer == issuer.subject: raise CouldNotGetValidCRLError( f"CRL failed issuer validation. " f"Expected: {issuer.subject.rfc4514_string()} " f"Actual: {crl.issuer.rfc4514_string()}.") # cast because mypy. The type of key is checked # when it is loaded in CertRetriever. if not crl.is_signature_valid(cast(RSAPublicKey, issuer.public_key())): raise CouldNotGetValidCRLError("CRL failed signature validation")
def _save( cls, key: PrivateKey, cert: x509.Certificate, directory: str, name: str, key_format: Encoding ) -> ((str, bytes), (str, bytes), (str, bytes)): private_file = f'{name}.{key_format.name.lower()}' private_path = join(directory, private_file) private_bytes = key.private_bytes(key_format, PrivateFormat.PKCS8, NoEncryption()) public_file = f'{name}.pub.{key_format.name.lower()}' public_path = join(directory, public_file) public_bytes = cert.public_key().public_bytes(key_format, PublicFormat.PKCS1) cert_file = f'{name}.cert.{key_format.name.lower()}' cert_path = join(directory, cert_file) cert_bytes = cert.public_bytes(key_format) serialized = ((private_path, private_bytes), (public_path, public_bytes), (cert_path, cert_bytes)) for file_path, file_bytes in serialized: with open(file_path, 'wb') as out: out.write(file_bytes) return serialized
def assertBasic( # pylint: disable=invalid-name self, cert: x509.Certificate, algo: typing.Type[hashes.HashAlgorithm] = hashes.SHA256 ) -> None: """Assert some basic key properties.""" self.assertEqual(cert.version, x509.Version.v3) self.assertIsInstance(cert.public_key(), rsa.RSAPublicKey) self.assertIsInstance(cert.signature_hash_algorithm, algo)
def from_certificate(cls, cert: Certificate) -> "SrkItem": """Pick up the right implementation of an SRK item.""" assert isinstance(cert, Certificate) public_key = cert.public_key() if isinstance(public_key, rsa.RSAPublicKey): return SrkItemRSA.from_certificate(cert) raise NotImplementedSRKCertificate()
def x509_certificate_to_json(certificate: x509.Certificate) -> Dict[str, Any]: public_key = certificate.public_key() try: public_key_size = public_key.key_size # type: ignore except AttributeError: public_key_size = None public_key_json = _PublicKeyAsJson( algorithm=public_key.__class__.__name__, key_size=public_key_size, # EC-only fields ec_curve_name=public_key.curve.name if isinstance(public_key, EllipticCurvePublicKey) else None, ec_x=public_key.public_numbers().x if isinstance(public_key, EllipticCurvePublicKey) else None, ec_y=public_key.public_numbers().y if isinstance(public_key, EllipticCurvePublicKey) else None, # RSA-only fields rsa_e=public_key.public_numbers().e if isinstance(public_key, RSAPublicKey) else None, rsa_n=public_key.public_numbers().n if isinstance(public_key, RSAPublicKey) else None, ) signature_hash_algorithm: Optional[_HashAlgorithmAsJson] if certificate.signature_hash_algorithm: signature_hash_algorithm = _HashAlgorithmAsJson( name=certificate.signature_hash_algorithm.name, digest_size=certificate.signature_hash_algorithm.digest_size, ) else: signature_hash_algorithm = None # We may get garbage/invalid certificates so we need to handle ValueErrors. # See https://github.com/nabla-c0d3/sslyze/issues/403 for more information subject_field: Optional[x509.name.Name] try: subject_field = certificate.subject except ValueError: subject_field = None issuer_field: Optional[x509.name.Name] try: issuer_field = certificate.issuer except ValueError: issuer_field = None cert_as_json = _X509CertificateAsJson( as_pem=certificate.public_bytes(Encoding.PEM).decode("ascii"), hpkp_pin=b64encode(get_public_key_sha256(certificate)).decode("ascii"), fingerprint_sha1=b64encode(certificate.fingerprint(hashes.SHA1())).decode("ascii"), fingerprint_sha256=b64encode(certificate.fingerprint(hashes.SHA256())).decode("ascii"), serial_number=certificate.serial_number, not_valid_before=certificate.not_valid_before, not_valid_after=certificate.not_valid_after, subject_alternative_name=_SubjAltNameAsJson(dns=extract_dns_subject_alternative_names(certificate)), signature_hash_algorithm=signature_hash_algorithm, signature_algorithm_oid=certificate.signature_algorithm_oid, subject=subject_field, issuer=issuer_field, public_key=public_key_json, ) return asdict(cert_as_json)
def verify(signature: bytes, data: bytes, certificate: Certificate, hasher: HashAlgorithm) -> None: key = certificate.public_key() if not isinstance(key, RSAPublicKey): raise TypeError( f"Only certificates with RSA Keys are supported. Got {key!r} instead." ) key.verify(signature, data, padding.PKCS1v15(), hasher)
def _check_ocsp_response_signature(response_ocsp: 'ocsp.OCSPResponse', issuer_cert: x509.Certificate, cert_path: str) -> None: """Verify an OCSP response signature against certificate issuer or responder""" def _key_hash(cert: x509.Certificate) -> bytes: return x509.SubjectKeyIdentifier.from_public_key(cert.public_key()).digest if (response_ocsp.responder_name == issuer_cert.subject or response_ocsp.responder_key_hash == _key_hash(issuer_cert)): # Case where the OCSP responder is also the certificate issuer logger.debug('OCSP response for certificate %s is signed by the certificate\'s issuer.', cert_path) responder_cert = issuer_cert else: # Case where the OCSP responder is not the certificate issuer logger.debug('OCSP response for certificate %s is delegated to an external responder.', cert_path) responder_certs = [cert for cert in response_ocsp.certificates if response_ocsp.responder_name == cert.subject or \ response_ocsp.responder_key_hash == _key_hash(cert)] if not responder_certs: raise AssertionError('no matching responder certificate could be found') # We suppose here that the ACME server support only one certificate in the OCSP status # request. This is currently the case for LetsEncrypt servers. # See https://github.com/letsencrypt/boulder/issues/2331 responder_cert = responder_certs[0] if responder_cert.issuer != issuer_cert.subject: raise AssertionError('responder certificate is not signed ' 'by the certificate\'s issuer') try: extension = responder_cert.extensions.get_extension_for_class(x509.ExtendedKeyUsage) delegate_authorized = x509.oid.ExtendedKeyUsageOID.OCSP_SIGNING in extension.value except (x509.ExtensionNotFound, IndexError): delegate_authorized = False if not delegate_authorized: raise AssertionError('responder is not authorized by issuer to sign OCSP responses') # Following line may raise UnsupportedAlgorithm chosen_cert_hash = responder_cert.signature_hash_algorithm # For a delegate OCSP responder, we need first check that its certificate is effectively # signed by the certificate issuer. crypto_util.verify_signed_payload(issuer_cert.public_key(), responder_cert.signature, responder_cert.tbs_certificate_bytes, chosen_cert_hash) # Following line may raise UnsupportedAlgorithm chosen_response_hash = response_ocsp.signature_hash_algorithm # We check that the OSCP response is effectively signed by the responder # (an authorized delegate one or the certificate issuer itself). if not chosen_response_hash: raise AssertionError("no signature hash algorithm defined") crypto_util.verify_signed_payload(responder_cert.public_key(), response_ocsp.signature, response_ocsp.tbs_response_bytes, chosen_response_hash)
def dummy_cert( privkey: rsa.RSAPrivateKey, cacert: x509.Certificate, commonname: Optional[str], sans: List[str], organization: Optional[str] = None, ) -> Cert: """ Generates a dummy certificate. privkey: CA private key cacert: CA certificate commonname: Common name for the generated certificate. sans: A list of Subject Alternate Names. organization: Organization name for the generated certificate. Returns cert if operation succeeded, None if not. """ builder = x509.CertificateBuilder() builder = builder.issuer_name(cacert.subject) builder = builder.add_extension(x509.ExtendedKeyUsage( [ExtendedKeyUsageOID.SERVER_AUTH]), critical=False) builder = builder.public_key(cacert.public_key()) now = datetime.datetime.now() builder = builder.not_valid_before(now - datetime.timedelta(days=2)) builder = builder.not_valid_after(now + CERT_EXPIRY) subject = [] is_valid_commonname = (commonname is not None and len(commonname) < 64) if is_valid_commonname: assert commonname is not None subject.append(x509.NameAttribute(NameOID.COMMON_NAME, commonname)) if organization is not None: assert organization is not None subject.append( x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization)) builder = builder.subject_name(x509.Name(subject)) builder = builder.serial_number(x509.random_serial_number()) ss: List[x509.GeneralName] = [] for x in sans: try: ip = ipaddress.ip_address(x) except ValueError: ss.append(x509.DNSName(x)) else: ss.append(x509.IPAddress(ip)) # RFC 5280 §4.2.1.6: subjectAltName is critical if subject is empty. builder = builder.add_extension(x509.SubjectAlternativeName(ss), critical=not is_valid_commonname) cert = builder.sign(private_key=privkey, algorithm=hashes.SHA256()) # type: ignore return Cert(cert)
def get_spki_hash(certificate: Certificate) -> bytes: """ Get the public key hash, as per RFC 7469: base64 of sha256 of the public key encoded in DER + Subject Public Key Info format. We use the URL-safe base64 variant, since this is typically found in NURLs. """ public_key_bytes = certificate.public_key().public_bytes( Encoding.DER, PublicFormat.SubjectPublicKeyInfo) return urlsafe_b64encode( sha256(public_key_bytes).digest()).strip().rstrip(b"=")
def verify(root: str, signature: str, cert: Certificate): """ Verify signature over root of Merkle Tree """ sig = base64.b64decode(signature) pk = cert.public_key() assert isinstance(pk, ec.EllipticCurvePublicKey) pk.verify( sig, bytes.fromhex(root), ec.ECDSA(utils.Prehashed(hashes.SHA256())), )
def check_endorsement(endorsee: Certificate, endorser: Certificate): """ Check endorser has endorsed endorsee """ digest_algo = endorsee.signature_hash_algorithm assert digest_algo digester = hashes.Hash(digest_algo) digester.update(endorsee.tbs_certificate_bytes) digest = digester.finalize() endorser_pk = endorser.public_key() assert isinstance(endorser_pk, ec.EllipticCurvePublicKey) endorser_pk.verify(endorsee.signature, digest, ec.ECDSA(utils.Prehashed(digest_algo)))
def x509_certificate_to_json(certificate: x509.Certificate) -> Dict[str, Any]: result: Dict[str, Union[None, str, Dict[str, Any]]] = { # Add general info "as_pem": certificate.public_bytes(Encoding.PEM).decode("ascii"), "hpkp_pin": b64encode( get_public_key_sha256(certificate)).decode("utf-8"), # RFC 7469 # Add some of the fields of the cert "serialNumber": str(certificate.serial_number), "notBefore": certificate.not_valid_before.isoformat(), "notAfter": certificate.not_valid_after.isoformat(), "subjectAlternativeName": { "DNS": extract_dns_subject_alternative_names(certificate) }, } if certificate.signature_hash_algorithm: # The signature_hash_algorithm can be None if signature did not use separate hash (ED25519, ED448) # https://cryptography.io/en/latest/x509/reference/#cryptography.x509.Certificate.signature_hash_algorithm result[ "signatureAlgorithm"] = certificate.signature_hash_algorithm.name else: result["signatureAlgorithm"] = None # We may get garbage/invalid certificates so we need to handle ValueErrors. # See https://github.com/nabla-c0d3/sslyze/issues/403 for more information for name_field in ["subject", "issuer"]: try: result[name_field] = getattr(certificate, name_field) except ValueError as e: x509name_as_json = _X509NameAsJson(rfc4514_string=None, attributes=None, parsing_error=e.args[0]) result[name_field] = asdict(x509name_as_json) # Add some info about the public key public_key = certificate.public_key() public_key_dict: Dict[str, Union[str, int]] = { "algorithm": public_key.__class__.__name__ } if isinstance(public_key, EllipticCurvePublicKey): public_key_dict["size"] = public_key.curve.key_size public_key_dict["curve"] = public_key.curve.name elif isinstance(public_key, RSAPublicKey): public_key_dict["size"] = public_key.key_size public_key_dict["exponent"] = public_key.public_numbers().e else: # DSA Key? https://github.com/nabla-c0d3/sslyze/issues/402 pass result["publicKey"] = public_key_dict return result
def matches_key_and_cert(priv_key: bytes, cert: Certificate) -> bool: """Verify that given private key matches the public certificate. :param priv_key: to be tested; decrypted binary data in PEM format :param cert: to be used for verification :return: True if yes; False otherwise """ signature = crypto_backend().rsa_sign(priv_key, bytes()) assert isinstance(cert, Certificate) cert_pub_key = cert.public_key() # public key of last certificate assert isinstance(cert_pub_key, RSAPublicKey) return crypto_backend().rsa_verify(cert_pub_key.public_numbers().n, cert_pub_key.public_numbers().e, signature, bytes())
def _validate_cert_against_issuer(cert: x509.Certificate, issuer: x509.Certificate) -> bool: """Validates a certificate against it's (alleged) issuer""" if cert.issuer != issuer.subject: return False try: # casts because mypy. The type of key is checked # when it is loaded in CertRetriever. cast(RSAPublicKey, issuer.public_key()).verify( cert.signature, cert.tbs_certificate_bytes, PKCS1v15(), cast(HashAlgorithm, cert.signature_hash_algorithm), ) except InvalidSignature: return False return True
def from_orm(cls, certificate: x509.Certificate) -> "_CertificateAsJson": signature_hash_algorithm: Optional[_HashAlgorithmAsJson] if certificate.signature_hash_algorithm: signature_hash_algorithm = _HashAlgorithmAsJson.from_orm( certificate.signature_hash_algorithm) else: signature_hash_algorithm = None # We may get garbage/invalid certificates so we need to handle ValueErrors. # See https://github.com/nabla-c0d3/sslyze/issues/403 for more information subject_field: Optional[_X509NameAsJson] try: subject_field = _X509NameAsJson.from_orm(certificate.subject) except ValueError: subject_field = None issuer_field: Optional[_X509NameAsJson] try: issuer_field = _X509NameAsJson.from_orm(certificate.issuer) except ValueError: issuer_field = None return cls( as_pem=certificate.public_bytes(Encoding.PEM).decode("ascii"), hpkp_pin=b64encode( get_public_key_sha256(certificate)).decode("ascii"), fingerprint_sha1=b64encode(certificate.fingerprint( hashes.SHA1())).decode("ascii"), fingerprint_sha256=b64encode( certificate.fingerprint(hashes.SHA256())).decode("ascii"), serial_number=certificate.serial_number, not_valid_before=certificate.not_valid_before, not_valid_after=certificate.not_valid_after, subject_alternative_name=_SubjAltNameAsJson( dns=extract_dns_subject_alternative_names(certificate)), signature_hash_algorithm=signature_hash_algorithm, signature_algorithm_oid=certificate.signature_algorithm_oid, subject=subject_field, issuer=issuer_field, public_key=_PublicKeyAsJson.from_orm(certificate.public_key()), )
def sign_csr( csr: CertificateSigningRequest, ca_cert: Certificate, key: EllipticCurvePrivateKey, expiration_date: date, custom_extensions: Iterable[Union[KeyUsage, UnrecognizedExtension, BasicConstraints]] ) -> Certificate: """ Sign a CSR with CA credentials. :param csr: the CSR :param ca_cert: the CA certificate :param key: the CA private key :param expiration_date: expiration date :param custom_extensions: custom extensions to be added to the certificate :return: a certificate object """ issuer = ca_cert.subject now = datetime.utcnow() cert_builder = CertificateBuilder().issuer_name(issuer).subject_name( csr.subject).public_key(csr.public_key()).serial_number( x509.random_serial_number()).not_valid_before(now).not_valid_after( datetime.combine(expiration_date, time(), None)).add_extension( extension=AuthorityKeyIdentifier.from_issuer_public_key( ca_cert.public_key()), critical=False) try: cert_builder = cert_builder.add_extension( csr.extensions.get_extension_for_class( SubjectAlternativeName).value, critical=False) except ExtensionNotFound: pass for extension in custom_extensions: if isinstance(extension, UnrecognizedExtension): critical = False else: critical = True # pyre-fixme[6]: Expected `ExtensionType` for 1st param but got # `Union[BasicConstraints, KeyUsage, UnrecognizedExtension]`. cert_builder = cert_builder.add_extension(extension, critical=critical) return cert_builder.sign(key, SHA256(), backends.default_backend())
def storePublicKey(self, certName: str, cert: x509.Certificate) -> None: path = self.conf.getCertPath(certName=certName, ext="signed_certificate") certConf = self.conf.getCert(certName=certName) format = certConf.get("public_key").get("format", None) try: if type(format) == str and format.lower() == "openssh": content = serialization.ssh.serialize_ssh_public_key( cert.public_key()) with open(path, "wb") as f: f.write(content) # DER or PEM encodings else: encoding, _ = self._getParamsForPublicBytes(certConf=certConf) content = cert.public_bytes(encoding) with open(path, "wb") as f: f.write(content) except OSError: logging.error(f"can't save public key in {path}") sys.exit()
def _build_recipient_info(self, symmetric_key: bytes, recipient: x509.Certificate) -> RecipientInfo: """Build an ASN.1 data structure containing the encrypted symmetric key for the encrypted_content. NOTE: The recipient is always identified by issuerAndSerialNumber NOTE: Args: symmetric_key (bytes): Typically the randomly generated 3DES key for the encrypted_content. recipient (x509.Certificate): The certificate which will be used to encrypt the symmetric key. Returns: RecipientInfo: Instance of ASN.1 data structure with required attributes and encrypted key. """ encrypted_symkey = recipient.public_key().encrypt( symmetric_key, asympad.PKCS1v15()) asn1cert = parse_certificate( recipient.public_bytes(serialization.Encoding.DER)) ias = IssuerAndSerialNumber({ 'issuer': asn1cert.issuer, 'serial_number': asn1cert.serial_number }) ri = RecipientInfo( 'ktri', KeyTransRecipientInfo({ 'version': 0, 'rid': RecipientIdentifier('issuer_and_serial_number', ias), 'key_encryption_algorithm': KeyEncryptionAlgorithm( {'algorithm': KeyEncryptionAlgorithmId('rsa')}), 'encrypted_key': encrypted_symkey, })) return ri
def is_pareja(cer: x509.Certificate, private_key: rsa.RSAPrivateKey) -> bool: encoding = serialization.Encoding.PEM fmt = serialization.PublicFormat.PKCS1 return cer.public_key().public_bytes(encoding, fmt) == private_key.public_key().public_bytes(encoding, fmt)
def _key_hash(cert: x509.Certificate) -> bytes: return x509.SubjectKeyIdentifier.from_public_key( cert.public_key()).digest
def _keyid(cert: Certificate) -> str: pub = cert.public_key() h = hashlib.sha256() h.update(pub.public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)) return h.hexdigest()
def get_public_key_sha256(certificate: x509.Certificate) -> bytes: pub_bytes = certificate.public_key().public_bytes(encoding=Encoding.DER, format=PublicFormat.SubjectPublicKeyInfo) digest = sha256(pub_bytes).digest() return digest
def _check_certificate_properties( self, certificate: Certificate, ocsp_stapling: bool) -> Tuple[List[str], List[str], str]: errors = [] warnings = [] # check certificate lifespan lifespan = certificate.not_valid_after - certificate.not_valid_before if self.target_profile["maximum_certificate_lifespan"] < lifespan.days: errors.append( f"Certificate lifespan too long (is {lifespan.days}, " f"should be less than {self.target_profile['maximum_certificate_lifespan']})" ) elif (self.target_profile["recommended_certificate_lifespan"] and self.target_profile["recommended_certificate_lifespan"] < lifespan.days): warnings.append( f"Certificate lifespan is {lifespan.days} days but the recommended lifespan is {self.target_profile['recommended_certificate_lifespan']} days." ) current_time = datetime.now() days_before_expire = certificate.not_valid_after - current_time if days_before_expire.days < self.cert_expire_warning: warnings.append( f"Certificate expires in {days_before_expire.days} days") # check certificate public key type pub_key_type = self._cert_type_string(certificate.public_key()) if pub_key_type.lower( ) not in self.target_profile["certificate_types"]: errors.append( f"Wrong certificate type (is {pub_key_type}), " f"should be one of {self.target_profile['certificate_types']}") # check key property pub_key = certificate.public_key() if (isinstance(pub_key, rsa.RSAPublicKey) and self.target_profile["rsa_key_size"] and pub_key.key_size != self.target_profile["rsa_key_size"]): errors.append( f"RSA certificate has wrong key size (is {pub_key.key_size}, " f"should be {self.target_profile['rsa_key_size']})") elif (isinstance(pub_key, ec.EllipticCurvePublicKey) and self.target_profile["certificate_curves"] and pub_key.curve.name not in self.target_profile["certificate_curves_preprocessed"]): errors.append( f"ECDSA certificate uses wrong curve " f"(is {pub_key.curve.name}, should be one of {self.target_profile['certificate_curves']})" ) # check certificate signature if (certificate.signature_algorithm_oid._name not in self.target_profile["certificate_signatures"]): errors.append( f"Certificate has a wrong signature (is {certificate.signature_algorithm_oid._name}), " f"should be one of {self.target_profile['certificate_signatures']}" ) # check if ocsp stabling is supported if ocsp_stapling != self.target_profile["ocsp_staple"]: if self.target_profile["ocsp_staple"]: errors.append(f"OCSP stapling must be supported") else: errors.append(f"OCSP stapling should not be supported") return errors, warnings, pub_key_type