Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 1 | Tutorial |
| 2 | ======== |
| 3 | |
Alex Gaynor | 077d89d | 2016-02-10 23:35:20 -0500 | [diff] [blame] | 4 | X.509 certificates are used to authenticate clients and servers. The most |
| 5 | common use case is for web servers using HTTPS. |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 6 | |
| 7 | Creating a Certificate Signing Request (CSR) |
| 8 | -------------------------------------------- |
| 9 | |
| 10 | When obtaining a certificate from a certificate authority (CA), the usual |
Alex Gaynor | 0c91ddc | 2015-07-13 21:15:03 -0400 | [diff] [blame] | 11 | flow is: |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 12 | |
| 13 | 1. You generate a private/public key pair. |
Alex Gaynor | 5bcd8e8 | 2015-07-13 21:17:31 -0400 | [diff] [blame] | 14 | 2. You create a request for a certificate, which is signed by your key (to |
| 15 | prove that you own that key). |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 16 | 3. You give your CSR to a CA (but *not* the private key). |
| 17 | 4. The CA validates that you own the resource (e.g. domain) you want a |
| 18 | certificate for. |
Alex Gaynor | 5bcd8e8 | 2015-07-13 21:17:31 -0400 | [diff] [blame] | 19 | 5. The CA gives you a certificate, signed by them, which identifies your public |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 20 | key, and the resource you are authenticated for. |
Alex Gaynor | 5bcd8e8 | 2015-07-13 21:17:31 -0400 | [diff] [blame] | 21 | 6. You configure your server to use that certificate, combined with your |
| 22 | private key, to server traffic. |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 23 | |
| 24 | If you want to obtain a certificate from a typical commercial CA, here's how. |
| 25 | First, you'll need to generate a private key, we'll generate an RSA key (these |
| 26 | are 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 Buchwaldt | e782061 | 2015-08-26 19:15:03 +0200 | [diff] [blame] | 40 | >>> with open("path/to/store/key.pem", "wb") as f: |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 41 | ... f.write(key.private_bytes( |
| 42 | ... encoding=serialization.Encoding.PEM, |
| 43 | ... format=serialization.PrivateFormat.TraditionalOpenSSL, |
| 44 | ... encryption_algorithm=serialization.BestAvailableEncryption(b"passphrase"), |
Alex Gaynor | d02bc12 | 2015-07-13 21:18:56 -0400 | [diff] [blame] | 45 | ... )) |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 46 | |
| 47 | If you've already generated a key you can load it with |
Alex Gaynor | 9ca4851 | 2015-07-16 07:03:40 -0400 | [diff] [blame] | 48 | :func:`~cryptography.hazmat.primitives.serialization.load_pem_private_key`. |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 49 | |
Alex Gaynor | 5bcd8e8 | 2015-07-13 21:17:31 -0400 | [diff] [blame] | 50 | Next we need to generate a certificate signing request. A typical CSR contains |
| 51 | a few details: |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 52 | |
| 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 Kehrer | 246daf7 | 2015-08-10 21:12:24 -0500 | [diff] [blame] | 60 | >>> from cryptography.x509.oid import NameOID |
Abhijeet Rastogi | a5e999a | 2015-08-15 08:44:14 +0530 | [diff] [blame] | 61 | >>> from cryptography.hazmat.primitives import hashes |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 62 | >>> # Generate a CSR |
| 63 | >>> csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ |
| 64 | ... # Provide various details about who we are. |
Paul Kehrer | 246daf7 | 2015-08-10 21:12:24 -0500 | [diff] [blame] | 65 | ... 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 Gaynor | f648734 | 2015-09-03 10:10:49 -0400 | [diff] [blame] | 70 | ... ])).add_extension( |
| 71 | ... x509.SubjectAlternativeName([ |
| 72 | ... # Describe what sites we want this certificate for. |
Alex Gaynor | cdaf3ff | 2017-07-30 13:08:51 -0400 | [diff] [blame] | 73 | ... x509.DNSName(b"mysite.com"), |
| 74 | ... x509.DNSName(b"www.mysite.com"), |
| 75 | ... x509.DNSName(b"subdomain.mysite.com"), |
Alex Gaynor | f648734 | 2015-09-03 10:10:49 -0400 | [diff] [blame] | 76 | ... ]), |
| 77 | ... critical=False, |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 78 | ... # Sign the CSR with our private key. |
Alex Gaynor | f648734 | 2015-09-03 10:10:49 -0400 | [diff] [blame] | 79 | ... ).sign(key, hashes.SHA256(), default_backend()) |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 80 | >>> # Write our CSR out to disk. |
Tim Buchwaldt | e782061 | 2015-08-26 19:15:03 +0200 | [diff] [blame] | 81 | >>> with open("path/to/csr.pem", "wb") as f: |
Alex Gaynor | 11b00cd | 2015-07-13 21:12:39 -0400 | [diff] [blame] | 82 | ... f.write(csr.public_bytes(serialization.Encoding.PEM)) |
| 83 | |
| 84 | Now we can give our CSR to a CA, who will give a certificate to us in return. |
Alex Gaynor | d57a72e | 2016-03-14 12:04:30 -0400 | [diff] [blame] | 85 | |
| 86 | Creating a self-signed certificate |
| 87 | ---------------------------------- |
| 88 | |
| 89 | While most of the time you want a certificate that has been *signed* by someone |
| 90 | else (i.e. a certificate authority), so that trust is established, sometimes |
Alex Gaynor | 84c58c4 | 2016-03-14 12:17:19 -0400 | [diff] [blame] | 91 | you want to create a self-signed certificate. Self-signed certificates are not |
Alex Gaynor | 1cc3890 | 2016-03-14 12:34:52 -0400 | [diff] [blame] | 92 | issued by a certificate authority, but instead they are signed by the private |
| 93 | key corresponding to the public key they embed. |
Alex Gaynor | d57a72e | 2016-03-14 12:04:30 -0400 | [diff] [blame] | 94 | |
| 95 | This means that other people don't trust these certificates, but it also means |
| 96 | they can be issued very easily. In general the only use case for a self-signed |
| 97 | certificate is local testing, where you don't need anyone else to trust your |
| 98 | certificate. |
| 99 | |
| 100 | Like 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 | |
| 118 | Then 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( |
AlexanderWeyman | 9794588 | 2017-02-13 03:00:19 +0100 | [diff] [blame] | 136 | ... key.public_key() |
Alex Gaynor | 0fa6b39 | 2016-09-09 21:32:14 -0400 | [diff] [blame] | 137 | ... ).serial_number( |
| 138 | ... x509.random_serial_number() |
Alex Gaynor | d57a72e | 2016-03-14 12:04:30 -0400 | [diff] [blame] | 139 | ... ).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( |
Alex Gaynor | cdaf3ff | 2017-07-30 13:08:51 -0400 | [diff] [blame] | 145 | ... x509.SubjectAlternativeName([x509.DNSName(b"localhost")]), |
Alex Gaynor | d57a72e | 2016-03-14 12:04:30 -0400 | [diff] [blame] | 146 | ... critical=False, |
| 147 | ... # Sign our certificate with our private key |
AlexanderWeyman | 9794588 | 2017-02-13 03:00:19 +0100 | [diff] [blame] | 148 | ... ).sign(key, hashes.SHA256(), default_backend()) |
Alex Gaynor | d57a72e | 2016-03-14 12:04:30 -0400 | [diff] [blame] | 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 | |
| 153 | And now we have a private key and certificate that can be used for local |
| 154 | testing. |
Paul Kehrer | 136b324 | 2017-05-24 19:24:54 -0700 | [diff] [blame] | 155 | |
| 156 | Determining Certificate or Certificate Signing Request Key Type |
| 157 | --------------------------------------------------------------- |
| 158 | |
| 159 | Certificates and certificate signing requests can be issued with multiple |
| 160 | key types. You can determine what the key type is by using ``isinstance`` |
| 161 | checks: |
| 162 | |
| 163 | .. code-block:: pycon |
| 164 | |
| 165 | >>> public_key = cert.public_key() |
| 166 | >>> if isinstance(public_key, rsa.RSAPublicKey): |
| 167 | ... # Do something RSA specific |
| 168 | ... elif isinstance(public_key, ec.EllipticCurvePublicKey): |
| 169 | ... # Do something EC specific |
| 170 | ... else: |
| 171 | ... # Remember to handle this case |