Paul Kehrer | 016e08a | 2014-11-26 09:41:18 -1000 | [diff] [blame] | 1 | # This file is dual licensed under the terms of the Apache License, Version |
| 2 | # 2.0, and the BSD License. See the LICENSE file in the root of this repository |
| 3 | # for complete details. |
| 4 | |
| 5 | from __future__ import absolute_import, division, print_function |
| 6 | |
Paul Kehrer | b2de948 | 2014-12-11 14:54:48 -0600 | [diff] [blame] | 7 | import abc |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 8 | import datetime |
Paul Kehrer | 016e08a | 2014-11-26 09:41:18 -1000 | [diff] [blame] | 9 | from enum import Enum |
| 10 | |
Paul Kehrer | b2de948 | 2014-12-11 14:54:48 -0600 | [diff] [blame] | 11 | import six |
| 12 | |
Paul Kehrer | 912d3fb | 2015-01-29 11:19:22 -0600 | [diff] [blame] | 13 | from cryptography import utils |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 14 | from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa |
Paul Kehrer | aa7a322 | 2015-08-11 00:00:54 -0500 | [diff] [blame] | 15 | from cryptography.x509.extensions import Extension, ExtensionType |
Paul Kehrer | ed036d2 | 2015-08-09 20:40:48 -0500 | [diff] [blame] | 16 | from cryptography.x509.name import Name |
Paul Kehrer | 912d3fb | 2015-01-29 11:19:22 -0600 | [diff] [blame] | 17 | |
Paul Kehrer | 016e08a | 2014-11-26 09:41:18 -1000 | [diff] [blame] | 18 | |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 19 | _UNIX_EPOCH = datetime.datetime(1970, 1, 1) |
| 20 | |
Paul Kehrer | 9089c91 | 2015-04-20 22:15:20 -0500 | [diff] [blame] | 21 | |
Paul Kehrer | e76cd27 | 2014-12-14 19:00:51 -0600 | [diff] [blame] | 22 | class Version(Enum): |
Paul Kehrer | 016e08a | 2014-11-26 09:41:18 -1000 | [diff] [blame] | 23 | v1 = 0 |
| 24 | v3 = 2 |
| 25 | |
| 26 | |
Paul Kehrer | 016e08a | 2014-11-26 09:41:18 -1000 | [diff] [blame] | 27 | def load_pem_x509_certificate(data, backend): |
| 28 | return backend.load_pem_x509_certificate(data) |
| 29 | |
| 30 | |
Paul Kehrer | 016e08a | 2014-11-26 09:41:18 -1000 | [diff] [blame] | 31 | def load_der_x509_certificate(data, backend): |
| 32 | return backend.load_der_x509_certificate(data) |
Paul Kehrer | a68fd33 | 2014-11-27 07:08:40 -1000 | [diff] [blame] | 33 | |
| 34 | |
Paul Kehrer | 31e3988 | 2015-03-11 11:37:04 -0500 | [diff] [blame] | 35 | def load_pem_x509_csr(data, backend): |
| 36 | return backend.load_pem_x509_csr(data) |
Paul Kehrer | dc480ad | 2015-02-23 12:14:54 -0600 | [diff] [blame] | 37 | |
| 38 | |
Paul Kehrer | 1effb6e | 2015-03-30 15:05:59 -0500 | [diff] [blame] | 39 | def load_der_x509_csr(data, backend): |
| 40 | return backend.load_der_x509_csr(data) |
| 41 | |
| 42 | |
Erik Trauschke | dc57040 | 2015-09-24 20:24:28 -0700 | [diff] [blame] | 43 | def load_pem_x509_crl(data, backend): |
| 44 | return backend.load_pem_x509_crl(data) |
| 45 | |
| 46 | |
| 47 | def load_der_x509_crl(data, backend): |
| 48 | return backend.load_der_x509_crl(data) |
| 49 | |
| 50 | |
Paul Kehrer | e76cd27 | 2014-12-14 19:00:51 -0600 | [diff] [blame] | 51 | class InvalidVersion(Exception): |
Paul Kehrer | d5cccf7 | 2014-12-15 17:20:33 -0600 | [diff] [blame] | 52 | def __init__(self, msg, parsed_version): |
| 53 | super(InvalidVersion, self).__init__(msg) |
| 54 | self.parsed_version = parsed_version |
Paul Kehrer | b2de948 | 2014-12-11 14:54:48 -0600 | [diff] [blame] | 55 | |
| 56 | |
| 57 | @six.add_metaclass(abc.ABCMeta) |
Paul Kehrer | e76cd27 | 2014-12-14 19:00:51 -0600 | [diff] [blame] | 58 | class Certificate(object): |
Paul Kehrer | b2de948 | 2014-12-11 14:54:48 -0600 | [diff] [blame] | 59 | @abc.abstractmethod |
| 60 | def fingerprint(self, algorithm): |
| 61 | """ |
| 62 | Returns bytes using digest passed. |
| 63 | """ |
| 64 | |
| 65 | @abc.abstractproperty |
| 66 | def serial(self): |
| 67 | """ |
| 68 | Returns certificate serial number |
| 69 | """ |
| 70 | |
| 71 | @abc.abstractproperty |
| 72 | def version(self): |
| 73 | """ |
| 74 | Returns the certificate version |
| 75 | """ |
| 76 | |
| 77 | @abc.abstractmethod |
| 78 | def public_key(self): |
| 79 | """ |
| 80 | Returns the public key |
| 81 | """ |
| 82 | |
| 83 | @abc.abstractproperty |
| 84 | def not_valid_before(self): |
| 85 | """ |
| 86 | Not before time (represented as UTC datetime) |
| 87 | """ |
| 88 | |
| 89 | @abc.abstractproperty |
| 90 | def not_valid_after(self): |
| 91 | """ |
| 92 | Not after time (represented as UTC datetime) |
| 93 | """ |
Paul Kehrer | 719d536 | 2015-01-01 20:03:52 -0600 | [diff] [blame] | 94 | |
| 95 | @abc.abstractproperty |
| 96 | def issuer(self): |
| 97 | """ |
| 98 | Returns the issuer name object. |
| 99 | """ |
| 100 | |
| 101 | @abc.abstractproperty |
| 102 | def subject(self): |
| 103 | """ |
| 104 | Returns the subject name object. |
| 105 | """ |
Paul Kehrer | 56da2a5 | 2015-02-11 23:35:07 -0600 | [diff] [blame] | 106 | |
| 107 | @abc.abstractproperty |
Paul Kehrer | 8802a5b | 2015-02-13 12:06:57 -0600 | [diff] [blame] | 108 | def signature_hash_algorithm(self): |
Paul Kehrer | 56da2a5 | 2015-02-11 23:35:07 -0600 | [diff] [blame] | 109 | """ |
Paul Kehrer | 8802a5b | 2015-02-13 12:06:57 -0600 | [diff] [blame] | 110 | Returns a HashAlgorithm corresponding to the type of the digest signed |
| 111 | in the certificate. |
Paul Kehrer | 56da2a5 | 2015-02-11 23:35:07 -0600 | [diff] [blame] | 112 | """ |
Paul Kehrer | dc480ad | 2015-02-23 12:14:54 -0600 | [diff] [blame] | 113 | |
Paul Kehrer | 8c234d1 | 2015-05-15 09:27:22 -0700 | [diff] [blame] | 114 | @abc.abstractproperty |
| 115 | def extensions(self): |
| 116 | """ |
| 117 | Returns an Extensions object. |
| 118 | """ |
| 119 | |
Paul Kehrer | 8bbdc6f | 2015-04-30 16:47:16 -0500 | [diff] [blame] | 120 | @abc.abstractmethod |
| 121 | def __eq__(self, other): |
| 122 | """ |
| 123 | Checks equality. |
| 124 | """ |
| 125 | |
| 126 | @abc.abstractmethod |
| 127 | def __ne__(self, other): |
| 128 | """ |
| 129 | Checks not equal. |
| 130 | """ |
| 131 | |
Andre Caron | a8aded6 | 2015-05-19 20:11:57 -0400 | [diff] [blame] | 132 | @abc.abstractmethod |
Alex Gaynor | 969f3a5 | 2015-07-06 18:52:41 -0400 | [diff] [blame] | 133 | def __hash__(self): |
| 134 | """ |
| 135 | Computes a hash. |
| 136 | """ |
| 137 | |
| 138 | @abc.abstractmethod |
Andre Caron | a8aded6 | 2015-05-19 20:11:57 -0400 | [diff] [blame] | 139 | def public_bytes(self, encoding): |
Andre Caron | 18ef34b | 2015-05-19 21:24:31 -0400 | [diff] [blame] | 140 | """ |
| 141 | Serializes the certificate to PEM or DER format. |
| 142 | """ |
Andre Caron | a8aded6 | 2015-05-19 20:11:57 -0400 | [diff] [blame] | 143 | |
Paul Kehrer | dc480ad | 2015-02-23 12:14:54 -0600 | [diff] [blame] | 144 | |
| 145 | @six.add_metaclass(abc.ABCMeta) |
Erik Trauschke | 2dcce90 | 2015-05-14 16:12:24 -0700 | [diff] [blame] | 146 | class CertificateRevocationList(object): |
| 147 | |
| 148 | @abc.abstractmethod |
| 149 | def fingerprint(self, algorithm): |
| 150 | """ |
| 151 | Returns bytes using digest passed. |
| 152 | """ |
| 153 | |
| 154 | @abc.abstractproperty |
| 155 | def signature_hash_algorithm(self): |
| 156 | """ |
| 157 | Returns a HashAlgorithm corresponding to the type of the digest signed |
| 158 | in the certificate. |
| 159 | """ |
| 160 | |
| 161 | @abc.abstractproperty |
| 162 | def issuer(self): |
| 163 | """ |
| 164 | Returns the X509Name with the issuer of this CRL. |
| 165 | """ |
| 166 | |
| 167 | @abc.abstractproperty |
| 168 | def next_update(self): |
| 169 | """ |
| 170 | Returns the date of next update for this CRL. |
| 171 | """ |
| 172 | |
| 173 | @abc.abstractproperty |
| 174 | def last_update(self): |
| 175 | """ |
| 176 | Returns the date of last update for this CRL. |
| 177 | """ |
| 178 | |
| 179 | @abc.abstractproperty |
Erik Trauschke | abb7b6e | 2015-05-27 15:07:35 -0700 | [diff] [blame] | 180 | def revoked_certificates(self): |
Erik Trauschke | 2dcce90 | 2015-05-14 16:12:24 -0700 | [diff] [blame] | 181 | """ |
| 182 | Returns a list of RevokedCertificate objects for this CRL. |
| 183 | """ |
| 184 | |
| 185 | @abc.abstractproperty |
| 186 | def extensions(self): |
| 187 | """ |
| 188 | Returns an Extensions object containing a list of CRL extensions. |
| 189 | """ |
| 190 | |
| 191 | @abc.abstractmethod |
Erik Trauschke | 2dcce90 | 2015-05-14 16:12:24 -0700 | [diff] [blame] | 192 | def __eq__(self, other): |
| 193 | """ |
| 194 | Checks equality. |
| 195 | """ |
| 196 | |
| 197 | @abc.abstractmethod |
| 198 | def __ne__(self, other): |
| 199 | """ |
| 200 | Checks not equal. |
| 201 | """ |
| 202 | |
| 203 | |
| 204 | @six.add_metaclass(abc.ABCMeta) |
Paul Kehrer | a1a1f23 | 2015-03-15 15:34:35 -0500 | [diff] [blame] | 205 | class CertificateSigningRequest(object): |
Alex Gaynor | 935f6ca | 2015-07-06 21:03:46 -0400 | [diff] [blame] | 206 | @abc.abstractmethod |
Alex Gaynor | 70c8f8b | 2015-07-06 21:02:54 -0400 | [diff] [blame] | 207 | def __eq__(self, other): |
| 208 | """ |
| 209 | Checks equality. |
| 210 | """ |
| 211 | |
| 212 | @abc.abstractmethod |
| 213 | def __ne__(self, other): |
| 214 | """ |
| 215 | Checks not equal. |
| 216 | """ |
| 217 | |
Paul Kehrer | dc480ad | 2015-02-23 12:14:54 -0600 | [diff] [blame] | 218 | @abc.abstractmethod |
Alex Gaynor | 978137d | 2015-07-08 20:59:16 -0400 | [diff] [blame] | 219 | def __hash__(self): |
| 220 | """ |
| 221 | Computes a hash. |
| 222 | """ |
| 223 | |
| 224 | @abc.abstractmethod |
Paul Kehrer | dc480ad | 2015-02-23 12:14:54 -0600 | [diff] [blame] | 225 | def public_key(self): |
| 226 | """ |
| 227 | Returns the public key |
| 228 | """ |
| 229 | |
| 230 | @abc.abstractproperty |
| 231 | def subject(self): |
| 232 | """ |
| 233 | Returns the subject name object. |
| 234 | """ |
| 235 | |
| 236 | @abc.abstractproperty |
| 237 | def signature_hash_algorithm(self): |
| 238 | """ |
| 239 | Returns a HashAlgorithm corresponding to the type of the digest signed |
| 240 | in the certificate. |
| 241 | """ |
Andre Caron | 6e721a9 | 2015-05-17 15:08:48 -0400 | [diff] [blame] | 242 | |
| 243 | @abc.abstractproperty |
| 244 | def extensions(self): |
| 245 | """ |
| 246 | Returns the extensions in the signing request. |
| 247 | """ |
Andre Caron | 476c5df | 2015-05-18 10:23:28 -0400 | [diff] [blame] | 248 | |
| 249 | @abc.abstractmethod |
| 250 | def public_bytes(self, encoding): |
| 251 | """ |
| 252 | Encodes the request to PEM or DER format. |
| 253 | """ |
Erik Trauschke | 2dcce90 | 2015-05-14 16:12:24 -0700 | [diff] [blame] | 254 | |
| 255 | |
| 256 | @six.add_metaclass(abc.ABCMeta) |
| 257 | class RevokedCertificate(object): |
| 258 | @abc.abstractproperty |
| 259 | def serial_number(self): |
| 260 | """ |
| 261 | Returns the serial number of the revoked certificate. |
| 262 | """ |
| 263 | |
| 264 | @abc.abstractproperty |
| 265 | def revocation_date(self): |
| 266 | """ |
| 267 | Returns the date of when this certificate was revoked. |
| 268 | """ |
| 269 | |
| 270 | @abc.abstractproperty |
| 271 | def extensions(self): |
| 272 | """ |
| 273 | Returns an Extensions object containing a list of Revoked extensions. |
| 274 | """ |
Andre Caron | 0ef595f | 2015-05-18 13:53:43 -0400 | [diff] [blame] | 275 | |
| 276 | |
| 277 | class CertificateSigningRequestBuilder(object): |
Andre Caron | 99d0f90 | 2015-06-01 08:36:59 -0400 | [diff] [blame] | 278 | def __init__(self, subject_name=None, extensions=[]): |
Andre Caron | 0ef595f | 2015-05-18 13:53:43 -0400 | [diff] [blame] | 279 | """ |
| 280 | Creates an empty X.509 certificate request (v1). |
| 281 | """ |
Andre Caron | fc164c5 | 2015-05-31 17:36:18 -0400 | [diff] [blame] | 282 | self._subject_name = subject_name |
Ian Cordasco | 41f51ce | 2015-06-17 11:49:11 -0500 | [diff] [blame] | 283 | self._extensions = extensions |
Andre Caron | 0ef595f | 2015-05-18 13:53:43 -0400 | [diff] [blame] | 284 | |
Andre Caron | a9a5117 | 2015-06-06 20:18:44 -0400 | [diff] [blame] | 285 | def subject_name(self, name): |
Andre Caron | 0ef595f | 2015-05-18 13:53:43 -0400 | [diff] [blame] | 286 | """ |
| 287 | Sets the certificate requestor's distinguished name. |
| 288 | """ |
| 289 | if not isinstance(name, Name): |
| 290 | raise TypeError('Expecting x509.Name object.') |
Ian Cordasco | d09ec37 | 2015-06-17 21:37:51 -0500 | [diff] [blame] | 291 | if self._subject_name is not None: |
| 292 | raise ValueError('The subject name may only be set once.') |
Andre Caron | 99d0f90 | 2015-06-01 08:36:59 -0400 | [diff] [blame] | 293 | return CertificateSigningRequestBuilder(name, self._extensions) |
Andre Caron | 0ef595f | 2015-05-18 13:53:43 -0400 | [diff] [blame] | 294 | |
Ian Cordasco | f06b6be | 2015-06-21 10:09:18 -0500 | [diff] [blame] | 295 | def add_extension(self, extension, critical): |
Andre Caron | 0ef595f | 2015-05-18 13:53:43 -0400 | [diff] [blame] | 296 | """ |
| 297 | Adds an X.509 extension to the certificate request. |
| 298 | """ |
Paul Kehrer | e59fd22 | 2015-08-08 22:50:19 -0500 | [diff] [blame] | 299 | if not isinstance(extension, ExtensionType): |
| 300 | raise TypeError("extension must be an ExtensionType") |
| 301 | |
| 302 | extension = Extension(extension.oid, critical, extension) |
| 303 | |
Ian Cordasco | f06b6be | 2015-06-21 10:09:18 -0500 | [diff] [blame] | 304 | # TODO: This is quadratic in the number of extensions |
Andre Caron | 0ef595f | 2015-05-18 13:53:43 -0400 | [diff] [blame] | 305 | for e in self._extensions: |
| 306 | if e.oid == extension.oid: |
| 307 | raise ValueError('This extension has already been set.') |
Andre Caron | fc164c5 | 2015-05-31 17:36:18 -0400 | [diff] [blame] | 308 | return CertificateSigningRequestBuilder( |
Andre Caron | 99d0f90 | 2015-06-01 08:36:59 -0400 | [diff] [blame] | 309 | self._subject_name, self._extensions + [extension] |
Andre Caron | fc164c5 | 2015-05-31 17:36:18 -0400 | [diff] [blame] | 310 | ) |
Andre Caron | 0ef595f | 2015-05-18 13:53:43 -0400 | [diff] [blame] | 311 | |
Alex Gaynor | b3b0fbe | 2015-06-26 19:57:18 -0400 | [diff] [blame] | 312 | def sign(self, private_key, algorithm, backend): |
Andre Caron | 0ef595f | 2015-05-18 13:53:43 -0400 | [diff] [blame] | 313 | """ |
| 314 | Signs the request using the requestor's private key. |
| 315 | """ |
Alex Gaynor | ba19c2e | 2015-06-27 00:07:09 -0400 | [diff] [blame] | 316 | if self._subject_name is None: |
| 317 | raise ValueError("A CertificateSigningRequest must have a subject") |
Andre Caron | a33ea28 | 2015-05-31 16:32:26 -0400 | [diff] [blame] | 318 | return backend.create_x509_csr(self, private_key, algorithm) |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 319 | |
| 320 | |
| 321 | class CertificateBuilder(object): |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 322 | def __init__(self, issuer_name=None, subject_name=None, |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 323 | public_key=None, serial_number=None, not_valid_before=None, |
| 324 | not_valid_after=None, extensions=[]): |
Ian Cordasco | 893246f | 2015-07-24 14:52:18 -0500 | [diff] [blame] | 325 | self._version = Version.v3 |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 326 | self._issuer_name = issuer_name |
| 327 | self._subject_name = subject_name |
| 328 | self._public_key = public_key |
| 329 | self._serial_number = serial_number |
| 330 | self._not_valid_before = not_valid_before |
| 331 | self._not_valid_after = not_valid_after |
| 332 | self._extensions = extensions |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 333 | |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 334 | def issuer_name(self, name): |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 335 | """ |
| 336 | Sets the CA's distinguished name. |
| 337 | """ |
| 338 | if not isinstance(name, Name): |
| 339 | raise TypeError('Expecting x509.Name object.') |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 340 | if self._issuer_name is not None: |
| 341 | raise ValueError('The issuer name may only be set once.') |
| 342 | return CertificateBuilder( |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 343 | name, self._subject_name, self._public_key, |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 344 | self._serial_number, self._not_valid_before, |
| 345 | self._not_valid_after, self._extensions |
| 346 | ) |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 347 | |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 348 | def subject_name(self, name): |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 349 | """ |
| 350 | Sets the requestor's distinguished name. |
| 351 | """ |
| 352 | if not isinstance(name, Name): |
| 353 | raise TypeError('Expecting x509.Name object.') |
Ian Cordasco | 43ae738 | 2015-07-18 23:27:31 -0500 | [diff] [blame] | 354 | if self._subject_name is not None: |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 355 | raise ValueError('The subject name may only be set once.') |
| 356 | return CertificateBuilder( |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 357 | self._issuer_name, name, self._public_key, |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 358 | self._serial_number, self._not_valid_before, |
| 359 | self._not_valid_after, self._extensions |
| 360 | ) |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 361 | |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 362 | def public_key(self, key): |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 363 | """ |
| 364 | Sets the requestor's public key (as found in the signing request). |
| 365 | """ |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 366 | if not isinstance(key, (dsa.DSAPublicKey, rsa.RSAPublicKey, |
| 367 | ec.EllipticCurvePublicKey)): |
| 368 | raise TypeError('Expecting one of DSAPublicKey, RSAPublicKey,' |
| 369 | ' or EllipticCurvePublicKey.') |
| 370 | if self._public_key is not None: |
| 371 | raise ValueError('The public key may only be set once.') |
| 372 | return CertificateBuilder( |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 373 | self._issuer_name, self._subject_name, key, |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 374 | self._serial_number, self._not_valid_before, |
| 375 | self._not_valid_after, self._extensions |
| 376 | ) |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 377 | |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 378 | def serial_number(self, number): |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 379 | """ |
| 380 | Sets the certificate serial number. |
| 381 | """ |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 382 | if not isinstance(number, six.integer_types): |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 383 | raise TypeError('Serial number must be of integral type.') |
Ian Cordasco | 43ae738 | 2015-07-18 23:27:31 -0500 | [diff] [blame] | 384 | if self._serial_number is not None: |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 385 | raise ValueError('The serial number may only be set once.') |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 386 | if number < 0: |
| 387 | raise ValueError('The serial number should be non-negative.') |
| 388 | if utils.bit_length(number) > 160: # As defined in RFC 5280 |
| 389 | raise ValueError('The serial number should not be more than 160 ' |
| 390 | 'bits.') |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 391 | return CertificateBuilder( |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 392 | self._issuer_name, self._subject_name, |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 393 | self._public_key, number, self._not_valid_before, |
| 394 | self._not_valid_after, self._extensions |
| 395 | ) |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 396 | |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 397 | def not_valid_before(self, time): |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 398 | """ |
| 399 | Sets the certificate activation time. |
| 400 | """ |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 401 | if not isinstance(time, datetime.datetime): |
| 402 | raise TypeError('Expecting datetime object.') |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 403 | if self._not_valid_before is not None: |
| 404 | raise ValueError('The not valid before may only be set once.') |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 405 | if time <= _UNIX_EPOCH: |
| 406 | raise ValueError('The not valid before date must be after the unix' |
| 407 | ' epoch (1970 January 1).') |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 408 | return CertificateBuilder( |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 409 | self._issuer_name, self._subject_name, |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 410 | self._public_key, self._serial_number, time, |
| 411 | self._not_valid_after, self._extensions |
| 412 | ) |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 413 | |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 414 | def not_valid_after(self, time): |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 415 | """ |
| 416 | Sets the certificate expiration time. |
| 417 | """ |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 418 | if not isinstance(time, datetime.datetime): |
| 419 | raise TypeError('Expecting datetime object.') |
Ian Cordasco | 43ae738 | 2015-07-18 23:27:31 -0500 | [diff] [blame] | 420 | if self._not_valid_after is not None: |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 421 | raise ValueError('The not valid after may only be set once.') |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 422 | if time <= _UNIX_EPOCH: |
| 423 | raise ValueError('The not valid after date must be after the unix' |
| 424 | ' epoch (1970 January 1).') |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 425 | return CertificateBuilder( |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 426 | self._issuer_name, self._subject_name, |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 427 | self._public_key, self._serial_number, self._not_valid_before, |
| 428 | time, self._extensions |
| 429 | ) |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 430 | |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 431 | def add_extension(self, extension, critical): |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 432 | """ |
| 433 | Adds an X.509 extension to the certificate. |
| 434 | """ |
Paul Kehrer | 08f950e | 2015-08-08 22:14:42 -0500 | [diff] [blame] | 435 | if not isinstance(extension, ExtensionType): |
| 436 | raise TypeError("extension must be an ExtensionType") |
| 437 | |
| 438 | extension = Extension(extension.oid, critical, extension) |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 439 | |
| 440 | # TODO: This is quadratic in the number of extensions |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 441 | for e in self._extensions: |
| 442 | if e.oid == extension.oid: |
| 443 | raise ValueError('This extension has already been set.') |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 444 | |
| 445 | return CertificateBuilder( |
Ian Cordasco | c5e1c25 | 2015-07-31 23:33:35 -0500 | [diff] [blame] | 446 | self._issuer_name, self._subject_name, |
Ian Cordasco | b3ed484 | 2015-07-01 22:46:03 -0500 | [diff] [blame] | 447 | self._public_key, self._serial_number, self._not_valid_before, |
| 448 | self._not_valid_after, self._extensions + [extension] |
| 449 | ) |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 450 | |
Paul Kehrer | 9add80e | 2015-08-03 17:53:14 +0100 | [diff] [blame] | 451 | def sign(self, private_key, algorithm, backend): |
Andre Caron | 9bbfcea | 2015-05-18 20:55:29 -0400 | [diff] [blame] | 452 | """ |
| 453 | Signs the certificate using the CA's private key. |
| 454 | """ |
Paul Kehrer | 25f1922 | 2015-08-04 23:05:09 +0100 | [diff] [blame] | 455 | if self._subject_name is None: |
| 456 | raise ValueError("A certificate must have a subject name") |
| 457 | |
| 458 | if self._issuer_name is None: |
| 459 | raise ValueError("A certificate must have an issuer name") |
| 460 | |
| 461 | if self._serial_number is None: |
| 462 | raise ValueError("A certificate must have a serial number") |
| 463 | |
| 464 | if self._not_valid_before is None: |
| 465 | raise ValueError("A certificate must have a not valid before time") |
| 466 | |
| 467 | if self._not_valid_after is None: |
| 468 | raise ValueError("A certificate must have a not valid after time") |
| 469 | |
| 470 | if self._public_key is None: |
| 471 | raise ValueError("A certificate must have a public key") |
| 472 | |
Paul Kehrer | 1ae7653 | 2015-08-06 12:37:10 +0100 | [diff] [blame] | 473 | return backend.create_x509_certificate(self, private_key, algorithm) |