blob: 5a46983b0d9741c1295359583cc8e68f71d9d45a [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05003from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
5
6from six import (
7 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -04008 text_type as _text_type,
9 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080010
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050011from OpenSSL._util import (
12 ffi as _ffi,
13 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050014 exception_from_error_queue as _exception_from_error_queue,
15 byte_string as _byte_string,
16 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080017
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050018FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
19FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080020
21# TODO This was an API mistake. OpenSSL has no such constant.
22FILETYPE_TEXT = 2 ** 16 - 1
23
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050024TYPE_RSA = _lib.EVP_PKEY_RSA
25TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080026
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080027
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070028
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050029class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050030 """
31 An error occurred in an `OpenSSL.crypto` API.
32 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050033
34
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070035
36def _exception_from_context_error(exception_type, store_ctx):
37 """
38 Convert a :py:func:`OpenSSL.crypto.verify_cert` failure into a Python
39 exception.
40
41 When a call to native OpenSSL X509_verify_cert fails, additonal information
Jean-Paul Calderone41a4b722015-01-18 15:26:13 -050042 about the failure can be obtained from the store context.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070043 """
44
45 errors = [
46 _lib.X509_STORE_CTX_get_error(store_ctx._store_ctx),
47 _lib.X509_STORE_CTX_get_error_depth(store_ctx._store_ctx),
Jean-Paul Calderone093d9fe2015-01-18 15:26:20 -050048 _native(_ffi.string(_lib.X509_verify_cert_error_string(
49 _lib.X509_STORE_CTX_get_error(store_ctx._store_ctx)))),
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070050 ]
51 _x509 = _lib.X509_STORE_CTX_get_current_cert(store_ctx._store_ctx)
52 if _x509 != _ffi.NULL:
53 _cert = _lib.X509_dup(_x509)
54 pycert = X509.__new__(X509)
55 pycert._x509 = _ffi.gc(_cert, _lib.X509_free)
56 e = exception_type(errors)
57 e.certificate = pycert
58 raise e
59
60
61
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050062_raise_current_error = partial(_exception_from_error_queue, Error)
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070063_raise_context_error = partial(_exception_from_context_error, Error)
64
65
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050066
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050067def _untested_error(where):
68 """
69 An OpenSSL API failed somehow. Additionally, the failure which was
70 encountered isn't one that's exercised by the test suite so future behavior
71 of pyOpenSSL is now somewhat less predictable.
72 """
73 raise RuntimeError("Unknown %s failure" % (where,))
74
75
76
77def _new_mem_buf(buffer=None):
78 """
79 Allocate a new OpenSSL memory BIO.
80
81 Arrange for the garbage collector to clean it up automatically.
82
83 :param buffer: None or some bytes to use to put into the BIO so that they
84 can be read out.
85 """
86 if buffer is None:
87 bio = _lib.BIO_new(_lib.BIO_s_mem())
88 free = _lib.BIO_free
89 else:
90 data = _ffi.new("char[]", buffer)
91 bio = _lib.BIO_new_mem_buf(data, len(buffer))
92 # Keep the memory alive as long as the bio is alive!
93 def free(bio, ref=data):
94 return _lib.BIO_free(bio)
95
96 if bio == _ffi.NULL:
97 # TODO: This is untested.
98 _raise_current_error()
99
100 bio = _ffi.gc(bio, free)
101 return bio
102
103
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500104
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800105def _bio_to_string(bio):
106 """
107 Copy the contents of an OpenSSL BIO object into a Python byte string.
108 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500109 result_buffer = _ffi.new('char**')
110 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
111 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800112
113
114
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800115def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500116 """
117 The the time value of an ASN1 time object.
118
119 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
120 castable to that type) which will have its value set.
121 @param when: A string representation of the desired time value.
122
123 @raise TypeError: If C{when} is not a L{bytes} string.
124 @raise ValueError: If C{when} does not represent a time in the required
125 format.
126 @raise RuntimeError: If the time value cannot be set for some other
127 (unspecified) reason.
128 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800129 if not isinstance(when, bytes):
130 raise TypeError("when must be a byte string")
131
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500132 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
133 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800134 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500135 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
136 _lib.ASN1_STRING_set(dummy, when, len(when))
137 check_result = _lib.ASN1_GENERALIZEDTIME_check(
138 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800139 if not check_result:
140 raise ValueError("Invalid string")
141 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500142 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800143
144
145
146def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500147 """
148 Retrieve the time value of an ASN1 time object.
149
150 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
151 that type) from which the time value will be retrieved.
152
153 @return: The time value from C{timestamp} as a L{bytes} string in a certain
154 format. Or C{None} if the object contains no time value.
155 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500156 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
157 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800158 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500159 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
160 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800161 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500162 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
163 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
164 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500165 # This may happen:
166 # - if timestamp was not an ASN1_TIME
167 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
168 # - if a copy of the time data from timestamp cannot be made for
169 # the newly allocated ASN1_GENERALIZEDTIME
170 #
171 # These are difficult to test. cffi enforces the ASN1_TIME type.
172 # Memory allocation failures are a pain to trigger
173 # deterministically.
174 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800175 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500176 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800177 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500178 string_data = _lib.ASN1_STRING_data(string_timestamp)
179 string_result = _ffi.string(string_data)
180 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800181 return string_result
182
183
184
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800185class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800186 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800187 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800188
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800189 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500190 pkey = _lib.EVP_PKEY_new()
191 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800192 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800193
194
195 def generate_key(self, type, bits):
196 """
197 Generate a key of a given type, with a given number of a bits
198
199 :param type: The key type (TYPE_RSA or TYPE_DSA)
200 :param bits: The number of bits
201
202 :return: None
203 """
204 if not isinstance(type, int):
205 raise TypeError("type must be an integer")
206
207 if not isinstance(bits, int):
208 raise TypeError("bits must be an integer")
209
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800210 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500211 exponent = _lib.BN_new()
212 exponent = _ffi.gc(exponent, _lib.BN_free)
213 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800214
215 if type == TYPE_RSA:
216 if bits <= 0:
217 raise ValueError("Invalid number of bits")
218
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500219 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800220
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500221 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500222 if result == 0:
223 # TODO: The test for this case is commented out. Different
224 # builds of OpenSSL appear to have different failure modes that
225 # make it hard to test. Visual inspection of the OpenSSL
226 # source reveals that a return value of 0 signals an error.
227 # Manual testing on a particular build of OpenSSL suggests that
228 # this is probably the appropriate way to handle those errors.
229 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800230
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500231 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800232 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500233 # TODO: It appears as though this can fail if an engine is in
234 # use which does not support RSA.
235 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800236
237 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500238 dsa = _lib.DSA_generate_parameters(
239 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
240 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500241 # TODO: This is untested.
242 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500243 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500244 # TODO: This is untested.
245 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500246 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500247 # TODO: This is untested.
248 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800249 else:
250 raise Error("No such key type")
251
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800252 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800253
254
255 def check(self):
256 """
257 Check the consistency of an RSA private key.
258
259 :return: True if key is consistent.
260 :raise Error: if the key is inconsistent.
261 :raise TypeError: if the key is of a type which cannot be checked.
262 Only RSA keys can currently be checked.
263 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800264 if self._only_public:
265 raise TypeError("public key only")
266
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500267 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800268 raise TypeError("key type unsupported")
269
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500270 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
271 rsa = _ffi.gc(rsa, _lib.RSA_free)
272 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800273 if result:
274 return True
275 _raise_current_error()
276
277
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800278 def type(self):
279 """
280 Returns the type of the key
281
282 :return: The type of the key.
283 """
284 return self._pkey.type
285
286
287 def bits(self):
288 """
289 Returns the number of bits of the key
290
291 :return: The number of bits of the key.
292 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500293 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800294PKeyType = PKey
295
296
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800297
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400298class _EllipticCurve(object):
299 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400300 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400301
302 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
303 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
304 instances each of which represents one curve supported by the system.
305 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400306 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400307 _curves = None
308
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400309 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400310 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400311 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400312 """
313 Implement cooperation with the right-hand side argument of ``!=``.
314
315 Python 3 seems to have dropped this cooperation in this very narrow
316 circumstance.
317 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400318 if isinstance(other, _EllipticCurve):
319 return super(_EllipticCurve, self).__ne__(other)
320 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400321
322
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400323 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400324 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400325 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400326 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400327
328 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400329
330 :return: A :py:type:`set` of ``cls`` instances giving the names of the
331 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400332 """
333 if lib.Cryptography_HAS_EC:
334 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
335 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
336 # The return value on this call should be num_curves again. We could
337 # check it to make sure but if it *isn't* then.. what could we do?
338 # Abort the whole process, I suppose...? -exarkun
339 lib.EC_get_builtin_curves(builtin_curves, num_curves)
340 return set(
341 cls.from_nid(lib, c.nid)
342 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400343 return set()
344
345
346 @classmethod
347 def _get_elliptic_curves(cls, lib):
348 """
349 Get, cache, and return the curves supported by OpenSSL.
350
351 :param lib: The OpenSSL library binding object.
352
353 :return: A :py:type:`set` of ``cls`` instances giving the names of the
354 elliptic curves the underlying library supports.
355 """
356 if cls._curves is None:
357 cls._curves = cls._load_elliptic_curves(lib)
358 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400359
360
361 @classmethod
362 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400363 """
364 Instantiate a new :py:class:`_EllipticCurve` associated with the given
365 OpenSSL NID.
366
367 :param lib: The OpenSSL library binding object.
368
369 :param nid: The OpenSSL NID the resulting curve object will represent.
370 This must be a curve NID (and not, for example, a hash NID) or
371 subsequent operations will fail in unpredictable ways.
372 :type nid: :py:class:`int`
373
374 :return: The curve object.
375 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400376 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
377
378
379 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400380 """
381 :param _lib: The :py:mod:`cryptography` binding instance used to
382 interface with OpenSSL.
383
384 :param _nid: The OpenSSL NID identifying the curve this object
385 represents.
386 :type _nid: :py:class:`int`
387
388 :param name: The OpenSSL short name identifying the curve this object
389 represents.
390 :type name: :py:class:`unicode`
391 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400392 self._lib = lib
393 self._nid = nid
394 self.name = name
395
396
397 def __repr__(self):
398 return "<Curve %r>" % (self.name,)
399
400
401 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400402 """
403 Create a new OpenSSL EC_KEY structure initialized to use this curve.
404
405 The structure is automatically garbage collected when the Python object
406 is garbage collected.
407 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400408 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
409 return _ffi.gc(key, _lib.EC_KEY_free)
410
411
412
413def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400414 """
415 Return a set of objects representing the elliptic curves supported in the
416 OpenSSL build in use.
417
418 The curve objects have a :py:class:`unicode` ``name`` attribute by which
419 they identify themselves.
420
421 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400422 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
423 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400424 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400425 return _EllipticCurve._get_elliptic_curves(_lib)
426
427
428
429def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400430 """
431 Return a single curve object selected by name.
432
433 See :py:func:`get_elliptic_curves` for information about curve objects.
434
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400435 :param name: The OpenSSL short name identifying the curve object to
436 retrieve.
437 :type name: :py:class:`unicode`
438
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400439 If the named curve is not supported then :py:class:`ValueError` is raised.
440 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400441 for curve in get_elliptic_curves():
442 if curve.name == name:
443 return curve
444 raise ValueError("unknown curve name", name)
445
446
447
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800448class X509Name(object):
449 def __init__(self, name):
450 """
451 Create a new X509Name, copying the given X509Name instance.
452
453 :param name: An X509Name object to copy
454 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500455 name = _lib.X509_NAME_dup(name._name)
456 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800457
458
459 def __setattr__(self, name, value):
460 if name.startswith('_'):
461 return super(X509Name, self).__setattr__(name, value)
462
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800463 # Note: we really do not want str subclasses here, so we do not use
464 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800465 if type(name) is not str:
466 raise TypeError("attribute name must be string, not '%.200s'" % (
467 type(value).__name__,))
468
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500469 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500470 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800471 try:
472 _raise_current_error()
473 except Error:
474 pass
475 raise AttributeError("No such attribute")
476
477 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500478 for i in range(_lib.X509_NAME_entry_count(self._name)):
479 ent = _lib.X509_NAME_get_entry(self._name, i)
480 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
481 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800482 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500483 ent = _lib.X509_NAME_delete_entry(self._name, i)
484 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800485 break
486
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500487 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800488 value = value.encode('utf-8')
489
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500490 add_result = _lib.X509_NAME_add_entry_by_NID(
491 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800492 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500493 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800494
495
496 def __getattr__(self, name):
497 """
498 Find attribute. An X509Name object has the following attributes:
499 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
500 organization (alias O), organizationalUnit (alias OU), commonName (alias
501 CN) and more...
502 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500503 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500504 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800505 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
506 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
507 # push something onto the error queue. If we don't clean that up
508 # now, someone else will bump into it later and be quite confused.
509 # See lp#314814.
510 try:
511 _raise_current_error()
512 except Error:
513 pass
514 return super(X509Name, self).__getattr__(name)
515
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500516 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800517 if entry_index == -1:
518 return None
519
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500520 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
521 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500523 result_buffer = _ffi.new("unsigned char**")
524 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800525 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500526 # TODO: This is untested.
527 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800528
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700529 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500530 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700531 finally:
532 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500533 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800534 return result
535
536
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500537 def _cmp(op):
538 def f(self, other):
539 if not isinstance(other, X509Name):
540 return NotImplemented
541 result = _lib.X509_NAME_cmp(self._name, other._name)
542 return op(result, 0)
543 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800544
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500545 __eq__ = _cmp(__eq__)
546 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800547
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500548 __lt__ = _cmp(__lt__)
549 __le__ = _cmp(__le__)
550
551 __gt__ = _cmp(__gt__)
552 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800553
554 def __repr__(self):
555 """
556 String representation of an X509Name
557 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500558 result_buffer = _ffi.new("char[]", 512);
559 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800560 self._name, result_buffer, len(result_buffer))
561
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500562 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500563 # TODO: This is untested.
564 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800565
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500566 return "<X509Name object '%s'>" % (
567 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800568
569
570 def hash(self):
571 """
572 Return the hash value of this name
573
574 :return: None
575 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500576 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800577
578
579 def der(self):
580 """
581 Return the DER encoding of this name
582
583 :return: A :py:class:`bytes` instance giving the DER encoded form of
584 this name.
585 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500586 result_buffer = _ffi.new('unsigned char**')
587 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800588 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500589 # TODO: This is untested.
590 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800591
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500592 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
593 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800594 return string_result
595
596
597 def get_components(self):
598 """
599 Returns the split-up components of this name.
600
601 :return: List of tuples (name, value).
602 """
603 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500604 for i in range(_lib.X509_NAME_entry_count(self._name)):
605 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800606
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500607 fname = _lib.X509_NAME_ENTRY_get_object(ent)
608 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800609
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500610 nid = _lib.OBJ_obj2nid(fname)
611 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800612
613 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500614 _ffi.string(name),
615 _ffi.string(
616 _lib.ASN1_STRING_data(fval),
617 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800618
619 return result
620X509NameType = X509Name
621
622
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800623class X509Extension(object):
624 def __init__(self, type_name, critical, value, subject=None, issuer=None):
625 """
626 :param typename: The name of the extension to create.
627 :type typename: :py:data:`str`
628
629 :param critical: A flag indicating whether this is a critical extension.
630
631 :param value: The value of the extension.
632 :type value: :py:data:`str`
633
634 :param subject: Optional X509 cert to use as subject.
635 :type subject: :py:class:`X509`
636
637 :param issuer: Optional X509 cert to use as issuer.
638 :type issuer: :py:class:`X509`
639
640 :return: The X509Extension object
641 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500642 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800643
644 # A context is necessary for any extension which uses the r2i conversion
645 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
646 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500647 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800648
649 # We have no configuration database - but perhaps we should (some
650 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500651 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800652
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800653 # Initialize the subject and issuer, if appropriate. ctx is a local,
654 # and as far as I can tell none of the X509V3_* APIs invoked here steal
655 # any references, so no need to mess with reference counts or duplicates.
656 if issuer is not None:
657 if not isinstance(issuer, X509):
658 raise TypeError("issuer must be an X509 instance")
659 ctx.issuer_cert = issuer._x509
660 if subject is not None:
661 if not isinstance(subject, X509):
662 raise TypeError("subject must be an X509 instance")
663 ctx.subject_cert = subject._x509
664
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800665 if critical:
666 # There are other OpenSSL APIs which would let us pass in critical
667 # separately, but they're harder to use, and since value is already
668 # a pile of crappy junk smuggling a ton of utterly important
669 # structured data, what's the point of trying to avoid nasty stuff
670 # with strings? (However, X509V3_EXT_i2d in particular seems like it
671 # would be a better API to invoke. I do not know where to get the
672 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500673 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800674
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500675 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
676 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800677 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500678 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800679
680
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400681 @property
682 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500683 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400684
685 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500686 _lib.GEN_EMAIL: "email",
687 _lib.GEN_DNS: "DNS",
688 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400689 }
690
691 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500692 method = _lib.X509V3_EXT_get(self._extension)
693 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500694 # TODO: This is untested.
695 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400696 payload = self._extension.value.data
697 length = self._extension.value.length
698
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500699 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400700 payloadptr[0] = payload
701
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500702 if method.it != _ffi.NULL:
703 ptr = _lib.ASN1_ITEM_ptr(method.it)
704 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
705 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400706 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500707 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400708 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500709 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400710
711 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500712 for i in range(_lib.sk_GENERAL_NAME_num(names)):
713 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400714 try:
715 label = self._prefixes[name.type]
716 except KeyError:
717 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500718 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500719 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400720 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500721 value = _native(
722 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
723 parts.append(label + ":" + value)
724 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400725
726
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800727 def __str__(self):
728 """
729 :return: a nice text representation of the extension
730 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500731 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400732 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800733
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400734 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500735 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800736 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500737 # TODO: This is untested.
738 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800739
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500740 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800741
742
743 def get_critical(self):
744 """
745 Returns the critical field of the X509Extension
746
747 :return: The critical field.
748 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500749 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800750
751
752 def get_short_name(self):
753 """
754 Returns the short version of the type name of the X509Extension
755
756 :return: The short type name.
757 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500758 obj = _lib.X509_EXTENSION_get_object(self._extension)
759 nid = _lib.OBJ_obj2nid(obj)
760 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800761
762
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800763 def get_data(self):
764 """
765 Returns the data of the X509Extension
766
767 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
768 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500769 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
770 string_result = _ffi.cast('ASN1_STRING*', octet_result)
771 char_result = _lib.ASN1_STRING_data(string_result)
772 result_length = _lib.ASN1_STRING_length(string_result)
773 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800774
775X509ExtensionType = X509Extension
776
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800777
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800778class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800779 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500780 req = _lib.X509_REQ_new()
781 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800782
783
784 def set_pubkey(self, pkey):
785 """
786 Set the public key of the certificate request
787
788 :param pkey: The public key to use
789 :return: None
790 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500791 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800792 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500793 # TODO: This is untested.
794 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800795
796
797 def get_pubkey(self):
798 """
799 Get the public key from the certificate request
800
801 :return: The public key
802 """
803 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500804 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
805 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500806 # TODO: This is untested.
807 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500808 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800809 pkey._only_public = True
810 return pkey
811
812
813 def set_version(self, version):
814 """
815 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
816 request.
817
818 :param version: The version number
819 :return: None
820 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500821 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800822 if not set_result:
823 _raise_current_error()
824
825
826 def get_version(self):
827 """
828 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
829 request.
830
831 :return: an integer giving the value of the version subfield
832 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500833 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800834
835
836 def get_subject(self):
837 """
838 Create an X509Name object for the subject of the certificate request
839
840 :return: An X509Name object
841 """
842 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500843 name._name = _lib.X509_REQ_get_subject_name(self._req)
844 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500845 # TODO: This is untested.
846 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800847
848 # The name is owned by the X509Req structure. As long as the X509Name
849 # Python object is alive, keep the X509Req Python object alive.
850 name._owner = self
851
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800852 return name
853
854
855 def add_extensions(self, extensions):
856 """
857 Add extensions to the request.
858
859 :param extensions: a sequence of X509Extension objects
860 :return: None
861 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500862 stack = _lib.sk_X509_EXTENSION_new_null()
863 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500864 # TODO: This is untested.
865 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800866
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500867 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800868
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800869 for ext in extensions:
870 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800871 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800872
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800873 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500874 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800875
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500876 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800877 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500878 # TODO: This is untested.
879 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800880
881
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800882 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800883 """
884 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800885
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500886 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800887 """
888 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500889 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500890 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800891 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500892 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800893 exts.append(ext)
894 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800895
896
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800897 def sign(self, pkey, digest):
898 """
899 Sign the certificate request using the supplied key and digest
900
901 :param pkey: The key to sign with
902 :param digest: The message digest to use
903 :return: None
904 """
905 if pkey._only_public:
906 raise ValueError("Key has only public part")
907
908 if not pkey._initialized:
909 raise ValueError("Key is uninitialized")
910
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500911 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500912 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800913 raise ValueError("No such digest method")
914
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500915 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800916 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500917 # TODO: This is untested.
918 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800919
920
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800921 def verify(self, pkey):
922 """
923 Verifies a certificate request using the supplied public key
924
925 :param key: a public key
926 :return: True if the signature is correct.
927
928 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
929 problem verifying the signature.
930 """
931 if not isinstance(pkey, PKey):
932 raise TypeError("pkey must be a PKey instance")
933
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500934 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800935 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500936 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800937
938 return result
939
940
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800941X509ReqType = X509Req
942
943
944
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800945class X509(object):
946 def __init__(self):
947 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500948 x509 = _lib.X509_new()
949 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800950
951
952 def set_version(self, version):
953 """
954 Set version number of the certificate
955
956 :param version: The version number
957 :type version: :py:class:`int`
958
959 :return: None
960 """
961 if not isinstance(version, int):
962 raise TypeError("version must be an integer")
963
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500964 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800965
966
967 def get_version(self):
968 """
969 Return version number of the certificate
970
971 :return: Version number as a Python integer
972 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500973 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800974
975
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800976 def get_pubkey(self):
977 """
978 Get the public key of the certificate
979
980 :return: The public key
981 """
982 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500983 pkey._pkey = _lib.X509_get_pubkey(self._x509)
984 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800985 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500986 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800987 pkey._only_public = True
988 return pkey
989
990
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800991 def set_pubkey(self, pkey):
992 """
993 Set the public key of the certificate
994
995 :param pkey: The public key
996
997 :return: None
998 """
999 if not isinstance(pkey, PKey):
1000 raise TypeError("pkey must be a PKey instance")
1001
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001002 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001003 if not set_result:
1004 _raise_current_error()
1005
1006
1007 def sign(self, pkey, digest):
1008 """
1009 Sign the certificate using the supplied key and digest
1010
1011 :param pkey: The key to sign with
1012 :param digest: The message digest to use
1013 :return: None
1014 """
1015 if not isinstance(pkey, PKey):
1016 raise TypeError("pkey must be a PKey instance")
1017
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001018 if pkey._only_public:
1019 raise ValueError("Key only has public part")
1020
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -08001021 if not pkey._initialized:
1022 raise ValueError("Key is uninitialized")
1023
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001024 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001025 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001026 raise ValueError("No such digest method")
1027
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001028 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001029 if not sign_result:
1030 _raise_current_error()
1031
1032
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001033 def get_signature_algorithm(self):
1034 """
1035 Retrieve the signature algorithm used in the certificate
1036
1037 :return: A byte string giving the name of the signature algorithm used in
1038 the certificate.
1039 :raise ValueError: If the signature algorithm is undefined.
1040 """
1041 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001042 nid = _lib.OBJ_obj2nid(alg)
1043 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001044 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001045 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001046
1047
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001048 def digest(self, digest_name):
1049 """
1050 Return the digest of the X509 object.
1051
1052 :param digest_name: The name of the digest algorithm to use.
1053 :type digest_name: :py:class:`bytes`
1054
1055 :return: The digest of the object
1056 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001057 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001058 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001059 raise ValueError("No such digest method")
1060
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001061 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1062 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001063 result_length[0] = len(result_buffer)
1064
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001065 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001066 self._x509, digest, result_buffer, result_length)
1067
1068 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001069 # TODO: This is untested.
1070 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001071
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001072 return b":".join([
1073 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001074 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001075
1076
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001077 def subject_name_hash(self):
1078 """
1079 Return the hash of the X509 subject.
1080
1081 :return: The hash of the subject.
1082 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001083 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001084
1085
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001086 def set_serial_number(self, serial):
1087 """
1088 Set serial number of the certificate
1089
1090 :param serial: The serial number
1091 :type serial: :py:class:`int`
1092
1093 :return: None
1094 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001095 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001096 raise TypeError("serial must be an integer")
1097
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001098 hex_serial = hex(serial)[2:]
1099 if not isinstance(hex_serial, bytes):
1100 hex_serial = hex_serial.encode('ascii')
1101
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001102 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001103
1104 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1105 # it. If bignum is still NULL after this call, then the return value is
1106 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001107 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001108
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001109 if bignum_serial[0] == _ffi.NULL:
1110 set_result = _lib.ASN1_INTEGER_set(
1111 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001112 if set_result:
1113 # TODO Not tested
1114 _raise_current_error()
1115 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001116 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1117 _lib.BN_free(bignum_serial[0])
1118 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001119 # TODO Not tested
1120 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001121 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1122 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001123 if not set_result:
1124 # TODO Not tested
1125 _raise_current_error()
1126
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001127
1128 def get_serial_number(self):
1129 """
1130 Return serial number of the certificate
1131
1132 :return: Serial number as a Python integer
1133 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001134 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1135 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001136 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001137 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001138 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001139 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001140 serial = int(hexstring_serial, 16)
1141 return serial
1142 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001143 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001144 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001145 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001146
1147
1148 def gmtime_adj_notAfter(self, amount):
1149 """
1150 Adjust the time stamp for when the certificate stops being valid
1151
1152 :param amount: The number of seconds by which to adjust the ending
1153 validity time.
1154 :type amount: :py:class:`int`
1155
1156 :return: None
1157 """
1158 if not isinstance(amount, int):
1159 raise TypeError("amount must be an integer")
1160
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001161 notAfter = _lib.X509_get_notAfter(self._x509)
1162 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001163
1164
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001165 def gmtime_adj_notBefore(self, amount):
1166 """
1167 Change the timestamp for when the certificate starts being valid to the current
1168 time plus an offset.
1169
1170 :param amount: The number of seconds by which to adjust the starting validity
1171 time.
1172 :return: None
1173 """
1174 if not isinstance(amount, int):
1175 raise TypeError("amount must be an integer")
1176
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001177 notBefore = _lib.X509_get_notBefore(self._x509)
1178 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001179
1180
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001181 def has_expired(self):
1182 """
1183 Check whether the certificate has expired.
1184
1185 :return: True if the certificate has expired, false otherwise
1186 """
1187 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001188 notAfter = _lib.X509_get_notAfter(self._x509)
1189 return _lib.ASN1_UTCTIME_cmp_time_t(
1190 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001191
1192
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001193 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001194 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001195
1196
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001197 def get_notBefore(self):
1198 """
1199 Retrieve the time stamp for when the certificate starts being valid
1200
1201 :return: A string giving the timestamp, in the format::
1202
1203 YYYYMMDDhhmmssZ
1204 YYYYMMDDhhmmss+hhmm
1205 YYYYMMDDhhmmss-hhmm
1206
1207 or None if there is no value set.
1208 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001209 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001210
1211
1212 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001213 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001214
1215
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001216 def set_notBefore(self, when):
1217 """
1218 Set the time stamp for when the certificate starts being valid
1219
1220 :param when: A string giving the timestamp, in the format:
1221
1222 YYYYMMDDhhmmssZ
1223 YYYYMMDDhhmmss+hhmm
1224 YYYYMMDDhhmmss-hhmm
1225 :type when: :py:class:`bytes`
1226
1227 :return: None
1228 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001229 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001230
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001231
1232 def get_notAfter(self):
1233 """
1234 Retrieve the time stamp for when the certificate stops being valid
1235
1236 :return: A string giving the timestamp, in the format::
1237
1238 YYYYMMDDhhmmssZ
1239 YYYYMMDDhhmmss+hhmm
1240 YYYYMMDDhhmmss-hhmm
1241
1242 or None if there is no value set.
1243 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001244 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001245
1246
1247 def set_notAfter(self, when):
1248 """
1249 Set the time stamp for when the certificate stops being valid
1250
1251 :param when: A string giving the timestamp, in the format:
1252
1253 YYYYMMDDhhmmssZ
1254 YYYYMMDDhhmmss+hhmm
1255 YYYYMMDDhhmmss-hhmm
1256 :type when: :py:class:`bytes`
1257
1258 :return: None
1259 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001260 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001261
1262
1263 def _get_name(self, which):
1264 name = X509Name.__new__(X509Name)
1265 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001266 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001267 # TODO: This is untested.
1268 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001269
1270 # The name is owned by the X509 structure. As long as the X509Name
1271 # Python object is alive, keep the X509 Python object alive.
1272 name._owner = self
1273
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001274 return name
1275
1276
1277 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001278 if not isinstance(name, X509Name):
1279 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001280 set_result = which(self._x509, name._name)
1281 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001282 # TODO: This is untested.
1283 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001284
1285
1286 def get_issuer(self):
1287 """
1288 Create an X509Name object for the issuer of the certificate
1289
1290 :return: An X509Name object
1291 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001292 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001293
1294
1295 def set_issuer(self, issuer):
1296 """
1297 Set the issuer of the certificate
1298
1299 :param issuer: The issuer name
1300 :type issuer: :py:class:`X509Name`
1301
1302 :return: None
1303 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001304 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001305
1306
1307 def get_subject(self):
1308 """
1309 Create an X509Name object for the subject of the certificate
1310
1311 :return: An X509Name object
1312 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001313 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001314
1315
1316 def set_subject(self, subject):
1317 """
1318 Set the subject of the certificate
1319
1320 :param subject: The subject name
1321 :type subject: :py:class:`X509Name`
1322 :return: None
1323 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001324 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001325
1326
1327 def get_extension_count(self):
1328 """
1329 Get the number of extensions on the certificate.
1330
1331 :return: The number of extensions as an integer.
1332 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001333 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001334
1335
1336 def add_extensions(self, extensions):
1337 """
1338 Add extensions to the certificate.
1339
1340 :param extensions: a sequence of X509Extension objects
1341 :return: None
1342 """
1343 for ext in extensions:
1344 if not isinstance(ext, X509Extension):
1345 raise ValueError("One of the elements is not an X509Extension")
1346
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001347 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001348 if not add_result:
1349 _raise_current_error()
1350
1351
1352 def get_extension(self, index):
1353 """
1354 Get a specific extension of the certificate by index.
1355
1356 :param index: The index of the extension to retrieve.
1357 :return: The X509Extension object at the specified index.
1358 """
1359 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001360 ext._extension = _lib.X509_get_ext(self._x509, index)
1361 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001362 raise IndexError("extension index out of bounds")
1363
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001364 extension = _lib.X509_EXTENSION_dup(ext._extension)
1365 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001366 return ext
1367
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001368X509Type = X509
1369
1370
1371
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001372class X509Store(object):
1373 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001374 store = _lib.X509_STORE_new()
1375 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001376
1377
1378 def add_cert(self, cert):
1379 if not isinstance(cert, X509):
1380 raise TypeError()
1381
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001382 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001383 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001384 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001385
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001386
1387X509StoreType = X509Store
1388
1389
1390
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001391class X509StoreContext(object):
1392 """
1393 An X.509 store context.
1394
Jean-Paul Calderonec1a15a42015-01-18 15:27:34 -05001395 An :py:class:`X509StoreContext` is used to verify a certificate in some
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001396 context in conjunction with :py:func:`verify_cert`. The information
1397 encapsulated in this object includes, but is not limited to, a set of
1398 trusted certificates, verification parameters and revoked certificates.
1399
Jean-Paul Calderonec1a15a42015-01-18 15:27:34 -05001400 :param store: An :py:class:`X509Store` of trusted certificates.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001401 :param cert: An :py:class:`X509` certificate to be validated during a
Jean-Paul Calderone282d3c82015-01-18 15:27:41 -05001402 subsequent call to :py:func:`verify_cert`.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001403 """
1404
1405 def __init__(self, store, cert):
1406 store_ctx = _lib.X509_STORE_CTX_new()
1407 self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
1408 self._store = store
1409 self._cert = cert
1410
1411 def _init(self):
1412 """
1413 Set up the store context for a subsequent verification operation.
1414 """
1415 ret = _lib.X509_STORE_CTX_init(self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL)
1416 if ret <= 0:
1417 _raise_current_error()
1418
1419 def _cleanup(self):
1420 """
1421 Internally cleans up the store context.
1422
1423 The store context can then be reused with a new call to
1424 :py:meth:`init`.
1425 """
1426 _lib.X509_STORE_CTX_cleanup(self._store_ctx)
1427
1428
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001429def load_certificate(type, buffer):
1430 """
1431 Load a certificate from a buffer
1432
1433 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1434
1435 :param buffer: The buffer the certificate is stored in
1436 :type buffer: :py:class:`bytes`
1437
1438 :return: The X509 object
1439 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001440 if isinstance(buffer, _text_type):
1441 buffer = buffer.encode("ascii")
1442
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001443 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001444
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001445 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001446 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001447 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001448 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001449 else:
1450 raise ValueError(
1451 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001452
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001453 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001454 _raise_current_error()
1455
1456 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001457 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001458 return cert
1459
1460
1461def dump_certificate(type, cert):
1462 """
1463 Dump a certificate to a buffer
1464
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001465 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1466 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001467 :param cert: The certificate to dump
1468 :return: The buffer with the dumped certificate in
1469 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001470 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001471
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001472 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001473 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001474 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001475 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001476 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001477 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001478 else:
1479 raise ValueError(
1480 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1481 "FILETYPE_TEXT")
1482
1483 return _bio_to_string(bio)
1484
1485
1486
1487def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1488 """
1489 Dump a private key to a buffer
1490
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001491 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1492 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001493 :param pkey: The PKey to dump
1494 :param cipher: (optional) if encrypted PEM format, the cipher to
1495 use
1496 :param passphrase: (optional) if encrypted PEM format, this can be either
1497 the passphrase to use, or a callback for providing the
1498 passphrase.
1499 :return: The buffer with the dumped key in
1500 :rtype: :py:data:`str`
1501 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001502 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001503
1504 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001505 if passphrase is None:
1506 raise TypeError(
1507 "if a value is given for cipher "
1508 "one must also be given for passphrase")
1509 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001510 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001511 raise ValueError("Invalid cipher name")
1512 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001513 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001514
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001515 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001516 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001517 result_code = _lib.PEM_write_bio_PrivateKey(
1518 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001519 helper.callback, helper.callback_args)
1520 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001521 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001522 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001523 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001524 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1525 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001526 # TODO RSA_free(rsa)?
1527 else:
1528 raise ValueError(
1529 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1530 "FILETYPE_TEXT")
1531
1532 if result_code == 0:
1533 _raise_current_error()
1534
1535 return _bio_to_string(bio)
1536
1537
1538
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001539def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001540 copy = _lib.X509_REVOKED_new()
1541 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001542 # TODO: This is untested.
1543 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001544
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001545 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001546 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001547 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001548
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001549 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001550 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001551 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001552
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001553 if original.extensions != _ffi.NULL:
1554 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1555 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1556 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1557 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1558 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001559 copy.extensions = extension_stack
1560
1561 copy.sequence = original.sequence
1562 return copy
1563
1564
1565
1566class Revoked(object):
1567 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1568 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1569 # OCSP_crl_reason_str. We use the latter, just like the command line
1570 # program.
1571 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001572 b"unspecified",
1573 b"keyCompromise",
1574 b"CACompromise",
1575 b"affiliationChanged",
1576 b"superseded",
1577 b"cessationOfOperation",
1578 b"certificateHold",
1579 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001580 ]
1581
1582 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001583 revoked = _lib.X509_REVOKED_new()
1584 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001585
1586
1587 def set_serial(self, hex_str):
1588 """
1589 Set the serial number of a revoked Revoked structure
1590
1591 :param hex_str: The new serial number.
1592 :type hex_str: :py:data:`str`
1593 :return: None
1594 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001595 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1596 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001597 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001598 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001599 if not bn_result:
1600 raise ValueError("bad hex string")
1601
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001602 asn1_serial = _ffi.gc(
1603 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1604 _lib.ASN1_INTEGER_free)
1605 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001606
1607
1608 def get_serial(self):
1609 """
1610 Return the serial number of a Revoked structure
1611
1612 :return: The serial number as a string
1613 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001614 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001615
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001616 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001617 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001618 # TODO: This is untested.
1619 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001620
1621 return _bio_to_string(bio)
1622
1623
1624 def _delete_reason(self):
1625 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001626 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1627 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1628 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1629 _lib.X509_EXTENSION_free(ext)
1630 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001631 break
1632
1633
1634 def set_reason(self, reason):
1635 """
1636 Set the reason of a Revoked object.
1637
1638 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1639
1640 :param reason: The reason string.
1641 :type reason: :py:class:`str` or :py:class:`NoneType`
1642 :return: None
1643 """
1644 if reason is None:
1645 self._delete_reason()
1646 elif not isinstance(reason, bytes):
1647 raise TypeError("reason must be None or a byte string")
1648 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001649 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001650 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1651
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001652 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1653 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001654 # TODO: This is untested.
1655 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001656 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001657
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001658 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1659 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001660 # TODO: This is untested.
1661 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001662
1663 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001664 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1665 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001666
1667 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001668 # TODO: This is untested.
1669 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001670
1671
1672 def get_reason(self):
1673 """
1674 Return the reason of a Revoked object.
1675
1676 :return: The reason as a string
1677 """
1678 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001679 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1680 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1681 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001682 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001683
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001684 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001685 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001686 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001687 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001688 # TODO: This is untested.
1689 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001690
1691 return _bio_to_string(bio)
1692
1693
1694 def all_reasons(self):
1695 """
1696 Return a list of all the supported reason strings.
1697
1698 :return: A list of reason strings.
1699 """
1700 return self._crl_reasons[:]
1701
1702
1703 def set_rev_date(self, when):
1704 """
1705 Set the revocation timestamp
1706
1707 :param when: A string giving the timestamp, in the format:
1708
1709 YYYYMMDDhhmmssZ
1710 YYYYMMDDhhmmss+hhmm
1711 YYYYMMDDhhmmss-hhmm
1712
1713 :return: None
1714 """
1715 return _set_asn1_time(self._revoked.revocationDate, when)
1716
1717
1718 def get_rev_date(self):
1719 """
1720 Retrieve the revocation date
1721
1722 :return: A string giving the timestamp, in the format:
1723
1724 YYYYMMDDhhmmssZ
1725 YYYYMMDDhhmmss+hhmm
1726 YYYYMMDDhhmmss-hhmm
1727 """
1728 return _get_asn1_time(self._revoked.revocationDate)
1729
1730
1731
1732class CRL(object):
1733 def __init__(self):
1734 """
1735 Create a new empty CRL object.
1736 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001737 crl = _lib.X509_CRL_new()
1738 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001739
1740
1741 def get_revoked(self):
1742 """
1743 Return revoked portion of the CRL structure (by value not reference).
1744
1745 :return: A tuple of Revoked objects.
1746 """
1747 results = []
1748 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001749 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1750 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001751 revoked_copy = _X509_REVOKED_dup(revoked)
1752 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001753 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001754 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001755 if results:
1756 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001757
1758
1759 def add_revoked(self, revoked):
1760 """
1761 Add a revoked (by value not reference) to the CRL structure
1762
1763 :param revoked: The new revoked.
1764 :type revoked: :class:`X509`
1765
1766 :return: None
1767 """
1768 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001769 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001770 # TODO: This is untested.
1771 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001772
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001773 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001774 if add_result == 0:
1775 # TODO: This is untested.
1776 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001777
1778
1779 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1780 """
1781 export a CRL as a string
1782
1783 :param cert: Used to sign CRL.
1784 :type cert: :class:`X509`
1785
1786 :param key: Used to sign CRL.
1787 :type key: :class:`PKey`
1788
1789 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1790
1791 :param days: The number of days until the next update of this CRL.
1792 :type days: :py:data:`int`
1793
1794 :return: :py:data:`str`
1795 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001796 if not isinstance(cert, X509):
1797 raise TypeError("cert must be an X509 instance")
1798 if not isinstance(key, PKey):
1799 raise TypeError("key must be a PKey instance")
1800 if not isinstance(type, int):
1801 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001802
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001803 bio = _lib.BIO_new(_lib.BIO_s_mem())
1804 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001805 # TODO: This is untested.
1806 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001807
1808 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001809 sometime = _lib.ASN1_TIME_new()
1810 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001811 # TODO: This is untested.
1812 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001813
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001814 _lib.X509_gmtime_adj(sometime, 0)
1815 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001816
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001817 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1818 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001819
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001820 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001821
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001822 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001823 if not sign_result:
1824 _raise_current_error()
1825
1826 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001827 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001828 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001829 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001830 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001831 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001832 else:
1833 raise ValueError(
1834 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1835
1836 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001837 # TODO: This is untested.
1838 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001839
1840 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001841CRLType = CRL
1842
1843
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001844
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001845class PKCS7(object):
1846 def type_is_signed(self):
1847 """
1848 Check if this NID_pkcs7_signed object
1849
1850 :return: True if the PKCS7 is of type signed
1851 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001852 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001853 return True
1854 return False
1855
1856
1857 def type_is_enveloped(self):
1858 """
1859 Check if this NID_pkcs7_enveloped object
1860
1861 :returns: True if the PKCS7 is of type enveloped
1862 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001863 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001864 return True
1865 return False
1866
1867
1868 def type_is_signedAndEnveloped(self):
1869 """
1870 Check if this NID_pkcs7_signedAndEnveloped object
1871
1872 :returns: True if the PKCS7 is of type signedAndEnveloped
1873 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001874 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001875 return True
1876 return False
1877
1878
1879 def type_is_data(self):
1880 """
1881 Check if this NID_pkcs7_data object
1882
1883 :return: True if the PKCS7 is of type data
1884 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001885 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001886 return True
1887 return False
1888
1889
1890 def get_type_name(self):
1891 """
1892 Returns the type name of the PKCS7 structure
1893
1894 :return: A string with the typename
1895 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001896 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1897 string_type = _lib.OBJ_nid2sn(nid)
1898 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001899
1900PKCS7Type = PKCS7
1901
1902
1903
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001904class PKCS12(object):
1905 def __init__(self):
1906 self._pkey = None
1907 self._cert = None
1908 self._cacerts = None
1909 self._friendlyname = None
1910
1911
1912 def get_certificate(self):
1913 """
1914 Return certificate portion of the PKCS12 structure
1915
1916 :return: X509 object containing the certificate
1917 """
1918 return self._cert
1919
1920
1921 def set_certificate(self, cert):
1922 """
1923 Replace the certificate portion of the PKCS12 structure
1924
1925 :param cert: The new certificate.
1926 :type cert: :py:class:`X509` or :py:data:`None`
1927 :return: None
1928 """
1929 if not isinstance(cert, X509):
1930 raise TypeError("cert must be an X509 instance")
1931 self._cert = cert
1932
1933
1934 def get_privatekey(self):
1935 """
1936 Return private key portion of the PKCS12 structure
1937
1938 :returns: PKey object containing the private key
1939 """
1940 return self._pkey
1941
1942
1943 def set_privatekey(self, pkey):
1944 """
1945 Replace or set the certificate portion of the PKCS12 structure
1946
1947 :param pkey: The new private key.
1948 :type pkey: :py:class:`PKey`
1949 :return: None
1950 """
1951 if not isinstance(pkey, PKey):
1952 raise TypeError("pkey must be a PKey instance")
1953 self._pkey = pkey
1954
1955
1956 def get_ca_certificates(self):
1957 """
1958 Return CA certificates within of the PKCS12 object
1959
1960 :return: A newly created tuple containing the CA certificates in the chain,
1961 if any are present, or None if no CA certificates are present.
1962 """
1963 if self._cacerts is not None:
1964 return tuple(self._cacerts)
1965
1966
1967 def set_ca_certificates(self, cacerts):
1968 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08001969 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001970
1971 :param cacerts: The new CA certificates.
1972 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1973 :return: None
1974 """
1975 if cacerts is None:
1976 self._cacerts = None
1977 else:
1978 cacerts = list(cacerts)
1979 for cert in cacerts:
1980 if not isinstance(cert, X509):
1981 raise TypeError("iterable must only contain X509 instances")
1982 self._cacerts = cacerts
1983
1984
1985 def set_friendlyname(self, name):
1986 """
1987 Replace or set the certificate portion of the PKCS12 structure
1988
1989 :param name: The new friendly name.
1990 :type name: :py:class:`bytes`
1991 :return: None
1992 """
1993 if name is None:
1994 self._friendlyname = None
1995 elif not isinstance(name, bytes):
1996 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1997 self._friendlyname = name
1998
1999
2000 def get_friendlyname(self):
2001 """
2002 Return friendly name portion of the PKCS12 structure
2003
2004 :returns: String containing the friendlyname
2005 """
2006 return self._friendlyname
2007
2008
2009 def export(self, passphrase=None, iter=2048, maciter=1):
2010 """
2011 Dump a PKCS12 object as a string. See also "man PKCS12_create".
2012
2013 :param passphrase: used to encrypt the PKCS12
2014 :type passphrase: :py:data:`bytes`
2015
2016 :param iter: How many times to repeat the encryption
2017 :type iter: :py:data:`int`
2018
2019 :param maciter: How many times to repeat the MAC
2020 :type maciter: :py:data:`int`
2021
2022 :return: The string containing the PKCS12
2023 """
2024 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002025 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002026 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002027 cacerts = _lib.sk_X509_new_null()
2028 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002029 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002030 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002031
2032 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002033 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002034
2035 friendlyname = self._friendlyname
2036 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002037 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002038
2039 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002040 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002041 else:
2042 pkey = self._pkey._pkey
2043
2044 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002045 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002046 else:
2047 cert = self._cert._x509
2048
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002049 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002050 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002051 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2052 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002053 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002054 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002055 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002056 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002057
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002058 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002059 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002060 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002061
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002062PKCS12Type = PKCS12
2063
2064
2065
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002066class NetscapeSPKI(object):
2067 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002068 spki = _lib.NETSCAPE_SPKI_new()
2069 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002070
2071
2072 def sign(self, pkey, digest):
2073 """
2074 Sign the certificate request using the supplied key and digest
2075
2076 :param pkey: The key to sign with
2077 :param digest: The message digest to use
2078 :return: None
2079 """
2080 if pkey._only_public:
2081 raise ValueError("Key has only public part")
2082
2083 if not pkey._initialized:
2084 raise ValueError("Key is uninitialized")
2085
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002086 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002087 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002088 raise ValueError("No such digest method")
2089
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002090 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002091 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002092 # TODO: This is untested.
2093 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002094
2095
2096 def verify(self, key):
2097 """
2098 Verifies a certificate request using the supplied public key
2099
2100 :param key: a public key
2101 :return: True if the signature is correct.
2102 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2103 problem verifying the signature.
2104 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002105 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002106 if answer <= 0:
2107 _raise_current_error()
2108 return True
2109
2110
2111 def b64_encode(self):
2112 """
2113 Generate a base64 encoded string from an SPKI
2114
2115 :return: The base64 encoded string
2116 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002117 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2118 result = _ffi.string(encoded)
2119 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002120 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002121
2122
2123 def get_pubkey(self):
2124 """
2125 Get the public key of the certificate
2126
2127 :return: The public key
2128 """
2129 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002130 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2131 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002132 # TODO: This is untested.
2133 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002134 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002135 pkey._only_public = True
2136 return pkey
2137
2138
2139 def set_pubkey(self, pkey):
2140 """
2141 Set the public key of the certificate
2142
2143 :param pkey: The public key
2144 :return: None
2145 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002146 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002147 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002148 # TODO: This is untested.
2149 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002150NetscapeSPKIType = NetscapeSPKI
2151
2152
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002153class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002154 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002155 if type != FILETYPE_PEM and passphrase is not None:
2156 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002157 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002158 self._more_args = more_args
2159 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002160 self._problems = []
2161
2162
2163 @property
2164 def callback(self):
2165 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002166 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002167 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002168 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002169 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002170 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002171 else:
2172 raise TypeError("Last argument must be string or callable")
2173
2174
2175 @property
2176 def callback_args(self):
2177 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002178 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002179 elif isinstance(self._passphrase, bytes):
2180 return self._passphrase
2181 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002182 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002183 else:
2184 raise TypeError("Last argument must be string or callable")
2185
2186
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002187 def raise_if_problem(self, exceptionType=Error):
2188 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002189 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002190 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002191 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002192 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002193 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002194 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002195
2196
2197 def _read_passphrase(self, buf, size, rwflag, userdata):
2198 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002199 if self._more_args:
2200 result = self._passphrase(size, rwflag, userdata)
2201 else:
2202 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002203 if not isinstance(result, bytes):
2204 raise ValueError("String expected")
2205 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002206 if self._truncate:
2207 result = result[:size]
2208 else:
2209 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002210 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002211 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002212 return len(result)
2213 except Exception as e:
2214 self._problems.append(e)
2215 return 0
2216
2217
2218
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002219def load_privatekey(type, buffer, passphrase=None):
2220 """
2221 Load a private key from a buffer
2222
2223 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2224 :param buffer: The buffer the key is stored in
2225 :param passphrase: (optional) if encrypted PEM format, this can be
2226 either the passphrase to use, or a callback for
2227 providing the passphrase.
2228
2229 :return: The PKey object
2230 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002231 if isinstance(buffer, _text_type):
2232 buffer = buffer.encode("ascii")
2233
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002234 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002235
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002236 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002237 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002238 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2239 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002240 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002241 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002242 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002243 else:
2244 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2245
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002246 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002247 _raise_current_error()
2248
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002249 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002250 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002251 return pkey
2252
2253
2254
2255def dump_certificate_request(type, req):
2256 """
2257 Dump a certificate request to a buffer
2258
2259 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2260 :param req: The certificate request to dump
2261 :return: The buffer with the dumped certificate request in
2262 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002263 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002264
2265 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002266 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002267 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002268 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002269 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002270 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002271 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002272 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002273
2274 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002275 # TODO: This is untested.
2276 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002277
2278 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002279
2280
2281
2282def load_certificate_request(type, buffer):
2283 """
2284 Load a certificate request from a buffer
2285
2286 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2287 :param buffer: The buffer the certificate request is stored in
2288 :return: The X509Req object
2289 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002290 if isinstance(buffer, _text_type):
2291 buffer = buffer.encode("ascii")
2292
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002293 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002294
2295 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002296 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002297 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002298 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002299 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002300 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002301
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002302 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002303 # TODO: This is untested.
2304 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002305
2306 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002307 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002308 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002309
2310
2311
2312def sign(pkey, data, digest):
2313 """
2314 Sign data with a digest
2315
2316 :param pkey: Pkey to sign with
2317 :param data: data to be signed
2318 :param digest: message digest to use
2319 :return: signature
2320 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002321 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002322 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002323 raise ValueError("No such digest method")
2324
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002325 md_ctx = _ffi.new("EVP_MD_CTX*")
2326 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002327
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002328 _lib.EVP_SignInit(md_ctx, digest_obj)
2329 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002330
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002331 signature_buffer = _ffi.new("unsigned char[]", 512)
2332 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002333 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002334 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002335 md_ctx, signature_buffer, signature_length, pkey._pkey)
2336
2337 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002338 # TODO: This is untested.
2339 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002340
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002341 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002342
2343
2344
2345def verify(cert, signature, data, digest):
2346 """
2347 Verify a signature
2348
2349 :param cert: signing certificate (X509 object)
2350 :param signature: signature returned by sign function
2351 :param data: data to be verified
2352 :param digest: message digest to use
2353 :return: None if the signature is correct, raise exception otherwise
2354 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002355 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002356 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002357 raise ValueError("No such digest method")
2358
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002359 pkey = _lib.X509_get_pubkey(cert._x509)
2360 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002361 # TODO: This is untested.
2362 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002363 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002364
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002365 md_ctx = _ffi.new("EVP_MD_CTX*")
2366 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002367
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002368 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2369 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2370 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002371
2372 if verify_result != 1:
2373 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002374
2375
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07002376def verify_cert(store_ctx):
2377 """
2378 Verify a certificate in a context.
2379
2380 :param store_ctx: The :py:class:`X509StoreContext` to verify.
2381 :raises: Error
2382 """
2383 store_ctx._init()
2384 ret = _lib.X509_verify_cert(store_ctx._store_ctx)
2385 store_ctx._cleanup()
2386 if ret <= 0:
2387 _raise_context_error(store_ctx)
2388
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002389
2390def load_crl(type, buffer):
2391 """
2392 Load a certificate revocation list from a buffer
2393
2394 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2395 :param buffer: The buffer the CRL is stored in
2396
2397 :return: The PKey object
2398 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002399 if isinstance(buffer, _text_type):
2400 buffer = buffer.encode("ascii")
2401
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002402 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002403
2404 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002405 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002406 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002407 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002408 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002409 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2410
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002411 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002412 _raise_current_error()
2413
2414 result = CRL.__new__(CRL)
2415 result._crl = crl
2416 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002417
2418
2419
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002420def load_pkcs7_data(type, buffer):
2421 """
2422 Load pkcs7 data from a buffer
2423
2424 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2425 :param buffer: The buffer with the pkcs7 data.
2426 :return: The PKCS7 object
2427 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002428 if isinstance(buffer, _text_type):
2429 buffer = buffer.encode("ascii")
2430
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002431 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002432
2433 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002434 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002435 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002436 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002437 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002438 # TODO: This is untested.
2439 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002440 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2441
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002442 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002443 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002444
2445 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002446 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002447 return pypkcs7
2448
2449
2450
Stephen Holsapple38482622014-04-05 20:29:34 -07002451def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002452 """
2453 Load a PKCS12 object from a buffer
2454
2455 :param buffer: The buffer the certificate is stored in
2456 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2457 :returns: The PKCS12 object
2458 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002459 if isinstance(buffer, _text_type):
2460 buffer = buffer.encode("ascii")
2461
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002462 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002463
Stephen Holsapple38482622014-04-05 20:29:34 -07002464 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2465 # password based encryption no password and a zero length password are two
2466 # different things, but OpenSSL implementation will try both to figure out
2467 # which one works.
2468 if not passphrase:
2469 passphrase = _ffi.NULL
2470
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002471 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2472 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002473 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002474 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002475
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002476 pkey = _ffi.new("EVP_PKEY**")
2477 cert = _ffi.new("X509**")
2478 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002479
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002480 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002481 if not parse_result:
2482 _raise_current_error()
2483
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002484 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002485
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002486 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2487 # queue for no particular reason. This error isn't interesting to anyone
2488 # outside this function. It's not even interesting to us. Get rid of it.
2489 try:
2490 _raise_current_error()
2491 except Error:
2492 pass
2493
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002494 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002495 pykey = None
2496 else:
2497 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002498 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002499
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002500 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002501 pycert = None
2502 friendlyname = None
2503 else:
2504 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002505 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002506
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002507 friendlyname_length = _ffi.new("int*")
2508 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2509 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2510 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002511 friendlyname = None
2512
2513 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002514 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002515 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002516 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002517 pycacerts.append(pycacert)
2518 if not pycacerts:
2519 pycacerts = None
2520
2521 pkcs12 = PKCS12.__new__(PKCS12)
2522 pkcs12._pkey = pykey
2523 pkcs12._cert = pycert
2524 pkcs12._cacerts = pycacerts
2525 pkcs12._friendlyname = friendlyname
2526 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002527
2528
2529def _initialize_openssl_threads(get_ident, Lock):
2530 import _ssl
2531 return
2532
2533 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2534
2535 def locking_function(mode, index, filename, line):
2536 if mode & _lib.CRYPTO_LOCK:
2537 locks[index].acquire()
2538 else:
2539 locks[index].release()
2540
2541 _lib.CRYPTO_set_id_callback(
2542 _ffi.callback("unsigned long (*)(void)", get_ident))
2543
2544 _lib.CRYPTO_set_locking_callback(
2545 _ffi.callback(
2546 "void (*)(int, int, const char*, int)", locking_function))
2547
2548
2549try:
2550 from thread import get_ident
2551 from threading import Lock
2552except ImportError:
2553 pass
2554else:
2555 _initialize_openssl_threads(get_ident, Lock)
2556 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002557
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002558# There are no direct unit tests for this initialization. It is tested
2559# indirectly since it is necessary for functions like dump_privatekey when
2560# using encryption.
2561#
2562# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2563# and some other similar tests may fail without this (though they may not if
2564# the Python runtime has already done some initialization of the underlying
2565# OpenSSL library (and is linked against the same one that cryptography is
2566# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002567_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002568
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002569# This is similar but exercised mainly by exception_from_error_queue. It calls
2570# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2571_lib.SSL_load_error_strings()