blob: fbccb6d33feb7b42f79b8189140eb9339f11d0eb [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 Calderoneb7b7fb92015-01-18 15:37:10 -05001400 :ivar _store_ctx: The underlying X509_STORE_CTX structure used by this
1401 instance. It is dynamically allocated and automatically garbage
1402 collected.
1403
1404 :ivar _store: See the ``store`` `__init__`` parameter.
1405
1406 :ivar _cert: See the ``certificate`` `__init__`` parameter.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001407 """
1408
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001409 def __init__(self, store, certificate):
1410 """
1411 :param X509Store store: The certificates which will be trusted for the
1412 purposes of any verifications.
1413
1414 :param X509 certificate: The certificate to be verified.
1415 """
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001416 store_ctx = _lib.X509_STORE_CTX_new()
1417 self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
1418 self._store = store
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001419 self._cert = certificate
1420
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001421
1422 def _init(self):
1423 """
1424 Set up the store context for a subsequent verification operation.
1425 """
1426 ret = _lib.X509_STORE_CTX_init(self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL)
1427 if ret <= 0:
1428 _raise_current_error()
1429
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001430
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001431 def _cleanup(self):
1432 """
1433 Internally cleans up the store context.
1434
1435 The store context can then be reused with a new call to
1436 :py:meth:`init`.
1437 """
1438 _lib.X509_STORE_CTX_cleanup(self._store_ctx)
1439
1440
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001441
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001442def load_certificate(type, buffer):
1443 """
1444 Load a certificate from a buffer
1445
1446 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1447
1448 :param buffer: The buffer the certificate is stored in
1449 :type buffer: :py:class:`bytes`
1450
1451 :return: The X509 object
1452 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001453 if isinstance(buffer, _text_type):
1454 buffer = buffer.encode("ascii")
1455
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001456 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001457
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001458 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001459 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001460 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001461 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001462 else:
1463 raise ValueError(
1464 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001465
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001466 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001467 _raise_current_error()
1468
1469 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001470 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001471 return cert
1472
1473
1474def dump_certificate(type, cert):
1475 """
1476 Dump a certificate to a buffer
1477
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001478 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1479 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001480 :param cert: The certificate to dump
1481 :return: The buffer with the dumped certificate in
1482 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001483 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001484
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001485 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001486 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001487 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001488 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001489 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001490 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001491 else:
1492 raise ValueError(
1493 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1494 "FILETYPE_TEXT")
1495
1496 return _bio_to_string(bio)
1497
1498
1499
1500def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1501 """
1502 Dump a private key to a buffer
1503
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001504 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1505 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001506 :param pkey: The PKey to dump
1507 :param cipher: (optional) if encrypted PEM format, the cipher to
1508 use
1509 :param passphrase: (optional) if encrypted PEM format, this can be either
1510 the passphrase to use, or a callback for providing the
1511 passphrase.
1512 :return: The buffer with the dumped key in
1513 :rtype: :py:data:`str`
1514 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001515 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001516
1517 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001518 if passphrase is None:
1519 raise TypeError(
1520 "if a value is given for cipher "
1521 "one must also be given for passphrase")
1522 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001523 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001524 raise ValueError("Invalid cipher name")
1525 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001526 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001527
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001528 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001529 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001530 result_code = _lib.PEM_write_bio_PrivateKey(
1531 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001532 helper.callback, helper.callback_args)
1533 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001534 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001535 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001536 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001537 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1538 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001539 # TODO RSA_free(rsa)?
1540 else:
1541 raise ValueError(
1542 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1543 "FILETYPE_TEXT")
1544
1545 if result_code == 0:
1546 _raise_current_error()
1547
1548 return _bio_to_string(bio)
1549
1550
1551
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001552def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001553 copy = _lib.X509_REVOKED_new()
1554 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001555 # TODO: This is untested.
1556 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001557
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001558 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001559 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001560 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001561
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001562 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001563 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001564 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001565
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001566 if original.extensions != _ffi.NULL:
1567 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1568 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1569 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1570 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1571 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001572 copy.extensions = extension_stack
1573
1574 copy.sequence = original.sequence
1575 return copy
1576
1577
1578
1579class Revoked(object):
1580 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1581 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1582 # OCSP_crl_reason_str. We use the latter, just like the command line
1583 # program.
1584 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001585 b"unspecified",
1586 b"keyCompromise",
1587 b"CACompromise",
1588 b"affiliationChanged",
1589 b"superseded",
1590 b"cessationOfOperation",
1591 b"certificateHold",
1592 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001593 ]
1594
1595 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001596 revoked = _lib.X509_REVOKED_new()
1597 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001598
1599
1600 def set_serial(self, hex_str):
1601 """
1602 Set the serial number of a revoked Revoked structure
1603
1604 :param hex_str: The new serial number.
1605 :type hex_str: :py:data:`str`
1606 :return: None
1607 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001608 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1609 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001610 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001611 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001612 if not bn_result:
1613 raise ValueError("bad hex string")
1614
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001615 asn1_serial = _ffi.gc(
1616 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1617 _lib.ASN1_INTEGER_free)
1618 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001619
1620
1621 def get_serial(self):
1622 """
1623 Return the serial number of a Revoked structure
1624
1625 :return: The serial number as a string
1626 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001627 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001628
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001629 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001630 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001631 # TODO: This is untested.
1632 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001633
1634 return _bio_to_string(bio)
1635
1636
1637 def _delete_reason(self):
1638 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001639 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1640 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1641 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1642 _lib.X509_EXTENSION_free(ext)
1643 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001644 break
1645
1646
1647 def set_reason(self, reason):
1648 """
1649 Set the reason of a Revoked object.
1650
1651 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1652
1653 :param reason: The reason string.
1654 :type reason: :py:class:`str` or :py:class:`NoneType`
1655 :return: None
1656 """
1657 if reason is None:
1658 self._delete_reason()
1659 elif not isinstance(reason, bytes):
1660 raise TypeError("reason must be None or a byte string")
1661 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001662 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001663 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1664
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001665 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1666 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001667 # TODO: This is untested.
1668 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001669 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001670
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001671 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1672 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001673 # TODO: This is untested.
1674 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001675
1676 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001677 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1678 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001679
1680 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001681 # TODO: This is untested.
1682 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001683
1684
1685 def get_reason(self):
1686 """
1687 Return the reason of a Revoked object.
1688
1689 :return: The reason as a string
1690 """
1691 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001692 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1693 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1694 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001695 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001696
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001697 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001698 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001699 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001700 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001701 # TODO: This is untested.
1702 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001703
1704 return _bio_to_string(bio)
1705
1706
1707 def all_reasons(self):
1708 """
1709 Return a list of all the supported reason strings.
1710
1711 :return: A list of reason strings.
1712 """
1713 return self._crl_reasons[:]
1714
1715
1716 def set_rev_date(self, when):
1717 """
1718 Set the revocation timestamp
1719
1720 :param when: A string giving the timestamp, in the format:
1721
1722 YYYYMMDDhhmmssZ
1723 YYYYMMDDhhmmss+hhmm
1724 YYYYMMDDhhmmss-hhmm
1725
1726 :return: None
1727 """
1728 return _set_asn1_time(self._revoked.revocationDate, when)
1729
1730
1731 def get_rev_date(self):
1732 """
1733 Retrieve the revocation date
1734
1735 :return: A string giving the timestamp, in the format:
1736
1737 YYYYMMDDhhmmssZ
1738 YYYYMMDDhhmmss+hhmm
1739 YYYYMMDDhhmmss-hhmm
1740 """
1741 return _get_asn1_time(self._revoked.revocationDate)
1742
1743
1744
1745class CRL(object):
1746 def __init__(self):
1747 """
1748 Create a new empty CRL object.
1749 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001750 crl = _lib.X509_CRL_new()
1751 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001752
1753
1754 def get_revoked(self):
1755 """
1756 Return revoked portion of the CRL structure (by value not reference).
1757
1758 :return: A tuple of Revoked objects.
1759 """
1760 results = []
1761 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001762 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1763 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001764 revoked_copy = _X509_REVOKED_dup(revoked)
1765 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001766 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001767 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001768 if results:
1769 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001770
1771
1772 def add_revoked(self, revoked):
1773 """
1774 Add a revoked (by value not reference) to the CRL structure
1775
1776 :param revoked: The new revoked.
1777 :type revoked: :class:`X509`
1778
1779 :return: None
1780 """
1781 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001782 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001783 # TODO: This is untested.
1784 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001785
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001786 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001787 if add_result == 0:
1788 # TODO: This is untested.
1789 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001790
1791
1792 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1793 """
1794 export a CRL as a string
1795
1796 :param cert: Used to sign CRL.
1797 :type cert: :class:`X509`
1798
1799 :param key: Used to sign CRL.
1800 :type key: :class:`PKey`
1801
1802 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1803
1804 :param days: The number of days until the next update of this CRL.
1805 :type days: :py:data:`int`
1806
1807 :return: :py:data:`str`
1808 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001809 if not isinstance(cert, X509):
1810 raise TypeError("cert must be an X509 instance")
1811 if not isinstance(key, PKey):
1812 raise TypeError("key must be a PKey instance")
1813 if not isinstance(type, int):
1814 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001815
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001816 bio = _lib.BIO_new(_lib.BIO_s_mem())
1817 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001818 # TODO: This is untested.
1819 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001820
1821 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001822 sometime = _lib.ASN1_TIME_new()
1823 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001824 # TODO: This is untested.
1825 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001826
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001827 _lib.X509_gmtime_adj(sometime, 0)
1828 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001829
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001830 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1831 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001832
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001833 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001834
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001835 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001836 if not sign_result:
1837 _raise_current_error()
1838
1839 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001840 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001841 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001842 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001843 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001844 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001845 else:
1846 raise ValueError(
1847 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1848
1849 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001850 # TODO: This is untested.
1851 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001852
1853 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001854CRLType = CRL
1855
1856
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001857
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001858class PKCS7(object):
1859 def type_is_signed(self):
1860 """
1861 Check if this NID_pkcs7_signed object
1862
1863 :return: True if the PKCS7 is of type signed
1864 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001865 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001866 return True
1867 return False
1868
1869
1870 def type_is_enveloped(self):
1871 """
1872 Check if this NID_pkcs7_enveloped object
1873
1874 :returns: True if the PKCS7 is of type enveloped
1875 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001876 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001877 return True
1878 return False
1879
1880
1881 def type_is_signedAndEnveloped(self):
1882 """
1883 Check if this NID_pkcs7_signedAndEnveloped object
1884
1885 :returns: True if the PKCS7 is of type signedAndEnveloped
1886 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001887 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001888 return True
1889 return False
1890
1891
1892 def type_is_data(self):
1893 """
1894 Check if this NID_pkcs7_data object
1895
1896 :return: True if the PKCS7 is of type data
1897 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001898 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001899 return True
1900 return False
1901
1902
1903 def get_type_name(self):
1904 """
1905 Returns the type name of the PKCS7 structure
1906
1907 :return: A string with the typename
1908 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001909 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1910 string_type = _lib.OBJ_nid2sn(nid)
1911 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001912
1913PKCS7Type = PKCS7
1914
1915
1916
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001917class PKCS12(object):
1918 def __init__(self):
1919 self._pkey = None
1920 self._cert = None
1921 self._cacerts = None
1922 self._friendlyname = None
1923
1924
1925 def get_certificate(self):
1926 """
1927 Return certificate portion of the PKCS12 structure
1928
1929 :return: X509 object containing the certificate
1930 """
1931 return self._cert
1932
1933
1934 def set_certificate(self, cert):
1935 """
1936 Replace the certificate portion of the PKCS12 structure
1937
1938 :param cert: The new certificate.
1939 :type cert: :py:class:`X509` or :py:data:`None`
1940 :return: None
1941 """
1942 if not isinstance(cert, X509):
1943 raise TypeError("cert must be an X509 instance")
1944 self._cert = cert
1945
1946
1947 def get_privatekey(self):
1948 """
1949 Return private key portion of the PKCS12 structure
1950
1951 :returns: PKey object containing the private key
1952 """
1953 return self._pkey
1954
1955
1956 def set_privatekey(self, pkey):
1957 """
1958 Replace or set the certificate portion of the PKCS12 structure
1959
1960 :param pkey: The new private key.
1961 :type pkey: :py:class:`PKey`
1962 :return: None
1963 """
1964 if not isinstance(pkey, PKey):
1965 raise TypeError("pkey must be a PKey instance")
1966 self._pkey = pkey
1967
1968
1969 def get_ca_certificates(self):
1970 """
1971 Return CA certificates within of the PKCS12 object
1972
1973 :return: A newly created tuple containing the CA certificates in the chain,
1974 if any are present, or None if no CA certificates are present.
1975 """
1976 if self._cacerts is not None:
1977 return tuple(self._cacerts)
1978
1979
1980 def set_ca_certificates(self, cacerts):
1981 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08001982 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001983
1984 :param cacerts: The new CA certificates.
1985 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1986 :return: None
1987 """
1988 if cacerts is None:
1989 self._cacerts = None
1990 else:
1991 cacerts = list(cacerts)
1992 for cert in cacerts:
1993 if not isinstance(cert, X509):
1994 raise TypeError("iterable must only contain X509 instances")
1995 self._cacerts = cacerts
1996
1997
1998 def set_friendlyname(self, name):
1999 """
2000 Replace or set the certificate portion of the PKCS12 structure
2001
2002 :param name: The new friendly name.
2003 :type name: :py:class:`bytes`
2004 :return: None
2005 """
2006 if name is None:
2007 self._friendlyname = None
2008 elif not isinstance(name, bytes):
2009 raise TypeError("name must be a byte string or None (not %r)" % (name,))
2010 self._friendlyname = name
2011
2012
2013 def get_friendlyname(self):
2014 """
2015 Return friendly name portion of the PKCS12 structure
2016
2017 :returns: String containing the friendlyname
2018 """
2019 return self._friendlyname
2020
2021
2022 def export(self, passphrase=None, iter=2048, maciter=1):
2023 """
2024 Dump a PKCS12 object as a string. See also "man PKCS12_create".
2025
2026 :param passphrase: used to encrypt the PKCS12
2027 :type passphrase: :py:data:`bytes`
2028
2029 :param iter: How many times to repeat the encryption
2030 :type iter: :py:data:`int`
2031
2032 :param maciter: How many times to repeat the MAC
2033 :type maciter: :py:data:`int`
2034
2035 :return: The string containing the PKCS12
2036 """
2037 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002038 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002039 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002040 cacerts = _lib.sk_X509_new_null()
2041 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002042 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002043 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002044
2045 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002046 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002047
2048 friendlyname = self._friendlyname
2049 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002050 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002051
2052 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002053 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002054 else:
2055 pkey = self._pkey._pkey
2056
2057 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002058 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002059 else:
2060 cert = self._cert._x509
2061
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002062 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002063 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002064 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2065 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002066 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002067 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002068 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002069 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002070
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002071 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002072 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002073 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002074
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002075PKCS12Type = PKCS12
2076
2077
2078
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002079class NetscapeSPKI(object):
2080 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002081 spki = _lib.NETSCAPE_SPKI_new()
2082 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002083
2084
2085 def sign(self, pkey, digest):
2086 """
2087 Sign the certificate request using the supplied key and digest
2088
2089 :param pkey: The key to sign with
2090 :param digest: The message digest to use
2091 :return: None
2092 """
2093 if pkey._only_public:
2094 raise ValueError("Key has only public part")
2095
2096 if not pkey._initialized:
2097 raise ValueError("Key is uninitialized")
2098
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002099 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002100 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002101 raise ValueError("No such digest method")
2102
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002103 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002104 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002105 # TODO: This is untested.
2106 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002107
2108
2109 def verify(self, key):
2110 """
2111 Verifies a certificate request using the supplied public key
2112
2113 :param key: a public key
2114 :return: True if the signature is correct.
2115 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2116 problem verifying the signature.
2117 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002118 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002119 if answer <= 0:
2120 _raise_current_error()
2121 return True
2122
2123
2124 def b64_encode(self):
2125 """
2126 Generate a base64 encoded string from an SPKI
2127
2128 :return: The base64 encoded string
2129 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002130 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2131 result = _ffi.string(encoded)
2132 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002133 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002134
2135
2136 def get_pubkey(self):
2137 """
2138 Get the public key of the certificate
2139
2140 :return: The public key
2141 """
2142 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002143 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2144 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002145 # TODO: This is untested.
2146 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002147 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002148 pkey._only_public = True
2149 return pkey
2150
2151
2152 def set_pubkey(self, pkey):
2153 """
2154 Set the public key of the certificate
2155
2156 :param pkey: The public key
2157 :return: None
2158 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002159 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002160 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002161 # TODO: This is untested.
2162 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002163NetscapeSPKIType = NetscapeSPKI
2164
2165
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002166class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002167 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002168 if type != FILETYPE_PEM and passphrase is not None:
2169 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002170 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002171 self._more_args = more_args
2172 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002173 self._problems = []
2174
2175
2176 @property
2177 def callback(self):
2178 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002179 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002180 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002181 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002182 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002183 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002184 else:
2185 raise TypeError("Last argument must be string or callable")
2186
2187
2188 @property
2189 def callback_args(self):
2190 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002191 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002192 elif isinstance(self._passphrase, bytes):
2193 return self._passphrase
2194 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002195 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002196 else:
2197 raise TypeError("Last argument must be string or callable")
2198
2199
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002200 def raise_if_problem(self, exceptionType=Error):
2201 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002202 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002203 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002204 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002205 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002206 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002207 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002208
2209
2210 def _read_passphrase(self, buf, size, rwflag, userdata):
2211 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002212 if self._more_args:
2213 result = self._passphrase(size, rwflag, userdata)
2214 else:
2215 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002216 if not isinstance(result, bytes):
2217 raise ValueError("String expected")
2218 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002219 if self._truncate:
2220 result = result[:size]
2221 else:
2222 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002223 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002224 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002225 return len(result)
2226 except Exception as e:
2227 self._problems.append(e)
2228 return 0
2229
2230
2231
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002232def load_privatekey(type, buffer, passphrase=None):
2233 """
2234 Load a private key from a buffer
2235
2236 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2237 :param buffer: The buffer the key is stored in
2238 :param passphrase: (optional) if encrypted PEM format, this can be
2239 either the passphrase to use, or a callback for
2240 providing the passphrase.
2241
2242 :return: The PKey object
2243 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002244 if isinstance(buffer, _text_type):
2245 buffer = buffer.encode("ascii")
2246
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002247 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002248
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002249 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002250 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002251 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2252 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002253 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002254 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002255 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002256 else:
2257 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2258
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002259 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002260 _raise_current_error()
2261
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002262 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002263 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002264 return pkey
2265
2266
2267
2268def dump_certificate_request(type, req):
2269 """
2270 Dump a certificate request to a buffer
2271
2272 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2273 :param req: The certificate request to dump
2274 :return: The buffer with the dumped certificate request in
2275 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002276 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002277
2278 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002279 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002280 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002281 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002282 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002283 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002284 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002285 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002286
2287 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002288 # TODO: This is untested.
2289 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002290
2291 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002292
2293
2294
2295def load_certificate_request(type, buffer):
2296 """
2297 Load a certificate request from a buffer
2298
2299 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2300 :param buffer: The buffer the certificate request is stored in
2301 :return: The X509Req object
2302 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002303 if isinstance(buffer, _text_type):
2304 buffer = buffer.encode("ascii")
2305
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002306 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002307
2308 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002309 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002310 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002311 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002312 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002313 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002314
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002315 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002316 # TODO: This is untested.
2317 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002318
2319 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002320 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002321 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002322
2323
2324
2325def sign(pkey, data, digest):
2326 """
2327 Sign data with a digest
2328
2329 :param pkey: Pkey to sign with
2330 :param data: data to be signed
2331 :param digest: message digest to use
2332 :return: signature
2333 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002334 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002335 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002336 raise ValueError("No such digest method")
2337
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002338 md_ctx = _ffi.new("EVP_MD_CTX*")
2339 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002340
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002341 _lib.EVP_SignInit(md_ctx, digest_obj)
2342 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002343
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002344 signature_buffer = _ffi.new("unsigned char[]", 512)
2345 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002346 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002347 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002348 md_ctx, signature_buffer, signature_length, pkey._pkey)
2349
2350 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002351 # TODO: This is untested.
2352 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002353
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002354 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002355
2356
2357
2358def verify(cert, signature, data, digest):
2359 """
2360 Verify a signature
2361
2362 :param cert: signing certificate (X509 object)
2363 :param signature: signature returned by sign function
2364 :param data: data to be verified
2365 :param digest: message digest to use
2366 :return: None if the signature is correct, raise exception otherwise
2367 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002368 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002369 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002370 raise ValueError("No such digest method")
2371
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002372 pkey = _lib.X509_get_pubkey(cert._x509)
2373 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002374 # TODO: This is untested.
2375 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002376 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002377
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002378 md_ctx = _ffi.new("EVP_MD_CTX*")
2379 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002380
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002381 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2382 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2383 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002384
2385 if verify_result != 1:
2386 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002387
2388
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07002389def verify_cert(store_ctx):
2390 """
2391 Verify a certificate in a context.
2392
2393 :param store_ctx: The :py:class:`X509StoreContext` to verify.
2394 :raises: Error
2395 """
2396 store_ctx._init()
2397 ret = _lib.X509_verify_cert(store_ctx._store_ctx)
2398 store_ctx._cleanup()
2399 if ret <= 0:
2400 _raise_context_error(store_ctx)
2401
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002402
2403def load_crl(type, buffer):
2404 """
2405 Load a certificate revocation list from a buffer
2406
2407 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2408 :param buffer: The buffer the CRL is stored in
2409
2410 :return: The PKey object
2411 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002412 if isinstance(buffer, _text_type):
2413 buffer = buffer.encode("ascii")
2414
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002415 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002416
2417 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002418 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002419 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002420 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002421 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002422 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2423
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002424 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002425 _raise_current_error()
2426
2427 result = CRL.__new__(CRL)
2428 result._crl = crl
2429 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002430
2431
2432
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002433def load_pkcs7_data(type, buffer):
2434 """
2435 Load pkcs7 data from a buffer
2436
2437 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2438 :param buffer: The buffer with the pkcs7 data.
2439 :return: The PKCS7 object
2440 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002441 if isinstance(buffer, _text_type):
2442 buffer = buffer.encode("ascii")
2443
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002444 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002445
2446 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002447 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002448 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002449 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002450 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002451 # TODO: This is untested.
2452 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002453 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2454
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002455 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002456 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002457
2458 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002459 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002460 return pypkcs7
2461
2462
2463
Stephen Holsapple38482622014-04-05 20:29:34 -07002464def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002465 """
2466 Load a PKCS12 object from a buffer
2467
2468 :param buffer: The buffer the certificate is stored in
2469 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2470 :returns: The PKCS12 object
2471 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002472 if isinstance(buffer, _text_type):
2473 buffer = buffer.encode("ascii")
2474
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002475 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002476
Stephen Holsapple38482622014-04-05 20:29:34 -07002477 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2478 # password based encryption no password and a zero length password are two
2479 # different things, but OpenSSL implementation will try both to figure out
2480 # which one works.
2481 if not passphrase:
2482 passphrase = _ffi.NULL
2483
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002484 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2485 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002486 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002487 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002488
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002489 pkey = _ffi.new("EVP_PKEY**")
2490 cert = _ffi.new("X509**")
2491 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002492
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002493 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002494 if not parse_result:
2495 _raise_current_error()
2496
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002497 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002498
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002499 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2500 # queue for no particular reason. This error isn't interesting to anyone
2501 # outside this function. It's not even interesting to us. Get rid of it.
2502 try:
2503 _raise_current_error()
2504 except Error:
2505 pass
2506
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002507 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002508 pykey = None
2509 else:
2510 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002511 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002512
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002513 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002514 pycert = None
2515 friendlyname = None
2516 else:
2517 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002518 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002519
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002520 friendlyname_length = _ffi.new("int*")
2521 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2522 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2523 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002524 friendlyname = None
2525
2526 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002527 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002528 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002529 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002530 pycacerts.append(pycacert)
2531 if not pycacerts:
2532 pycacerts = None
2533
2534 pkcs12 = PKCS12.__new__(PKCS12)
2535 pkcs12._pkey = pykey
2536 pkcs12._cert = pycert
2537 pkcs12._cacerts = pycacerts
2538 pkcs12._friendlyname = friendlyname
2539 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002540
2541
2542def _initialize_openssl_threads(get_ident, Lock):
2543 import _ssl
2544 return
2545
2546 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2547
2548 def locking_function(mode, index, filename, line):
2549 if mode & _lib.CRYPTO_LOCK:
2550 locks[index].acquire()
2551 else:
2552 locks[index].release()
2553
2554 _lib.CRYPTO_set_id_callback(
2555 _ffi.callback("unsigned long (*)(void)", get_ident))
2556
2557 _lib.CRYPTO_set_locking_callback(
2558 _ffi.callback(
2559 "void (*)(int, int, const char*, int)", locking_function))
2560
2561
2562try:
2563 from thread import get_ident
2564 from threading import Lock
2565except ImportError:
2566 pass
2567else:
2568 _initialize_openssl_threads(get_ident, Lock)
2569 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002570
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002571# There are no direct unit tests for this initialization. It is tested
2572# indirectly since it is necessary for functions like dump_privatekey when
2573# using encryption.
2574#
2575# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2576# and some other similar tests may fail without this (though they may not if
2577# the Python runtime has already done some initialization of the underlying
2578# OpenSSL library (and is linked against the same one that cryptography is
2579# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002580_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002581
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002582# This is similar but exercised mainly by exception_from_error_queue. It calls
2583# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2584_lib.SSL_load_error_strings()