blob: 64542457a4cc29c0a01f8e5e60611130a5d11a8d [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
91you want to create self-signed certificate. Self-signed certificates are not
92issued by a certificate authority, but are instead signed by themselves.
93
94This means that other people don't trust these certificates, but it also means
95they can be issued very easily. In general the only use case for a self-signed
96certificate is local testing, where you don't need anyone else to trust your
97certificate.
98
99Like generating a CSR, we start with creating a new private key:
100
101.. code-block:: pycon
102
103 >>> # Generate our key
104 >>> key = rsa.generate_private_key(
105 ... public_exponent=65537,
106 ... key_size=2048,
107 ... backend=default_backend()
108 ... )
109 >>> # Write our key to disk for safe keeping
110 >>> with open("path/to/store/key.pem", "wb") as f:
111 ... f.write(key.private_bytes(
112 ... encoding=serialization.Encoding.PEM,
113 ... format=serialization.PrivateFormat.TraditionalOpenSSL,
114 ... encryption_algorithm=serialization.BestAvailableEncryption(b"passphrase"),
115 ... ))
116
117Then we generate the certificate itself:
118
119.. code-block:: pycon
120
121 >>> # Various details about who we are. For a self-signed certificate the
122 >>> # subject and issuer are always the same.
123 >>> subject = issuer = x509.Name([
124 ... x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
125 ... x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"CA"),
126 ... x509.NameAttribute(NameOID.LOCALITY_NAME, u"San Francisco"),
127 ... x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My Company"),
128 ... x509.NameAttribute(NameOID.COMMON_NAME, u"mysite.com"),
129 ... ])
130 >>> cert = x509.CertificateBuilder().subject_name(
131 ... subject
132 ... ).issuer_name(
133 ... issuer
134 ... ).public_key(
135 ... private_key.public_key()
136 ... ).not_valid_before(
137 ... datetime.datetime.utcnow()
138 ... ).not_valid_after(
139 ... # Our certificate will be valid for 10 days
140 ... datetime.datetime.utcnow() + datetime.timedelta(days=10)
141 ... ).add_extension(
142 ... x509.SubjectAlternativeName([x509.DNSName(u"localhost")]),
143 ... critical=False,
144 ... # Sign our certificate with our private key
145 ... ).sign(private_key, hashes.SHA256(), default_backend())
146 >>> # Write our certificate out to disk.
147 >>> with open("path/to/certificate.pem", "wb") as f:
148 ... f.write(cert.public_bytes(serialization.Encoding.PEM))
149
150And now we have a private key and certificate that can be used for local
151testing.