blob: c3d4c9b360ac7ab9d778c23788fbe32418038943 [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 ]
Stephen Holsapple1f713eb2015-02-09 19:19:44 -08001442 # A context error should always be associated with a certificate, so we
1443 # expect this call to never return :class:`None`.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001444 _x509 = _lib.X509_STORE_CTX_get_current_cert(self._store_ctx)
Stephen Holsapple1f713eb2015-02-09 19:19:44 -08001445 _cert = _lib.X509_dup(_x509)
1446 pycert = X509.__new__(X509)
1447 pycert._x509 = _ffi.gc(_cert, _lib.X509_free)
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001448 return X509StoreContextError(errors, pycert)
1449
1450
1451 def verify_certificate(self):
1452 """
1453 Verify a certificate in a context.
1454
1455 :param store_ctx: The :py:class:`X509StoreContext` to verify.
1456 :raises: Error
1457 """
1458 self._init()
1459 ret = _lib.X509_verify_cert(self._store_ctx)
1460 self._cleanup()
1461 if ret <= 0:
1462 raise self._exception_from_context()
1463
1464
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001465
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001466def load_certificate(type, buffer):
1467 """
1468 Load a certificate from a buffer
1469
1470 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1471
1472 :param buffer: The buffer the certificate is stored in
1473 :type buffer: :py:class:`bytes`
1474
1475 :return: The X509 object
1476 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001477 if isinstance(buffer, _text_type):
1478 buffer = buffer.encode("ascii")
1479
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001480 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001481
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001482 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001483 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001484 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001485 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001486 else:
1487 raise ValueError(
1488 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001489
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001490 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001491 _raise_current_error()
1492
1493 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001494 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001495 return cert
1496
1497
1498def dump_certificate(type, cert):
1499 """
1500 Dump a certificate to a buffer
1501
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001502 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1503 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001504 :param cert: The certificate to dump
1505 :return: The buffer with the dumped certificate in
1506 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001507 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001508
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001509 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001510 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001511 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001512 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001513 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001514 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001515 else:
1516 raise ValueError(
1517 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1518 "FILETYPE_TEXT")
1519
1520 return _bio_to_string(bio)
1521
1522
1523
1524def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1525 """
1526 Dump a private key to a buffer
1527
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001528 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1529 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001530 :param pkey: The PKey to dump
1531 :param cipher: (optional) if encrypted PEM format, the cipher to
1532 use
1533 :param passphrase: (optional) if encrypted PEM format, this can be either
1534 the passphrase to use, or a callback for providing the
1535 passphrase.
1536 :return: The buffer with the dumped key in
1537 :rtype: :py:data:`str`
1538 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001539 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001540
1541 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001542 if passphrase is None:
1543 raise TypeError(
1544 "if a value is given for cipher "
1545 "one must also be given for passphrase")
1546 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001547 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001548 raise ValueError("Invalid cipher name")
1549 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001550 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001551
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001552 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001553 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001554 result_code = _lib.PEM_write_bio_PrivateKey(
1555 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001556 helper.callback, helper.callback_args)
1557 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001558 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001559 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001560 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001561 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1562 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001563 # TODO RSA_free(rsa)?
1564 else:
1565 raise ValueError(
1566 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1567 "FILETYPE_TEXT")
1568
1569 if result_code == 0:
1570 _raise_current_error()
1571
1572 return _bio_to_string(bio)
1573
1574
1575
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001576def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001577 copy = _lib.X509_REVOKED_new()
1578 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001579 # TODO: This is untested.
1580 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001581
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001582 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001583 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001584 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001585
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001586 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001587 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001588 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001589
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001590 if original.extensions != _ffi.NULL:
1591 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1592 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1593 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1594 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1595 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001596 copy.extensions = extension_stack
1597
1598 copy.sequence = original.sequence
1599 return copy
1600
1601
1602
1603class Revoked(object):
1604 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1605 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1606 # OCSP_crl_reason_str. We use the latter, just like the command line
1607 # program.
1608 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001609 b"unspecified",
1610 b"keyCompromise",
1611 b"CACompromise",
1612 b"affiliationChanged",
1613 b"superseded",
1614 b"cessationOfOperation",
1615 b"certificateHold",
1616 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001617 ]
1618
1619 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001620 revoked = _lib.X509_REVOKED_new()
1621 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001622
1623
1624 def set_serial(self, hex_str):
1625 """
1626 Set the serial number of a revoked Revoked structure
1627
1628 :param hex_str: The new serial number.
1629 :type hex_str: :py:data:`str`
1630 :return: None
1631 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001632 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1633 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001634 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001635 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001636 if not bn_result:
1637 raise ValueError("bad hex string")
1638
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001639 asn1_serial = _ffi.gc(
1640 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1641 _lib.ASN1_INTEGER_free)
1642 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001643
1644
1645 def get_serial(self):
1646 """
1647 Return the serial number of a Revoked structure
1648
1649 :return: The serial number as a string
1650 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001651 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001652
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001653 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001654 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001655 # TODO: This is untested.
1656 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001657
1658 return _bio_to_string(bio)
1659
1660
1661 def _delete_reason(self):
1662 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001663 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1664 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1665 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1666 _lib.X509_EXTENSION_free(ext)
1667 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001668 break
1669
1670
1671 def set_reason(self, reason):
1672 """
1673 Set the reason of a Revoked object.
1674
1675 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1676
1677 :param reason: The reason string.
1678 :type reason: :py:class:`str` or :py:class:`NoneType`
1679 :return: None
1680 """
1681 if reason is None:
1682 self._delete_reason()
1683 elif not isinstance(reason, bytes):
1684 raise TypeError("reason must be None or a byte string")
1685 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001686 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001687 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1688
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001689 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1690 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001691 # TODO: This is untested.
1692 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001693 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001694
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001695 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1696 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001697 # TODO: This is untested.
1698 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001699
1700 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001701 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1702 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001703
1704 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001705 # TODO: This is untested.
1706 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001707
1708
1709 def get_reason(self):
1710 """
1711 Return the reason of a Revoked object.
1712
1713 :return: The reason as a string
1714 """
1715 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001716 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1717 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1718 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001719 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001720
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001721 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001722 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001723 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001724 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001725 # TODO: This is untested.
1726 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001727
1728 return _bio_to_string(bio)
1729
1730
1731 def all_reasons(self):
1732 """
1733 Return a list of all the supported reason strings.
1734
1735 :return: A list of reason strings.
1736 """
1737 return self._crl_reasons[:]
1738
1739
1740 def set_rev_date(self, when):
1741 """
1742 Set the revocation timestamp
1743
1744 :param when: A string giving the timestamp, in the format:
1745
1746 YYYYMMDDhhmmssZ
1747 YYYYMMDDhhmmss+hhmm
1748 YYYYMMDDhhmmss-hhmm
1749
1750 :return: None
1751 """
1752 return _set_asn1_time(self._revoked.revocationDate, when)
1753
1754
1755 def get_rev_date(self):
1756 """
1757 Retrieve the revocation date
1758
1759 :return: A string giving the timestamp, in the format:
1760
1761 YYYYMMDDhhmmssZ
1762 YYYYMMDDhhmmss+hhmm
1763 YYYYMMDDhhmmss-hhmm
1764 """
1765 return _get_asn1_time(self._revoked.revocationDate)
1766
1767
1768
1769class CRL(object):
1770 def __init__(self):
1771 """
1772 Create a new empty CRL object.
1773 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001774 crl = _lib.X509_CRL_new()
1775 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001776
1777
1778 def get_revoked(self):
1779 """
1780 Return revoked portion of the CRL structure (by value not reference).
1781
1782 :return: A tuple of Revoked objects.
1783 """
1784 results = []
1785 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001786 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1787 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001788 revoked_copy = _X509_REVOKED_dup(revoked)
1789 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001790 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001791 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001792 if results:
1793 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001794
1795
1796 def add_revoked(self, revoked):
1797 """
1798 Add a revoked (by value not reference) to the CRL structure
1799
1800 :param revoked: The new revoked.
1801 :type revoked: :class:`X509`
1802
1803 :return: None
1804 """
1805 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001806 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001807 # TODO: This is untested.
1808 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001809
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001810 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001811 if add_result == 0:
1812 # TODO: This is untested.
1813 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001814
1815
1816 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1817 """
1818 export a CRL as a string
1819
1820 :param cert: Used to sign CRL.
1821 :type cert: :class:`X509`
1822
1823 :param key: Used to sign CRL.
1824 :type key: :class:`PKey`
1825
1826 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1827
1828 :param days: The number of days until the next update of this CRL.
1829 :type days: :py:data:`int`
1830
1831 :return: :py:data:`str`
1832 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001833 if not isinstance(cert, X509):
1834 raise TypeError("cert must be an X509 instance")
1835 if not isinstance(key, PKey):
1836 raise TypeError("key must be a PKey instance")
1837 if not isinstance(type, int):
1838 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001839
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001840 bio = _lib.BIO_new(_lib.BIO_s_mem())
1841 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001842 # TODO: This is untested.
1843 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001844
1845 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001846 sometime = _lib.ASN1_TIME_new()
1847 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001848 # TODO: This is untested.
1849 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001850
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001851 _lib.X509_gmtime_adj(sometime, 0)
1852 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001853
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001854 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1855 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001856
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001857 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001858
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001859 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001860 if not sign_result:
1861 _raise_current_error()
1862
1863 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001864 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001865 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001866 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001867 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001868 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001869 else:
1870 raise ValueError(
1871 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1872
1873 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001874 # TODO: This is untested.
1875 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001876
1877 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001878CRLType = CRL
1879
1880
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001881
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001882class PKCS7(object):
1883 def type_is_signed(self):
1884 """
1885 Check if this NID_pkcs7_signed object
1886
1887 :return: True if the PKCS7 is of type signed
1888 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001889 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001890 return True
1891 return False
1892
1893
1894 def type_is_enveloped(self):
1895 """
1896 Check if this NID_pkcs7_enveloped object
1897
1898 :returns: True if the PKCS7 is of type enveloped
1899 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001900 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001901 return True
1902 return False
1903
1904
1905 def type_is_signedAndEnveloped(self):
1906 """
1907 Check if this NID_pkcs7_signedAndEnveloped object
1908
1909 :returns: True if the PKCS7 is of type signedAndEnveloped
1910 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001911 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001912 return True
1913 return False
1914
1915
1916 def type_is_data(self):
1917 """
1918 Check if this NID_pkcs7_data object
1919
1920 :return: True if the PKCS7 is of type data
1921 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001922 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001923 return True
1924 return False
1925
1926
1927 def get_type_name(self):
1928 """
1929 Returns the type name of the PKCS7 structure
1930
1931 :return: A string with the typename
1932 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001933 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1934 string_type = _lib.OBJ_nid2sn(nid)
1935 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001936
1937PKCS7Type = PKCS7
1938
1939
1940
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001941class PKCS12(object):
1942 def __init__(self):
1943 self._pkey = None
1944 self._cert = None
1945 self._cacerts = None
1946 self._friendlyname = None
1947
1948
1949 def get_certificate(self):
1950 """
1951 Return certificate portion of the PKCS12 structure
1952
1953 :return: X509 object containing the certificate
1954 """
1955 return self._cert
1956
1957
1958 def set_certificate(self, cert):
1959 """
1960 Replace the certificate portion of the PKCS12 structure
1961
1962 :param cert: The new certificate.
1963 :type cert: :py:class:`X509` or :py:data:`None`
1964 :return: None
1965 """
1966 if not isinstance(cert, X509):
1967 raise TypeError("cert must be an X509 instance")
1968 self._cert = cert
1969
1970
1971 def get_privatekey(self):
1972 """
1973 Return private key portion of the PKCS12 structure
1974
1975 :returns: PKey object containing the private key
1976 """
1977 return self._pkey
1978
1979
1980 def set_privatekey(self, pkey):
1981 """
1982 Replace or set the certificate portion of the PKCS12 structure
1983
1984 :param pkey: The new private key.
1985 :type pkey: :py:class:`PKey`
1986 :return: None
1987 """
1988 if not isinstance(pkey, PKey):
1989 raise TypeError("pkey must be a PKey instance")
1990 self._pkey = pkey
1991
1992
1993 def get_ca_certificates(self):
1994 """
1995 Return CA certificates within of the PKCS12 object
1996
1997 :return: A newly created tuple containing the CA certificates in the chain,
1998 if any are present, or None if no CA certificates are present.
1999 """
2000 if self._cacerts is not None:
2001 return tuple(self._cacerts)
2002
2003
2004 def set_ca_certificates(self, cacerts):
2005 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08002006 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002007
2008 :param cacerts: The new CA certificates.
2009 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
2010 :return: None
2011 """
2012 if cacerts is None:
2013 self._cacerts = None
2014 else:
2015 cacerts = list(cacerts)
2016 for cert in cacerts:
2017 if not isinstance(cert, X509):
2018 raise TypeError("iterable must only contain X509 instances")
2019 self._cacerts = cacerts
2020
2021
2022 def set_friendlyname(self, name):
2023 """
2024 Replace or set the certificate portion of the PKCS12 structure
2025
2026 :param name: The new friendly name.
2027 :type name: :py:class:`bytes`
2028 :return: None
2029 """
2030 if name is None:
2031 self._friendlyname = None
2032 elif not isinstance(name, bytes):
2033 raise TypeError("name must be a byte string or None (not %r)" % (name,))
2034 self._friendlyname = name
2035
2036
2037 def get_friendlyname(self):
2038 """
2039 Return friendly name portion of the PKCS12 structure
2040
2041 :returns: String containing the friendlyname
2042 """
2043 return self._friendlyname
2044
2045
2046 def export(self, passphrase=None, iter=2048, maciter=1):
2047 """
2048 Dump a PKCS12 object as a string. See also "man PKCS12_create".
2049
2050 :param passphrase: used to encrypt the PKCS12
2051 :type passphrase: :py:data:`bytes`
2052
2053 :param iter: How many times to repeat the encryption
2054 :type iter: :py:data:`int`
2055
2056 :param maciter: How many times to repeat the MAC
2057 :type maciter: :py:data:`int`
2058
2059 :return: The string containing the PKCS12
2060 """
2061 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002062 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002063 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002064 cacerts = _lib.sk_X509_new_null()
2065 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002066 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002067 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002068
2069 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002070 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002071
2072 friendlyname = self._friendlyname
2073 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002074 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002075
2076 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002077 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002078 else:
2079 pkey = self._pkey._pkey
2080
2081 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002082 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002083 else:
2084 cert = self._cert._x509
2085
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002086 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002087 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002088 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2089 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002090 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002091 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002092 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002093 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002094
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002095 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002096 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002097 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002098
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002099PKCS12Type = PKCS12
2100
2101
2102
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002103class NetscapeSPKI(object):
2104 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002105 spki = _lib.NETSCAPE_SPKI_new()
2106 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002107
2108
2109 def sign(self, pkey, digest):
2110 """
2111 Sign the certificate request using the supplied key and digest
2112
2113 :param pkey: The key to sign with
2114 :param digest: The message digest to use
2115 :return: None
2116 """
2117 if pkey._only_public:
2118 raise ValueError("Key has only public part")
2119
2120 if not pkey._initialized:
2121 raise ValueError("Key is uninitialized")
2122
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002123 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002124 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002125 raise ValueError("No such digest method")
2126
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002127 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002128 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002129 # TODO: This is untested.
2130 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002131
2132
2133 def verify(self, key):
2134 """
2135 Verifies a certificate request using the supplied public key
2136
2137 :param key: a public key
2138 :return: True if the signature is correct.
2139 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2140 problem verifying the signature.
2141 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002142 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002143 if answer <= 0:
2144 _raise_current_error()
2145 return True
2146
2147
2148 def b64_encode(self):
2149 """
2150 Generate a base64 encoded string from an SPKI
2151
2152 :return: The base64 encoded string
2153 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002154 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2155 result = _ffi.string(encoded)
2156 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002157 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002158
2159
2160 def get_pubkey(self):
2161 """
2162 Get the public key of the certificate
2163
2164 :return: The public key
2165 """
2166 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002167 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2168 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002169 # TODO: This is untested.
2170 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002171 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002172 pkey._only_public = True
2173 return pkey
2174
2175
2176 def set_pubkey(self, pkey):
2177 """
2178 Set the public key of the certificate
2179
2180 :param pkey: The public key
2181 :return: None
2182 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002183 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002184 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002185 # TODO: This is untested.
2186 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002187NetscapeSPKIType = NetscapeSPKI
2188
2189
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002190class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002191 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002192 if type != FILETYPE_PEM and passphrase is not None:
2193 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002194 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002195 self._more_args = more_args
2196 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002197 self._problems = []
2198
2199
2200 @property
2201 def callback(self):
2202 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002203 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002204 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002205 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002206 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002207 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002208 else:
2209 raise TypeError("Last argument must be string or callable")
2210
2211
2212 @property
2213 def callback_args(self):
2214 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002215 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002216 elif isinstance(self._passphrase, bytes):
2217 return self._passphrase
2218 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002219 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002220 else:
2221 raise TypeError("Last argument must be string or callable")
2222
2223
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002224 def raise_if_problem(self, exceptionType=Error):
2225 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002226 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002227 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002228 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002229 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002230 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002231 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002232
2233
2234 def _read_passphrase(self, buf, size, rwflag, userdata):
2235 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002236 if self._more_args:
2237 result = self._passphrase(size, rwflag, userdata)
2238 else:
2239 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002240 if not isinstance(result, bytes):
2241 raise ValueError("String expected")
2242 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002243 if self._truncate:
2244 result = result[:size]
2245 else:
2246 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002247 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002248 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002249 return len(result)
2250 except Exception as e:
2251 self._problems.append(e)
2252 return 0
2253
2254
2255
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002256def load_privatekey(type, buffer, passphrase=None):
2257 """
2258 Load a private key from a buffer
2259
2260 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2261 :param buffer: The buffer the key is stored in
2262 :param passphrase: (optional) if encrypted PEM format, this can be
2263 either the passphrase to use, or a callback for
2264 providing the passphrase.
2265
2266 :return: The PKey object
2267 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002268 if isinstance(buffer, _text_type):
2269 buffer = buffer.encode("ascii")
2270
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002271 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002272
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002273 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002274 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002275 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2276 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002277 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002278 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002279 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002280 else:
2281 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2282
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002283 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002284 _raise_current_error()
2285
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002286 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002287 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002288 return pkey
2289
2290
2291
2292def dump_certificate_request(type, req):
2293 """
2294 Dump a certificate request to a buffer
2295
2296 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2297 :param req: The certificate request to dump
2298 :return: The buffer with the dumped certificate request in
2299 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002300 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002301
2302 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002303 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002304 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002305 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002306 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002307 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002308 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002309 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002310
2311 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002312 # TODO: This is untested.
2313 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002314
2315 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002316
2317
2318
2319def load_certificate_request(type, buffer):
2320 """
2321 Load a certificate request from a buffer
2322
2323 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2324 :param buffer: The buffer the certificate request is stored in
2325 :return: The X509Req object
2326 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002327 if isinstance(buffer, _text_type):
2328 buffer = buffer.encode("ascii")
2329
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002330 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002331
2332 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002333 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002334 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002335 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002336 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002337 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002338
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002339 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002340 # TODO: This is untested.
2341 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002342
2343 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002344 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002345 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002346
2347
2348
2349def sign(pkey, data, digest):
2350 """
2351 Sign data with a digest
2352
2353 :param pkey: Pkey to sign with
2354 :param data: data to be signed
2355 :param digest: message digest to use
2356 :return: signature
2357 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002358 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002359 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002360 raise ValueError("No such digest method")
2361
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002362 md_ctx = _ffi.new("EVP_MD_CTX*")
2363 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002364
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002365 _lib.EVP_SignInit(md_ctx, digest_obj)
2366 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002367
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002368 signature_buffer = _ffi.new("unsigned char[]", 512)
2369 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002370 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002371 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002372 md_ctx, signature_buffer, signature_length, pkey._pkey)
2373
2374 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002375 # TODO: This is untested.
2376 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002377
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002378 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002379
2380
2381
2382def verify(cert, signature, data, digest):
2383 """
2384 Verify a signature
2385
2386 :param cert: signing certificate (X509 object)
2387 :param signature: signature returned by sign function
2388 :param data: data to be verified
2389 :param digest: message digest to use
2390 :return: None if the signature is correct, raise exception otherwise
2391 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002392 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002393 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002394 raise ValueError("No such digest method")
2395
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002396 pkey = _lib.X509_get_pubkey(cert._x509)
2397 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002398 # TODO: This is untested.
2399 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002400 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002401
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002402 md_ctx = _ffi.new("EVP_MD_CTX*")
2403 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002404
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002405 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2406 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2407 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002408
2409 if verify_result != 1:
2410 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002411
2412
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002413def load_crl(type, buffer):
2414 """
2415 Load a certificate revocation list from a buffer
2416
2417 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2418 :param buffer: The buffer the CRL is stored in
2419
2420 :return: The PKey object
2421 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002422 if isinstance(buffer, _text_type):
2423 buffer = buffer.encode("ascii")
2424
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002425 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002426
2427 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002428 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002429 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002430 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002431 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002432 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2433
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002434 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002435 _raise_current_error()
2436
2437 result = CRL.__new__(CRL)
2438 result._crl = crl
2439 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002440
2441
2442
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002443def load_pkcs7_data(type, buffer):
2444 """
2445 Load pkcs7 data from a buffer
2446
2447 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2448 :param buffer: The buffer with the pkcs7 data.
2449 :return: The PKCS7 object
2450 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002451 if isinstance(buffer, _text_type):
2452 buffer = buffer.encode("ascii")
2453
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002454 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002455
2456 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002457 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002458 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002459 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002460 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002461 # TODO: This is untested.
2462 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002463 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2464
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002465 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002466 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002467
2468 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002469 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002470 return pypkcs7
2471
2472
2473
Stephen Holsapple38482622014-04-05 20:29:34 -07002474def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002475 """
2476 Load a PKCS12 object from a buffer
2477
2478 :param buffer: The buffer the certificate is stored in
2479 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2480 :returns: The PKCS12 object
2481 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002482 if isinstance(buffer, _text_type):
2483 buffer = buffer.encode("ascii")
2484
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002485 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002486
Stephen Holsapple38482622014-04-05 20:29:34 -07002487 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2488 # password based encryption no password and a zero length password are two
2489 # different things, but OpenSSL implementation will try both to figure out
2490 # which one works.
2491 if not passphrase:
2492 passphrase = _ffi.NULL
2493
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002494 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2495 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002496 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002497 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002498
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002499 pkey = _ffi.new("EVP_PKEY**")
2500 cert = _ffi.new("X509**")
2501 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002502
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002503 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002504 if not parse_result:
2505 _raise_current_error()
2506
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002507 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002508
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002509 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2510 # queue for no particular reason. This error isn't interesting to anyone
2511 # outside this function. It's not even interesting to us. Get rid of it.
2512 try:
2513 _raise_current_error()
2514 except Error:
2515 pass
2516
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002517 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002518 pykey = None
2519 else:
2520 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002521 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002522
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002523 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002524 pycert = None
2525 friendlyname = None
2526 else:
2527 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002528 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002529
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002530 friendlyname_length = _ffi.new("int*")
2531 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2532 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2533 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002534 friendlyname = None
2535
2536 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002537 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002538 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002539 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002540 pycacerts.append(pycacert)
2541 if not pycacerts:
2542 pycacerts = None
2543
2544 pkcs12 = PKCS12.__new__(PKCS12)
2545 pkcs12._pkey = pykey
2546 pkcs12._cert = pycert
2547 pkcs12._cacerts = pycacerts
2548 pkcs12._friendlyname = friendlyname
2549 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002550
2551
2552def _initialize_openssl_threads(get_ident, Lock):
2553 import _ssl
2554 return
2555
2556 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2557
2558 def locking_function(mode, index, filename, line):
2559 if mode & _lib.CRYPTO_LOCK:
2560 locks[index].acquire()
2561 else:
2562 locks[index].release()
2563
2564 _lib.CRYPTO_set_id_callback(
2565 _ffi.callback("unsigned long (*)(void)", get_ident))
2566
2567 _lib.CRYPTO_set_locking_callback(
2568 _ffi.callback(
2569 "void (*)(int, int, const char*, int)", locking_function))
2570
2571
2572try:
2573 from thread import get_ident
2574 from threading import Lock
2575except ImportError:
2576 pass
2577else:
2578 _initialize_openssl_threads(get_ident, Lock)
2579 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002580
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002581# There are no direct unit tests for this initialization. It is tested
2582# indirectly since it is necessary for functions like dump_privatekey when
2583# using encryption.
2584#
2585# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2586# and some other similar tests may fail without this (though they may not if
2587# the Python runtime has already done some initialization of the underlying
2588# OpenSSL library (and is linked against the same one that cryptography is
2589# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002590_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002591
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002592# This is similar but exercised mainly by exception_from_error_queue. It calls
2593# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2594_lib.SSL_load_error_strings()