blob: 395f27306c379045c794d9b1f650ef8a823eb8ed [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
1365 `OpenSSL.X509StoreContext.verify_certificate`.
1366
1367 :param certificate: The certificate which caused verificate failure.
1368 :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
1391 :ivar _store: See the ``store`` `__init__`` parameter.
1392
1393 :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
1407
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001408
1409 def _init(self):
1410 """
1411 Set up the store context for a subsequent verification operation.
1412 """
1413 ret = _lib.X509_STORE_CTX_init(self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL)
1414 if ret <= 0:
1415 _raise_current_error()
1416
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001417
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001418 def _cleanup(self):
1419 """
1420 Internally cleans up the store context.
1421
1422 The store context can then be reused with a new call to
1423 :py:meth:`init`.
1424 """
1425 _lib.X509_STORE_CTX_cleanup(self._store_ctx)
1426
1427
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001428 def _exception_from_context(self):
1429 """
1430 Convert an OpenSSL native context error failure into a Python
1431 exception.
1432
1433 When a call to native OpenSSL X509_verify_cert fails, additonal information
1434 about the failure can be obtained from the store context.
1435 """
1436 errors = [
1437 _lib.X509_STORE_CTX_get_error(self._store_ctx),
1438 _lib.X509_STORE_CTX_get_error_depth(self._store_ctx),
1439 _native(_ffi.string(_lib.X509_verify_cert_error_string(
1440 _lib.X509_STORE_CTX_get_error(self._store_ctx)))),
1441 ]
1442 _x509 = _lib.X509_STORE_CTX_get_current_cert(self._store_ctx)
1443 if _x509 != _ffi.NULL:
1444 _cert = _lib.X509_dup(_x509)
1445 pycert = X509.__new__(X509)
1446 pycert._x509 = _ffi.gc(_cert, _lib.X509_free)
1447 return X509StoreContextError(errors, pycert)
1448
1449
1450 def verify_certificate(self):
1451 """
1452 Verify a certificate in a context.
1453
1454 :param store_ctx: The :py:class:`X509StoreContext` to verify.
1455 :raises: Error
1456 """
1457 self._init()
1458 ret = _lib.X509_verify_cert(self._store_ctx)
1459 self._cleanup()
1460 if ret <= 0:
1461 raise self._exception_from_context()
1462
1463
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001464
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001465def load_certificate(type, buffer):
1466 """
1467 Load a certificate from a buffer
1468
1469 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1470
1471 :param buffer: The buffer the certificate is stored in
1472 :type buffer: :py:class:`bytes`
1473
1474 :return: The X509 object
1475 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001476 if isinstance(buffer, _text_type):
1477 buffer = buffer.encode("ascii")
1478
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001479 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001480
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001481 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001482 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001483 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001484 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001485 else:
1486 raise ValueError(
1487 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001488
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001489 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001490 _raise_current_error()
1491
1492 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001493 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001494 return cert
1495
1496
1497def dump_certificate(type, cert):
1498 """
1499 Dump a certificate to a buffer
1500
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001501 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1502 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001503 :param cert: The certificate to dump
1504 :return: The buffer with the dumped certificate in
1505 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001506 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001507
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001508 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001509 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001510 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001511 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001512 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001513 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001514 else:
1515 raise ValueError(
1516 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1517 "FILETYPE_TEXT")
1518
1519 return _bio_to_string(bio)
1520
1521
1522
1523def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1524 """
1525 Dump a private key to a buffer
1526
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001527 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1528 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001529 :param pkey: The PKey to dump
1530 :param cipher: (optional) if encrypted PEM format, the cipher to
1531 use
1532 :param passphrase: (optional) if encrypted PEM format, this can be either
1533 the passphrase to use, or a callback for providing the
1534 passphrase.
1535 :return: The buffer with the dumped key in
1536 :rtype: :py:data:`str`
1537 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001538 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001539
1540 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001541 if passphrase is None:
1542 raise TypeError(
1543 "if a value is given for cipher "
1544 "one must also be given for passphrase")
1545 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001546 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001547 raise ValueError("Invalid cipher name")
1548 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001549 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001550
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001551 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001552 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001553 result_code = _lib.PEM_write_bio_PrivateKey(
1554 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001555 helper.callback, helper.callback_args)
1556 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001557 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001558 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001559 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001560 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1561 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001562 # TODO RSA_free(rsa)?
1563 else:
1564 raise ValueError(
1565 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1566 "FILETYPE_TEXT")
1567
1568 if result_code == 0:
1569 _raise_current_error()
1570
1571 return _bio_to_string(bio)
1572
1573
1574
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001575def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001576 copy = _lib.X509_REVOKED_new()
1577 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001578 # TODO: This is untested.
1579 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001580
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001581 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001582 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001583 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001584
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001585 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001586 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001587 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001588
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001589 if original.extensions != _ffi.NULL:
1590 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1591 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1592 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1593 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1594 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001595 copy.extensions = extension_stack
1596
1597 copy.sequence = original.sequence
1598 return copy
1599
1600
1601
1602class Revoked(object):
1603 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1604 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1605 # OCSP_crl_reason_str. We use the latter, just like the command line
1606 # program.
1607 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001608 b"unspecified",
1609 b"keyCompromise",
1610 b"CACompromise",
1611 b"affiliationChanged",
1612 b"superseded",
1613 b"cessationOfOperation",
1614 b"certificateHold",
1615 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001616 ]
1617
1618 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001619 revoked = _lib.X509_REVOKED_new()
1620 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001621
1622
1623 def set_serial(self, hex_str):
1624 """
1625 Set the serial number of a revoked Revoked structure
1626
1627 :param hex_str: The new serial number.
1628 :type hex_str: :py:data:`str`
1629 :return: None
1630 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001631 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1632 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001633 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001634 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001635 if not bn_result:
1636 raise ValueError("bad hex string")
1637
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001638 asn1_serial = _ffi.gc(
1639 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1640 _lib.ASN1_INTEGER_free)
1641 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001642
1643
1644 def get_serial(self):
1645 """
1646 Return the serial number of a Revoked structure
1647
1648 :return: The serial number as a string
1649 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001650 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001651
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001652 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001653 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001654 # TODO: This is untested.
1655 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001656
1657 return _bio_to_string(bio)
1658
1659
1660 def _delete_reason(self):
1661 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001662 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1663 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1664 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1665 _lib.X509_EXTENSION_free(ext)
1666 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001667 break
1668
1669
1670 def set_reason(self, reason):
1671 """
1672 Set the reason of a Revoked object.
1673
1674 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1675
1676 :param reason: The reason string.
1677 :type reason: :py:class:`str` or :py:class:`NoneType`
1678 :return: None
1679 """
1680 if reason is None:
1681 self._delete_reason()
1682 elif not isinstance(reason, bytes):
1683 raise TypeError("reason must be None or a byte string")
1684 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001685 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001686 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1687
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001688 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1689 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001690 # TODO: This is untested.
1691 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001692 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001693
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001694 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1695 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001696 # TODO: This is untested.
1697 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001698
1699 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001700 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1701 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001702
1703 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001704 # TODO: This is untested.
1705 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001706
1707
1708 def get_reason(self):
1709 """
1710 Return the reason of a Revoked object.
1711
1712 :return: The reason as a string
1713 """
1714 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001715 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1716 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1717 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001718 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001719
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001720 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001721 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001722 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001723 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001724 # TODO: This is untested.
1725 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001726
1727 return _bio_to_string(bio)
1728
1729
1730 def all_reasons(self):
1731 """
1732 Return a list of all the supported reason strings.
1733
1734 :return: A list of reason strings.
1735 """
1736 return self._crl_reasons[:]
1737
1738
1739 def set_rev_date(self, when):
1740 """
1741 Set the revocation timestamp
1742
1743 :param when: A string giving the timestamp, in the format:
1744
1745 YYYYMMDDhhmmssZ
1746 YYYYMMDDhhmmss+hhmm
1747 YYYYMMDDhhmmss-hhmm
1748
1749 :return: None
1750 """
1751 return _set_asn1_time(self._revoked.revocationDate, when)
1752
1753
1754 def get_rev_date(self):
1755 """
1756 Retrieve the revocation date
1757
1758 :return: A string giving the timestamp, in the format:
1759
1760 YYYYMMDDhhmmssZ
1761 YYYYMMDDhhmmss+hhmm
1762 YYYYMMDDhhmmss-hhmm
1763 """
1764 return _get_asn1_time(self._revoked.revocationDate)
1765
1766
1767
1768class CRL(object):
1769 def __init__(self):
1770 """
1771 Create a new empty CRL object.
1772 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001773 crl = _lib.X509_CRL_new()
1774 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001775
1776
1777 def get_revoked(self):
1778 """
1779 Return revoked portion of the CRL structure (by value not reference).
1780
1781 :return: A tuple of Revoked objects.
1782 """
1783 results = []
1784 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001785 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1786 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001787 revoked_copy = _X509_REVOKED_dup(revoked)
1788 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001789 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001790 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001791 if results:
1792 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001793
1794
1795 def add_revoked(self, revoked):
1796 """
1797 Add a revoked (by value not reference) to the CRL structure
1798
1799 :param revoked: The new revoked.
1800 :type revoked: :class:`X509`
1801
1802 :return: None
1803 """
1804 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001805 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001806 # TODO: This is untested.
1807 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001808
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001809 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001810 if add_result == 0:
1811 # TODO: This is untested.
1812 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001813
1814
1815 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1816 """
1817 export a CRL as a string
1818
1819 :param cert: Used to sign CRL.
1820 :type cert: :class:`X509`
1821
1822 :param key: Used to sign CRL.
1823 :type key: :class:`PKey`
1824
1825 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1826
1827 :param days: The number of days until the next update of this CRL.
1828 :type days: :py:data:`int`
1829
1830 :return: :py:data:`str`
1831 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001832 if not isinstance(cert, X509):
1833 raise TypeError("cert must be an X509 instance")
1834 if not isinstance(key, PKey):
1835 raise TypeError("key must be a PKey instance")
1836 if not isinstance(type, int):
1837 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001838
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001839 bio = _lib.BIO_new(_lib.BIO_s_mem())
1840 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001841 # TODO: This is untested.
1842 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001843
1844 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001845 sometime = _lib.ASN1_TIME_new()
1846 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001847 # TODO: This is untested.
1848 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001849
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001850 _lib.X509_gmtime_adj(sometime, 0)
1851 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001852
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001853 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1854 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001855
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001856 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001857
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001858 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001859 if not sign_result:
1860 _raise_current_error()
1861
1862 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001863 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001864 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001865 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001866 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001867 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001868 else:
1869 raise ValueError(
1870 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1871
1872 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001873 # TODO: This is untested.
1874 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001875
1876 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001877CRLType = CRL
1878
1879
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001880
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001881class PKCS7(object):
1882 def type_is_signed(self):
1883 """
1884 Check if this NID_pkcs7_signed object
1885
1886 :return: True if the PKCS7 is of type signed
1887 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001888 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001889 return True
1890 return False
1891
1892
1893 def type_is_enveloped(self):
1894 """
1895 Check if this NID_pkcs7_enveloped object
1896
1897 :returns: True if the PKCS7 is of type enveloped
1898 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001899 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001900 return True
1901 return False
1902
1903
1904 def type_is_signedAndEnveloped(self):
1905 """
1906 Check if this NID_pkcs7_signedAndEnveloped object
1907
1908 :returns: True if the PKCS7 is of type signedAndEnveloped
1909 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001910 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001911 return True
1912 return False
1913
1914
1915 def type_is_data(self):
1916 """
1917 Check if this NID_pkcs7_data object
1918
1919 :return: True if the PKCS7 is of type data
1920 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001921 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001922 return True
1923 return False
1924
1925
1926 def get_type_name(self):
1927 """
1928 Returns the type name of the PKCS7 structure
1929
1930 :return: A string with the typename
1931 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001932 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1933 string_type = _lib.OBJ_nid2sn(nid)
1934 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001935
1936PKCS7Type = PKCS7
1937
1938
1939
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001940class PKCS12(object):
1941 def __init__(self):
1942 self._pkey = None
1943 self._cert = None
1944 self._cacerts = None
1945 self._friendlyname = None
1946
1947
1948 def get_certificate(self):
1949 """
1950 Return certificate portion of the PKCS12 structure
1951
1952 :return: X509 object containing the certificate
1953 """
1954 return self._cert
1955
1956
1957 def set_certificate(self, cert):
1958 """
1959 Replace the certificate portion of the PKCS12 structure
1960
1961 :param cert: The new certificate.
1962 :type cert: :py:class:`X509` or :py:data:`None`
1963 :return: None
1964 """
1965 if not isinstance(cert, X509):
1966 raise TypeError("cert must be an X509 instance")
1967 self._cert = cert
1968
1969
1970 def get_privatekey(self):
1971 """
1972 Return private key portion of the PKCS12 structure
1973
1974 :returns: PKey object containing the private key
1975 """
1976 return self._pkey
1977
1978
1979 def set_privatekey(self, pkey):
1980 """
1981 Replace or set the certificate portion of the PKCS12 structure
1982
1983 :param pkey: The new private key.
1984 :type pkey: :py:class:`PKey`
1985 :return: None
1986 """
1987 if not isinstance(pkey, PKey):
1988 raise TypeError("pkey must be a PKey instance")
1989 self._pkey = pkey
1990
1991
1992 def get_ca_certificates(self):
1993 """
1994 Return CA certificates within of the PKCS12 object
1995
1996 :return: A newly created tuple containing the CA certificates in the chain,
1997 if any are present, or None if no CA certificates are present.
1998 """
1999 if self._cacerts is not None:
2000 return tuple(self._cacerts)
2001
2002
2003 def set_ca_certificates(self, cacerts):
2004 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08002005 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002006
2007 :param cacerts: The new CA certificates.
2008 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
2009 :return: None
2010 """
2011 if cacerts is None:
2012 self._cacerts = None
2013 else:
2014 cacerts = list(cacerts)
2015 for cert in cacerts:
2016 if not isinstance(cert, X509):
2017 raise TypeError("iterable must only contain X509 instances")
2018 self._cacerts = cacerts
2019
2020
2021 def set_friendlyname(self, name):
2022 """
2023 Replace or set the certificate portion of the PKCS12 structure
2024
2025 :param name: The new friendly name.
2026 :type name: :py:class:`bytes`
2027 :return: None
2028 """
2029 if name is None:
2030 self._friendlyname = None
2031 elif not isinstance(name, bytes):
2032 raise TypeError("name must be a byte string or None (not %r)" % (name,))
2033 self._friendlyname = name
2034
2035
2036 def get_friendlyname(self):
2037 """
2038 Return friendly name portion of the PKCS12 structure
2039
2040 :returns: String containing the friendlyname
2041 """
2042 return self._friendlyname
2043
2044
2045 def export(self, passphrase=None, iter=2048, maciter=1):
2046 """
2047 Dump a PKCS12 object as a string. See also "man PKCS12_create".
2048
2049 :param passphrase: used to encrypt the PKCS12
2050 :type passphrase: :py:data:`bytes`
2051
2052 :param iter: How many times to repeat the encryption
2053 :type iter: :py:data:`int`
2054
2055 :param maciter: How many times to repeat the MAC
2056 :type maciter: :py:data:`int`
2057
2058 :return: The string containing the PKCS12
2059 """
2060 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002061 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002062 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002063 cacerts = _lib.sk_X509_new_null()
2064 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002065 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002066 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002067
2068 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002069 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002070
2071 friendlyname = self._friendlyname
2072 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002073 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002074
2075 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002076 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002077 else:
2078 pkey = self._pkey._pkey
2079
2080 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002081 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002082 else:
2083 cert = self._cert._x509
2084
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002085 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002086 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002087 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2088 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002089 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002090 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002091 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002092 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002093
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002094 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002095 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002096 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002097
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002098PKCS12Type = PKCS12
2099
2100
2101
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002102class NetscapeSPKI(object):
2103 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002104 spki = _lib.NETSCAPE_SPKI_new()
2105 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002106
2107
2108 def sign(self, pkey, digest):
2109 """
2110 Sign the certificate request using the supplied key and digest
2111
2112 :param pkey: The key to sign with
2113 :param digest: The message digest to use
2114 :return: None
2115 """
2116 if pkey._only_public:
2117 raise ValueError("Key has only public part")
2118
2119 if not pkey._initialized:
2120 raise ValueError("Key is uninitialized")
2121
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002122 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002123 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002124 raise ValueError("No such digest method")
2125
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002126 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002127 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002128 # TODO: This is untested.
2129 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002130
2131
2132 def verify(self, key):
2133 """
2134 Verifies a certificate request using the supplied public key
2135
2136 :param key: a public key
2137 :return: True if the signature is correct.
2138 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2139 problem verifying the signature.
2140 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002141 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002142 if answer <= 0:
2143 _raise_current_error()
2144 return True
2145
2146
2147 def b64_encode(self):
2148 """
2149 Generate a base64 encoded string from an SPKI
2150
2151 :return: The base64 encoded string
2152 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002153 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2154 result = _ffi.string(encoded)
2155 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002156 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002157
2158
2159 def get_pubkey(self):
2160 """
2161 Get the public key of the certificate
2162
2163 :return: The public key
2164 """
2165 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002166 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2167 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002168 # TODO: This is untested.
2169 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002170 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002171 pkey._only_public = True
2172 return pkey
2173
2174
2175 def set_pubkey(self, pkey):
2176 """
2177 Set the public key of the certificate
2178
2179 :param pkey: The public key
2180 :return: None
2181 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002182 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002183 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002184 # TODO: This is untested.
2185 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002186NetscapeSPKIType = NetscapeSPKI
2187
2188
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002189class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002190 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002191 if type != FILETYPE_PEM and passphrase is not None:
2192 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002193 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002194 self._more_args = more_args
2195 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002196 self._problems = []
2197
2198
2199 @property
2200 def callback(self):
2201 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002202 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002203 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002204 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002205 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002206 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002207 else:
2208 raise TypeError("Last argument must be string or callable")
2209
2210
2211 @property
2212 def callback_args(self):
2213 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002214 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002215 elif isinstance(self._passphrase, bytes):
2216 return self._passphrase
2217 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002218 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002219 else:
2220 raise TypeError("Last argument must be string or callable")
2221
2222
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002223 def raise_if_problem(self, exceptionType=Error):
2224 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002225 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002226 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002227 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002228 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002229 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002230 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002231
2232
2233 def _read_passphrase(self, buf, size, rwflag, userdata):
2234 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002235 if self._more_args:
2236 result = self._passphrase(size, rwflag, userdata)
2237 else:
2238 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002239 if not isinstance(result, bytes):
2240 raise ValueError("String expected")
2241 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002242 if self._truncate:
2243 result = result[:size]
2244 else:
2245 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002246 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002247 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002248 return len(result)
2249 except Exception as e:
2250 self._problems.append(e)
2251 return 0
2252
2253
2254
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002255def load_privatekey(type, buffer, passphrase=None):
2256 """
2257 Load a private key from a buffer
2258
2259 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2260 :param buffer: The buffer the key is stored in
2261 :param passphrase: (optional) if encrypted PEM format, this can be
2262 either the passphrase to use, or a callback for
2263 providing the passphrase.
2264
2265 :return: The PKey object
2266 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002267 if isinstance(buffer, _text_type):
2268 buffer = buffer.encode("ascii")
2269
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002270 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002271
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002272 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002273 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002274 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2275 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002276 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002277 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002278 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002279 else:
2280 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2281
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002282 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002283 _raise_current_error()
2284
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002285 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002286 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002287 return pkey
2288
2289
2290
2291def dump_certificate_request(type, req):
2292 """
2293 Dump a certificate request to a buffer
2294
2295 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2296 :param req: The certificate request to dump
2297 :return: The buffer with the dumped certificate request in
2298 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002299 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002300
2301 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002302 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002303 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002304 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002305 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002306 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002307 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002308 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002309
2310 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002311 # TODO: This is untested.
2312 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002313
2314 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002315
2316
2317
2318def load_certificate_request(type, buffer):
2319 """
2320 Load a certificate request from a buffer
2321
2322 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2323 :param buffer: The buffer the certificate request is stored in
2324 :return: The X509Req object
2325 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002326 if isinstance(buffer, _text_type):
2327 buffer = buffer.encode("ascii")
2328
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002329 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002330
2331 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002332 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002333 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002334 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002335 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002336 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002337
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002338 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002339 # TODO: This is untested.
2340 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002341
2342 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002343 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002344 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002345
2346
2347
2348def sign(pkey, data, digest):
2349 """
2350 Sign data with a digest
2351
2352 :param pkey: Pkey to sign with
2353 :param data: data to be signed
2354 :param digest: message digest to use
2355 :return: signature
2356 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002357 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002358 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002359 raise ValueError("No such digest method")
2360
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002361 md_ctx = _ffi.new("EVP_MD_CTX*")
2362 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002363
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002364 _lib.EVP_SignInit(md_ctx, digest_obj)
2365 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002366
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002367 signature_buffer = _ffi.new("unsigned char[]", 512)
2368 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002369 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002370 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002371 md_ctx, signature_buffer, signature_length, pkey._pkey)
2372
2373 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002374 # TODO: This is untested.
2375 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002376
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002377 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002378
2379
2380
2381def verify(cert, signature, data, digest):
2382 """
2383 Verify a signature
2384
2385 :param cert: signing certificate (X509 object)
2386 :param signature: signature returned by sign function
2387 :param data: data to be verified
2388 :param digest: message digest to use
2389 :return: None if the signature is correct, raise exception otherwise
2390 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002391 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002392 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002393 raise ValueError("No such digest method")
2394
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002395 pkey = _lib.X509_get_pubkey(cert._x509)
2396 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002397 # TODO: This is untested.
2398 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002399 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002400
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002401 md_ctx = _ffi.new("EVP_MD_CTX*")
2402 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002403
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002404 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2405 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2406 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002407
2408 if verify_result != 1:
2409 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002410
2411
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002412def load_crl(type, buffer):
2413 """
2414 Load a certificate revocation list from a buffer
2415
2416 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2417 :param buffer: The buffer the CRL is stored in
2418
2419 :return: The PKey object
2420 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002421 if isinstance(buffer, _text_type):
2422 buffer = buffer.encode("ascii")
2423
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002424 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002425
2426 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002427 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002428 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002429 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002430 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002431 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2432
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002433 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002434 _raise_current_error()
2435
2436 result = CRL.__new__(CRL)
2437 result._crl = crl
2438 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002439
2440
2441
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002442def load_pkcs7_data(type, buffer):
2443 """
2444 Load pkcs7 data from a buffer
2445
2446 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2447 :param buffer: The buffer with the pkcs7 data.
2448 :return: The PKCS7 object
2449 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002450 if isinstance(buffer, _text_type):
2451 buffer = buffer.encode("ascii")
2452
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002453 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002454
2455 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002456 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002457 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002458 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002459 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002460 # TODO: This is untested.
2461 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002462 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2463
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002464 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002465 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002466
2467 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002468 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002469 return pypkcs7
2470
2471
2472
Stephen Holsapple38482622014-04-05 20:29:34 -07002473def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002474 """
2475 Load a PKCS12 object from a buffer
2476
2477 :param buffer: The buffer the certificate is stored in
2478 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2479 :returns: The PKCS12 object
2480 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002481 if isinstance(buffer, _text_type):
2482 buffer = buffer.encode("ascii")
2483
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002484 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002485
Stephen Holsapple38482622014-04-05 20:29:34 -07002486 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2487 # password based encryption no password and a zero length password are two
2488 # different things, but OpenSSL implementation will try both to figure out
2489 # which one works.
2490 if not passphrase:
2491 passphrase = _ffi.NULL
2492
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002493 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2494 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002495 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002496 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002497
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002498 pkey = _ffi.new("EVP_PKEY**")
2499 cert = _ffi.new("X509**")
2500 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002501
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002502 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002503 if not parse_result:
2504 _raise_current_error()
2505
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002506 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002507
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002508 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2509 # queue for no particular reason. This error isn't interesting to anyone
2510 # outside this function. It's not even interesting to us. Get rid of it.
2511 try:
2512 _raise_current_error()
2513 except Error:
2514 pass
2515
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002516 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002517 pykey = None
2518 else:
2519 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002520 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002521
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002522 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002523 pycert = None
2524 friendlyname = None
2525 else:
2526 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002527 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002528
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002529 friendlyname_length = _ffi.new("int*")
2530 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2531 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2532 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002533 friendlyname = None
2534
2535 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002536 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002537 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002538 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002539 pycacerts.append(pycacert)
2540 if not pycacerts:
2541 pycacerts = None
2542
2543 pkcs12 = PKCS12.__new__(PKCS12)
2544 pkcs12._pkey = pykey
2545 pkcs12._cert = pycert
2546 pkcs12._cacerts = pycacerts
2547 pkcs12._friendlyname = friendlyname
2548 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002549
2550
2551def _initialize_openssl_threads(get_ident, Lock):
2552 import _ssl
2553 return
2554
2555 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2556
2557 def locking_function(mode, index, filename, line):
2558 if mode & _lib.CRYPTO_LOCK:
2559 locks[index].acquire()
2560 else:
2561 locks[index].release()
2562
2563 _lib.CRYPTO_set_id_callback(
2564 _ffi.callback("unsigned long (*)(void)", get_ident))
2565
2566 _lib.CRYPTO_set_locking_callback(
2567 _ffi.callback(
2568 "void (*)(int, int, const char*, int)", locking_function))
2569
2570
2571try:
2572 from thread import get_ident
2573 from threading import Lock
2574except ImportError:
2575 pass
2576else:
2577 _initialize_openssl_threads(get_ident, Lock)
2578 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002579
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002580# There are no direct unit tests for this initialization. It is tested
2581# indirectly since it is necessary for functions like dump_privatekey when
2582# using encryption.
2583#
2584# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2585# and some other similar tests may fail without this (though they may not if
2586# the Python runtime has already done some initialization of the underlying
2587# OpenSSL library (and is linked against the same one that cryptography is
2588# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002589_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002590
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002591# This is similar but exercised mainly by exception_from_error_queue. It calls
2592# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2593_lib.SSL_load_error_strings()