blob: 0e99576f8f20e766b001e7a520b4f4b140350ec3 [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05003from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
5
6from six import (
7 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -04008 text_type as _text_type,
9 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080010
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050011from OpenSSL._util import (
12 ffi as _ffi,
13 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050014 exception_from_error_queue as _exception_from_error_queue,
15 byte_string as _byte_string,
16 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080017
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050018FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
19FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080020
21# TODO This was an API mistake. OpenSSL has no such constant.
22FILETYPE_TEXT = 2 ** 16 - 1
23
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050024TYPE_RSA = _lib.EVP_PKEY_RSA
25TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080026
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080027
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070028
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050029class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050030 """
31 An error occurred in an `OpenSSL.crypto` API.
32 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050033
34
35_raise_current_error = partial(_exception_from_error_queue, Error)
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070036
37
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050038
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050039def _untested_error(where):
40 """
41 An OpenSSL API failed somehow. Additionally, the failure which was
42 encountered isn't one that's exercised by the test suite so future behavior
43 of pyOpenSSL is now somewhat less predictable.
44 """
45 raise RuntimeError("Unknown %s failure" % (where,))
46
47
48
49def _new_mem_buf(buffer=None):
50 """
51 Allocate a new OpenSSL memory BIO.
52
53 Arrange for the garbage collector to clean it up automatically.
54
55 :param buffer: None or some bytes to use to put into the BIO so that they
56 can be read out.
57 """
58 if buffer is None:
59 bio = _lib.BIO_new(_lib.BIO_s_mem())
60 free = _lib.BIO_free
61 else:
62 data = _ffi.new("char[]", buffer)
63 bio = _lib.BIO_new_mem_buf(data, len(buffer))
64 # Keep the memory alive as long as the bio is alive!
65 def free(bio, ref=data):
66 return _lib.BIO_free(bio)
67
68 if bio == _ffi.NULL:
69 # TODO: This is untested.
70 _raise_current_error()
71
72 bio = _ffi.gc(bio, free)
73 return bio
74
75
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050076
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080077def _bio_to_string(bio):
78 """
79 Copy the contents of an OpenSSL BIO object into a Python byte string.
80 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050081 result_buffer = _ffi.new('char**')
82 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
83 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080084
85
86
Jean-Paul Calderone57122982013-02-21 08:47:05 -080087def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050088 """
89 The the time value of an ASN1 time object.
90
91 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
92 castable to that type) which will have its value set.
93 @param when: A string representation of the desired time value.
94
95 @raise TypeError: If C{when} is not a L{bytes} string.
96 @raise ValueError: If C{when} does not represent a time in the required
97 format.
98 @raise RuntimeError: If the time value cannot be set for some other
99 (unspecified) reason.
100 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800101 if not isinstance(when, bytes):
102 raise TypeError("when must be a byte string")
103
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500104 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
105 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800106 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500107 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
108 _lib.ASN1_STRING_set(dummy, when, len(when))
109 check_result = _lib.ASN1_GENERALIZEDTIME_check(
110 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800111 if not check_result:
112 raise ValueError("Invalid string")
113 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500114 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800115
116
117
118def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500119 """
120 Retrieve the time value of an ASN1 time object.
121
122 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
123 that type) from which the time value will be retrieved.
124
125 @return: The time value from C{timestamp} as a L{bytes} string in a certain
126 format. Or C{None} if the object contains no time value.
127 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500128 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
129 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800130 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500131 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
132 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800133 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500134 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
135 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
136 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500137 # This may happen:
138 # - if timestamp was not an ASN1_TIME
139 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
140 # - if a copy of the time data from timestamp cannot be made for
141 # the newly allocated ASN1_GENERALIZEDTIME
142 #
143 # These are difficult to test. cffi enforces the ASN1_TIME type.
144 # Memory allocation failures are a pain to trigger
145 # deterministically.
146 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800147 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500148 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800149 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500150 string_data = _lib.ASN1_STRING_data(string_timestamp)
151 string_result = _ffi.string(string_data)
152 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800153 return string_result
154
155
156
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800157class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800158 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800159 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800160
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800161 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500162 pkey = _lib.EVP_PKEY_new()
163 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800164 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800165
166
167 def generate_key(self, type, bits):
168 """
169 Generate a key of a given type, with a given number of a bits
170
171 :param type: The key type (TYPE_RSA or TYPE_DSA)
172 :param bits: The number of bits
173
174 :return: None
175 """
176 if not isinstance(type, int):
177 raise TypeError("type must be an integer")
178
179 if not isinstance(bits, int):
180 raise TypeError("bits must be an integer")
181
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800182 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500183 exponent = _lib.BN_new()
184 exponent = _ffi.gc(exponent, _lib.BN_free)
185 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800186
187 if type == TYPE_RSA:
188 if bits <= 0:
189 raise ValueError("Invalid number of bits")
190
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500191 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800192
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500193 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500194 if result == 0:
195 # TODO: The test for this case is commented out. Different
196 # builds of OpenSSL appear to have different failure modes that
197 # make it hard to test. Visual inspection of the OpenSSL
198 # source reveals that a return value of 0 signals an error.
199 # Manual testing on a particular build of OpenSSL suggests that
200 # this is probably the appropriate way to handle those errors.
201 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800202
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500203 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800204 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500205 # TODO: It appears as though this can fail if an engine is in
206 # use which does not support RSA.
207 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800208
209 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500210 dsa = _lib.DSA_generate_parameters(
211 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
212 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500213 # TODO: This is untested.
214 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500215 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500216 # TODO: This is untested.
217 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500218 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500219 # TODO: This is untested.
220 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800221 else:
222 raise Error("No such key type")
223
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800224 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800225
226
227 def check(self):
228 """
229 Check the consistency of an RSA private key.
230
231 :return: True if key is consistent.
232 :raise Error: if the key is inconsistent.
233 :raise TypeError: if the key is of a type which cannot be checked.
234 Only RSA keys can currently be checked.
235 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800236 if self._only_public:
237 raise TypeError("public key only")
238
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500239 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800240 raise TypeError("key type unsupported")
241
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500242 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
243 rsa = _ffi.gc(rsa, _lib.RSA_free)
244 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800245 if result:
246 return True
247 _raise_current_error()
248
249
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800250 def type(self):
251 """
252 Returns the type of the key
253
254 :return: The type of the key.
255 """
256 return self._pkey.type
257
258
259 def bits(self):
260 """
261 Returns the number of bits of the key
262
263 :return: The number of bits of the key.
264 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500265 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800266PKeyType = PKey
267
268
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800269
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400270class _EllipticCurve(object):
271 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400272 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400273
274 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
275 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
276 instances each of which represents one curve supported by the system.
277 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400278 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400279 _curves = None
280
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400281 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400282 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400283 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400284 """
285 Implement cooperation with the right-hand side argument of ``!=``.
286
287 Python 3 seems to have dropped this cooperation in this very narrow
288 circumstance.
289 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400290 if isinstance(other, _EllipticCurve):
291 return super(_EllipticCurve, self).__ne__(other)
292 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400293
294
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400295 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400296 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400297 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400298 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400299
300 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400301
302 :return: A :py:type:`set` of ``cls`` instances giving the names of the
303 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400304 """
305 if lib.Cryptography_HAS_EC:
306 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
307 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
308 # The return value on this call should be num_curves again. We could
309 # check it to make sure but if it *isn't* then.. what could we do?
310 # Abort the whole process, I suppose...? -exarkun
311 lib.EC_get_builtin_curves(builtin_curves, num_curves)
312 return set(
313 cls.from_nid(lib, c.nid)
314 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400315 return set()
316
317
318 @classmethod
319 def _get_elliptic_curves(cls, lib):
320 """
321 Get, cache, and return the curves supported by OpenSSL.
322
323 :param lib: The OpenSSL library binding object.
324
325 :return: A :py:type:`set` of ``cls`` instances giving the names of the
326 elliptic curves the underlying library supports.
327 """
328 if cls._curves is None:
329 cls._curves = cls._load_elliptic_curves(lib)
330 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400331
332
333 @classmethod
334 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400335 """
336 Instantiate a new :py:class:`_EllipticCurve` associated with the given
337 OpenSSL NID.
338
339 :param lib: The OpenSSL library binding object.
340
341 :param nid: The OpenSSL NID the resulting curve object will represent.
342 This must be a curve NID (and not, for example, a hash NID) or
343 subsequent operations will fail in unpredictable ways.
344 :type nid: :py:class:`int`
345
346 :return: The curve object.
347 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400348 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
349
350
351 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400352 """
353 :param _lib: The :py:mod:`cryptography` binding instance used to
354 interface with OpenSSL.
355
356 :param _nid: The OpenSSL NID identifying the curve this object
357 represents.
358 :type _nid: :py:class:`int`
359
360 :param name: The OpenSSL short name identifying the curve this object
361 represents.
362 :type name: :py:class:`unicode`
363 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400364 self._lib = lib
365 self._nid = nid
366 self.name = name
367
368
369 def __repr__(self):
370 return "<Curve %r>" % (self.name,)
371
372
373 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400374 """
375 Create a new OpenSSL EC_KEY structure initialized to use this curve.
376
377 The structure is automatically garbage collected when the Python object
378 is garbage collected.
379 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400380 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
381 return _ffi.gc(key, _lib.EC_KEY_free)
382
383
384
385def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400386 """
387 Return a set of objects representing the elliptic curves supported in the
388 OpenSSL build in use.
389
390 The curve objects have a :py:class:`unicode` ``name`` attribute by which
391 they identify themselves.
392
393 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400394 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
395 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400396 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400397 return _EllipticCurve._get_elliptic_curves(_lib)
398
399
400
401def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400402 """
403 Return a single curve object selected by name.
404
405 See :py:func:`get_elliptic_curves` for information about curve objects.
406
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400407 :param name: The OpenSSL short name identifying the curve object to
408 retrieve.
409 :type name: :py:class:`unicode`
410
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400411 If the named curve is not supported then :py:class:`ValueError` is raised.
412 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400413 for curve in get_elliptic_curves():
414 if curve.name == name:
415 return curve
416 raise ValueError("unknown curve name", name)
417
418
419
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800420class X509Name(object):
421 def __init__(self, name):
422 """
423 Create a new X509Name, copying the given X509Name instance.
424
425 :param name: An X509Name object to copy
426 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500427 name = _lib.X509_NAME_dup(name._name)
428 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800429
430
431 def __setattr__(self, name, value):
432 if name.startswith('_'):
433 return super(X509Name, self).__setattr__(name, value)
434
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800435 # Note: we really do not want str subclasses here, so we do not use
436 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800437 if type(name) is not str:
438 raise TypeError("attribute name must be string, not '%.200s'" % (
439 type(value).__name__,))
440
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500441 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500442 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800443 try:
444 _raise_current_error()
445 except Error:
446 pass
447 raise AttributeError("No such attribute")
448
449 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500450 for i in range(_lib.X509_NAME_entry_count(self._name)):
451 ent = _lib.X509_NAME_get_entry(self._name, i)
452 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
453 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800454 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500455 ent = _lib.X509_NAME_delete_entry(self._name, i)
456 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800457 break
458
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500459 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800460 value = value.encode('utf-8')
461
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500462 add_result = _lib.X509_NAME_add_entry_by_NID(
463 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800464 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500465 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800466
467
468 def __getattr__(self, name):
469 """
470 Find attribute. An X509Name object has the following attributes:
471 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
472 organization (alias O), organizationalUnit (alias OU), commonName (alias
473 CN) and more...
474 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500475 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500476 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800477 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
478 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
479 # push something onto the error queue. If we don't clean that up
480 # now, someone else will bump into it later and be quite confused.
481 # See lp#314814.
482 try:
483 _raise_current_error()
484 except Error:
485 pass
486 return super(X509Name, self).__getattr__(name)
487
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500488 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800489 if entry_index == -1:
490 return None
491
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500492 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
493 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800494
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500495 result_buffer = _ffi.new("unsigned char**")
496 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800497 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500498 # TODO: This is untested.
499 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800500
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700501 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500502 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700503 finally:
504 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500505 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800506 return result
507
508
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500509 def _cmp(op):
510 def f(self, other):
511 if not isinstance(other, X509Name):
512 return NotImplemented
513 result = _lib.X509_NAME_cmp(self._name, other._name)
514 return op(result, 0)
515 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800516
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500517 __eq__ = _cmp(__eq__)
518 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800519
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500520 __lt__ = _cmp(__lt__)
521 __le__ = _cmp(__le__)
522
523 __gt__ = _cmp(__gt__)
524 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800525
526 def __repr__(self):
527 """
528 String representation of an X509Name
529 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500530 result_buffer = _ffi.new("char[]", 512);
531 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800532 self._name, result_buffer, len(result_buffer))
533
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500534 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500535 # TODO: This is untested.
536 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800537
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500538 return "<X509Name object '%s'>" % (
539 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800540
541
542 def hash(self):
543 """
544 Return the hash value of this name
545
546 :return: None
547 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500548 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800549
550
551 def der(self):
552 """
553 Return the DER encoding of this name
554
555 :return: A :py:class:`bytes` instance giving the DER encoded form of
556 this name.
557 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500558 result_buffer = _ffi.new('unsigned char**')
559 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800560 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500561 # TODO: This is untested.
562 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800563
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500564 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
565 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800566 return string_result
567
568
569 def get_components(self):
570 """
571 Returns the split-up components of this name.
572
573 :return: List of tuples (name, value).
574 """
575 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500576 for i in range(_lib.X509_NAME_entry_count(self._name)):
577 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800578
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500579 fname = _lib.X509_NAME_ENTRY_get_object(ent)
580 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800581
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500582 nid = _lib.OBJ_obj2nid(fname)
583 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800584
585 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500586 _ffi.string(name),
587 _ffi.string(
588 _lib.ASN1_STRING_data(fval),
589 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800590
591 return result
592X509NameType = X509Name
593
594
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800595class X509Extension(object):
596 def __init__(self, type_name, critical, value, subject=None, issuer=None):
597 """
598 :param typename: The name of the extension to create.
599 :type typename: :py:data:`str`
600
601 :param critical: A flag indicating whether this is a critical extension.
602
603 :param value: The value of the extension.
604 :type value: :py:data:`str`
605
606 :param subject: Optional X509 cert to use as subject.
607 :type subject: :py:class:`X509`
608
609 :param issuer: Optional X509 cert to use as issuer.
610 :type issuer: :py:class:`X509`
611
612 :return: The X509Extension object
613 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500614 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800615
616 # A context is necessary for any extension which uses the r2i conversion
617 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
618 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500619 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800620
621 # We have no configuration database - but perhaps we should (some
622 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500623 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800624
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800625 # Initialize the subject and issuer, if appropriate. ctx is a local,
626 # and as far as I can tell none of the X509V3_* APIs invoked here steal
627 # any references, so no need to mess with reference counts or duplicates.
628 if issuer is not None:
629 if not isinstance(issuer, X509):
630 raise TypeError("issuer must be an X509 instance")
631 ctx.issuer_cert = issuer._x509
632 if subject is not None:
633 if not isinstance(subject, X509):
634 raise TypeError("subject must be an X509 instance")
635 ctx.subject_cert = subject._x509
636
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800637 if critical:
638 # There are other OpenSSL APIs which would let us pass in critical
639 # separately, but they're harder to use, and since value is already
640 # a pile of crappy junk smuggling a ton of utterly important
641 # structured data, what's the point of trying to avoid nasty stuff
642 # with strings? (However, X509V3_EXT_i2d in particular seems like it
643 # would be a better API to invoke. I do not know where to get the
644 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500645 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800646
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500647 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
648 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800649 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500650 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800651
652
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400653 @property
654 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500655 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400656
657 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500658 _lib.GEN_EMAIL: "email",
659 _lib.GEN_DNS: "DNS",
660 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400661 }
662
663 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500664 method = _lib.X509V3_EXT_get(self._extension)
665 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500666 # TODO: This is untested.
667 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400668 payload = self._extension.value.data
669 length = self._extension.value.length
670
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500671 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400672 payloadptr[0] = payload
673
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500674 if method.it != _ffi.NULL:
675 ptr = _lib.ASN1_ITEM_ptr(method.it)
676 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
677 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400678 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500679 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400680 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500681 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400682
683 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500684 for i in range(_lib.sk_GENERAL_NAME_num(names)):
685 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400686 try:
687 label = self._prefixes[name.type]
688 except KeyError:
689 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500690 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500691 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400692 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500693 value = _native(
694 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
695 parts.append(label + ":" + value)
696 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400697
698
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800699 def __str__(self):
700 """
701 :return: a nice text representation of the extension
702 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500703 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400704 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800705
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400706 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500707 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800708 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500709 # TODO: This is untested.
710 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800711
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500712 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800713
714
715 def get_critical(self):
716 """
717 Returns the critical field of the X509Extension
718
719 :return: The critical field.
720 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500721 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800722
723
724 def get_short_name(self):
725 """
726 Returns the short version of the type name of the X509Extension
727
728 :return: The short type name.
729 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500730 obj = _lib.X509_EXTENSION_get_object(self._extension)
731 nid = _lib.OBJ_obj2nid(obj)
732 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800733
734
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800735 def get_data(self):
736 """
737 Returns the data of the X509Extension
738
739 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
740 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500741 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
742 string_result = _ffi.cast('ASN1_STRING*', octet_result)
743 char_result = _lib.ASN1_STRING_data(string_result)
744 result_length = _lib.ASN1_STRING_length(string_result)
745 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800746
747X509ExtensionType = X509Extension
748
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800749
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800750class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800751 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500752 req = _lib.X509_REQ_new()
753 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800754
755
756 def set_pubkey(self, pkey):
757 """
758 Set the public key of the certificate request
759
760 :param pkey: The public key to use
761 :return: None
762 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500763 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800764 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500765 # TODO: This is untested.
766 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800767
768
769 def get_pubkey(self):
770 """
771 Get the public key from the certificate request
772
773 :return: The public key
774 """
775 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500776 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
777 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500778 # TODO: This is untested.
779 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500780 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800781 pkey._only_public = True
782 return pkey
783
784
785 def set_version(self, version):
786 """
787 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
788 request.
789
790 :param version: The version number
791 :return: None
792 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500793 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800794 if not set_result:
795 _raise_current_error()
796
797
798 def get_version(self):
799 """
800 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
801 request.
802
803 :return: an integer giving the value of the version subfield
804 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500805 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800806
807
808 def get_subject(self):
809 """
810 Create an X509Name object for the subject of the certificate request
811
812 :return: An X509Name object
813 """
814 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500815 name._name = _lib.X509_REQ_get_subject_name(self._req)
816 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500817 # TODO: This is untested.
818 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800819
820 # The name is owned by the X509Req structure. As long as the X509Name
821 # Python object is alive, keep the X509Req Python object alive.
822 name._owner = self
823
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800824 return name
825
826
827 def add_extensions(self, extensions):
828 """
829 Add extensions to the request.
830
831 :param extensions: a sequence of X509Extension objects
832 :return: None
833 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500834 stack = _lib.sk_X509_EXTENSION_new_null()
835 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500836 # TODO: This is untested.
837 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800838
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500839 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800840
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800841 for ext in extensions:
842 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800843 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800844
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800845 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500846 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800847
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500848 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800849 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500850 # TODO: This is untested.
851 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800852
853
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800854 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800855 """
856 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800857
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500858 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800859 """
860 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500861 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500862 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800863 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500864 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800865 exts.append(ext)
866 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800867
868
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800869 def sign(self, pkey, digest):
870 """
871 Sign the certificate request using the supplied key and digest
872
873 :param pkey: The key to sign with
874 :param digest: The message digest to use
875 :return: None
876 """
877 if pkey._only_public:
878 raise ValueError("Key has only public part")
879
880 if not pkey._initialized:
881 raise ValueError("Key is uninitialized")
882
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500883 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500884 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800885 raise ValueError("No such digest method")
886
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500887 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800888 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500889 # TODO: This is untested.
890 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800891
892
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800893 def verify(self, pkey):
894 """
895 Verifies a certificate request using the supplied public key
896
897 :param key: a public key
898 :return: True if the signature is correct.
899
900 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
901 problem verifying the signature.
902 """
903 if not isinstance(pkey, PKey):
904 raise TypeError("pkey must be a PKey instance")
905
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500906 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800907 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500908 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800909
910 return result
911
912
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800913X509ReqType = X509Req
914
915
916
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800917class X509(object):
918 def __init__(self):
919 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500920 x509 = _lib.X509_new()
921 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800922
923
924 def set_version(self, version):
925 """
926 Set version number of the certificate
927
928 :param version: The version number
929 :type version: :py:class:`int`
930
931 :return: None
932 """
933 if not isinstance(version, int):
934 raise TypeError("version must be an integer")
935
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500936 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800937
938
939 def get_version(self):
940 """
941 Return version number of the certificate
942
943 :return: Version number as a Python integer
944 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500945 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800946
947
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800948 def get_pubkey(self):
949 """
950 Get the public key of the certificate
951
952 :return: The public key
953 """
954 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500955 pkey._pkey = _lib.X509_get_pubkey(self._x509)
956 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800957 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500958 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800959 pkey._only_public = True
960 return pkey
961
962
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800963 def set_pubkey(self, pkey):
964 """
965 Set the public key of the certificate
966
967 :param pkey: The public key
968
969 :return: None
970 """
971 if not isinstance(pkey, PKey):
972 raise TypeError("pkey must be a PKey instance")
973
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500974 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800975 if not set_result:
976 _raise_current_error()
977
978
979 def sign(self, pkey, digest):
980 """
981 Sign the certificate using the supplied key and digest
982
983 :param pkey: The key to sign with
984 :param digest: The message digest to use
985 :return: None
986 """
987 if not isinstance(pkey, PKey):
988 raise TypeError("pkey must be a PKey instance")
989
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800990 if pkey._only_public:
991 raise ValueError("Key only has public part")
992
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800993 if not pkey._initialized:
994 raise ValueError("Key is uninitialized")
995
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500996 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500997 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800998 raise ValueError("No such digest method")
999
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001000 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001001 if not sign_result:
1002 _raise_current_error()
1003
1004
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001005 def get_signature_algorithm(self):
1006 """
1007 Retrieve the signature algorithm used in the certificate
1008
1009 :return: A byte string giving the name of the signature algorithm used in
1010 the certificate.
1011 :raise ValueError: If the signature algorithm is undefined.
1012 """
1013 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001014 nid = _lib.OBJ_obj2nid(alg)
1015 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001016 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001017 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001018
1019
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001020 def digest(self, digest_name):
1021 """
1022 Return the digest of the X509 object.
1023
1024 :param digest_name: The name of the digest algorithm to use.
1025 :type digest_name: :py:class:`bytes`
1026
1027 :return: The digest of the object
1028 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001029 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001030 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001031 raise ValueError("No such digest method")
1032
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001033 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1034 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001035 result_length[0] = len(result_buffer)
1036
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001037 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001038 self._x509, digest, result_buffer, result_length)
1039
1040 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001041 # TODO: This is untested.
1042 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001043
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001044 return b":".join([
1045 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001046 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001047
1048
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001049 def subject_name_hash(self):
1050 """
1051 Return the hash of the X509 subject.
1052
1053 :return: The hash of the subject.
1054 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001055 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001056
1057
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001058 def set_serial_number(self, serial):
1059 """
1060 Set serial number of the certificate
1061
1062 :param serial: The serial number
1063 :type serial: :py:class:`int`
1064
1065 :return: None
1066 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001067 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001068 raise TypeError("serial must be an integer")
1069
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001070 hex_serial = hex(serial)[2:]
1071 if not isinstance(hex_serial, bytes):
1072 hex_serial = hex_serial.encode('ascii')
1073
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001074 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001075
1076 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1077 # it. If bignum is still NULL after this call, then the return value is
1078 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001079 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001080
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001081 if bignum_serial[0] == _ffi.NULL:
1082 set_result = _lib.ASN1_INTEGER_set(
1083 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001084 if set_result:
1085 # TODO Not tested
1086 _raise_current_error()
1087 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001088 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1089 _lib.BN_free(bignum_serial[0])
1090 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001091 # TODO Not tested
1092 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001093 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1094 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001095 if not set_result:
1096 # TODO Not tested
1097 _raise_current_error()
1098
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001099
1100 def get_serial_number(self):
1101 """
1102 Return serial number of the certificate
1103
1104 :return: Serial number as a Python integer
1105 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001106 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1107 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001108 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001109 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001110 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001111 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001112 serial = int(hexstring_serial, 16)
1113 return serial
1114 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001115 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001116 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001117 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001118
1119
1120 def gmtime_adj_notAfter(self, amount):
1121 """
1122 Adjust the time stamp for when the certificate stops being valid
1123
1124 :param amount: The number of seconds by which to adjust the ending
1125 validity time.
1126 :type amount: :py:class:`int`
1127
1128 :return: None
1129 """
1130 if not isinstance(amount, int):
1131 raise TypeError("amount must be an integer")
1132
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001133 notAfter = _lib.X509_get_notAfter(self._x509)
1134 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001135
1136
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001137 def gmtime_adj_notBefore(self, amount):
1138 """
1139 Change the timestamp for when the certificate starts being valid to the current
1140 time plus an offset.
1141
1142 :param amount: The number of seconds by which to adjust the starting validity
1143 time.
1144 :return: None
1145 """
1146 if not isinstance(amount, int):
1147 raise TypeError("amount must be an integer")
1148
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001149 notBefore = _lib.X509_get_notBefore(self._x509)
1150 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001151
1152
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001153 def has_expired(self):
1154 """
1155 Check whether the certificate has expired.
1156
1157 :return: True if the certificate has expired, false otherwise
1158 """
1159 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001160 notAfter = _lib.X509_get_notAfter(self._x509)
1161 return _lib.ASN1_UTCTIME_cmp_time_t(
1162 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001163
1164
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001165 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001166 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001167
1168
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001169 def get_notBefore(self):
1170 """
1171 Retrieve the time stamp for when the certificate starts being valid
1172
1173 :return: A string giving the timestamp, in the format::
1174
1175 YYYYMMDDhhmmssZ
1176 YYYYMMDDhhmmss+hhmm
1177 YYYYMMDDhhmmss-hhmm
1178
1179 or None if there is no value set.
1180 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001181 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001182
1183
1184 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001185 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001186
1187
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001188 def set_notBefore(self, when):
1189 """
1190 Set the time stamp for when the certificate starts being valid
1191
1192 :param when: A string giving the timestamp, in the format:
1193
1194 YYYYMMDDhhmmssZ
1195 YYYYMMDDhhmmss+hhmm
1196 YYYYMMDDhhmmss-hhmm
1197 :type when: :py:class:`bytes`
1198
1199 :return: None
1200 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001201 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001202
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001203
1204 def get_notAfter(self):
1205 """
1206 Retrieve the time stamp for when the certificate stops being valid
1207
1208 :return: A string giving the timestamp, in the format::
1209
1210 YYYYMMDDhhmmssZ
1211 YYYYMMDDhhmmss+hhmm
1212 YYYYMMDDhhmmss-hhmm
1213
1214 or None if there is no value set.
1215 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001216 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001217
1218
1219 def set_notAfter(self, when):
1220 """
1221 Set the time stamp for when the certificate stops being valid
1222
1223 :param when: A string giving the timestamp, in the format:
1224
1225 YYYYMMDDhhmmssZ
1226 YYYYMMDDhhmmss+hhmm
1227 YYYYMMDDhhmmss-hhmm
1228 :type when: :py:class:`bytes`
1229
1230 :return: None
1231 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001232 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001233
1234
1235 def _get_name(self, which):
1236 name = X509Name.__new__(X509Name)
1237 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001238 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001239 # TODO: This is untested.
1240 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001241
1242 # The name is owned by the X509 structure. As long as the X509Name
1243 # Python object is alive, keep the X509 Python object alive.
1244 name._owner = self
1245
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001246 return name
1247
1248
1249 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001250 if not isinstance(name, X509Name):
1251 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001252 set_result = which(self._x509, name._name)
1253 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001254 # TODO: This is untested.
1255 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001256
1257
1258 def get_issuer(self):
1259 """
1260 Create an X509Name object for the issuer of the certificate
1261
1262 :return: An X509Name object
1263 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001264 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001265
1266
1267 def set_issuer(self, issuer):
1268 """
1269 Set the issuer of the certificate
1270
1271 :param issuer: The issuer name
1272 :type issuer: :py:class:`X509Name`
1273
1274 :return: None
1275 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001276 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001277
1278
1279 def get_subject(self):
1280 """
1281 Create an X509Name object for the subject of the certificate
1282
1283 :return: An X509Name object
1284 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001285 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001286
1287
1288 def set_subject(self, subject):
1289 """
1290 Set the subject of the certificate
1291
1292 :param subject: The subject name
1293 :type subject: :py:class:`X509Name`
1294 :return: None
1295 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001296 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001297
1298
1299 def get_extension_count(self):
1300 """
1301 Get the number of extensions on the certificate.
1302
1303 :return: The number of extensions as an integer.
1304 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001305 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001306
1307
1308 def add_extensions(self, extensions):
1309 """
1310 Add extensions to the certificate.
1311
1312 :param extensions: a sequence of X509Extension objects
1313 :return: None
1314 """
1315 for ext in extensions:
1316 if not isinstance(ext, X509Extension):
1317 raise ValueError("One of the elements is not an X509Extension")
1318
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001319 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001320 if not add_result:
1321 _raise_current_error()
1322
1323
1324 def get_extension(self, index):
1325 """
1326 Get a specific extension of the certificate by index.
1327
1328 :param index: The index of the extension to retrieve.
1329 :return: The X509Extension object at the specified index.
1330 """
1331 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001332 ext._extension = _lib.X509_get_ext(self._x509, index)
1333 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001334 raise IndexError("extension index out of bounds")
1335
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001336 extension = _lib.X509_EXTENSION_dup(ext._extension)
1337 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001338 return ext
1339
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001340X509Type = X509
1341
1342
1343
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001344class X509Store(object):
1345 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001346 store = _lib.X509_STORE_new()
1347 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001348
1349
1350 def add_cert(self, cert):
1351 if not isinstance(cert, X509):
1352 raise TypeError()
1353
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001354 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001355 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001356 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001357
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001358
1359X509StoreType = X509Store
1360
1361
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001362class X509StoreContextError(Exception):
1363 """
1364 An error occurred while verifying a certificate using
Jean-Paul Calderonefeb17432015-03-15 15:49:45 -04001365 `OpenSSL.X509StoreContext.verify_certificate`.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001366
Jean-Paul Calderonefeb17432015-03-15 15:49:45 -04001367 :ivar certificate: The certificate which caused verificate failure.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001368 :type cert: :class:`X509`
1369
1370 """
1371 def __init__(self, message, certificate):
1372 super(X509StoreContextError, self).__init__(message)
1373 self.certificate = certificate
1374
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001375
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001376class X509StoreContext(object):
1377 """
1378 An X.509 store context.
1379
Jean-Paul Calderone13a81682015-01-18 15:49:15 -05001380 An :py:class:`X509StoreContext` is used to define some of the criteria for
1381 certificate verification. The information encapsulated in this object
1382 includes, but is not limited to, a set of trusted certificates,
1383 verification parameters, and revoked certificates.
1384
1385 Of these, only the set of trusted certificates is currently exposed.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001386
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001387 :ivar _store_ctx: The underlying X509_STORE_CTX structure used by this
1388 instance. It is dynamically allocated and automatically garbage
1389 collected.
1390
Jean-Paul Calderone64b6b842015-03-15 16:08:02 -04001391 :ivar _store: See the ``store`` ``__init__`` parameter.
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001392
Jean-Paul Calderone64b6b842015-03-15 16:08:02 -04001393 :ivar _cert: See the ``certificate`` ``__init__`` parameter.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001394 """
1395
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001396 def __init__(self, store, certificate):
1397 """
1398 :param X509Store store: The certificates which will be trusted for the
1399 purposes of any verifications.
1400
1401 :param X509 certificate: The certificate to be verified.
1402 """
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001403 store_ctx = _lib.X509_STORE_CTX_new()
1404 self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
1405 self._store = store
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001406 self._cert = certificate
Stephen Holsapple46a09252015-02-12 14:45:43 -08001407 # Make the store context available for use after instantiating this
1408 # class by initializing it now. Per testing, subsequent calls to
1409 # :py:meth:`_init` have no adverse affect.
1410 self._init()
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001411
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001412
1413 def _init(self):
1414 """
1415 Set up the store context for a subsequent verification operation.
1416 """
1417 ret = _lib.X509_STORE_CTX_init(self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL)
1418 if ret <= 0:
1419 _raise_current_error()
1420
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001421
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001422 def _cleanup(self):
1423 """
1424 Internally cleans up the store context.
1425
1426 The store context can then be reused with a new call to
Stephen Holsapple46a09252015-02-12 14:45:43 -08001427 :py:meth:`_init`.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001428 """
1429 _lib.X509_STORE_CTX_cleanup(self._store_ctx)
1430
1431
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001432 def _exception_from_context(self):
1433 """
1434 Convert an OpenSSL native context error failure into a Python
1435 exception.
1436
1437 When a call to native OpenSSL X509_verify_cert fails, additonal information
1438 about the failure can be obtained from the store context.
1439 """
1440 errors = [
1441 _lib.X509_STORE_CTX_get_error(self._store_ctx),
1442 _lib.X509_STORE_CTX_get_error_depth(self._store_ctx),
1443 _native(_ffi.string(_lib.X509_verify_cert_error_string(
1444 _lib.X509_STORE_CTX_get_error(self._store_ctx)))),
1445 ]
Stephen Holsapple1f713eb2015-02-09 19:19:44 -08001446 # A context error should always be associated with a certificate, so we
1447 # expect this call to never return :class:`None`.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001448 _x509 = _lib.X509_STORE_CTX_get_current_cert(self._store_ctx)
Stephen Holsapple1f713eb2015-02-09 19:19:44 -08001449 _cert = _lib.X509_dup(_x509)
1450 pycert = X509.__new__(X509)
1451 pycert._x509 = _ffi.gc(_cert, _lib.X509_free)
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001452 return X509StoreContextError(errors, pycert)
1453
1454
Stephen Holsapple46a09252015-02-12 14:45:43 -08001455 def set_store(self, store):
1456 """
1457 Set the context's trust store.
1458
1459 :param X509Store store: The certificates which will be trusted for the
1460 purposes of any *future* verifications.
1461 """
1462 self._store = store
1463
1464
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001465 def verify_certificate(self):
1466 """
1467 Verify a certificate in a context.
1468
1469 :param store_ctx: The :py:class:`X509StoreContext` to verify.
1470 :raises: Error
1471 """
Stephen Holsapple46a09252015-02-12 14:45:43 -08001472 # Always re-initialize the store context in case
1473 # :py:meth:`verify_certificate` is called multiple times.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001474 self._init()
1475 ret = _lib.X509_verify_cert(self._store_ctx)
1476 self._cleanup()
1477 if ret <= 0:
1478 raise self._exception_from_context()
1479
1480
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001481
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001482def load_certificate(type, buffer):
1483 """
1484 Load a certificate from a buffer
1485
1486 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1487
1488 :param buffer: The buffer the certificate is stored in
1489 :type buffer: :py:class:`bytes`
1490
1491 :return: The X509 object
1492 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001493 if isinstance(buffer, _text_type):
1494 buffer = buffer.encode("ascii")
1495
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001496 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001497
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001498 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001499 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001500 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001501 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001502 else:
1503 raise ValueError(
1504 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001505
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001506 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001507 _raise_current_error()
1508
1509 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001510 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001511 return cert
1512
1513
1514def dump_certificate(type, cert):
1515 """
1516 Dump a certificate to a buffer
1517
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001518 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1519 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001520 :param cert: The certificate to dump
1521 :return: The buffer with the dumped certificate in
1522 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001523 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001524
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001525 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001526 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001527 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001528 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001529 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001530 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001531 else:
1532 raise ValueError(
1533 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1534 "FILETYPE_TEXT")
1535
1536 return _bio_to_string(bio)
1537
1538
1539
1540def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1541 """
1542 Dump a private key to a buffer
1543
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001544 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1545 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001546 :param pkey: The PKey to dump
1547 :param cipher: (optional) if encrypted PEM format, the cipher to
1548 use
1549 :param passphrase: (optional) if encrypted PEM format, this can be either
1550 the passphrase to use, or a callback for providing the
1551 passphrase.
1552 :return: The buffer with the dumped key in
1553 :rtype: :py:data:`str`
1554 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001555 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001556
1557 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001558 if passphrase is None:
1559 raise TypeError(
1560 "if a value is given for cipher "
1561 "one must also be given for passphrase")
1562 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001563 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001564 raise ValueError("Invalid cipher name")
1565 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001566 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001567
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001568 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001569 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001570 result_code = _lib.PEM_write_bio_PrivateKey(
1571 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001572 helper.callback, helper.callback_args)
1573 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001574 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001575 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001576 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001577 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1578 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001579 # TODO RSA_free(rsa)?
1580 else:
1581 raise ValueError(
1582 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1583 "FILETYPE_TEXT")
1584
1585 if result_code == 0:
1586 _raise_current_error()
1587
1588 return _bio_to_string(bio)
1589
1590
1591
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001592def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001593 copy = _lib.X509_REVOKED_new()
1594 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001595 # TODO: This is untested.
1596 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001597
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001598 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001599 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001600 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001601
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001602 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001603 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001604 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001605
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001606 if original.extensions != _ffi.NULL:
1607 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1608 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1609 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1610 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1611 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001612 copy.extensions = extension_stack
1613
1614 copy.sequence = original.sequence
1615 return copy
1616
1617
1618
1619class Revoked(object):
1620 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1621 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1622 # OCSP_crl_reason_str. We use the latter, just like the command line
1623 # program.
1624 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001625 b"unspecified",
1626 b"keyCompromise",
1627 b"CACompromise",
1628 b"affiliationChanged",
1629 b"superseded",
1630 b"cessationOfOperation",
1631 b"certificateHold",
1632 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001633 ]
1634
1635 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001636 revoked = _lib.X509_REVOKED_new()
1637 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001638
1639
1640 def set_serial(self, hex_str):
1641 """
1642 Set the serial number of a revoked Revoked structure
1643
1644 :param hex_str: The new serial number.
1645 :type hex_str: :py:data:`str`
1646 :return: None
1647 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001648 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1649 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001650 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001651 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001652 if not bn_result:
1653 raise ValueError("bad hex string")
1654
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001655 asn1_serial = _ffi.gc(
1656 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1657 _lib.ASN1_INTEGER_free)
1658 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001659
1660
1661 def get_serial(self):
1662 """
1663 Return the serial number of a Revoked structure
1664
1665 :return: The serial number as a string
1666 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001667 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001668
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001669 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001670 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001671 # TODO: This is untested.
1672 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001673
1674 return _bio_to_string(bio)
1675
1676
1677 def _delete_reason(self):
1678 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001679 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1680 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1681 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1682 _lib.X509_EXTENSION_free(ext)
1683 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001684 break
1685
1686
1687 def set_reason(self, reason):
1688 """
1689 Set the reason of a Revoked object.
1690
1691 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1692
1693 :param reason: The reason string.
1694 :type reason: :py:class:`str` or :py:class:`NoneType`
1695 :return: None
1696 """
1697 if reason is None:
1698 self._delete_reason()
1699 elif not isinstance(reason, bytes):
1700 raise TypeError("reason must be None or a byte string")
1701 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001702 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001703 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1704
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001705 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1706 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001707 # TODO: This is untested.
1708 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001709 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001710
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001711 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1712 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001713 # TODO: This is untested.
1714 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001715
1716 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001717 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1718 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001719
1720 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001721 # TODO: This is untested.
1722 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001723
1724
1725 def get_reason(self):
1726 """
1727 Return the reason of a Revoked object.
1728
1729 :return: The reason as a string
1730 """
1731 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001732 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1733 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1734 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001735 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001736
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001737 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001738 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001739 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001740 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001741 # TODO: This is untested.
1742 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001743
1744 return _bio_to_string(bio)
1745
1746
1747 def all_reasons(self):
1748 """
1749 Return a list of all the supported reason strings.
1750
1751 :return: A list of reason strings.
1752 """
1753 return self._crl_reasons[:]
1754
1755
1756 def set_rev_date(self, when):
1757 """
1758 Set the revocation timestamp
1759
1760 :param when: A string giving the timestamp, in the format:
1761
1762 YYYYMMDDhhmmssZ
1763 YYYYMMDDhhmmss+hhmm
1764 YYYYMMDDhhmmss-hhmm
1765
1766 :return: None
1767 """
1768 return _set_asn1_time(self._revoked.revocationDate, when)
1769
1770
1771 def get_rev_date(self):
1772 """
1773 Retrieve the revocation date
1774
1775 :return: A string giving the timestamp, in the format:
1776
1777 YYYYMMDDhhmmssZ
1778 YYYYMMDDhhmmss+hhmm
1779 YYYYMMDDhhmmss-hhmm
1780 """
1781 return _get_asn1_time(self._revoked.revocationDate)
1782
1783
1784
1785class CRL(object):
1786 def __init__(self):
1787 """
1788 Create a new empty CRL object.
1789 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001790 crl = _lib.X509_CRL_new()
1791 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001792
1793
1794 def get_revoked(self):
1795 """
1796 Return revoked portion of the CRL structure (by value not reference).
1797
1798 :return: A tuple of Revoked objects.
1799 """
1800 results = []
1801 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001802 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1803 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001804 revoked_copy = _X509_REVOKED_dup(revoked)
1805 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001806 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001807 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001808 if results:
1809 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001810
1811
1812 def add_revoked(self, revoked):
1813 """
1814 Add a revoked (by value not reference) to the CRL structure
1815
1816 :param revoked: The new revoked.
1817 :type revoked: :class:`X509`
1818
1819 :return: None
1820 """
1821 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001822 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001823 # TODO: This is untested.
1824 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001825
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001826 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001827 if add_result == 0:
1828 # TODO: This is untested.
1829 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001830
1831
1832 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1833 """
1834 export a CRL as a string
1835
1836 :param cert: Used to sign CRL.
1837 :type cert: :class:`X509`
1838
1839 :param key: Used to sign CRL.
1840 :type key: :class:`PKey`
1841
1842 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1843
1844 :param days: The number of days until the next update of this CRL.
1845 :type days: :py:data:`int`
1846
1847 :return: :py:data:`str`
1848 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001849 if not isinstance(cert, X509):
1850 raise TypeError("cert must be an X509 instance")
1851 if not isinstance(key, PKey):
1852 raise TypeError("key must be a PKey instance")
1853 if not isinstance(type, int):
1854 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001855
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001856 bio = _lib.BIO_new(_lib.BIO_s_mem())
1857 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001858 # TODO: This is untested.
1859 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001860
1861 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001862 sometime = _lib.ASN1_TIME_new()
1863 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001864 # TODO: This is untested.
1865 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001866
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001867 _lib.X509_gmtime_adj(sometime, 0)
1868 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001869
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001870 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1871 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001872
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001873 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001874
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001875 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001876 if not sign_result:
1877 _raise_current_error()
1878
1879 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001880 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001881 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001882 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001883 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001884 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001885 else:
1886 raise ValueError(
1887 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1888
1889 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001890 # TODO: This is untested.
1891 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001892
1893 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001894CRLType = CRL
1895
1896
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001897
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001898class PKCS7(object):
1899 def type_is_signed(self):
1900 """
1901 Check if this NID_pkcs7_signed object
1902
1903 :return: True if the PKCS7 is of type signed
1904 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001905 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001906 return True
1907 return False
1908
1909
1910 def type_is_enveloped(self):
1911 """
1912 Check if this NID_pkcs7_enveloped object
1913
1914 :returns: True if the PKCS7 is of type enveloped
1915 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001916 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001917 return True
1918 return False
1919
1920
1921 def type_is_signedAndEnveloped(self):
1922 """
1923 Check if this NID_pkcs7_signedAndEnveloped object
1924
1925 :returns: True if the PKCS7 is of type signedAndEnveloped
1926 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001927 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001928 return True
1929 return False
1930
1931
1932 def type_is_data(self):
1933 """
1934 Check if this NID_pkcs7_data object
1935
1936 :return: True if the PKCS7 is of type data
1937 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001938 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001939 return True
1940 return False
1941
1942
1943 def get_type_name(self):
1944 """
1945 Returns the type name of the PKCS7 structure
1946
1947 :return: A string with the typename
1948 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001949 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1950 string_type = _lib.OBJ_nid2sn(nid)
1951 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001952
1953PKCS7Type = PKCS7
1954
1955
1956
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001957class PKCS12(object):
1958 def __init__(self):
1959 self._pkey = None
1960 self._cert = None
1961 self._cacerts = None
1962 self._friendlyname = None
1963
1964
1965 def get_certificate(self):
1966 """
1967 Return certificate portion of the PKCS12 structure
1968
1969 :return: X509 object containing the certificate
1970 """
1971 return self._cert
1972
1973
1974 def set_certificate(self, cert):
1975 """
1976 Replace the certificate portion of the PKCS12 structure
1977
1978 :param cert: The new certificate.
1979 :type cert: :py:class:`X509` or :py:data:`None`
1980 :return: None
1981 """
1982 if not isinstance(cert, X509):
1983 raise TypeError("cert must be an X509 instance")
1984 self._cert = cert
1985
1986
1987 def get_privatekey(self):
1988 """
1989 Return private key portion of the PKCS12 structure
1990
1991 :returns: PKey object containing the private key
1992 """
1993 return self._pkey
1994
1995
1996 def set_privatekey(self, pkey):
1997 """
1998 Replace or set the certificate portion of the PKCS12 structure
1999
2000 :param pkey: The new private key.
2001 :type pkey: :py:class:`PKey`
2002 :return: None
2003 """
2004 if not isinstance(pkey, PKey):
2005 raise TypeError("pkey must be a PKey instance")
2006 self._pkey = pkey
2007
2008
2009 def get_ca_certificates(self):
2010 """
2011 Return CA certificates within of the PKCS12 object
2012
2013 :return: A newly created tuple containing the CA certificates in the chain,
2014 if any are present, or None if no CA certificates are present.
2015 """
2016 if self._cacerts is not None:
2017 return tuple(self._cacerts)
2018
2019
2020 def set_ca_certificates(self, cacerts):
2021 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08002022 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002023
2024 :param cacerts: The new CA certificates.
2025 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
2026 :return: None
2027 """
2028 if cacerts is None:
2029 self._cacerts = None
2030 else:
2031 cacerts = list(cacerts)
2032 for cert in cacerts:
2033 if not isinstance(cert, X509):
2034 raise TypeError("iterable must only contain X509 instances")
2035 self._cacerts = cacerts
2036
2037
2038 def set_friendlyname(self, name):
2039 """
2040 Replace or set the certificate portion of the PKCS12 structure
2041
2042 :param name: The new friendly name.
2043 :type name: :py:class:`bytes`
2044 :return: None
2045 """
2046 if name is None:
2047 self._friendlyname = None
2048 elif not isinstance(name, bytes):
2049 raise TypeError("name must be a byte string or None (not %r)" % (name,))
2050 self._friendlyname = name
2051
2052
2053 def get_friendlyname(self):
2054 """
2055 Return friendly name portion of the PKCS12 structure
2056
2057 :returns: String containing the friendlyname
2058 """
2059 return self._friendlyname
2060
2061
2062 def export(self, passphrase=None, iter=2048, maciter=1):
2063 """
2064 Dump a PKCS12 object as a string. See also "man PKCS12_create".
2065
2066 :param passphrase: used to encrypt the PKCS12
2067 :type passphrase: :py:data:`bytes`
2068
2069 :param iter: How many times to repeat the encryption
2070 :type iter: :py:data:`int`
2071
2072 :param maciter: How many times to repeat the MAC
2073 :type maciter: :py:data:`int`
2074
2075 :return: The string containing the PKCS12
2076 """
2077 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002078 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002079 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002080 cacerts = _lib.sk_X509_new_null()
2081 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002082 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002083 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002084
2085 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002086 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002087
2088 friendlyname = self._friendlyname
2089 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002090 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002091
2092 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002093 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002094 else:
2095 pkey = self._pkey._pkey
2096
2097 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002098 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002099 else:
2100 cert = self._cert._x509
2101
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002102 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002103 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002104 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2105 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002106 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002107 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002108 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002109 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002110
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002111 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002112 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002113 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002114
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002115PKCS12Type = PKCS12
2116
2117
2118
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002119class NetscapeSPKI(object):
2120 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002121 spki = _lib.NETSCAPE_SPKI_new()
2122 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002123
2124
2125 def sign(self, pkey, digest):
2126 """
2127 Sign the certificate request using the supplied key and digest
2128
2129 :param pkey: The key to sign with
2130 :param digest: The message digest to use
2131 :return: None
2132 """
2133 if pkey._only_public:
2134 raise ValueError("Key has only public part")
2135
2136 if not pkey._initialized:
2137 raise ValueError("Key is uninitialized")
2138
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002139 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002140 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002141 raise ValueError("No such digest method")
2142
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002143 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002144 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002145 # TODO: This is untested.
2146 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002147
2148
2149 def verify(self, key):
2150 """
2151 Verifies a certificate request using the supplied public key
2152
2153 :param key: a public key
2154 :return: True if the signature is correct.
2155 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2156 problem verifying the signature.
2157 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002158 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002159 if answer <= 0:
2160 _raise_current_error()
2161 return True
2162
2163
2164 def b64_encode(self):
2165 """
2166 Generate a base64 encoded string from an SPKI
2167
2168 :return: The base64 encoded string
2169 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002170 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2171 result = _ffi.string(encoded)
2172 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002173 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002174
2175
2176 def get_pubkey(self):
2177 """
2178 Get the public key of the certificate
2179
2180 :return: The public key
2181 """
2182 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002183 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2184 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002185 # TODO: This is untested.
2186 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002187 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002188 pkey._only_public = True
2189 return pkey
2190
2191
2192 def set_pubkey(self, pkey):
2193 """
2194 Set the public key of the certificate
2195
2196 :param pkey: The public key
2197 :return: None
2198 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002199 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002200 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002201 # TODO: This is untested.
2202 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002203NetscapeSPKIType = NetscapeSPKI
2204
2205
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002206class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002207 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002208 if type != FILETYPE_PEM and passphrase is not None:
2209 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002210 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002211 self._more_args = more_args
2212 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002213 self._problems = []
2214
2215
2216 @property
2217 def callback(self):
2218 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002219 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002220 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002221 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002222 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002223 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002224 else:
2225 raise TypeError("Last argument must be string or callable")
2226
2227
2228 @property
2229 def callback_args(self):
2230 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002231 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002232 elif isinstance(self._passphrase, bytes):
2233 return self._passphrase
2234 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002235 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002236 else:
2237 raise TypeError("Last argument must be string or callable")
2238
2239
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002240 def raise_if_problem(self, exceptionType=Error):
2241 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002242 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002243 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002244 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002245 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002246 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002247 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002248
2249
2250 def _read_passphrase(self, buf, size, rwflag, userdata):
2251 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002252 if self._more_args:
2253 result = self._passphrase(size, rwflag, userdata)
2254 else:
2255 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002256 if not isinstance(result, bytes):
2257 raise ValueError("String expected")
2258 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002259 if self._truncate:
2260 result = result[:size]
2261 else:
2262 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002263 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002264 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002265 return len(result)
2266 except Exception as e:
2267 self._problems.append(e)
2268 return 0
2269
2270
2271
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002272def load_privatekey(type, buffer, passphrase=None):
2273 """
2274 Load a private key from a buffer
2275
2276 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2277 :param buffer: The buffer the key is stored in
2278 :param passphrase: (optional) if encrypted PEM format, this can be
2279 either the passphrase to use, or a callback for
2280 providing the passphrase.
2281
2282 :return: The PKey object
2283 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002284 if isinstance(buffer, _text_type):
2285 buffer = buffer.encode("ascii")
2286
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002287 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002288
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002289 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002290 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002291 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2292 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002293 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002294 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002295 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002296 else:
2297 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2298
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002299 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002300 _raise_current_error()
2301
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002302 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002303 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002304 return pkey
2305
2306
2307
2308def dump_certificate_request(type, req):
2309 """
2310 Dump a certificate request to a buffer
2311
2312 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2313 :param req: The certificate request to dump
2314 :return: The buffer with the dumped certificate request in
2315 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002316 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002317
2318 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002319 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002320 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002321 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002322 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002323 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002324 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002325 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002326
2327 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002328 # TODO: This is untested.
2329 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002330
2331 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002332
2333
2334
2335def load_certificate_request(type, buffer):
2336 """
2337 Load a certificate request from a buffer
2338
2339 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2340 :param buffer: The buffer the certificate request is stored in
2341 :return: The X509Req object
2342 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002343 if isinstance(buffer, _text_type):
2344 buffer = buffer.encode("ascii")
2345
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002346 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002347
2348 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002349 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002350 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002351 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002352 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002353 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002354
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002355 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002356 # TODO: This is untested.
2357 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002358
2359 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002360 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002361 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002362
2363
2364
2365def sign(pkey, data, digest):
2366 """
2367 Sign data with a digest
2368
2369 :param pkey: Pkey to sign with
2370 :param data: data to be signed
2371 :param digest: message digest to use
2372 :return: signature
2373 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002374 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002375 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002376 raise ValueError("No such digest method")
2377
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002378 md_ctx = _ffi.new("EVP_MD_CTX*")
2379 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002380
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002381 _lib.EVP_SignInit(md_ctx, digest_obj)
2382 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002383
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002384 signature_buffer = _ffi.new("unsigned char[]", 512)
2385 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002386 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002387 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002388 md_ctx, signature_buffer, signature_length, pkey._pkey)
2389
2390 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002391 # TODO: This is untested.
2392 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002393
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002394 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002395
2396
2397
2398def verify(cert, signature, data, digest):
2399 """
2400 Verify a signature
2401
2402 :param cert: signing certificate (X509 object)
2403 :param signature: signature returned by sign function
2404 :param data: data to be verified
2405 :param digest: message digest to use
2406 :return: None if the signature is correct, raise exception otherwise
2407 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002408 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002409 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002410 raise ValueError("No such digest method")
2411
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002412 pkey = _lib.X509_get_pubkey(cert._x509)
2413 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002414 # TODO: This is untested.
2415 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002416 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002417
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002418 md_ctx = _ffi.new("EVP_MD_CTX*")
2419 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002420
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002421 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2422 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2423 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002424
2425 if verify_result != 1:
2426 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002427
2428
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002429def load_crl(type, buffer):
2430 """
2431 Load a certificate revocation list from a buffer
2432
2433 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2434 :param buffer: The buffer the CRL is stored in
2435
2436 :return: The PKey object
2437 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002438 if isinstance(buffer, _text_type):
2439 buffer = buffer.encode("ascii")
2440
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002441 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002442
2443 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002444 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002445 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002446 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002447 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002448 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2449
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002450 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002451 _raise_current_error()
2452
2453 result = CRL.__new__(CRL)
2454 result._crl = crl
2455 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002456
2457
2458
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002459def load_pkcs7_data(type, buffer):
2460 """
2461 Load pkcs7 data from a buffer
2462
2463 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2464 :param buffer: The buffer with the pkcs7 data.
2465 :return: The PKCS7 object
2466 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002467 if isinstance(buffer, _text_type):
2468 buffer = buffer.encode("ascii")
2469
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002470 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002471
2472 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002473 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002474 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002475 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002476 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002477 # TODO: This is untested.
2478 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002479 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2480
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002481 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002482 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002483
2484 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002485 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002486 return pypkcs7
2487
2488
2489
Stephen Holsapple38482622014-04-05 20:29:34 -07002490def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002491 """
2492 Load a PKCS12 object from a buffer
2493
2494 :param buffer: The buffer the certificate is stored in
2495 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2496 :returns: The PKCS12 object
2497 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002498 if isinstance(buffer, _text_type):
2499 buffer = buffer.encode("ascii")
2500
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002501 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002502
Stephen Holsapple38482622014-04-05 20:29:34 -07002503 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2504 # password based encryption no password and a zero length password are two
2505 # different things, but OpenSSL implementation will try both to figure out
2506 # which one works.
2507 if not passphrase:
2508 passphrase = _ffi.NULL
2509
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002510 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2511 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002512 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002513 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002514
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002515 pkey = _ffi.new("EVP_PKEY**")
2516 cert = _ffi.new("X509**")
2517 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002518
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002519 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002520 if not parse_result:
2521 _raise_current_error()
2522
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002523 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002524
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002525 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2526 # queue for no particular reason. This error isn't interesting to anyone
2527 # outside this function. It's not even interesting to us. Get rid of it.
2528 try:
2529 _raise_current_error()
2530 except Error:
2531 pass
2532
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002533 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002534 pykey = None
2535 else:
2536 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002537 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002538
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002539 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002540 pycert = None
2541 friendlyname = None
2542 else:
2543 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002544 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002545
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002546 friendlyname_length = _ffi.new("int*")
2547 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2548 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2549 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002550 friendlyname = None
2551
2552 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002553 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002554 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002555 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002556 pycacerts.append(pycacert)
2557 if not pycacerts:
2558 pycacerts = None
2559
2560 pkcs12 = PKCS12.__new__(PKCS12)
2561 pkcs12._pkey = pykey
2562 pkcs12._cert = pycert
2563 pkcs12._cacerts = pycacerts
2564 pkcs12._friendlyname = friendlyname
2565 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002566
2567
2568def _initialize_openssl_threads(get_ident, Lock):
2569 import _ssl
2570 return
2571
2572 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2573
2574 def locking_function(mode, index, filename, line):
2575 if mode & _lib.CRYPTO_LOCK:
2576 locks[index].acquire()
2577 else:
2578 locks[index].release()
2579
2580 _lib.CRYPTO_set_id_callback(
2581 _ffi.callback("unsigned long (*)(void)", get_ident))
2582
2583 _lib.CRYPTO_set_locking_callback(
2584 _ffi.callback(
2585 "void (*)(int, int, const char*, int)", locking_function))
2586
2587
2588try:
2589 from thread import get_ident
2590 from threading import Lock
2591except ImportError:
2592 pass
2593else:
2594 _initialize_openssl_threads(get_ident, Lock)
2595 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002596
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002597# There are no direct unit tests for this initialization. It is tested
2598# indirectly since it is necessary for functions like dump_privatekey when
2599# using encryption.
2600#
2601# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2602# and some other similar tests may fail without this (though they may not if
2603# the Python runtime has already done some initialization of the underlying
2604# OpenSSL library (and is linked against the same one that cryptography is
2605# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002606_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002607
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002608# This is similar but exercised mainly by exception_from_error_queue. It calls
2609# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2610_lib.SSL_load_error_strings()