blob: 143cdfc567157646a9803c9a16dfcca87dae3910 [file] [log] [blame]
Alex Gaynor11b00cd2015-07-13 21:12:39 -04001Tutorial
2========
3
Alex Gaynor077d89d2016-02-10 23:35:20 -05004X.509 certificates are used to authenticate clients and servers. The most
5common use case is for web servers using HTTPS.
Alex Gaynor11b00cd2015-07-13 21:12:39 -04006
7Creating a Certificate Signing Request (CSR)
8--------------------------------------------
9
10When obtaining a certificate from a certificate authority (CA), the usual
Alex Gaynor0c91ddc2015-07-13 21:15:03 -040011flow is:
Alex Gaynor11b00cd2015-07-13 21:12:39 -040012
131. You generate a private/public key pair.
Alex Gaynor5bcd8e82015-07-13 21:17:31 -0400142. You create a request for a certificate, which is signed by your key (to
15 prove that you own that key).
Alex Gaynor11b00cd2015-07-13 21:12:39 -0400163. You give your CSR to a CA (but *not* the private key).
174. The CA validates that you own the resource (e.g. domain) you want a
18 certificate for.
Alex Gaynor5bcd8e82015-07-13 21:17:31 -0400195. The CA gives you a certificate, signed by them, which identifies your public
Alex Gaynor11b00cd2015-07-13 21:12:39 -040020 key, and the resource you are authenticated for.
Alex Gaynor5bcd8e82015-07-13 21:17:31 -0400216. You configure your server to use that certificate, combined with your
22 private key, to server traffic.
Alex Gaynor11b00cd2015-07-13 21:12:39 -040023
24If you want to obtain a certificate from a typical commercial CA, here's how.
25First, you'll need to generate a private key, we'll generate an RSA key (these
26are the most common types of keys on the web right now):
27
28.. code-block:: pycon
29
30 >>> from cryptography.hazmat.backends import default_backend
31 >>> from cryptography.hazmat.primitives import serialization
32 >>> from cryptography.hazmat.primitives.asymmetric import rsa
33 >>> # Generate our key
34 >>> key = rsa.generate_private_key(
35 ... public_exponent=65537,
36 ... key_size=2048,
37 ... backend=default_backend()
38 ... )
39 >>> # Write our key to disk for safe keeping
Tim Buchwaldte7820612015-08-26 19:15:03 +020040 >>> with open("path/to/store/key.pem", "wb") as f:
Alex Gaynor11b00cd2015-07-13 21:12:39 -040041 ... f.write(key.private_bytes(
42 ... encoding=serialization.Encoding.PEM,
43 ... format=serialization.PrivateFormat.TraditionalOpenSSL,
44 ... encryption_algorithm=serialization.BestAvailableEncryption(b"passphrase"),
Alex Gaynord02bc122015-07-13 21:18:56 -040045 ... ))
Alex Gaynor11b00cd2015-07-13 21:12:39 -040046
47If you've already generated a key you can load it with
Alex Gaynor9ca48512015-07-16 07:03:40 -040048:func:`~cryptography.hazmat.primitives.serialization.load_pem_private_key`.
Alex Gaynor11b00cd2015-07-13 21:12:39 -040049
Alex Gaynor5bcd8e82015-07-13 21:17:31 -040050Next we need to generate a certificate signing request. A typical CSR contains
51a few details:
Alex Gaynor11b00cd2015-07-13 21:12:39 -040052
53* Information about our public key (including a signature of the entire body).
54* Information about who *we* are.
55* Information about what domains this certificate is for.
56
57.. code-block:: pycon
58
59 >>> from cryptography import x509
Paul Kehrer246daf72015-08-10 21:12:24 -050060 >>> from cryptography.x509.oid import NameOID
Abhijeet Rastogia5e999a2015-08-15 08:44:14 +053061 >>> from cryptography.hazmat.primitives import hashes
Alex Gaynor11b00cd2015-07-13 21:12:39 -040062 >>> # Generate a CSR
63 >>> csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
64 ... # Provide various details about who we are.
Paul Kehrer246daf72015-08-10 21:12:24 -050065 ... x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
66 ... x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"CA"),
67 ... x509.NameAttribute(NameOID.LOCALITY_NAME, u"San Francisco"),
68 ... x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My Company"),
69 ... x509.NameAttribute(NameOID.COMMON_NAME, u"mysite.com"),
Alex Gaynorf6487342015-09-03 10:10:49 -040070 ... ])).add_extension(
71 ... x509.SubjectAlternativeName([
72 ... # Describe what sites we want this certificate for.
73 ... x509.DNSName(u"mysite.com"),
74 ... x509.DNSName(u"www.mysite.com"),
75 ... x509.DNSName(u"subdomain.mysite.com"),
76 ... ]),
77 ... critical=False,
Alex Gaynor11b00cd2015-07-13 21:12:39 -040078 ... # Sign the CSR with our private key.
Alex Gaynorf6487342015-09-03 10:10:49 -040079 ... ).sign(key, hashes.SHA256(), default_backend())
Alex Gaynor11b00cd2015-07-13 21:12:39 -040080 >>> # Write our CSR out to disk.
Tim Buchwaldte7820612015-08-26 19:15:03 +020081 >>> with open("path/to/csr.pem", "wb") as f:
Alex Gaynor11b00cd2015-07-13 21:12:39 -040082 ... f.write(csr.public_bytes(serialization.Encoding.PEM))
83
84Now we can give our CSR to a CA, who will give a certificate to us in return.
Alex Gaynord57a72e2016-03-14 12:04:30 -040085
86Creating a self-signed certificate
87----------------------------------
88
89While most of the time you want a certificate that has been *signed* by someone
90else (i.e. a certificate authority), so that trust is established, sometimes
Alex Gaynor84c58c42016-03-14 12:17:19 -040091you want to create a self-signed certificate. Self-signed certificates are not
Alex Gaynor1cc38902016-03-14 12:34:52 -040092issued by a certificate authority, but instead they are signed by the private
93key corresponding to the public key they embed.
Alex Gaynord57a72e2016-03-14 12:04:30 -040094
95This means that other people don't trust these certificates, but it also means
96they can be issued very easily. In general the only use case for a self-signed
97certificate is local testing, where you don't need anyone else to trust your
98certificate.
99
100Like generating a CSR, we start with creating a new private key:
101
102.. code-block:: pycon
103
104 >>> # Generate our key
105 >>> key = rsa.generate_private_key(
106 ... public_exponent=65537,
107 ... key_size=2048,
108 ... backend=default_backend()
109 ... )
110 >>> # Write our key to disk for safe keeping
111 >>> with open("path/to/store/key.pem", "wb") as f:
112 ... f.write(key.private_bytes(
113 ... encoding=serialization.Encoding.PEM,
114 ... format=serialization.PrivateFormat.TraditionalOpenSSL,
115 ... encryption_algorithm=serialization.BestAvailableEncryption(b"passphrase"),
116 ... ))
117
118Then we generate the certificate itself:
119
120.. code-block:: pycon
121
122 >>> # Various details about who we are. For a self-signed certificate the
123 >>> # subject and issuer are always the same.
124 >>> subject = issuer = x509.Name([
125 ... x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
126 ... x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"CA"),
127 ... x509.NameAttribute(NameOID.LOCALITY_NAME, u"San Francisco"),
128 ... x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My Company"),
129 ... x509.NameAttribute(NameOID.COMMON_NAME, u"mysite.com"),
130 ... ])
131 >>> cert = x509.CertificateBuilder().subject_name(
132 ... subject
133 ... ).issuer_name(
134 ... issuer
135 ... ).public_key(
136 ... private_key.public_key()
Alex Gaynor0fa6b392016-09-09 21:32:14 -0400137 ... ).serial_number(
138 ... x509.random_serial_number()
Alex Gaynord57a72e2016-03-14 12:04:30 -0400139 ... ).not_valid_before(
140 ... datetime.datetime.utcnow()
141 ... ).not_valid_after(
142 ... # Our certificate will be valid for 10 days
143 ... datetime.datetime.utcnow() + datetime.timedelta(days=10)
144 ... ).add_extension(
145 ... x509.SubjectAlternativeName([x509.DNSName(u"localhost")]),
146 ... critical=False,
147 ... # Sign our certificate with our private key
148 ... ).sign(private_key, hashes.SHA256(), default_backend())
149 >>> # Write our certificate out to disk.
150 >>> with open("path/to/certificate.pem", "wb") as f:
151 ... f.write(cert.public_bytes(serialization.Encoding.PEM))
152
153And now we have a private key and certificate that can be used for local
154testing.