blob: 753da6b4dfa76bfa7867603bab2d90d3ea4bad3c [file] [log] [blame]
Michael Johnson0a0f9b32019-01-02 16:58:21 -08001# Copyright 2018 Rackspace US Inc. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import datetime
16
17from cryptography.hazmat.backends import default_backend
18from cryptography.hazmat.primitives.asymmetric import rsa
19from cryptography.hazmat.primitives import hashes
Gregory Thiemongea66952e2022-07-21 12:21:40 +020020from cryptography.hazmat.primitives.serialization import NoEncryption
21from cryptography.hazmat.primitives.serialization import pkcs12
Michael Johnson0a0f9b32019-01-02 16:58:21 -080022from cryptography import x509
23from cryptography.x509.oid import NameOID
24import OpenSSL
25
26
27def generate_ca_cert_and_key():
28 """Creates a CA cert and key for testing.
29
30 :returns: The cryptography CA cert and CA key objects.
31 """
32
33 ca_key = rsa.generate_private_key(
34 public_exponent=65537, key_size=2048, backend=default_backend())
35
36 subject = issuer = x509.Name([
37 x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
38 x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Denial"),
39 x509.NameAttribute(NameOID.LOCALITY_NAME, u"Corvallis"),
40 x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"OpenStack"),
41 x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"Octavia"),
42 x509.NameAttribute(NameOID.COMMON_NAME, u"ca_cert.example.com"),
43 ])
44
45 ca_cert = x509.CertificateBuilder().subject_name(
46 subject
47 ).issuer_name(
48 issuer
49 ).public_key(
50 ca_key.public_key()
51 ).serial_number(
52 x509.random_serial_number()
53 ).not_valid_before(
54 datetime.datetime.utcnow()
55 ).not_valid_after(
56 datetime.datetime.utcnow() + datetime.timedelta(days=10)
57 ).add_extension(
58 x509.SubjectAlternativeName([x509.DNSName(u"ca_cert.example.com")]),
59 critical=False,
60 ).add_extension(
61 x509.BasicConstraints(ca=True, path_length=None),
62 critical=True,
Michael Johnson63786632019-11-08 23:22:13 -080063 ).add_extension(
64 # KeyUsage(digital_signature, content_commitment, key_encipherment,
65 # data_encipherment, key_agreement, key_cert_sign, crl_sign,
66 # encipher_only, decipher_only)
67 x509.KeyUsage(True, False, False, False, False,
68 True, True, False, False),
69 critical=True,
Michael Johnson0a0f9b32019-01-02 16:58:21 -080070 ).sign(ca_key, hashes.SHA256(), default_backend())
71
72 return ca_cert, ca_key
73
74
75def generate_server_cert_and_key(ca_cert, ca_key, server_uuid):
76 """Creates a server cert and key for testing.
77
78 :param ca_cert: A cryptography CA certificate (x509) object.
79 :param ca_key: A cryptography CA key (x509) object.
80 :param server_uuid: A UUID identifying the server.
81 :returns: The cryptography server cert and key objects.
82 """
83
84 server_key = rsa.generate_private_key(
85 public_exponent=65537, key_size=2048, backend=default_backend())
86
87 subject = x509.Name([
88 x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
89 x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Denial"),
90 x509.NameAttribute(NameOID.LOCALITY_NAME, u"Corvallis"),
91 x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"OpenStack"),
92 x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"Octavia"),
93 x509.NameAttribute(NameOID.COMMON_NAME, u"{}.example.com".format(
94 server_uuid)),
95 ])
96
97 server_cert = x509.CertificateBuilder().subject_name(
98 subject
99 ).issuer_name(
100 ca_cert.subject
101 ).public_key(
102 server_key.public_key()
103 ).serial_number(
104 x509.random_serial_number()
105 ).not_valid_before(
106 datetime.datetime.utcnow()
107 ).not_valid_after(
108 datetime.datetime.utcnow() + datetime.timedelta(days=10)
109 ).add_extension(
110 x509.SubjectAlternativeName(
111 [x509.DNSName(u"{}.example.com".format(server_uuid))]),
112 critical=False,
113 ).add_extension(
114 x509.BasicConstraints(ca=False, path_length=None),
115 critical=True,
Michael Johnson63786632019-11-08 23:22:13 -0800116 ).add_extension(
117 # KeyUsage(digital_signature, content_commitment, key_encipherment,
118 # data_encipherment, key_agreement, key_cert_sign, crl_sign,
119 # encipher_only, decipher_only)
120 x509.KeyUsage(True, False, True, False, False,
121 False, False, False, False),
122 critical=True,
Michael Johnson0a0f9b32019-01-02 16:58:21 -0800123 ).sign(ca_key, hashes.SHA256(), default_backend())
124
125 return server_cert, server_key
126
127
Michael Johnson63786632019-11-08 23:22:13 -0800128def generate_client_cert_and_key(ca_cert, ca_key, client_uuid):
129 """Creates a client cert and key for testing.
130
131 :param ca_cert: A cryptography CA certificate (x509) object.
132 :param ca_key: A cryptography CA key (x509) object.
133 :param client_uuid: A UUID identifying the client.
134 :returns: The cryptography server cert and key objects.
135 """
136
137 client_key = rsa.generate_private_key(
138 public_exponent=65537, key_size=2048, backend=default_backend())
139
140 subject = x509.Name([
141 x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
142 x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Denial"),
143 x509.NameAttribute(NameOID.LOCALITY_NAME, u"Corvallis"),
144 x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"OpenStack"),
145 x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"Octavia"),
146 x509.NameAttribute(NameOID.COMMON_NAME, u"{}".format(client_uuid)),
147 ])
148
149 client_cert = x509.CertificateBuilder().subject_name(
150 subject
151 ).issuer_name(
152 ca_cert.subject
153 ).public_key(
154 client_key.public_key()
155 ).serial_number(
156 x509.random_serial_number()
157 ).not_valid_before(
158 datetime.datetime.utcnow()
159 ).not_valid_after(
160 datetime.datetime.utcnow() + datetime.timedelta(days=10)
161 ).add_extension(
162 x509.BasicConstraints(ca=False, path_length=None),
163 critical=True,
164 ).add_extension(
165 # KeyUsage(digital_signature, content_commitment, key_encipherment,
166 # data_encipherment, key_agreement, key_cert_sign, crl_sign,
167 # encipher_only, decipher_only)
168 x509.KeyUsage(True, True, True, False, False, False,
169 False, False, False),
170 critical=True,
171 ).sign(ca_key, hashes.SHA256(), default_backend())
172
173 return client_cert, client_key
174
175
Michael Johnson0a0f9b32019-01-02 16:58:21 -0800176def generate_pkcs12_bundle(server_cert, server_key):
177 """Creates a pkcs12 formated bundle.
178
179 Note: This uses pyOpenSSL as the cryptography package does not yet
180 support creating pkcs12 bundles. The currently un-released
181 2.5 version of cryptography supports reading pkcs12, but not
182 creation. This method should be updated to only use
183 cryptography once it supports creating pkcs12 bundles.
184
185 :param server_cert: A cryptography certificate (x509) object.
186 :param server_key: A cryptography key (x509) object.
187 :returns: A pkcs12 bundle.
188 """
Gregory Thiemongea66952e2022-07-21 12:21:40 +0200189 # Use the PKCS12 serialization function from cryptography if it exists
190 # (>=3.0), otherwise use the pyOpenSSL module.
191 #
192 # The PKCS12 class of the pyOpenSSL module is not compliant with FIPS.
193 # It uses the SHA1 function [0] which is not allowed when generating
194 # digital signatures [1]
195 #
196 # [0] https://github.com/pyca/pyopenssl/blob/
197 # 65ca53a7a06a7c78c1749200a6b3a007e47d3214/src/OpenSSL/
198 # crypto.py#L2748-L2749
199 # [1] https://nvlpubs.nist.gov/nistpubs/SpecialPublications/
200 # NIST.SP.800-131Ar1.pdf
201 if hasattr(pkcs12, 'serialize_key_and_certificates'):
202 p12 = pkcs12.serialize_key_and_certificates(
203 b'', server_key, server_cert,
204 cas=None, encryption_algorithm=NoEncryption())
205 else:
206 p12 = OpenSSL.crypto.PKCS12()
207 p12.set_privatekey(
208 OpenSSL.crypto.PKey.from_cryptography_key(server_key))
209 p12.set_certificate(OpenSSL.crypto.X509.from_cryptography(server_cert))
210 p12 = p12.export()
211 return p12
Michael Johnson63786632019-11-08 23:22:13 -0800212
213
214def generate_certificate_revocation_list(ca_cert, ca_key, cert_to_revoke):
215 """Create a certificate revocation list with a revoked certificate.
216
217 :param ca_cert: A cryptography CA certificate (x509) object.
218 :param ca_key: A cryptography CA key (x509) object.
219 :param cert_to_revoke: A cryptography CA certificate (x509) object.
220 :returns: A signed certificate revocation list.
221 """
222 crl_builder = x509.CertificateRevocationListBuilder()
223 crl_builder = crl_builder.issuer_name(ca_cert.subject)
Gregory Thiemongee474c3e2019-12-10 19:24:10 +0100224 crl_builder = crl_builder.last_update(datetime.datetime.utcnow())
225 crl_builder = crl_builder.next_update(datetime.datetime.utcnow() +
Michael Johnson63786632019-11-08 23:22:13 -0800226 datetime.timedelta(1, 0, 0))
227
228 revoked_cert = x509.RevokedCertificateBuilder().serial_number(
229 cert_to_revoke.serial_number
230 ).revocation_date(
Gregory Thiemongee474c3e2019-12-10 19:24:10 +0100231 datetime.datetime.utcnow()
Michael Johnson63786632019-11-08 23:22:13 -0800232 ).build(default_backend())
233
234 crl_builder = crl_builder.add_revoked_certificate(revoked_cert)
235 return crl_builder.sign(private_key=ca_key, algorithm=hashes.SHA256(),
236 backend=default_backend())