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 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 _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 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 _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 store_certificate(self, certificate: Certificate) -> Path: """Store the supplied certificate as a PEM file. """ # A given certificate's path is always <SHA-256>.pem. cert_file_name = hexlify(certificate.fingerprint(SHA256())).decode('ascii') cert_path = self._path / f'{cert_file_name}.pem' # If the cert is NOT already there, add it if not cert_path.exists(): with open(cert_path, 'w') as cert_file: cert_file.write(certificate.public_bytes(Encoding.PEM).decode('ascii')) return cert_path
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 from_crypto(cls, certificate: x509.Certificate): # TODO: sometimes serial numbers are too large even for SQLite BIGINT m = cls() m.pem_data = certificate.public_bytes( encoding=serialization.Encoding.PEM) m.not_after = certificate.not_valid_after m.not_before = certificate.not_valid_before m.fingerprint = certificate.fingerprint(hashes.SHA256()) subject: x509.Name = certificate.subject cns = subject.get_attributes_for_oid(NameOID.COMMON_NAME) if cns is not None: m.x509_cn = cns[0].value return m
def from_crypto_type(cls, certificate: x509.Certificate, certtype: CertificateType): # type: (certtype, x509.Certificate, CertificateType) -> Certificate m = cls() m.pem_data = certificate.public_bytes(serialization.Encoding.PEM) m.not_after = certificate.not_valid_after m.not_before = certificate.not_valid_before m.fingerprint = certificate.fingerprint(hashes.SHA256()) m.discriminator = certtype.value subject: x509.Name = certificate.subject cns = subject.get_attributes_for_oid(NameOID.COMMON_NAME) if cns is not None: m.x509_cn = cns[0].value return m
def builder( *, config: SigningConfig, certificate: Certificate, element_id: str ) -> Element: return ds.Signature( ds.SignedInfo( ds.CanonicalizationMethod(Algorithm=XML_EXC_C14N), ds.SignatureMethod( Algorithm=utils.signature_method_algorithm(config.signature_method) ), ds.Reference( ds.Transforms( ds.Transform(Algorithm=XMLDSIG_ENVELOPED_SIGNATURE), ds.Transform(Algorithm=XML_EXC_C14N), ), ds.DigestMethod( Algorithm=utils.digest_method_algorithm(config.digest_method) ), ds.DigestValue(), URI="#" + element_id, ), ), ds.SignatureValue(), ds.KeyInfo( ds.X509Data(utils.ascii_b64(certificate.public_bytes(Encoding.DER))) ), )
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 put_certificate( cls, session: "core.AuthSession", object_id: int, label: str, domains: int, capabilities: CAPABILITY, certificate: x509.Certificate, ) -> "Opaque": """Import an X509 certificate into the YubiHSM as an Opaque. :param session: The session to import via. :param object_id: The ID to set for the object. Set to 0 to let the YubiHSM designate an ID. :param label: A text label to give the object. :param domains: The set of domains to assign the object to. :param capabilities: The set of capabilities to give the object. :param certificate: A certificate to import. :return: A reference to the newly created object. """ encoded_cert = certificate.public_bytes(Encoding.DER) return cls.put( session, object_id, label, domains, capabilities, ALGORITHM.OPAQUE_X509_CERTIFICATE, encoded_cert, )
def test_decode_base64_cert_ok( trusted_cert_b64: str, trusted_cert: Certificate, ) -> None: assert _decode_base64_cert(trusted_cert_b64).public_bytes( Encoding.DER ) == trusted_cert.public_bytes(Encoding.DER)
def create_degenerate_certificate(certificate: x509.Certificate) -> ContentInfo: """Produce a PKCS#7 Degenerate case with a single certificate. Args: certificate (x509.Certificate): The certificate to attach to the degenerate pkcs#7 payload. Returns: ContentInfo: The ContentInfo containing a SignedData structure. """ der_bytes = certificate.public_bytes( serialization.Encoding.DER ) asn1cert = parse_certificate(der_bytes) empty = ContentInfo({ 'content_type': ContentType('data') }) sd = SignedData({ 'version': CMSVersion(1), 'encap_content_info': empty, 'digest_algorithms': DigestAlgorithms([]), 'certificates': CertificateSet([CertificateChoices('certificate', asn1cert)]), 'signer_infos': SignerInfos([]), 'crls': RevocationInfoChoices([]), }) return ContentInfo({ 'content_type': ContentType('signed_data'), 'content': sd, })
def _write_tls_certificate( self, port: int, # used to avoid duplicate certs with the same IP certificate: Certificate, force: bool = True) -> Path: # Read x509 = OpenSSL.crypto.X509.from_cryptography(certificate) subject_components = x509.get_subject().get_components() common_name_as_bytes = subject_components[0][1] common_name_on_certificate = common_name_as_bytes.decode() host = common_name_on_certificate certificate_filepath = self.generate_certificate_filepath(host=host, port=port) certificate_already_exists = certificate_filepath.is_file() if force is False and certificate_already_exists: raise FileExistsError( 'A TLS certificate already exists at {}.'.format( certificate_filepath)) # Write certificate_filepath.parent.mkdir(parents=True, exist_ok=True) with open(certificate_filepath, 'wb') as certificate_file: public_pem_bytes = certificate.public_bytes( self.TLS_CERTIFICATE_ENCODING) certificate_file.write(public_pem_bytes) self.log.debug( f"Saved TLS certificate for {host} to {certificate_filepath}") return certificate_filepath
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 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 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 inspect_certificate(cert: x509.Certificate) -> typing.Dict[str, object]: """ Extract useful fields from a x509 client certificate object. """ name_attrs = cert.subject.get_attributes_for_oid(COMMON_NAME) common_name = name_attrs[0].value if name_attrs else "" fingerprint_bytes = cert.fingerprint(hashes.SHA256()) fingerprint = f"SHA256:{fingerprint_bytes.hex().zfill(64).upper()}" fingerprint_b64 = base64.urlsafe_b64encode(fingerprint_bytes).decode() not_before = cert.not_valid_before.strftime("%Y-%m-%dT%H:%M:%SZ") not_after = cert.not_valid_after.strftime("%Y-%m-%dT%H:%M:%SZ") serial_number = cert.serial_number data = { "common_name": common_name, "fingerprint": fingerprint, "fingerprint_b64": fingerprint_b64, "not_before": not_before, "not_after": not_after, "serial_number": serial_number, } return data
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 make_thumbprint(certificate: x509.Certificate, thumbprint_type: hashes.HashAlgorithm) -> str: """Create singular thumbprint, with provided certificate and algorithm type""" return (base64.urlsafe_b64encode(certificate.fingerprint( thumbprint_type)) # The thumbprint is a URL-encoded hash... .decode("utf-8") # ... as a Python string ... .strip("=") # ... with the padding removed. )
def from_crypto(cls, certificate: x509.Certificate): m = cls() m.pem_data = certificate.public_bytes( encoding=serialization.Encoding.PEM) m.not_after = certificate.not_valid_after m.not_before = certificate.not_valid_before m.fingerprint = certificate.fingerprint(hashes.SHA1()) subject: x509.Name = certificate.subject m.x509_cn = subject.get_attributes_for_oid( NameOID.COMMON_NAME)[0].value # m.x509_c = subject.get_attributes_for_oid(NameOID.COUNTRY_NAME) # m.x509_o = subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME) # m.x509_ou = subject.get_attributes_for_oid(NameOID.ORGANIZATIONAL_UNIT_NAME) # m.x509_st = subject.get_attributes_for_oid(NameOID.STATE_OR_PROVINCE_NAME) return m
def _write_tls_certificate(self, certificate: Certificate, host: str = None, force: bool = True) -> str: # Read x509 = OpenSSL.crypto.X509.from_cryptography(certificate) subject_components = x509.get_subject().get_components() common_name_as_bytes = subject_components[0][1] common_name_on_certificate = common_name_as_bytes.decode() if not host: host = common_name_on_certificate try: pseudonym = certificate.subject.get_attributes_for_oid( NameOID.PSEUDONYM)[0] except IndexError: raise self.InvalidNodeCertificate( f"Missing checksum address on certificate for host '{host}'. " f"Does this certificate belong to an Ursula?") else: checksum_address = pseudonym.value if not is_checksum_address(checksum_address): raise self.InvalidNodeCertificate( "Invalid certificate wallet address encountered: {}".format( checksum_address)) # Validate # TODO: It's better for us to have checked this a while ago so that this situation is impossible. #443 if host and (host != common_name_on_certificate): raise ValueError( f"You passed a hostname ('{host}') that does not match the certificate's common name." ) certificate_filepath = self.generate_certificate_filepath( checksum_address=checksum_address) certificate_already_exists = os.path.isfile(certificate_filepath) if force is False and certificate_already_exists: raise FileExistsError( 'A TLS certificate already exists at {}.'.format( certificate_filepath)) # Write os.makedirs(os.path.dirname(certificate_filepath), exist_ok=True) with open(certificate_filepath, 'wb') as certificate_file: public_pem_bytes = certificate.public_bytes( self.TLS_CERTIFICATE_ENCODING) certificate_file.write(public_pem_bytes) nickname, pairs = nickname_from_seed(checksum_address) self.log.debug( f"Saved TLS certificate for {nickname} {checksum_address}: {certificate_filepath}" ) return certificate_filepath
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 _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 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 to_pem(certificate: x509.Certificate) -> str: """Convert an instance of x509.Certificate to a PEM string Args: certificate (x509.Certificate): Cert to convert Returns: PEM string """ serialized = certificate.public_bytes(encoding=serialization.Encoding.PEM) return serialized
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 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 _write_tls_certificate(certificate: Certificate, full_filepath: str, force: bool = False, ) -> str: cert_already_exists = os.path.isfile(full_filepath) if force is False and cert_already_exists: raise FileExistsError('A TLS certificate already exists at {}.'.format(full_filepath)) with open(full_filepath, 'wb') as certificate_file: public_pem_bytes = certificate.public_bytes(TLS_CERTIFICATE_ENCODING) certificate_file.write(public_pem_bytes) return full_filepath