blob: 9f44b448cc4fdbb7e36973d7f5f07f6439187730 [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__
Jean-Paul Calderone60432792015-04-13 12:26:07 -04005from warnings import warn as _warn
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05006
7from six import (
8 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -04009 text_type as _text_type,
10 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080011
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050012from OpenSSL._util import (
13 ffi as _ffi,
14 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050015 exception_from_error_queue as _exception_from_error_queue,
16 byte_string as _byte_string,
17 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080018
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050019FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
20FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080021
22# TODO This was an API mistake. OpenSSL has no such constant.
23FILETYPE_TEXT = 2 ** 16 - 1
24
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050025TYPE_RSA = _lib.EVP_PKEY_RSA
26TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080027
Jean-Paul Calderone60432792015-04-13 12:26:07 -040028# A marker object to observe whether some optional arguments are passed any
29# value or not.
30_undefined = object()
31
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080032
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050033class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050034 """
35 An error occurred in an `OpenSSL.crypto` API.
36 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050037
38
39_raise_current_error = partial(_exception_from_error_queue, Error)
40
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050041def _untested_error(where):
42 """
43 An OpenSSL API failed somehow. Additionally, the failure which was
44 encountered isn't one that's exercised by the test suite so future behavior
45 of pyOpenSSL is now somewhat less predictable.
46 """
47 raise RuntimeError("Unknown %s failure" % (where,))
48
49
50
51def _new_mem_buf(buffer=None):
52 """
53 Allocate a new OpenSSL memory BIO.
54
55 Arrange for the garbage collector to clean it up automatically.
56
57 :param buffer: None or some bytes to use to put into the BIO so that they
58 can be read out.
59 """
60 if buffer is None:
61 bio = _lib.BIO_new(_lib.BIO_s_mem())
62 free = _lib.BIO_free
63 else:
64 data = _ffi.new("char[]", buffer)
65 bio = _lib.BIO_new_mem_buf(data, len(buffer))
66 # Keep the memory alive as long as the bio is alive!
67 def free(bio, ref=data):
68 return _lib.BIO_free(bio)
69
70 if bio == _ffi.NULL:
71 # TODO: This is untested.
72 _raise_current_error()
73
74 bio = _ffi.gc(bio, free)
75 return bio
76
77
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050078
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080079def _bio_to_string(bio):
80 """
81 Copy the contents of an OpenSSL BIO object into a Python byte string.
82 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050083 result_buffer = _ffi.new('char**')
84 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
85 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080086
87
88
Jean-Paul Calderone57122982013-02-21 08:47:05 -080089def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050090 """
91 The the time value of an ASN1 time object.
92
93 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
94 castable to that type) which will have its value set.
95 @param when: A string representation of the desired time value.
96
97 @raise TypeError: If C{when} is not a L{bytes} string.
98 @raise ValueError: If C{when} does not represent a time in the required
99 format.
100 @raise RuntimeError: If the time value cannot be set for some other
101 (unspecified) reason.
102 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800103 if not isinstance(when, bytes):
104 raise TypeError("when must be a byte string")
105
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500106 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
107 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800108 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500109 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
110 _lib.ASN1_STRING_set(dummy, when, len(when))
111 check_result = _lib.ASN1_GENERALIZEDTIME_check(
112 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800113 if not check_result:
114 raise ValueError("Invalid string")
115 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500116 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800117
118
119
120def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500121 """
122 Retrieve the time value of an ASN1 time object.
123
124 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
125 that type) from which the time value will be retrieved.
126
127 @return: The time value from C{timestamp} as a L{bytes} string in a certain
128 format. Or C{None} if the object contains no time value.
129 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500130 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
131 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800132 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500133 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
134 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800135 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500136 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
137 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
138 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500139 # This may happen:
140 # - if timestamp was not an ASN1_TIME
141 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
142 # - if a copy of the time data from timestamp cannot be made for
143 # the newly allocated ASN1_GENERALIZEDTIME
144 #
145 # These are difficult to test. cffi enforces the ASN1_TIME type.
146 # Memory allocation failures are a pain to trigger
147 # deterministically.
148 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800149 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500150 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800151 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500152 string_data = _lib.ASN1_STRING_data(string_timestamp)
153 string_result = _ffi.string(string_data)
154 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800155 return string_result
156
157
158
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800159class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800160 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800161 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800162
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800163 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500164 pkey = _lib.EVP_PKEY_new()
165 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800166 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800167
168
169 def generate_key(self, type, bits):
170 """
171 Generate a key of a given type, with a given number of a bits
172
173 :param type: The key type (TYPE_RSA or TYPE_DSA)
174 :param bits: The number of bits
175
176 :return: None
177 """
178 if not isinstance(type, int):
179 raise TypeError("type must be an integer")
180
181 if not isinstance(bits, int):
182 raise TypeError("bits must be an integer")
183
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800184 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500185 exponent = _lib.BN_new()
186 exponent = _ffi.gc(exponent, _lib.BN_free)
187 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800188
189 if type == TYPE_RSA:
190 if bits <= 0:
191 raise ValueError("Invalid number of bits")
192
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500193 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800194
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500195 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500196 if result == 0:
197 # TODO: The test for this case is commented out. Different
198 # builds of OpenSSL appear to have different failure modes that
199 # make it hard to test. Visual inspection of the OpenSSL
200 # source reveals that a return value of 0 signals an error.
201 # Manual testing on a particular build of OpenSSL suggests that
202 # this is probably the appropriate way to handle those errors.
203 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800204
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500205 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800206 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500207 # TODO: It appears as though this can fail if an engine is in
208 # use which does not support RSA.
209 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800210
211 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500212 dsa = _lib.DSA_generate_parameters(
213 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
214 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500215 # TODO: This is untested.
216 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500217 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500218 # TODO: This is untested.
219 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500220 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500221 # TODO: This is untested.
222 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800223 else:
224 raise Error("No such key type")
225
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800226 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800227
228
229 def check(self):
230 """
231 Check the consistency of an RSA private key.
232
233 :return: True if key is consistent.
234 :raise Error: if the key is inconsistent.
235 :raise TypeError: if the key is of a type which cannot be checked.
236 Only RSA keys can currently be checked.
237 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800238 if self._only_public:
239 raise TypeError("public key only")
240
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500241 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800242 raise TypeError("key type unsupported")
243
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500244 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
245 rsa = _ffi.gc(rsa, _lib.RSA_free)
246 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800247 if result:
248 return True
249 _raise_current_error()
250
251
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800252 def type(self):
253 """
254 Returns the type of the key
255
256 :return: The type of the key.
257 """
258 return self._pkey.type
259
260
261 def bits(self):
262 """
263 Returns the number of bits of the key
264
265 :return: The number of bits of the key.
266 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500267 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800268PKeyType = PKey
269
270
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800271
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400272class _EllipticCurve(object):
273 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400274 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400275
276 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
277 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
278 instances each of which represents one curve supported by the system.
279 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400280 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400281 _curves = None
282
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400283 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400284 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400285 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400286 """
287 Implement cooperation with the right-hand side argument of ``!=``.
288
289 Python 3 seems to have dropped this cooperation in this very narrow
290 circumstance.
291 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400292 if isinstance(other, _EllipticCurve):
293 return super(_EllipticCurve, self).__ne__(other)
294 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400295
296
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400297 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400298 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400299 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400300 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400301
302 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400303
304 :return: A :py:type:`set` of ``cls`` instances giving the names of the
305 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400306 """
307 if lib.Cryptography_HAS_EC:
308 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
309 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
310 # The return value on this call should be num_curves again. We could
311 # check it to make sure but if it *isn't* then.. what could we do?
312 # Abort the whole process, I suppose...? -exarkun
313 lib.EC_get_builtin_curves(builtin_curves, num_curves)
314 return set(
315 cls.from_nid(lib, c.nid)
316 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400317 return set()
318
319
320 @classmethod
321 def _get_elliptic_curves(cls, lib):
322 """
323 Get, cache, and return the curves supported by OpenSSL.
324
325 :param lib: The OpenSSL library binding object.
326
327 :return: A :py:type:`set` of ``cls`` instances giving the names of the
328 elliptic curves the underlying library supports.
329 """
330 if cls._curves is None:
331 cls._curves = cls._load_elliptic_curves(lib)
332 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400333
334
335 @classmethod
336 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400337 """
338 Instantiate a new :py:class:`_EllipticCurve` associated with the given
339 OpenSSL NID.
340
341 :param lib: The OpenSSL library binding object.
342
343 :param nid: The OpenSSL NID the resulting curve object will represent.
344 This must be a curve NID (and not, for example, a hash NID) or
345 subsequent operations will fail in unpredictable ways.
346 :type nid: :py:class:`int`
347
348 :return: The curve object.
349 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400350 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
351
352
353 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400354 """
355 :param _lib: The :py:mod:`cryptography` binding instance used to
356 interface with OpenSSL.
357
358 :param _nid: The OpenSSL NID identifying the curve this object
359 represents.
360 :type _nid: :py:class:`int`
361
362 :param name: The OpenSSL short name identifying the curve this object
363 represents.
364 :type name: :py:class:`unicode`
365 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400366 self._lib = lib
367 self._nid = nid
368 self.name = name
369
370
371 def __repr__(self):
372 return "<Curve %r>" % (self.name,)
373
374
375 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400376 """
377 Create a new OpenSSL EC_KEY structure initialized to use this curve.
378
379 The structure is automatically garbage collected when the Python object
380 is garbage collected.
381 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400382 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
383 return _ffi.gc(key, _lib.EC_KEY_free)
384
385
386
387def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400388 """
389 Return a set of objects representing the elliptic curves supported in the
390 OpenSSL build in use.
391
392 The curve objects have a :py:class:`unicode` ``name`` attribute by which
393 they identify themselves.
394
395 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400396 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
397 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400398 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400399 return _EllipticCurve._get_elliptic_curves(_lib)
400
401
402
403def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400404 """
405 Return a single curve object selected by name.
406
407 See :py:func:`get_elliptic_curves` for information about curve objects.
408
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400409 :param name: The OpenSSL short name identifying the curve object to
410 retrieve.
411 :type name: :py:class:`unicode`
412
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400413 If the named curve is not supported then :py:class:`ValueError` is raised.
414 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400415 for curve in get_elliptic_curves():
416 if curve.name == name:
417 return curve
418 raise ValueError("unknown curve name", name)
419
420
421
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800422class X509Name(object):
423 def __init__(self, name):
424 """
425 Create a new X509Name, copying the given X509Name instance.
426
427 :param name: An X509Name object to copy
428 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500429 name = _lib.X509_NAME_dup(name._name)
430 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800431
432
433 def __setattr__(self, name, value):
434 if name.startswith('_'):
435 return super(X509Name, self).__setattr__(name, value)
436
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800437 # Note: we really do not want str subclasses here, so we do not use
438 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800439 if type(name) is not str:
440 raise TypeError("attribute name must be string, not '%.200s'" % (
441 type(value).__name__,))
442
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500443 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500444 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800445 try:
446 _raise_current_error()
447 except Error:
448 pass
449 raise AttributeError("No such attribute")
450
451 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500452 for i in range(_lib.X509_NAME_entry_count(self._name)):
453 ent = _lib.X509_NAME_get_entry(self._name, i)
454 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
455 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800456 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500457 ent = _lib.X509_NAME_delete_entry(self._name, i)
458 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800459 break
460
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500461 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800462 value = value.encode('utf-8')
463
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500464 add_result = _lib.X509_NAME_add_entry_by_NID(
465 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800466 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500467 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800468
469
470 def __getattr__(self, name):
471 """
472 Find attribute. An X509Name object has the following attributes:
473 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
474 organization (alias O), organizationalUnit (alias OU), commonName (alias
475 CN) and more...
476 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500477 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500478 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800479 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
480 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
481 # push something onto the error queue. If we don't clean that up
482 # now, someone else will bump into it later and be quite confused.
483 # See lp#314814.
484 try:
485 _raise_current_error()
486 except Error:
487 pass
488 return super(X509Name, self).__getattr__(name)
489
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500490 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800491 if entry_index == -1:
492 return None
493
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500494 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
495 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800496
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500497 result_buffer = _ffi.new("unsigned char**")
498 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800499 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500500 # TODO: This is untested.
501 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800502
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700503 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500504 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700505 finally:
506 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500507 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800508 return result
509
510
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500511 def _cmp(op):
512 def f(self, other):
513 if not isinstance(other, X509Name):
514 return NotImplemented
515 result = _lib.X509_NAME_cmp(self._name, other._name)
516 return op(result, 0)
517 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800518
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500519 __eq__ = _cmp(__eq__)
520 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800521
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500522 __lt__ = _cmp(__lt__)
523 __le__ = _cmp(__le__)
524
525 __gt__ = _cmp(__gt__)
526 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800527
528 def __repr__(self):
529 """
530 String representation of an X509Name
531 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500532 result_buffer = _ffi.new("char[]", 512);
533 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800534 self._name, result_buffer, len(result_buffer))
535
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500536 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500537 # TODO: This is untested.
538 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800539
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500540 return "<X509Name object '%s'>" % (
541 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800542
543
544 def hash(self):
545 """
546 Return the hash value of this name
547
548 :return: None
549 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500550 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800551
552
553 def der(self):
554 """
555 Return the DER encoding of this name
556
557 :return: A :py:class:`bytes` instance giving the DER encoded form of
558 this name.
559 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500560 result_buffer = _ffi.new('unsigned char**')
561 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800562 if encode_result < 0:
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 Calderone6037d072013-12-28 18:04:00 -0500566 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
567 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800568 return string_result
569
570
571 def get_components(self):
572 """
573 Returns the split-up components of this name.
574
575 :return: List of tuples (name, value).
576 """
577 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500578 for i in range(_lib.X509_NAME_entry_count(self._name)):
579 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800580
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500581 fname = _lib.X509_NAME_ENTRY_get_object(ent)
582 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800583
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500584 nid = _lib.OBJ_obj2nid(fname)
585 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800586
587 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500588 _ffi.string(name),
589 _ffi.string(
590 _lib.ASN1_STRING_data(fval),
591 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800592
593 return result
594X509NameType = X509Name
595
596
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800597class X509Extension(object):
598 def __init__(self, type_name, critical, value, subject=None, issuer=None):
599 """
600 :param typename: The name of the extension to create.
601 :type typename: :py:data:`str`
602
603 :param critical: A flag indicating whether this is a critical extension.
604
605 :param value: The value of the extension.
606 :type value: :py:data:`str`
607
608 :param subject: Optional X509 cert to use as subject.
609 :type subject: :py:class:`X509`
610
611 :param issuer: Optional X509 cert to use as issuer.
612 :type issuer: :py:class:`X509`
613
614 :return: The X509Extension object
615 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500616 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800617
618 # A context is necessary for any extension which uses the r2i conversion
619 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
620 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500621 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800622
623 # We have no configuration database - but perhaps we should (some
624 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500625 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800626
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800627 # Initialize the subject and issuer, if appropriate. ctx is a local,
628 # and as far as I can tell none of the X509V3_* APIs invoked here steal
629 # any references, so no need to mess with reference counts or duplicates.
630 if issuer is not None:
631 if not isinstance(issuer, X509):
632 raise TypeError("issuer must be an X509 instance")
633 ctx.issuer_cert = issuer._x509
634 if subject is not None:
635 if not isinstance(subject, X509):
636 raise TypeError("subject must be an X509 instance")
637 ctx.subject_cert = subject._x509
638
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800639 if critical:
640 # There are other OpenSSL APIs which would let us pass in critical
641 # separately, but they're harder to use, and since value is already
642 # a pile of crappy junk smuggling a ton of utterly important
643 # structured data, what's the point of trying to avoid nasty stuff
644 # with strings? (However, X509V3_EXT_i2d in particular seems like it
645 # would be a better API to invoke. I do not know where to get the
646 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500647 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800648
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500649 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
650 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800651 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500652 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800653
654
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400655 @property
656 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500657 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400658
659 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500660 _lib.GEN_EMAIL: "email",
661 _lib.GEN_DNS: "DNS",
662 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400663 }
664
665 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500666 method = _lib.X509V3_EXT_get(self._extension)
667 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500668 # TODO: This is untested.
669 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400670 payload = self._extension.value.data
671 length = self._extension.value.length
672
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500673 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400674 payloadptr[0] = payload
675
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500676 if method.it != _ffi.NULL:
677 ptr = _lib.ASN1_ITEM_ptr(method.it)
678 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
679 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400680 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500681 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400682 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500683 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400684
685 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500686 for i in range(_lib.sk_GENERAL_NAME_num(names)):
687 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400688 try:
689 label = self._prefixes[name.type]
690 except KeyError:
691 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500692 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500693 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400694 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500695 value = _native(
696 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
697 parts.append(label + ":" + value)
698 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400699
700
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800701 def __str__(self):
702 """
703 :return: a nice text representation of the extension
704 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500705 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400706 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800707
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400708 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500709 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800710 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500711 # TODO: This is untested.
712 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800713
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500714 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800715
716
717 def get_critical(self):
718 """
719 Returns the critical field of the X509Extension
720
721 :return: The critical field.
722 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500723 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800724
725
726 def get_short_name(self):
727 """
728 Returns the short version of the type name of the X509Extension
729
730 :return: The short type name.
731 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500732 obj = _lib.X509_EXTENSION_get_object(self._extension)
733 nid = _lib.OBJ_obj2nid(obj)
734 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800735
736
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800737 def get_data(self):
738 """
739 Returns the data of the X509Extension
740
741 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
742 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500743 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
744 string_result = _ffi.cast('ASN1_STRING*', octet_result)
745 char_result = _lib.ASN1_STRING_data(string_result)
746 result_length = _lib.ASN1_STRING_length(string_result)
747 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800748
749X509ExtensionType = X509Extension
750
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800751
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800752class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800753 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500754 req = _lib.X509_REQ_new()
755 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800756
757
758 def set_pubkey(self, pkey):
759 """
760 Set the public key of the certificate request
761
762 :param pkey: The public key to use
763 :return: None
764 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500765 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800766 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500767 # TODO: This is untested.
768 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800769
770
771 def get_pubkey(self):
772 """
773 Get the public key from the certificate request
774
775 :return: The public key
776 """
777 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500778 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
779 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500780 # TODO: This is untested.
781 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500782 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800783 pkey._only_public = True
784 return pkey
785
786
787 def set_version(self, version):
788 """
789 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
790 request.
791
792 :param version: The version number
793 :return: None
794 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500795 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800796 if not set_result:
797 _raise_current_error()
798
799
800 def get_version(self):
801 """
802 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
803 request.
804
805 :return: an integer giving the value of the version subfield
806 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500807 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800808
809
810 def get_subject(self):
811 """
812 Create an X509Name object for the subject of the certificate request
813
814 :return: An X509Name object
815 """
816 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500817 name._name = _lib.X509_REQ_get_subject_name(self._req)
818 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500819 # TODO: This is untested.
820 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800821
822 # The name is owned by the X509Req structure. As long as the X509Name
823 # Python object is alive, keep the X509Req Python object alive.
824 name._owner = self
825
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800826 return name
827
828
829 def add_extensions(self, extensions):
830 """
831 Add extensions to the request.
832
833 :param extensions: a sequence of X509Extension objects
834 :return: None
835 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500836 stack = _lib.sk_X509_EXTENSION_new_null()
837 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500838 # TODO: This is untested.
839 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800840
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500841 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800842
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800843 for ext in extensions:
844 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800845 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800846
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800847 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500848 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800849
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500850 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800851 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500852 # TODO: This is untested.
853 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800854
855
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800856 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800857 """
858 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800859
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500860 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800861 """
862 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500863 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500864 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800865 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500866 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800867 exts.append(ext)
868 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800869
870
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800871 def sign(self, pkey, digest):
872 """
873 Sign the certificate request using the supplied key and digest
874
875 :param pkey: The key to sign with
876 :param digest: The message digest to use
877 :return: None
878 """
879 if pkey._only_public:
880 raise ValueError("Key has only public part")
881
882 if not pkey._initialized:
883 raise ValueError("Key is uninitialized")
884
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500885 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500886 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800887 raise ValueError("No such digest method")
888
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500889 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800890 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500891 # TODO: This is untested.
892 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800893
894
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800895 def verify(self, pkey):
896 """
897 Verifies a certificate request using the supplied public key
898
899 :param key: a public key
900 :return: True if the signature is correct.
901
902 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
903 problem verifying the signature.
904 """
905 if not isinstance(pkey, PKey):
906 raise TypeError("pkey must be a PKey instance")
907
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500908 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800909 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500910 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800911
912 return result
913
914
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800915X509ReqType = X509Req
916
917
918
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800919class X509(object):
920 def __init__(self):
921 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500922 x509 = _lib.X509_new()
923 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800924
925
926 def set_version(self, version):
927 """
928 Set version number of the certificate
929
930 :param version: The version number
931 :type version: :py:class:`int`
932
933 :return: None
934 """
935 if not isinstance(version, int):
936 raise TypeError("version must be an integer")
937
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500938 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800939
940
941 def get_version(self):
942 """
943 Return version number of the certificate
944
945 :return: Version number as a Python integer
946 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500947 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800948
949
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800950 def get_pubkey(self):
951 """
952 Get the public key of the certificate
953
954 :return: The public key
955 """
956 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500957 pkey._pkey = _lib.X509_get_pubkey(self._x509)
958 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800959 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500960 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800961 pkey._only_public = True
962 return pkey
963
964
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800965 def set_pubkey(self, pkey):
966 """
967 Set the public key of the certificate
968
969 :param pkey: The public key
970
971 :return: None
972 """
973 if not isinstance(pkey, PKey):
974 raise TypeError("pkey must be a PKey instance")
975
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500976 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800977 if not set_result:
978 _raise_current_error()
979
980
981 def sign(self, pkey, digest):
982 """
983 Sign the certificate using the supplied key and digest
984
985 :param pkey: The key to sign with
986 :param digest: The message digest to use
987 :return: None
988 """
989 if not isinstance(pkey, PKey):
990 raise TypeError("pkey must be a PKey instance")
991
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800992 if pkey._only_public:
993 raise ValueError("Key only has public part")
994
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800995 if not pkey._initialized:
996 raise ValueError("Key is uninitialized")
997
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500998 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500999 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001000 raise ValueError("No such digest method")
1001
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001002 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001003 if not sign_result:
1004 _raise_current_error()
1005
1006
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001007 def get_signature_algorithm(self):
1008 """
1009 Retrieve the signature algorithm used in the certificate
1010
1011 :return: A byte string giving the name of the signature algorithm used in
1012 the certificate.
1013 :raise ValueError: If the signature algorithm is undefined.
1014 """
1015 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001016 nid = _lib.OBJ_obj2nid(alg)
1017 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001018 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001019 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001020
1021
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001022 def digest(self, digest_name):
1023 """
1024 Return the digest of the X509 object.
1025
1026 :param digest_name: The name of the digest algorithm to use.
1027 :type digest_name: :py:class:`bytes`
1028
1029 :return: The digest of the object
1030 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001031 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001032 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001033 raise ValueError("No such digest method")
1034
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001035 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1036 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001037 result_length[0] = len(result_buffer)
1038
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001039 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001040 self._x509, digest, result_buffer, result_length)
1041
1042 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001043 # TODO: This is untested.
1044 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001045
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001046 return b":".join([
1047 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001048 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001049
1050
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001051 def subject_name_hash(self):
1052 """
1053 Return the hash of the X509 subject.
1054
1055 :return: The hash of the subject.
1056 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001057 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001058
1059
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001060 def set_serial_number(self, serial):
1061 """
1062 Set serial number of the certificate
1063
1064 :param serial: The serial number
1065 :type serial: :py:class:`int`
1066
1067 :return: None
1068 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001069 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001070 raise TypeError("serial must be an integer")
1071
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001072 hex_serial = hex(serial)[2:]
1073 if not isinstance(hex_serial, bytes):
1074 hex_serial = hex_serial.encode('ascii')
1075
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001076 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001077
1078 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1079 # it. If bignum is still NULL after this call, then the return value is
1080 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001081 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001082
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001083 if bignum_serial[0] == _ffi.NULL:
1084 set_result = _lib.ASN1_INTEGER_set(
1085 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001086 if set_result:
1087 # TODO Not tested
1088 _raise_current_error()
1089 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001090 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1091 _lib.BN_free(bignum_serial[0])
1092 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001093 # TODO Not tested
1094 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001095 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1096 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001097 if not set_result:
1098 # TODO Not tested
1099 _raise_current_error()
1100
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001101
1102 def get_serial_number(self):
1103 """
1104 Return serial number of the certificate
1105
1106 :return: Serial number as a Python integer
1107 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001108 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1109 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001110 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001111 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001112 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001113 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001114 serial = int(hexstring_serial, 16)
1115 return serial
1116 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001117 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001118 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001119 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001120
1121
1122 def gmtime_adj_notAfter(self, amount):
1123 """
1124 Adjust the time stamp for when the certificate stops being valid
1125
1126 :param amount: The number of seconds by which to adjust the ending
1127 validity time.
1128 :type amount: :py:class:`int`
1129
1130 :return: None
1131 """
1132 if not isinstance(amount, int):
1133 raise TypeError("amount must be an integer")
1134
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001135 notAfter = _lib.X509_get_notAfter(self._x509)
1136 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001137
1138
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001139 def gmtime_adj_notBefore(self, amount):
1140 """
1141 Change the timestamp for when the certificate starts being valid to the current
1142 time plus an offset.
1143
1144 :param amount: The number of seconds by which to adjust the starting validity
1145 time.
1146 :return: None
1147 """
1148 if not isinstance(amount, int):
1149 raise TypeError("amount must be an integer")
1150
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001151 notBefore = _lib.X509_get_notBefore(self._x509)
1152 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001153
1154
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001155 def has_expired(self):
1156 """
1157 Check whether the certificate has expired.
1158
1159 :return: True if the certificate has expired, false otherwise
1160 """
1161 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001162 notAfter = _lib.X509_get_notAfter(self._x509)
1163 return _lib.ASN1_UTCTIME_cmp_time_t(
1164 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001165
1166
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001167 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001168 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001169
1170
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001171 def get_notBefore(self):
1172 """
1173 Retrieve the time stamp for when the certificate starts being valid
1174
1175 :return: A string giving the timestamp, in the format::
1176
1177 YYYYMMDDhhmmssZ
1178 YYYYMMDDhhmmss+hhmm
1179 YYYYMMDDhhmmss-hhmm
1180
1181 or None if there is no value set.
1182 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001183 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001184
1185
1186 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001187 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001188
1189
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001190 def set_notBefore(self, when):
1191 """
1192 Set the time stamp for when the certificate starts being valid
1193
1194 :param when: A string giving the timestamp, in the format:
1195
1196 YYYYMMDDhhmmssZ
1197 YYYYMMDDhhmmss+hhmm
1198 YYYYMMDDhhmmss-hhmm
1199 :type when: :py:class:`bytes`
1200
1201 :return: None
1202 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001203 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001204
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001205
1206 def get_notAfter(self):
1207 """
1208 Retrieve the time stamp for when the certificate stops being valid
1209
1210 :return: A string giving the timestamp, in the format::
1211
1212 YYYYMMDDhhmmssZ
1213 YYYYMMDDhhmmss+hhmm
1214 YYYYMMDDhhmmss-hhmm
1215
1216 or None if there is no value set.
1217 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001218 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001219
1220
1221 def set_notAfter(self, when):
1222 """
1223 Set the time stamp for when the certificate stops being valid
1224
1225 :param when: A string giving the timestamp, in the format:
1226
1227 YYYYMMDDhhmmssZ
1228 YYYYMMDDhhmmss+hhmm
1229 YYYYMMDDhhmmss-hhmm
1230 :type when: :py:class:`bytes`
1231
1232 :return: None
1233 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001234 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001235
1236
1237 def _get_name(self, which):
1238 name = X509Name.__new__(X509Name)
1239 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001240 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001241 # TODO: This is untested.
1242 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001243
1244 # The name is owned by the X509 structure. As long as the X509Name
1245 # Python object is alive, keep the X509 Python object alive.
1246 name._owner = self
1247
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001248 return name
1249
1250
1251 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001252 if not isinstance(name, X509Name):
1253 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001254 set_result = which(self._x509, name._name)
1255 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001256 # TODO: This is untested.
1257 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001258
1259
1260 def get_issuer(self):
1261 """
1262 Create an X509Name object for the issuer of the certificate
1263
1264 :return: An X509Name object
1265 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001266 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001267
1268
1269 def set_issuer(self, issuer):
1270 """
1271 Set the issuer of the certificate
1272
1273 :param issuer: The issuer name
1274 :type issuer: :py:class:`X509Name`
1275
1276 :return: None
1277 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001278 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001279
1280
1281 def get_subject(self):
1282 """
1283 Create an X509Name object for the subject of the certificate
1284
1285 :return: An X509Name object
1286 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001287 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001288
1289
1290 def set_subject(self, subject):
1291 """
1292 Set the subject of the certificate
1293
1294 :param subject: The subject name
1295 :type subject: :py:class:`X509Name`
1296 :return: None
1297 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001298 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001299
1300
1301 def get_extension_count(self):
1302 """
1303 Get the number of extensions on the certificate.
1304
1305 :return: The number of extensions as an integer.
1306 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001307 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001308
1309
1310 def add_extensions(self, extensions):
1311 """
1312 Add extensions to the certificate.
1313
1314 :param extensions: a sequence of X509Extension objects
1315 :return: None
1316 """
1317 for ext in extensions:
1318 if not isinstance(ext, X509Extension):
1319 raise ValueError("One of the elements is not an X509Extension")
1320
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001321 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001322 if not add_result:
1323 _raise_current_error()
1324
1325
1326 def get_extension(self, index):
1327 """
1328 Get a specific extension of the certificate by index.
1329
1330 :param index: The index of the extension to retrieve.
1331 :return: The X509Extension object at the specified index.
1332 """
1333 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001334 ext._extension = _lib.X509_get_ext(self._x509, index)
1335 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001336 raise IndexError("extension index out of bounds")
1337
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001338 extension = _lib.X509_EXTENSION_dup(ext._extension)
1339 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001340 return ext
1341
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001342X509Type = X509
1343
1344
1345
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001346class X509Store(object):
1347 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001348 store = _lib.X509_STORE_new()
1349 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001350
1351
1352 def add_cert(self, cert):
1353 if not isinstance(cert, X509):
1354 raise TypeError()
1355
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001356 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001357 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001358 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001359
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001360
1361X509StoreType = X509Store
1362
1363
1364
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001365def load_certificate(type, buffer):
1366 """
1367 Load a certificate from a buffer
1368
1369 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1370
1371 :param buffer: The buffer the certificate is stored in
1372 :type buffer: :py:class:`bytes`
1373
1374 :return: The X509 object
1375 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001376 if isinstance(buffer, _text_type):
1377 buffer = buffer.encode("ascii")
1378
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001379 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001380
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001381 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001382 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001383 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001384 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001385 else:
1386 raise ValueError(
1387 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001388
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001389 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001390 _raise_current_error()
1391
1392 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001393 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001394 return cert
1395
1396
1397def dump_certificate(type, cert):
1398 """
1399 Dump a certificate to a buffer
1400
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001401 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1402 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001403 :param cert: The certificate to dump
1404 :return: The buffer with the dumped certificate in
1405 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001406 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001407
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001408 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001409 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001410 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001411 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001412 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001413 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001414 else:
1415 raise ValueError(
1416 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1417 "FILETYPE_TEXT")
1418
1419 return _bio_to_string(bio)
1420
1421
1422
1423def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1424 """
1425 Dump a private key to a buffer
1426
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001427 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1428 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001429 :param pkey: The PKey to dump
1430 :param cipher: (optional) if encrypted PEM format, the cipher to
1431 use
1432 :param passphrase: (optional) if encrypted PEM format, this can be either
1433 the passphrase to use, or a callback for providing the
1434 passphrase.
1435 :return: The buffer with the dumped key in
1436 :rtype: :py:data:`str`
1437 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001438 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001439
1440 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001441 if passphrase is None:
1442 raise TypeError(
1443 "if a value is given for cipher "
1444 "one must also be given for passphrase")
1445 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001446 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001447 raise ValueError("Invalid cipher name")
1448 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001449 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001450
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001451 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001452 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001453 result_code = _lib.PEM_write_bio_PrivateKey(
1454 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001455 helper.callback, helper.callback_args)
1456 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001457 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001458 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001459 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001460 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1461 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001462 # TODO RSA_free(rsa)?
1463 else:
1464 raise ValueError(
1465 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1466 "FILETYPE_TEXT")
1467
1468 if result_code == 0:
1469 _raise_current_error()
1470
1471 return _bio_to_string(bio)
1472
1473
1474
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001475def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001476 copy = _lib.X509_REVOKED_new()
1477 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001478 # TODO: This is untested.
1479 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001480
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001481 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001482 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001483 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001484
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001485 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001486 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001487 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001488
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001489 if original.extensions != _ffi.NULL:
1490 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1491 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1492 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1493 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1494 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001495 copy.extensions = extension_stack
1496
1497 copy.sequence = original.sequence
1498 return copy
1499
1500
1501
1502class Revoked(object):
1503 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1504 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1505 # OCSP_crl_reason_str. We use the latter, just like the command line
1506 # program.
1507 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001508 b"unspecified",
1509 b"keyCompromise",
1510 b"CACompromise",
1511 b"affiliationChanged",
1512 b"superseded",
1513 b"cessationOfOperation",
1514 b"certificateHold",
1515 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001516 ]
1517
1518 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001519 revoked = _lib.X509_REVOKED_new()
1520 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001521
1522
1523 def set_serial(self, hex_str):
1524 """
1525 Set the serial number of a revoked Revoked structure
1526
1527 :param hex_str: The new serial number.
1528 :type hex_str: :py:data:`str`
1529 :return: None
1530 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001531 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1532 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001533 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001534 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001535 if not bn_result:
1536 raise ValueError("bad hex string")
1537
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001538 asn1_serial = _ffi.gc(
1539 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1540 _lib.ASN1_INTEGER_free)
1541 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001542
1543
1544 def get_serial(self):
1545 """
1546 Return the serial number of a Revoked structure
1547
1548 :return: The serial number as a string
1549 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001550 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001551
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001552 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001553 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001554 # TODO: This is untested.
1555 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001556
1557 return _bio_to_string(bio)
1558
1559
1560 def _delete_reason(self):
1561 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001562 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1563 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1564 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1565 _lib.X509_EXTENSION_free(ext)
1566 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001567 break
1568
1569
1570 def set_reason(self, reason):
1571 """
1572 Set the reason of a Revoked object.
1573
1574 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1575
1576 :param reason: The reason string.
1577 :type reason: :py:class:`str` or :py:class:`NoneType`
1578 :return: None
1579 """
1580 if reason is None:
1581 self._delete_reason()
1582 elif not isinstance(reason, bytes):
1583 raise TypeError("reason must be None or a byte string")
1584 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001585 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001586 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1587
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001588 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1589 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001590 # TODO: This is untested.
1591 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001592 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001593
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001594 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1595 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001596 # TODO: This is untested.
1597 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001598
1599 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001600 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1601 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001602
1603 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001604 # TODO: This is untested.
1605 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001606
1607
1608 def get_reason(self):
1609 """
1610 Return the reason of a Revoked object.
1611
1612 :return: The reason as a string
1613 """
1614 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001615 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1616 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1617 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001618 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001619
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001620 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001621 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001622 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001623 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001624 # TODO: This is untested.
1625 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001626
1627 return _bio_to_string(bio)
1628
1629
1630 def all_reasons(self):
1631 """
1632 Return a list of all the supported reason strings.
1633
1634 :return: A list of reason strings.
1635 """
1636 return self._crl_reasons[:]
1637
1638
1639 def set_rev_date(self, when):
1640 """
1641 Set the revocation timestamp
1642
1643 :param when: A string giving the timestamp, in the format:
1644
1645 YYYYMMDDhhmmssZ
1646 YYYYMMDDhhmmss+hhmm
1647 YYYYMMDDhhmmss-hhmm
1648
1649 :return: None
1650 """
1651 return _set_asn1_time(self._revoked.revocationDate, when)
1652
1653
1654 def get_rev_date(self):
1655 """
1656 Retrieve the revocation date
1657
1658 :return: A string giving the timestamp, in the format:
1659
1660 YYYYMMDDhhmmssZ
1661 YYYYMMDDhhmmss+hhmm
1662 YYYYMMDDhhmmss-hhmm
1663 """
1664 return _get_asn1_time(self._revoked.revocationDate)
1665
1666
1667
1668class CRL(object):
1669 def __init__(self):
1670 """
1671 Create a new empty CRL object.
1672 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001673 crl = _lib.X509_CRL_new()
1674 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001675
1676
1677 def get_revoked(self):
1678 """
1679 Return revoked portion of the CRL structure (by value not reference).
1680
1681 :return: A tuple of Revoked objects.
1682 """
1683 results = []
1684 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001685 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1686 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001687 revoked_copy = _X509_REVOKED_dup(revoked)
1688 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001689 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001690 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001691 if results:
1692 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001693
1694
1695 def add_revoked(self, revoked):
1696 """
1697 Add a revoked (by value not reference) to the CRL structure
1698
1699 :param revoked: The new revoked.
1700 :type revoked: :class:`X509`
1701
1702 :return: None
1703 """
1704 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001705 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001706 # TODO: This is untested.
1707 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001708
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001709 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001710 if add_result == 0:
1711 # TODO: This is untested.
1712 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001713
1714
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001715 def export(self, cert, key, type=FILETYPE_PEM, days=100,
1716 digest=_undefined):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001717 """
1718 export a CRL as a string
1719
1720 :param cert: Used to sign CRL.
1721 :type cert: :class:`X509`
1722
1723 :param key: Used to sign CRL.
1724 :type key: :class:`PKey`
1725
1726 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1727
1728 :param days: The number of days until the next update of this CRL.
1729 :type days: :py:data:`int`
Bulat Gaifullin5f9eea42014-09-23 19:35:15 +04001730
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001731 :param digest: The message digest to use (eg ``"sha1"``).
Bulat Gaifullin5f9eea42014-09-23 19:35:15 +04001732 :type digest: :py:data:`str`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001733
1734 :return: :py:data:`str`
1735 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001736 if not isinstance(cert, X509):
1737 raise TypeError("cert must be an X509 instance")
1738 if not isinstance(key, PKey):
1739 raise TypeError("key must be a PKey instance")
1740 if not isinstance(type, int):
1741 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001742
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001743 if digest is _undefined:
1744 _warn(
1745 "The default message digest (md5) is deprecated. "
1746 "Pass the name of a message digest explicitly.",
1747 category=DeprecationWarning,
1748 stacklevel=2,
1749 )
1750 digest = "md5"
1751
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04001752 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
1753 if digest_obj == _ffi.NULL:
1754 raise ValueError("No such digest method")
1755
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001756 bio = _lib.BIO_new(_lib.BIO_s_mem())
1757 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001758 # TODO: This is untested.
1759 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001760
1761 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001762 sometime = _lib.ASN1_TIME_new()
1763 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001764 # TODO: This is untested.
1765 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001766
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001767 _lib.X509_gmtime_adj(sometime, 0)
1768 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001769
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001770 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1771 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001772
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001773 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001774
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04001775 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, digest_obj)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001776 if not sign_result:
1777 _raise_current_error()
1778
1779 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001780 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001781 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001782 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001783 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001784 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001785 else:
1786 raise ValueError(
1787 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1788
1789 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001790 # TODO: This is untested.
1791 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001792
1793 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001794CRLType = CRL
1795
1796
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001797
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001798class PKCS7(object):
1799 def type_is_signed(self):
1800 """
1801 Check if this NID_pkcs7_signed object
1802
1803 :return: True if the PKCS7 is of type signed
1804 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001805 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001806 return True
1807 return False
1808
1809
1810 def type_is_enveloped(self):
1811 """
1812 Check if this NID_pkcs7_enveloped object
1813
1814 :returns: True if the PKCS7 is of type enveloped
1815 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001816 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001817 return True
1818 return False
1819
1820
1821 def type_is_signedAndEnveloped(self):
1822 """
1823 Check if this NID_pkcs7_signedAndEnveloped object
1824
1825 :returns: True if the PKCS7 is of type signedAndEnveloped
1826 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001827 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001828 return True
1829 return False
1830
1831
1832 def type_is_data(self):
1833 """
1834 Check if this NID_pkcs7_data object
1835
1836 :return: True if the PKCS7 is of type data
1837 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001838 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001839 return True
1840 return False
1841
1842
1843 def get_type_name(self):
1844 """
1845 Returns the type name of the PKCS7 structure
1846
1847 :return: A string with the typename
1848 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001849 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1850 string_type = _lib.OBJ_nid2sn(nid)
1851 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001852
1853PKCS7Type = PKCS7
1854
1855
1856
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001857class PKCS12(object):
1858 def __init__(self):
1859 self._pkey = None
1860 self._cert = None
1861 self._cacerts = None
1862 self._friendlyname = None
1863
1864
1865 def get_certificate(self):
1866 """
1867 Return certificate portion of the PKCS12 structure
1868
1869 :return: X509 object containing the certificate
1870 """
1871 return self._cert
1872
1873
1874 def set_certificate(self, cert):
1875 """
1876 Replace the certificate portion of the PKCS12 structure
1877
1878 :param cert: The new certificate.
1879 :type cert: :py:class:`X509` or :py:data:`None`
1880 :return: None
1881 """
1882 if not isinstance(cert, X509):
1883 raise TypeError("cert must be an X509 instance")
1884 self._cert = cert
1885
1886
1887 def get_privatekey(self):
1888 """
1889 Return private key portion of the PKCS12 structure
1890
1891 :returns: PKey object containing the private key
1892 """
1893 return self._pkey
1894
1895
1896 def set_privatekey(self, pkey):
1897 """
1898 Replace or set the certificate portion of the PKCS12 structure
1899
1900 :param pkey: The new private key.
1901 :type pkey: :py:class:`PKey`
1902 :return: None
1903 """
1904 if not isinstance(pkey, PKey):
1905 raise TypeError("pkey must be a PKey instance")
1906 self._pkey = pkey
1907
1908
1909 def get_ca_certificates(self):
1910 """
1911 Return CA certificates within of the PKCS12 object
1912
1913 :return: A newly created tuple containing the CA certificates in the chain,
1914 if any are present, or None if no CA certificates are present.
1915 """
1916 if self._cacerts is not None:
1917 return tuple(self._cacerts)
1918
1919
1920 def set_ca_certificates(self, cacerts):
1921 """
1922 Replace or set the CA certificates withing the PKCS12 object.
1923
1924 :param cacerts: The new CA certificates.
1925 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1926 :return: None
1927 """
1928 if cacerts is None:
1929 self._cacerts = None
1930 else:
1931 cacerts = list(cacerts)
1932 for cert in cacerts:
1933 if not isinstance(cert, X509):
1934 raise TypeError("iterable must only contain X509 instances")
1935 self._cacerts = cacerts
1936
1937
1938 def set_friendlyname(self, name):
1939 """
1940 Replace or set the certificate portion of the PKCS12 structure
1941
1942 :param name: The new friendly name.
1943 :type name: :py:class:`bytes`
1944 :return: None
1945 """
1946 if name is None:
1947 self._friendlyname = None
1948 elif not isinstance(name, bytes):
1949 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1950 self._friendlyname = name
1951
1952
1953 def get_friendlyname(self):
1954 """
1955 Return friendly name portion of the PKCS12 structure
1956
1957 :returns: String containing the friendlyname
1958 """
1959 return self._friendlyname
1960
1961
1962 def export(self, passphrase=None, iter=2048, maciter=1):
1963 """
1964 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1965
1966 :param passphrase: used to encrypt the PKCS12
1967 :type passphrase: :py:data:`bytes`
1968
1969 :param iter: How many times to repeat the encryption
1970 :type iter: :py:data:`int`
1971
1972 :param maciter: How many times to repeat the MAC
1973 :type maciter: :py:data:`int`
1974
1975 :return: The string containing the PKCS12
1976 """
1977 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001978 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001979 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001980 cacerts = _lib.sk_X509_new_null()
1981 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001982 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001983 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001984
1985 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001986 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001987
1988 friendlyname = self._friendlyname
1989 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001990 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001991
1992 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001993 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001994 else:
1995 pkey = self._pkey._pkey
1996
1997 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001998 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001999 else:
2000 cert = self._cert._x509
2001
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002002 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002003 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002004 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2005 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002006 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002007 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002008 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002009 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002010
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002011 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002012 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002013 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002014
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002015PKCS12Type = PKCS12
2016
2017
2018
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002019class NetscapeSPKI(object):
2020 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002021 spki = _lib.NETSCAPE_SPKI_new()
2022 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002023
2024
2025 def sign(self, pkey, digest):
2026 """
2027 Sign the certificate request using the supplied key and digest
2028
2029 :param pkey: The key to sign with
2030 :param digest: The message digest to use
2031 :return: None
2032 """
2033 if pkey._only_public:
2034 raise ValueError("Key has only public part")
2035
2036 if not pkey._initialized:
2037 raise ValueError("Key is uninitialized")
2038
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002039 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002040 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002041 raise ValueError("No such digest method")
2042
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002043 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002044 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002045 # TODO: This is untested.
2046 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002047
2048
2049 def verify(self, key):
2050 """
2051 Verifies a certificate request using the supplied public key
2052
2053 :param key: a public key
2054 :return: True if the signature is correct.
2055 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2056 problem verifying the signature.
2057 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002058 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002059 if answer <= 0:
2060 _raise_current_error()
2061 return True
2062
2063
2064 def b64_encode(self):
2065 """
2066 Generate a base64 encoded string from an SPKI
2067
2068 :return: The base64 encoded string
2069 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002070 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2071 result = _ffi.string(encoded)
2072 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002073 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002074
2075
2076 def get_pubkey(self):
2077 """
2078 Get the public key of the certificate
2079
2080 :return: The public key
2081 """
2082 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002083 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2084 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002085 # TODO: This is untested.
2086 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002087 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002088 pkey._only_public = True
2089 return pkey
2090
2091
2092 def set_pubkey(self, pkey):
2093 """
2094 Set the public key of the certificate
2095
2096 :param pkey: The public key
2097 :return: None
2098 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002099 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002100 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002101 # TODO: This is untested.
2102 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002103NetscapeSPKIType = NetscapeSPKI
2104
2105
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002106class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002107 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002108 if type != FILETYPE_PEM and passphrase is not None:
2109 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002110 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002111 self._more_args = more_args
2112 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002113 self._problems = []
2114
2115
2116 @property
2117 def callback(self):
2118 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002119 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002120 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002121 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002122 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002123 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002124 else:
2125 raise TypeError("Last argument must be string or callable")
2126
2127
2128 @property
2129 def callback_args(self):
2130 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002131 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002132 elif isinstance(self._passphrase, bytes):
2133 return self._passphrase
2134 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002135 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002136 else:
2137 raise TypeError("Last argument must be string or callable")
2138
2139
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002140 def raise_if_problem(self, exceptionType=Error):
2141 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002142 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002143 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002144 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002145 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002146 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002147 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002148
2149
2150 def _read_passphrase(self, buf, size, rwflag, userdata):
2151 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002152 if self._more_args:
2153 result = self._passphrase(size, rwflag, userdata)
2154 else:
2155 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002156 if not isinstance(result, bytes):
2157 raise ValueError("String expected")
2158 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002159 if self._truncate:
2160 result = result[:size]
2161 else:
2162 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002163 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002164 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002165 return len(result)
2166 except Exception as e:
2167 self._problems.append(e)
2168 return 0
2169
2170
2171
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002172def load_privatekey(type, buffer, passphrase=None):
2173 """
2174 Load a private key from a buffer
2175
2176 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2177 :param buffer: The buffer the key is stored in
2178 :param passphrase: (optional) if encrypted PEM format, this can be
2179 either the passphrase to use, or a callback for
2180 providing the passphrase.
2181
2182 :return: The PKey object
2183 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002184 if isinstance(buffer, _text_type):
2185 buffer = buffer.encode("ascii")
2186
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002187 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002188
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002189 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002190 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002191 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2192 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002193 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002194 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002195 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002196 else:
2197 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2198
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002199 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002200 _raise_current_error()
2201
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002202 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002203 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002204 return pkey
2205
2206
2207
2208def dump_certificate_request(type, req):
2209 """
2210 Dump a certificate request to a buffer
2211
2212 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2213 :param req: The certificate request to dump
2214 :return: The buffer with the dumped certificate request in
2215 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002216 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002217
2218 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002219 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002220 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002221 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002222 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002223 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002224 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002225 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002226
2227 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002228 # TODO: This is untested.
2229 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002230
2231 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002232
2233
2234
2235def load_certificate_request(type, buffer):
2236 """
2237 Load a certificate request from a buffer
2238
2239 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2240 :param buffer: The buffer the certificate request is stored in
2241 :return: The X509Req object
2242 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002243 if isinstance(buffer, _text_type):
2244 buffer = buffer.encode("ascii")
2245
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002246 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002247
2248 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002249 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002250 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002251 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002252 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002253 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002254
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002255 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002256 # TODO: This is untested.
2257 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002258
2259 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002260 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002261 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002262
2263
2264
2265def sign(pkey, data, digest):
2266 """
2267 Sign data with a digest
2268
2269 :param pkey: Pkey to sign with
2270 :param data: data to be signed
2271 :param digest: message digest to use
2272 :return: signature
2273 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002274 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002275 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002276 raise ValueError("No such digest method")
2277
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002278 md_ctx = _ffi.new("EVP_MD_CTX*")
2279 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002280
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002281 _lib.EVP_SignInit(md_ctx, digest_obj)
2282 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002283
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002284 signature_buffer = _ffi.new("unsigned char[]", 512)
2285 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002286 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002287 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002288 md_ctx, signature_buffer, signature_length, pkey._pkey)
2289
2290 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002291 # TODO: This is untested.
2292 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002293
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002294 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002295
2296
2297
2298def verify(cert, signature, data, digest):
2299 """
2300 Verify a signature
2301
2302 :param cert: signing certificate (X509 object)
2303 :param signature: signature returned by sign function
2304 :param data: data to be verified
2305 :param digest: message digest to use
2306 :return: None if the signature is correct, raise exception otherwise
2307 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002308 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002309 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002310 raise ValueError("No such digest method")
2311
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002312 pkey = _lib.X509_get_pubkey(cert._x509)
2313 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002314 # TODO: This is untested.
2315 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002316 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002317
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002318 md_ctx = _ffi.new("EVP_MD_CTX*")
2319 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002320
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002321 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2322 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2323 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002324
2325 if verify_result != 1:
2326 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002327
2328
2329
2330def load_crl(type, buffer):
2331 """
2332 Load a certificate revocation list from a buffer
2333
2334 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2335 :param buffer: The buffer the CRL is stored in
2336
2337 :return: The PKey object
2338 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002339 if isinstance(buffer, _text_type):
2340 buffer = buffer.encode("ascii")
2341
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002342 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002343
2344 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002345 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002346 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002347 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002348 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002349 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2350
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002351 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002352 _raise_current_error()
2353
2354 result = CRL.__new__(CRL)
2355 result._crl = crl
2356 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002357
2358
2359
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002360def load_pkcs7_data(type, buffer):
2361 """
2362 Load pkcs7 data from a buffer
2363
2364 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2365 :param buffer: The buffer with the pkcs7 data.
2366 :return: The PKCS7 object
2367 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002368 if isinstance(buffer, _text_type):
2369 buffer = buffer.encode("ascii")
2370
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002371 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002372
2373 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002374 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002375 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002376 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002377 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002378 # TODO: This is untested.
2379 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002380 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2381
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002382 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002383 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002384
2385 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002386 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002387 return pypkcs7
2388
2389
2390
Stephen Holsapple38482622014-04-05 20:29:34 -07002391def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002392 """
2393 Load a PKCS12 object from a buffer
2394
2395 :param buffer: The buffer the certificate is stored in
2396 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2397 :returns: The PKCS12 object
2398 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002399 if isinstance(buffer, _text_type):
2400 buffer = buffer.encode("ascii")
2401
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002402 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002403
Stephen Holsapple38482622014-04-05 20:29:34 -07002404 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2405 # password based encryption no password and a zero length password are two
2406 # different things, but OpenSSL implementation will try both to figure out
2407 # which one works.
2408 if not passphrase:
2409 passphrase = _ffi.NULL
2410
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002411 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2412 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002413 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002414 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002415
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002416 pkey = _ffi.new("EVP_PKEY**")
2417 cert = _ffi.new("X509**")
2418 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002419
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002420 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002421 if not parse_result:
2422 _raise_current_error()
2423
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002424 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002425
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002426 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2427 # queue for no particular reason. This error isn't interesting to anyone
2428 # outside this function. It's not even interesting to us. Get rid of it.
2429 try:
2430 _raise_current_error()
2431 except Error:
2432 pass
2433
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002434 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002435 pykey = None
2436 else:
2437 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002438 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002439
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002440 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002441 pycert = None
2442 friendlyname = None
2443 else:
2444 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002445 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002446
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002447 friendlyname_length = _ffi.new("int*")
2448 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2449 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2450 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002451 friendlyname = None
2452
2453 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002454 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002455 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002456 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002457 pycacerts.append(pycacert)
2458 if not pycacerts:
2459 pycacerts = None
2460
2461 pkcs12 = PKCS12.__new__(PKCS12)
2462 pkcs12._pkey = pykey
2463 pkcs12._cert = pycert
2464 pkcs12._cacerts = pycacerts
2465 pkcs12._friendlyname = friendlyname
2466 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002467
2468
2469def _initialize_openssl_threads(get_ident, Lock):
2470 import _ssl
2471 return
2472
2473 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2474
2475 def locking_function(mode, index, filename, line):
2476 if mode & _lib.CRYPTO_LOCK:
2477 locks[index].acquire()
2478 else:
2479 locks[index].release()
2480
2481 _lib.CRYPTO_set_id_callback(
2482 _ffi.callback("unsigned long (*)(void)", get_ident))
2483
2484 _lib.CRYPTO_set_locking_callback(
2485 _ffi.callback(
2486 "void (*)(int, int, const char*, int)", locking_function))
2487
2488
2489try:
2490 from thread import get_ident
2491 from threading import Lock
2492except ImportError:
2493 pass
2494else:
2495 _initialize_openssl_threads(get_ident, Lock)
2496 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002497
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002498# There are no direct unit tests for this initialization. It is tested
2499# indirectly since it is necessary for functions like dump_privatekey when
2500# using encryption.
2501#
2502# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2503# and some other similar tests may fail without this (though they may not if
2504# the Python runtime has already done some initialization of the underlying
2505# OpenSSL library (and is linked against the same one that cryptography is
2506# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002507_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002508
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002509# This is similar but exercised mainly by exception_from_error_queue. It calls
2510# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2511_lib.SSL_load_error_strings()