Skip to content

certified.cert_base — FullCert

FullCert is the base class for all certificate types that hold both a certificate and its corresponding private key. CA and LeafCert (see ca.md) both extend this class.

Originally derived from trustme (MIT license).

FullCert

A full certificate contains both a certificate and private key.

Source code in certified/cert_base.py
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
class FullCert:
    """ A full certificate contains both a certificate and private key.
    """
    _certificate: x509.Certificate
    _private_key: CertificateIssuerPrivateKeyTypes

    def __init__(self, cert_bytes: bytes, private_key_bytes: bytes,
                 get_pw: PWCallback = None) -> None:
        """Create from an existing cert and private key.

        Args:
          cert_bytes: The bytes of the certificate in PEM format
          private_key_bytes: The bytes of the private key in PEM format
          get_pw: get the password used to decrypt the key (if a password was set)
        """
        #self.parent_cert = None
        self._certificate = x509.load_pem_x509_certificate(cert_bytes)
        password : Optional[bytes] = None
        if get_pw:
            password = get_pw()
        pkey = load_pem_private_key(
                    private_key_bytes, password=password
        )
        assert isinstance(pkey, (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey, rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)), f"Unusable key type: {type(pkey)}"
        self._private_key = pkey

    @classmethod
    def load(cls, base : Pstr, get_pw = None):
        cert = Blob.read(str(base) + ".crt")
        key  = Blob.read(str(base) + ".key")
        assert key.is_secret, f"{str(base)+'.key'} has compromised file permissions."
        return cls(cert.bytes(), key.bytes(), get_pw)

    def save(self, base : Pstr, overwrite = False):
        self.cert_pem.write(str(base) + ".crt")
        self._get_private_key().write(str(base) + ".key")

    @property
    def certificate(self) -> x509.Certificate:
        return self._certificate

    @property
    def pubkey(self) -> CertificatePublicKeyTypes:
        return self._certificate.public_key()

    @property
    def serial(self) -> str:
        return serial_number(self._certificate)

    @property
    def cert_pem(self) -> PublicBlob:
        """`Blob`: The PEM-encoded certificate for this CA. Add this to your
        trust store to trust this CA."""
        return PublicBlob(self._certificate)

    def _get_private_key(self) -> PrivateBlob:
        """`PrivateBlob`: The PEM-encoded private key.
           You should avoid using this if possible.
        """
        return PrivateBlob(self._private_key)

    def __str__(self) -> str:
        return str(self.cert_pem)

    def create_csr(self) -> x509.CertificateSigningRequest:
        """ Generate a CSR.
        """
        try:
            san = self._certificate.extensions.get_extension_for_class(
                x509.SubjectAlternativeName
            )
        except x509.ExtensionNotFound:
            san = None
        pubkey = self._certificate.public_key()
        csr = x509.CertificateSigningRequestBuilder().subject_name(
            self._certificate.subject
        )
        if san:
            csr = csr.add_extension(
                san.value,
                critical=san.critical,
            )
        return csr.sign(self._private_key, encode.hash_for_pubkey(pubkey))

    def revoke(self) -> None:
        # https://cryptography.io/en/latest/x509/reference/#x-509-certificate-revocation-list-builder
        raise RuntimeError("FIXME: Not implemented.")

cert_pem property

Blob: The PEM-encoded certificate for this CA. Add this to your trust store to trust this CA.

__init__(cert_bytes, private_key_bytes, get_pw=None)

Create from an existing cert and private key.

Parameters:

Name Type Description Default
cert_bytes bytes

The bytes of the certificate in PEM format

required
private_key_bytes bytes

The bytes of the private key in PEM format

required
get_pw PWCallback

get the password used to decrypt the key (if a password was set)

None
Source code in certified/cert_base.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def __init__(self, cert_bytes: bytes, private_key_bytes: bytes,
             get_pw: PWCallback = None) -> None:
    """Create from an existing cert and private key.

    Args:
      cert_bytes: The bytes of the certificate in PEM format
      private_key_bytes: The bytes of the private key in PEM format
      get_pw: get the password used to decrypt the key (if a password was set)
    """
    #self.parent_cert = None
    self._certificate = x509.load_pem_x509_certificate(cert_bytes)
    password : Optional[bytes] = None
    if get_pw:
        password = get_pw()
    pkey = load_pem_private_key(
                private_key_bytes, password=password
    )
    assert isinstance(pkey, (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey, rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)), f"Unusable key type: {type(pkey)}"
    self._private_key = pkey

create_csr()

Generate a CSR.

Source code in certified/cert_base.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def create_csr(self) -> x509.CertificateSigningRequest:
    """ Generate a CSR.
    """
    try:
        san = self._certificate.extensions.get_extension_for_class(
            x509.SubjectAlternativeName
        )
    except x509.ExtensionNotFound:
        san = None
    pubkey = self._certificate.public_key()
    csr = x509.CertificateSigningRequestBuilder().subject_name(
        self._certificate.subject
    )
    if san:
        csr = csr.add_extension(
            san.value,
            critical=san.critical,
        )
    return csr.sign(self._private_key, encode.hash_for_pubkey(pubkey))