blob: b671d9eda1e839b1fc96636bb63953db52bb8f8d [file] [log] [blame]
Abraham Martin82efe3e2015-03-25 10:50:09 +00001from warnings import warn
2from sys import version_info
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08003from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05005from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05006from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
7
8from six import (
9 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -040010 text_type as _text_type,
11 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080012
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050013from OpenSSL._util import (
14 ffi as _ffi,
15 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050016 exception_from_error_queue as _exception_from_error_queue,
17 byte_string as _byte_string,
18 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080019
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050020FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
21FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080022
23# TODO This was an API mistake. OpenSSL has no such constant.
24FILETYPE_TEXT = 2 ** 16 - 1
25
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050026TYPE_RSA = _lib.EVP_PKEY_RSA
27TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080028
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080029
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050030class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050031 """
32 An error occurred in an `OpenSSL.crypto` API.
33 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050034
35
36_raise_current_error = partial(_exception_from_error_queue, Error)
37
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050038def _untested_error(where):
39 """
40 An OpenSSL API failed somehow. Additionally, the failure which was
41 encountered isn't one that's exercised by the test suite so future behavior
42 of pyOpenSSL is now somewhat less predictable.
43 """
44 raise RuntimeError("Unknown %s failure" % (where,))
45
46
47
48def _new_mem_buf(buffer=None):
49 """
50 Allocate a new OpenSSL memory BIO.
51
52 Arrange for the garbage collector to clean it up automatically.
53
54 :param buffer: None or some bytes to use to put into the BIO so that they
55 can be read out.
56 """
57 if buffer is None:
58 bio = _lib.BIO_new(_lib.BIO_s_mem())
59 free = _lib.BIO_free
60 else:
61 data = _ffi.new("char[]", buffer)
62 bio = _lib.BIO_new_mem_buf(data, len(buffer))
63 # Keep the memory alive as long as the bio is alive!
64 def free(bio, ref=data):
65 return _lib.BIO_free(bio)
66
67 if bio == _ffi.NULL:
68 # TODO: This is untested.
69 _raise_current_error()
70
71 bio = _ffi.gc(bio, free)
72 return bio
73
74
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050075
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080076def _bio_to_string(bio):
77 """
78 Copy the contents of an OpenSSL BIO object into a Python byte string.
79 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050080 result_buffer = _ffi.new('char**')
81 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
82 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080083
84
85
Jean-Paul Calderone57122982013-02-21 08:47:05 -080086def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050087 """
88 The the time value of an ASN1 time object.
89
90 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
91 castable to that type) which will have its value set.
92 @param when: A string representation of the desired time value.
93
94 @raise TypeError: If C{when} is not a L{bytes} string.
95 @raise ValueError: If C{when} does not represent a time in the required
96 format.
97 @raise RuntimeError: If the time value cannot be set for some other
98 (unspecified) reason.
99 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800100 if not isinstance(when, bytes):
101 raise TypeError("when must be a byte string")
102
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500103 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
104 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800105 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500106 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
107 _lib.ASN1_STRING_set(dummy, when, len(when))
108 check_result = _lib.ASN1_GENERALIZEDTIME_check(
109 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800110 if not check_result:
111 raise ValueError("Invalid string")
112 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500113 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800114
115
116
117def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500118 """
119 Retrieve the time value of an ASN1 time object.
120
121 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
122 that type) from which the time value will be retrieved.
123
124 @return: The time value from C{timestamp} as a L{bytes} string in a certain
125 format. Or C{None} if the object contains no time value.
126 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500127 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
128 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800129 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500130 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
131 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800132 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500133 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
134 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
135 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500136 # This may happen:
137 # - if timestamp was not an ASN1_TIME
138 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
139 # - if a copy of the time data from timestamp cannot be made for
140 # the newly allocated ASN1_GENERALIZEDTIME
141 #
142 # These are difficult to test. cffi enforces the ASN1_TIME type.
143 # Memory allocation failures are a pain to trigger
144 # deterministically.
145 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800146 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500147 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800148 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500149 string_data = _lib.ASN1_STRING_data(string_timestamp)
150 string_result = _ffi.string(string_data)
151 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800152 return string_result
153
154
155
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800156class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800157 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800158 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800159
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800160 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500161 pkey = _lib.EVP_PKEY_new()
162 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800163 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800164
165
166 def generate_key(self, type, bits):
167 """
168 Generate a key of a given type, with a given number of a bits
169
170 :param type: The key type (TYPE_RSA or TYPE_DSA)
171 :param bits: The number of bits
172
173 :return: None
174 """
175 if not isinstance(type, int):
176 raise TypeError("type must be an integer")
177
178 if not isinstance(bits, int):
179 raise TypeError("bits must be an integer")
180
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800181 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500182 exponent = _lib.BN_new()
183 exponent = _ffi.gc(exponent, _lib.BN_free)
184 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800185
186 if type == TYPE_RSA:
187 if bits <= 0:
188 raise ValueError("Invalid number of bits")
189
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500190 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800191
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500192 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500193 if result == 0:
194 # TODO: The test for this case is commented out. Different
195 # builds of OpenSSL appear to have different failure modes that
196 # make it hard to test. Visual inspection of the OpenSSL
197 # source reveals that a return value of 0 signals an error.
198 # Manual testing on a particular build of OpenSSL suggests that
199 # this is probably the appropriate way to handle those errors.
200 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800201
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500202 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800203 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500204 # TODO: It appears as though this can fail if an engine is in
205 # use which does not support RSA.
206 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800207
208 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500209 dsa = _lib.DSA_generate_parameters(
210 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
211 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500212 # TODO: This is untested.
213 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500214 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500215 # TODO: This is untested.
216 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500217 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500218 # TODO: This is untested.
219 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800220 else:
221 raise Error("No such key type")
222
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800223 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800224
225
226 def check(self):
227 """
228 Check the consistency of an RSA private key.
229
230 :return: True if key is consistent.
231 :raise Error: if the key is inconsistent.
232 :raise TypeError: if the key is of a type which cannot be checked.
233 Only RSA keys can currently be checked.
234 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800235 if self._only_public:
236 raise TypeError("public key only")
237
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500238 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800239 raise TypeError("key type unsupported")
240
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500241 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
242 rsa = _ffi.gc(rsa, _lib.RSA_free)
243 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800244 if result:
245 return True
246 _raise_current_error()
247
248
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800249 def type(self):
250 """
251 Returns the type of the key
252
253 :return: The type of the key.
254 """
255 return self._pkey.type
256
257
258 def bits(self):
259 """
260 Returns the number of bits of the key
261
262 :return: The number of bits of the key.
263 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500264 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800265PKeyType = PKey
266
267
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800268
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400269class _EllipticCurve(object):
270 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400271 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400272
273 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
274 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
275 instances each of which represents one curve supported by the system.
276 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400277 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400278 _curves = None
279
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400280 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400281 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400282 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400283 """
284 Implement cooperation with the right-hand side argument of ``!=``.
285
286 Python 3 seems to have dropped this cooperation in this very narrow
287 circumstance.
288 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400289 if isinstance(other, _EllipticCurve):
290 return super(_EllipticCurve, self).__ne__(other)
291 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400292
293
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400294 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400295 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400296 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400297 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400298
299 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400300
301 :return: A :py:type:`set` of ``cls`` instances giving the names of the
302 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400303 """
304 if lib.Cryptography_HAS_EC:
305 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
306 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
307 # The return value on this call should be num_curves again. We could
308 # check it to make sure but if it *isn't* then.. what could we do?
309 # Abort the whole process, I suppose...? -exarkun
310 lib.EC_get_builtin_curves(builtin_curves, num_curves)
311 return set(
312 cls.from_nid(lib, c.nid)
313 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400314 return set()
315
316
317 @classmethod
318 def _get_elliptic_curves(cls, lib):
319 """
320 Get, cache, and return the curves supported by OpenSSL.
321
322 :param lib: The OpenSSL library binding object.
323
324 :return: A :py:type:`set` of ``cls`` instances giving the names of the
325 elliptic curves the underlying library supports.
326 """
327 if cls._curves is None:
328 cls._curves = cls._load_elliptic_curves(lib)
329 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400330
331
332 @classmethod
333 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400334 """
335 Instantiate a new :py:class:`_EllipticCurve` associated with the given
336 OpenSSL NID.
337
338 :param lib: The OpenSSL library binding object.
339
340 :param nid: The OpenSSL NID the resulting curve object will represent.
341 This must be a curve NID (and not, for example, a hash NID) or
342 subsequent operations will fail in unpredictable ways.
343 :type nid: :py:class:`int`
344
345 :return: The curve object.
346 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400347 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
348
349
350 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400351 """
352 :param _lib: The :py:mod:`cryptography` binding instance used to
353 interface with OpenSSL.
354
355 :param _nid: The OpenSSL NID identifying the curve this object
356 represents.
357 :type _nid: :py:class:`int`
358
359 :param name: The OpenSSL short name identifying the curve this object
360 represents.
361 :type name: :py:class:`unicode`
362 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400363 self._lib = lib
364 self._nid = nid
365 self.name = name
366
367
368 def __repr__(self):
369 return "<Curve %r>" % (self.name,)
370
371
372 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400373 """
374 Create a new OpenSSL EC_KEY structure initialized to use this curve.
375
376 The structure is automatically garbage collected when the Python object
377 is garbage collected.
378 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400379 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
380 return _ffi.gc(key, _lib.EC_KEY_free)
381
382
383
384def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400385 """
386 Return a set of objects representing the elliptic curves supported in the
387 OpenSSL build in use.
388
389 The curve objects have a :py:class:`unicode` ``name`` attribute by which
390 they identify themselves.
391
392 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400393 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
394 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400395 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400396 return _EllipticCurve._get_elliptic_curves(_lib)
397
398
399
400def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400401 """
402 Return a single curve object selected by name.
403
404 See :py:func:`get_elliptic_curves` for information about curve objects.
405
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400406 :param name: The OpenSSL short name identifying the curve object to
407 retrieve.
408 :type name: :py:class:`unicode`
409
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400410 If the named curve is not supported then :py:class:`ValueError` is raised.
411 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400412 for curve in get_elliptic_curves():
413 if curve.name == name:
414 return curve
415 raise ValueError("unknown curve name", name)
416
417
418
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800419class X509Name(object):
420 def __init__(self, name):
421 """
422 Create a new X509Name, copying the given X509Name instance.
423
424 :param name: An X509Name object to copy
425 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500426 name = _lib.X509_NAME_dup(name._name)
427 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800428
429
430 def __setattr__(self, name, value):
431 if name.startswith('_'):
432 return super(X509Name, self).__setattr__(name, value)
433
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800434 # Note: we really do not want str subclasses here, so we do not use
435 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800436 if type(name) is not str:
437 raise TypeError("attribute name must be string, not '%.200s'" % (
438 type(value).__name__,))
439
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500440 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500441 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800442 try:
443 _raise_current_error()
444 except Error:
445 pass
446 raise AttributeError("No such attribute")
447
448 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500449 for i in range(_lib.X509_NAME_entry_count(self._name)):
450 ent = _lib.X509_NAME_get_entry(self._name, i)
451 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
452 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800453 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500454 ent = _lib.X509_NAME_delete_entry(self._name, i)
455 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800456 break
457
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500458 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800459 value = value.encode('utf-8')
460
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500461 add_result = _lib.X509_NAME_add_entry_by_NID(
462 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800463 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500464 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800465
466
467 def __getattr__(self, name):
468 """
469 Find attribute. An X509Name object has the following attributes:
470 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
471 organization (alias O), organizationalUnit (alias OU), commonName (alias
472 CN) and more...
473 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500474 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500475 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800476 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
477 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
478 # push something onto the error queue. If we don't clean that up
479 # now, someone else will bump into it later and be quite confused.
480 # See lp#314814.
481 try:
482 _raise_current_error()
483 except Error:
484 pass
485 return super(X509Name, self).__getattr__(name)
486
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500487 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800488 if entry_index == -1:
489 return None
490
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500491 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
492 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800493
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500494 result_buffer = _ffi.new("unsigned char**")
495 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800496 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500497 # TODO: This is untested.
498 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800499
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700500 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500501 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700502 finally:
503 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500504 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800505 return result
506
507
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500508 def _cmp(op):
509 def f(self, other):
510 if not isinstance(other, X509Name):
511 return NotImplemented
512 result = _lib.X509_NAME_cmp(self._name, other._name)
513 return op(result, 0)
514 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800515
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500516 __eq__ = _cmp(__eq__)
517 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800518
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500519 __lt__ = _cmp(__lt__)
520 __le__ = _cmp(__le__)
521
522 __gt__ = _cmp(__gt__)
523 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800524
525 def __repr__(self):
526 """
527 String representation of an X509Name
528 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500529 result_buffer = _ffi.new("char[]", 512);
530 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800531 self._name, result_buffer, len(result_buffer))
532
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500533 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500534 # TODO: This is untested.
535 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800536
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500537 return "<X509Name object '%s'>" % (
538 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800539
540
541 def hash(self):
542 """
543 Return the hash value of this name
544
545 :return: None
546 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500547 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800548
549
550 def der(self):
551 """
552 Return the DER encoding of this name
553
554 :return: A :py:class:`bytes` instance giving the DER encoded form of
555 this name.
556 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500557 result_buffer = _ffi.new('unsigned char**')
558 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800559 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500560 # TODO: This is untested.
561 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800562
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500563 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
564 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800565 return string_result
566
567
568 def get_components(self):
569 """
570 Returns the split-up components of this name.
571
572 :return: List of tuples (name, value).
573 """
574 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500575 for i in range(_lib.X509_NAME_entry_count(self._name)):
576 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800577
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500578 fname = _lib.X509_NAME_ENTRY_get_object(ent)
579 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800580
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500581 nid = _lib.OBJ_obj2nid(fname)
582 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800583
584 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500585 _ffi.string(name),
586 _ffi.string(
587 _lib.ASN1_STRING_data(fval),
588 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800589
590 return result
591X509NameType = X509Name
592
593
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800594class X509Extension(object):
595 def __init__(self, type_name, critical, value, subject=None, issuer=None):
596 """
597 :param typename: The name of the extension to create.
598 :type typename: :py:data:`str`
599
600 :param critical: A flag indicating whether this is a critical extension.
601
602 :param value: The value of the extension.
603 :type value: :py:data:`str`
604
605 :param subject: Optional X509 cert to use as subject.
606 :type subject: :py:class:`X509`
607
608 :param issuer: Optional X509 cert to use as issuer.
609 :type issuer: :py:class:`X509`
610
611 :return: The X509Extension object
612 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500613 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800614
615 # A context is necessary for any extension which uses the r2i conversion
616 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
617 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500618 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800619
620 # We have no configuration database - but perhaps we should (some
621 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500622 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800623
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800624 # Initialize the subject and issuer, if appropriate. ctx is a local,
625 # and as far as I can tell none of the X509V3_* APIs invoked here steal
626 # any references, so no need to mess with reference counts or duplicates.
627 if issuer is not None:
628 if not isinstance(issuer, X509):
629 raise TypeError("issuer must be an X509 instance")
630 ctx.issuer_cert = issuer._x509
631 if subject is not None:
632 if not isinstance(subject, X509):
633 raise TypeError("subject must be an X509 instance")
634 ctx.subject_cert = subject._x509
635
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800636 if critical:
637 # There are other OpenSSL APIs which would let us pass in critical
638 # separately, but they're harder to use, and since value is already
639 # a pile of crappy junk smuggling a ton of utterly important
640 # structured data, what's the point of trying to avoid nasty stuff
641 # with strings? (However, X509V3_EXT_i2d in particular seems like it
642 # would be a better API to invoke. I do not know where to get the
643 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500644 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800645
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500646 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
647 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800648 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500649 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800650
651
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400652 @property
653 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500654 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400655
656 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500657 _lib.GEN_EMAIL: "email",
658 _lib.GEN_DNS: "DNS",
659 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400660 }
661
662 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500663 method = _lib.X509V3_EXT_get(self._extension)
664 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500665 # TODO: This is untested.
666 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400667 payload = self._extension.value.data
668 length = self._extension.value.length
669
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500670 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400671 payloadptr[0] = payload
672
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500673 if method.it != _ffi.NULL:
674 ptr = _lib.ASN1_ITEM_ptr(method.it)
675 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
676 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400677 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500678 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400679 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500680 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400681
682 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500683 for i in range(_lib.sk_GENERAL_NAME_num(names)):
684 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400685 try:
686 label = self._prefixes[name.type]
687 except KeyError:
688 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500689 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500690 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400691 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500692 value = _native(
693 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
694 parts.append(label + ":" + value)
695 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400696
697
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800698 def __str__(self):
699 """
700 :return: a nice text representation of the extension
701 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500702 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400703 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800704
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400705 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500706 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800707 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500708 # TODO: This is untested.
709 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800710
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500711 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800712
713
714 def get_critical(self):
715 """
716 Returns the critical field of the X509Extension
717
718 :return: The critical field.
719 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500720 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800721
722
723 def get_short_name(self):
724 """
725 Returns the short version of the type name of the X509Extension
726
727 :return: The short type name.
728 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500729 obj = _lib.X509_EXTENSION_get_object(self._extension)
730 nid = _lib.OBJ_obj2nid(obj)
731 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800732
733
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800734 def get_data(self):
735 """
736 Returns the data of the X509Extension
737
738 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
739 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500740 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
741 string_result = _ffi.cast('ASN1_STRING*', octet_result)
742 char_result = _lib.ASN1_STRING_data(string_result)
743 result_length = _lib.ASN1_STRING_length(string_result)
744 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800745
746X509ExtensionType = X509Extension
747
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800748
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800749class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800750 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500751 req = _lib.X509_REQ_new()
752 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800753
754
755 def set_pubkey(self, pkey):
756 """
757 Set the public key of the certificate request
758
759 :param pkey: The public key to use
760 :return: None
761 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500762 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800763 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500764 # TODO: This is untested.
765 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800766
767
768 def get_pubkey(self):
769 """
770 Get the public key from the certificate request
771
772 :return: The public key
773 """
774 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500775 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
776 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500777 # TODO: This is untested.
778 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500779 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800780 pkey._only_public = True
781 return pkey
782
783
784 def set_version(self, version):
785 """
786 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
787 request.
788
789 :param version: The version number
790 :return: None
791 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500792 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800793 if not set_result:
794 _raise_current_error()
795
796
797 def get_version(self):
798 """
799 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
800 request.
801
802 :return: an integer giving the value of the version subfield
803 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500804 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800805
806
807 def get_subject(self):
808 """
809 Create an X509Name object for the subject of the certificate request
810
811 :return: An X509Name object
812 """
813 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500814 name._name = _lib.X509_REQ_get_subject_name(self._req)
815 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500816 # TODO: This is untested.
817 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800818
819 # The name is owned by the X509Req structure. As long as the X509Name
820 # Python object is alive, keep the X509Req Python object alive.
821 name._owner = self
822
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800823 return name
824
825
826 def add_extensions(self, extensions):
827 """
828 Add extensions to the request.
829
830 :param extensions: a sequence of X509Extension objects
831 :return: None
832 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500833 stack = _lib.sk_X509_EXTENSION_new_null()
834 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500835 # TODO: This is untested.
836 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800837
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500838 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800839
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800840 for ext in extensions:
841 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800842 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800843
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800844 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500845 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800846
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500847 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800848 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500849 # TODO: This is untested.
850 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800851
852
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800853 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800854 """
855 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800856
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500857 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800858 """
859 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500860 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500861 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800862 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500863 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800864 exts.append(ext)
865 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800866
867
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800868 def sign(self, pkey, digest):
869 """
870 Sign the certificate request using the supplied key and digest
871
872 :param pkey: The key to sign with
873 :param digest: The message digest to use
874 :return: None
875 """
876 if pkey._only_public:
877 raise ValueError("Key has only public part")
878
879 if not pkey._initialized:
880 raise ValueError("Key is uninitialized")
881
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500882 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500883 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800884 raise ValueError("No such digest method")
885
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500886 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800887 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500888 # TODO: This is untested.
889 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800890
891
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800892 def verify(self, pkey):
893 """
894 Verifies a certificate request using the supplied public key
895
896 :param key: a public key
897 :return: True if the signature is correct.
898
899 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
900 problem verifying the signature.
901 """
902 if not isinstance(pkey, PKey):
903 raise TypeError("pkey must be a PKey instance")
904
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500905 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800906 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500907 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800908
909 return result
910
911
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800912X509ReqType = X509Req
913
914
915
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800916class X509(object):
917 def __init__(self):
918 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500919 x509 = _lib.X509_new()
920 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800921
922
923 def set_version(self, version):
924 """
925 Set version number of the certificate
926
927 :param version: The version number
928 :type version: :py:class:`int`
929
930 :return: None
931 """
932 if not isinstance(version, int):
933 raise TypeError("version must be an integer")
934
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500935 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800936
937
938 def get_version(self):
939 """
940 Return version number of the certificate
941
942 :return: Version number as a Python integer
943 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500944 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800945
946
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800947 def get_pubkey(self):
948 """
949 Get the public key of the certificate
950
951 :return: The public key
952 """
953 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500954 pkey._pkey = _lib.X509_get_pubkey(self._x509)
955 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800956 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500957 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800958 pkey._only_public = True
959 return pkey
960
961
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800962 def set_pubkey(self, pkey):
963 """
964 Set the public key of the certificate
965
966 :param pkey: The public key
967
968 :return: None
969 """
970 if not isinstance(pkey, PKey):
971 raise TypeError("pkey must be a PKey instance")
972
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500973 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800974 if not set_result:
975 _raise_current_error()
976
977
978 def sign(self, pkey, digest):
979 """
980 Sign the certificate using the supplied key and digest
981
982 :param pkey: The key to sign with
983 :param digest: The message digest to use
984 :return: None
985 """
986 if not isinstance(pkey, PKey):
987 raise TypeError("pkey must be a PKey instance")
988
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800989 if pkey._only_public:
990 raise ValueError("Key only has public part")
991
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800992 if not pkey._initialized:
993 raise ValueError("Key is uninitialized")
994
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500995 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500996 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800997 raise ValueError("No such digest method")
998
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500999 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001000 if not sign_result:
1001 _raise_current_error()
1002
1003
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001004 def get_signature_algorithm(self):
1005 """
1006 Retrieve the signature algorithm used in the certificate
1007
1008 :return: A byte string giving the name of the signature algorithm used in
1009 the certificate.
1010 :raise ValueError: If the signature algorithm is undefined.
1011 """
1012 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001013 nid = _lib.OBJ_obj2nid(alg)
1014 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001015 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001016 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001017
1018
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001019 def digest(self, digest_name):
1020 """
1021 Return the digest of the X509 object.
1022
1023 :param digest_name: The name of the digest algorithm to use.
1024 :type digest_name: :py:class:`bytes`
1025
1026 :return: The digest of the object
1027 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001028 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001029 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001030 raise ValueError("No such digest method")
1031
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001032 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1033 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001034 result_length[0] = len(result_buffer)
1035
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001036 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001037 self._x509, digest, result_buffer, result_length)
1038
1039 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001040 # TODO: This is untested.
1041 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001042
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001043 return b":".join([
1044 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001045 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001046
1047
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001048 def subject_name_hash(self):
1049 """
1050 Return the hash of the X509 subject.
1051
1052 :return: The hash of the subject.
1053 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001054 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001055
1056
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001057 def set_serial_number(self, serial):
1058 """
1059 Set serial number of the certificate
1060
1061 :param serial: The serial number
1062 :type serial: :py:class:`int`
1063
1064 :return: None
1065 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001066 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001067 raise TypeError("serial must be an integer")
1068
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001069 hex_serial = hex(serial)[2:]
1070 if not isinstance(hex_serial, bytes):
1071 hex_serial = hex_serial.encode('ascii')
1072
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001073 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001074
1075 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1076 # it. If bignum is still NULL after this call, then the return value is
1077 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001078 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001079
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001080 if bignum_serial[0] == _ffi.NULL:
1081 set_result = _lib.ASN1_INTEGER_set(
1082 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001083 if set_result:
1084 # TODO Not tested
1085 _raise_current_error()
1086 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001087 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1088 _lib.BN_free(bignum_serial[0])
1089 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001090 # TODO Not tested
1091 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001092 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1093 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001094 if not set_result:
1095 # TODO Not tested
1096 _raise_current_error()
1097
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001098
1099 def get_serial_number(self):
1100 """
1101 Return serial number of the certificate
1102
1103 :return: Serial number as a Python integer
1104 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001105 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1106 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001107 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001108 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001109 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001110 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001111 serial = int(hexstring_serial, 16)
1112 return serial
1113 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001114 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001115 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001116 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001117
1118
1119 def gmtime_adj_notAfter(self, amount):
1120 """
1121 Adjust the time stamp for when the certificate stops being valid
1122
1123 :param amount: The number of seconds by which to adjust the ending
1124 validity time.
1125 :type amount: :py:class:`int`
1126
1127 :return: None
1128 """
1129 if not isinstance(amount, int):
1130 raise TypeError("amount must be an integer")
1131
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001132 notAfter = _lib.X509_get_notAfter(self._x509)
1133 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001134
1135
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001136 def gmtime_adj_notBefore(self, amount):
1137 """
1138 Change the timestamp for when the certificate starts being valid to the current
1139 time plus an offset.
1140
1141 :param amount: The number of seconds by which to adjust the starting validity
1142 time.
1143 :return: None
1144 """
1145 if not isinstance(amount, int):
1146 raise TypeError("amount must be an integer")
1147
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001148 notBefore = _lib.X509_get_notBefore(self._x509)
1149 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001150
1151
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001152 def has_expired(self):
1153 """
1154 Check whether the certificate has expired.
1155
1156 :return: True if the certificate has expired, false otherwise
1157 """
1158 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001159 notAfter = _lib.X509_get_notAfter(self._x509)
1160 return _lib.ASN1_UTCTIME_cmp_time_t(
1161 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001162
1163
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001164 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001165 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001166
1167
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001168 def get_notBefore(self):
1169 """
1170 Retrieve the time stamp for when the certificate starts being valid
1171
1172 :return: A string giving the timestamp, in the format::
1173
1174 YYYYMMDDhhmmssZ
1175 YYYYMMDDhhmmss+hhmm
1176 YYYYMMDDhhmmss-hhmm
1177
1178 or None if there is no value set.
1179 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001180 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001181
1182
1183 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001184 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001185
1186
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001187 def set_notBefore(self, when):
1188 """
1189 Set the time stamp for when the certificate starts being valid
1190
1191 :param when: A string giving the timestamp, in the format:
1192
1193 YYYYMMDDhhmmssZ
1194 YYYYMMDDhhmmss+hhmm
1195 YYYYMMDDhhmmss-hhmm
1196 :type when: :py:class:`bytes`
1197
1198 :return: None
1199 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001200 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001201
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001202
1203 def get_notAfter(self):
1204 """
1205 Retrieve the time stamp for when the certificate stops being valid
1206
1207 :return: A string giving the timestamp, in the format::
1208
1209 YYYYMMDDhhmmssZ
1210 YYYYMMDDhhmmss+hhmm
1211 YYYYMMDDhhmmss-hhmm
1212
1213 or None if there is no value set.
1214 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001215 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001216
1217
1218 def set_notAfter(self, when):
1219 """
1220 Set the time stamp for when the certificate stops being valid
1221
1222 :param when: A string giving the timestamp, in the format:
1223
1224 YYYYMMDDhhmmssZ
1225 YYYYMMDDhhmmss+hhmm
1226 YYYYMMDDhhmmss-hhmm
1227 :type when: :py:class:`bytes`
1228
1229 :return: None
1230 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001231 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001232
1233
1234 def _get_name(self, which):
1235 name = X509Name.__new__(X509Name)
1236 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001237 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001238 # TODO: This is untested.
1239 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001240
1241 # The name is owned by the X509 structure. As long as the X509Name
1242 # Python object is alive, keep the X509 Python object alive.
1243 name._owner = self
1244
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001245 return name
1246
1247
1248 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001249 if not isinstance(name, X509Name):
1250 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001251 set_result = which(self._x509, name._name)
1252 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001253 # TODO: This is untested.
1254 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001255
1256
1257 def get_issuer(self):
1258 """
1259 Create an X509Name object for the issuer of the certificate
1260
1261 :return: An X509Name object
1262 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001263 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001264
1265
1266 def set_issuer(self, issuer):
1267 """
1268 Set the issuer of the certificate
1269
1270 :param issuer: The issuer name
1271 :type issuer: :py:class:`X509Name`
1272
1273 :return: None
1274 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001275 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001276
1277
1278 def get_subject(self):
1279 """
1280 Create an X509Name object for the subject of the certificate
1281
1282 :return: An X509Name object
1283 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001284 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001285
1286
1287 def set_subject(self, subject):
1288 """
1289 Set the subject of the certificate
1290
1291 :param subject: The subject name
1292 :type subject: :py:class:`X509Name`
1293 :return: None
1294 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001295 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001296
1297
1298 def get_extension_count(self):
1299 """
1300 Get the number of extensions on the certificate.
1301
1302 :return: The number of extensions as an integer.
1303 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001304 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001305
1306
1307 def add_extensions(self, extensions):
1308 """
1309 Add extensions to the certificate.
1310
1311 :param extensions: a sequence of X509Extension objects
1312 :return: None
1313 """
1314 for ext in extensions:
1315 if not isinstance(ext, X509Extension):
1316 raise ValueError("One of the elements is not an X509Extension")
1317
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001318 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001319 if not add_result:
1320 _raise_current_error()
1321
1322
1323 def get_extension(self, index):
1324 """
1325 Get a specific extension of the certificate by index.
1326
1327 :param index: The index of the extension to retrieve.
1328 :return: The X509Extension object at the specified index.
1329 """
1330 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001331 ext._extension = _lib.X509_get_ext(self._x509, index)
1332 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001333 raise IndexError("extension index out of bounds")
1334
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001335 extension = _lib.X509_EXTENSION_dup(ext._extension)
1336 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001337 return ext
1338
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001339X509Type = X509
1340
1341
1342
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001343class X509Store(object):
1344 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001345 store = _lib.X509_STORE_new()
1346 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001347
1348
1349 def add_cert(self, cert):
1350 if not isinstance(cert, X509):
1351 raise TypeError()
1352
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001353 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001354 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001355 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001356
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001357
1358X509StoreType = X509Store
1359
1360
1361
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001362def load_certificate(type, buffer):
1363 """
1364 Load a certificate from a buffer
1365
1366 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1367
1368 :param buffer: The buffer the certificate is stored in
1369 :type buffer: :py:class:`bytes`
1370
1371 :return: The X509 object
1372 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001373 if isinstance(buffer, _text_type):
1374 buffer = buffer.encode("ascii")
1375
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001376 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001377
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001378 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001379 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001380 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001381 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001382 else:
1383 raise ValueError(
1384 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001385
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001386 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001387 _raise_current_error()
1388
1389 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001390 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001391 return cert
1392
1393
1394def dump_certificate(type, cert):
1395 """
1396 Dump a certificate to a buffer
1397
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001398 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1399 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001400 :param cert: The certificate to dump
1401 :return: The buffer with the dumped certificate in
1402 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001403 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001404
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001405 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001406 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001407 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001408 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001409 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001410 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001411 else:
1412 raise ValueError(
1413 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1414 "FILETYPE_TEXT")
1415
1416 return _bio_to_string(bio)
1417
1418
1419
1420def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1421 """
1422 Dump a private key to a buffer
1423
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001424 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1425 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001426 :param pkey: The PKey to dump
1427 :param cipher: (optional) if encrypted PEM format, the cipher to
1428 use
1429 :param passphrase: (optional) if encrypted PEM format, this can be either
1430 the passphrase to use, or a callback for providing the
1431 passphrase.
1432 :return: The buffer with the dumped key in
1433 :rtype: :py:data:`str`
1434 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001435 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001436
1437 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001438 if passphrase is None:
1439 raise TypeError(
1440 "if a value is given for cipher "
1441 "one must also be given for passphrase")
1442 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001443 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001444 raise ValueError("Invalid cipher name")
1445 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001446 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001447
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001448 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001449 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001450 result_code = _lib.PEM_write_bio_PrivateKey(
1451 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001452 helper.callback, helper.callback_args)
1453 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001454 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001455 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001456 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001457 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1458 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001459 # TODO RSA_free(rsa)?
1460 else:
1461 raise ValueError(
1462 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1463 "FILETYPE_TEXT")
1464
1465 if result_code == 0:
1466 _raise_current_error()
1467
1468 return _bio_to_string(bio)
1469
1470
1471
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001472def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001473 copy = _lib.X509_REVOKED_new()
1474 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001475 # TODO: This is untested.
1476 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001477
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001478 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001479 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001480 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001481
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001482 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001483 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001484 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001485
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001486 if original.extensions != _ffi.NULL:
1487 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1488 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1489 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1490 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1491 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001492 copy.extensions = extension_stack
1493
1494 copy.sequence = original.sequence
1495 return copy
1496
1497
1498
1499class Revoked(object):
1500 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1501 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1502 # OCSP_crl_reason_str. We use the latter, just like the command line
1503 # program.
1504 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001505 b"unspecified",
1506 b"keyCompromise",
1507 b"CACompromise",
1508 b"affiliationChanged",
1509 b"superseded",
1510 b"cessationOfOperation",
1511 b"certificateHold",
1512 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001513 ]
1514
1515 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001516 revoked = _lib.X509_REVOKED_new()
1517 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001518
1519
1520 def set_serial(self, hex_str):
1521 """
1522 Set the serial number of a revoked Revoked structure
1523
1524 :param hex_str: The new serial number.
1525 :type hex_str: :py:data:`str`
1526 :return: None
1527 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001528 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1529 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001530 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001531 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001532 if not bn_result:
1533 raise ValueError("bad hex string")
1534
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001535 asn1_serial = _ffi.gc(
1536 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1537 _lib.ASN1_INTEGER_free)
1538 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001539
1540
1541 def get_serial(self):
1542 """
1543 Return the serial number of a Revoked structure
1544
1545 :return: The serial number as a string
1546 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001547 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001548
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001549 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001550 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001551 # TODO: This is untested.
1552 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001553
1554 return _bio_to_string(bio)
1555
1556
1557 def _delete_reason(self):
1558 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001559 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1560 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1561 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1562 _lib.X509_EXTENSION_free(ext)
1563 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001564 break
1565
1566
1567 def set_reason(self, reason):
1568 """
1569 Set the reason of a Revoked object.
1570
1571 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1572
1573 :param reason: The reason string.
1574 :type reason: :py:class:`str` or :py:class:`NoneType`
1575 :return: None
1576 """
1577 if reason is None:
1578 self._delete_reason()
1579 elif not isinstance(reason, bytes):
1580 raise TypeError("reason must be None or a byte string")
1581 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001582 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001583 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1584
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001585 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1586 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001587 # TODO: This is untested.
1588 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001589 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001590
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001591 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1592 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001593 # TODO: This is untested.
1594 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001595
1596 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001597 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1598 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001599
1600 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001601 # TODO: This is untested.
1602 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001603
1604
1605 def get_reason(self):
1606 """
1607 Return the reason of a Revoked object.
1608
1609 :return: The reason as a string
1610 """
1611 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001612 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1613 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1614 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001615 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001616
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001617 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001618 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001619 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001620 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001621 # TODO: This is untested.
1622 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001623
1624 return _bio_to_string(bio)
1625
1626
1627 def all_reasons(self):
1628 """
1629 Return a list of all the supported reason strings.
1630
1631 :return: A list of reason strings.
1632 """
1633 return self._crl_reasons[:]
1634
1635
1636 def set_rev_date(self, when):
1637 """
1638 Set the revocation timestamp
1639
1640 :param when: A string giving the timestamp, in the format:
1641
1642 YYYYMMDDhhmmssZ
1643 YYYYMMDDhhmmss+hhmm
1644 YYYYMMDDhhmmss-hhmm
1645
1646 :return: None
1647 """
1648 return _set_asn1_time(self._revoked.revocationDate, when)
1649
1650
1651 def get_rev_date(self):
1652 """
1653 Retrieve the revocation date
1654
1655 :return: A string giving the timestamp, in the format:
1656
1657 YYYYMMDDhhmmssZ
1658 YYYYMMDDhhmmss+hhmm
1659 YYYYMMDDhhmmss-hhmm
1660 """
1661 return _get_asn1_time(self._revoked.revocationDate)
1662
1663
1664
1665class CRL(object):
1666 def __init__(self):
1667 """
1668 Create a new empty CRL object.
1669 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001670 crl = _lib.X509_CRL_new()
1671 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001672
1673
1674 def get_revoked(self):
1675 """
1676 Return revoked portion of the CRL structure (by value not reference).
1677
1678 :return: A tuple of Revoked objects.
1679 """
1680 results = []
1681 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001682 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1683 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001684 revoked_copy = _X509_REVOKED_dup(revoked)
1685 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001686 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001687 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001688 if results:
1689 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001690
1691
1692 def add_revoked(self, revoked):
1693 """
1694 Add a revoked (by value not reference) to the CRL structure
1695
1696 :param revoked: The new revoked.
1697 :type revoked: :class:`X509`
1698
1699 :return: None
1700 """
1701 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001702 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001703 # TODO: This is untested.
1704 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001705
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001706 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001707 if add_result == 0:
1708 # TODO: This is untested.
1709 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001710
1711
1712 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1713 """
1714 export a CRL as a string
1715
1716 :param cert: Used to sign CRL.
1717 :type cert: :class:`X509`
1718
1719 :param key: Used to sign CRL.
1720 :type key: :class:`PKey`
1721
1722 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1723
1724 :param days: The number of days until the next update of this CRL.
1725 :type days: :py:data:`int`
1726
1727 :return: :py:data:`str`
1728 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001729 if not isinstance(cert, X509):
1730 raise TypeError("cert must be an X509 instance")
1731 if not isinstance(key, PKey):
1732 raise TypeError("key must be a PKey instance")
1733 if not isinstance(type, int):
1734 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001735
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001736 bio = _lib.BIO_new(_lib.BIO_s_mem())
1737 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001738 # TODO: This is untested.
1739 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001740
1741 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001742 sometime = _lib.ASN1_TIME_new()
1743 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001744 # TODO: This is untested.
1745 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001746
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001747 _lib.X509_gmtime_adj(sometime, 0)
1748 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001749
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001750 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1751 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001752
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001753 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001754
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001755 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001756 if not sign_result:
1757 _raise_current_error()
1758
1759 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001760 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001761 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001762 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001763 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001764 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001765 else:
1766 raise ValueError(
1767 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1768
1769 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001770 # TODO: This is untested.
1771 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001772
1773 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001774CRLType = CRL
1775
1776
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001777
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001778class PKCS7(object):
1779 def type_is_signed(self):
1780 """
1781 Check if this NID_pkcs7_signed object
1782
1783 :return: True if the PKCS7 is of type signed
1784 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001785 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001786 return True
1787 return False
1788
1789
1790 def type_is_enveloped(self):
1791 """
1792 Check if this NID_pkcs7_enveloped object
1793
1794 :returns: True if the PKCS7 is of type enveloped
1795 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001796 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001797 return True
1798 return False
1799
1800
1801 def type_is_signedAndEnveloped(self):
1802 """
1803 Check if this NID_pkcs7_signedAndEnveloped object
1804
1805 :returns: True if the PKCS7 is of type signedAndEnveloped
1806 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001807 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001808 return True
1809 return False
1810
1811
1812 def type_is_data(self):
1813 """
1814 Check if this NID_pkcs7_data object
1815
1816 :return: True if the PKCS7 is of type data
1817 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001818 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001819 return True
1820 return False
1821
1822
1823 def get_type_name(self):
1824 """
1825 Returns the type name of the PKCS7 structure
1826
1827 :return: A string with the typename
1828 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001829 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1830 string_type = _lib.OBJ_nid2sn(nid)
1831 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001832
1833PKCS7Type = PKCS7
1834
1835
1836
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001837class PKCS12(object):
1838 def __init__(self):
1839 self._pkey = None
1840 self._cert = None
1841 self._cacerts = None
1842 self._friendlyname = None
1843
1844
1845 def get_certificate(self):
1846 """
1847 Return certificate portion of the PKCS12 structure
1848
1849 :return: X509 object containing the certificate
1850 """
1851 return self._cert
1852
1853
1854 def set_certificate(self, cert):
1855 """
1856 Replace the certificate portion of the PKCS12 structure
1857
1858 :param cert: The new certificate.
1859 :type cert: :py:class:`X509` or :py:data:`None`
1860 :return: None
1861 """
1862 if not isinstance(cert, X509):
1863 raise TypeError("cert must be an X509 instance")
1864 self._cert = cert
1865
1866
1867 def get_privatekey(self):
1868 """
1869 Return private key portion of the PKCS12 structure
1870
1871 :returns: PKey object containing the private key
1872 """
1873 return self._pkey
1874
1875
1876 def set_privatekey(self, pkey):
1877 """
1878 Replace or set the certificate portion of the PKCS12 structure
1879
1880 :param pkey: The new private key.
1881 :type pkey: :py:class:`PKey`
1882 :return: None
1883 """
1884 if not isinstance(pkey, PKey):
1885 raise TypeError("pkey must be a PKey instance")
1886 self._pkey = pkey
1887
1888
1889 def get_ca_certificates(self):
1890 """
1891 Return CA certificates within of the PKCS12 object
1892
1893 :return: A newly created tuple containing the CA certificates in the chain,
1894 if any are present, or None if no CA certificates are present.
1895 """
1896 if self._cacerts is not None:
1897 return tuple(self._cacerts)
1898
1899
1900 def set_ca_certificates(self, cacerts):
1901 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08001902 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001903
1904 :param cacerts: The new CA certificates.
1905 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1906 :return: None
1907 """
1908 if cacerts is None:
1909 self._cacerts = None
1910 else:
1911 cacerts = list(cacerts)
1912 for cert in cacerts:
1913 if not isinstance(cert, X509):
1914 raise TypeError("iterable must only contain X509 instances")
1915 self._cacerts = cacerts
1916
1917
1918 def set_friendlyname(self, name):
1919 """
1920 Replace or set the certificate portion of the PKCS12 structure
1921
1922 :param name: The new friendly name.
1923 :type name: :py:class:`bytes`
1924 :return: None
1925 """
1926 if name is None:
1927 self._friendlyname = None
1928 elif not isinstance(name, bytes):
1929 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1930 self._friendlyname = name
1931
1932
1933 def get_friendlyname(self):
1934 """
1935 Return friendly name portion of the PKCS12 structure
1936
1937 :returns: String containing the friendlyname
1938 """
1939 return self._friendlyname
1940
1941
1942 def export(self, passphrase=None, iter=2048, maciter=1):
1943 """
1944 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1945
1946 :param passphrase: used to encrypt the PKCS12
1947 :type passphrase: :py:data:`bytes`
1948
1949 :param iter: How many times to repeat the encryption
1950 :type iter: :py:data:`int`
1951
1952 :param maciter: How many times to repeat the MAC
1953 :type maciter: :py:data:`int`
1954
1955 :return: The string containing the PKCS12
1956 """
Abraham Martine82326c2015-02-04 10:18:10 +00001957
1958 # Backward compatibility
1959 if isinstance(passphrase, _text_type):
Abraham Martin82efe3e2015-03-25 10:50:09 +00001960 if version_info.major == 2:
1961 warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning)
1962 elif version_info.major == 3:
1963 warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning)
1964 else:
1965 warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martine82326c2015-02-04 10:18:10 +00001966 passphrase = passphrase.encode('utf-8')
1967
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001968 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001969 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001970 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001971 cacerts = _lib.sk_X509_new_null()
1972 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001973 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001974 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001975
1976 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001977 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001978
1979 friendlyname = self._friendlyname
1980 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001981 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001982
1983 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001984 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001985 else:
1986 pkey = self._pkey._pkey
1987
1988 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001989 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001990 else:
1991 cert = self._cert._x509
1992
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001993 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001994 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001995 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1996 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001997 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001998 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001999 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002000 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002001
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002002 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002003 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002004 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002005
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002006PKCS12Type = PKCS12
2007
2008
2009
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002010class NetscapeSPKI(object):
2011 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002012 spki = _lib.NETSCAPE_SPKI_new()
2013 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002014
2015
2016 def sign(self, pkey, digest):
2017 """
2018 Sign the certificate request using the supplied key and digest
2019
2020 :param pkey: The key to sign with
2021 :param digest: The message digest to use
2022 :return: None
2023 """
2024 if pkey._only_public:
2025 raise ValueError("Key has only public part")
2026
2027 if not pkey._initialized:
2028 raise ValueError("Key is uninitialized")
2029
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002030 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002031 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002032 raise ValueError("No such digest method")
2033
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002034 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002035 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002036 # TODO: This is untested.
2037 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002038
2039
2040 def verify(self, key):
2041 """
2042 Verifies a certificate request using the supplied public key
2043
2044 :param key: a public key
2045 :return: True if the signature is correct.
2046 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2047 problem verifying the signature.
2048 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002049 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002050 if answer <= 0:
2051 _raise_current_error()
2052 return True
2053
2054
2055 def b64_encode(self):
2056 """
2057 Generate a base64 encoded string from an SPKI
2058
2059 :return: The base64 encoded string
2060 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002061 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2062 result = _ffi.string(encoded)
2063 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002064 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002065
2066
2067 def get_pubkey(self):
2068 """
2069 Get the public key of the certificate
2070
2071 :return: The public key
2072 """
2073 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002074 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2075 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002076 # TODO: This is untested.
2077 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002078 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002079 pkey._only_public = True
2080 return pkey
2081
2082
2083 def set_pubkey(self, pkey):
2084 """
2085 Set the public key of the certificate
2086
2087 :param pkey: The public key
2088 :return: None
2089 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002090 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002091 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002092 # TODO: This is untested.
2093 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002094NetscapeSPKIType = NetscapeSPKI
2095
2096
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002097class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002098 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002099 if type != FILETYPE_PEM and passphrase is not None:
2100 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002101 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002102 self._more_args = more_args
2103 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002104 self._problems = []
2105
2106
2107 @property
2108 def callback(self):
2109 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002110 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002111 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002112 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002113 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002114 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002115 else:
2116 raise TypeError("Last argument must be string or callable")
2117
2118
2119 @property
2120 def callback_args(self):
2121 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002122 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002123 elif isinstance(self._passphrase, bytes):
2124 return self._passphrase
2125 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002126 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002127 else:
2128 raise TypeError("Last argument must be string or callable")
2129
2130
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002131 def raise_if_problem(self, exceptionType=Error):
2132 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002133 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002134 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002135 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002136 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002137 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002138 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002139
2140
2141 def _read_passphrase(self, buf, size, rwflag, userdata):
2142 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002143 if self._more_args:
2144 result = self._passphrase(size, rwflag, userdata)
2145 else:
2146 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002147 if not isinstance(result, bytes):
2148 raise ValueError("String expected")
2149 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002150 if self._truncate:
2151 result = result[:size]
2152 else:
2153 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002154 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002155 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002156 return len(result)
2157 except Exception as e:
2158 self._problems.append(e)
2159 return 0
2160
2161
2162
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002163def load_privatekey(type, buffer, passphrase=None):
2164 """
2165 Load a private key from a buffer
2166
2167 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2168 :param buffer: The buffer the key is stored in
2169 :param passphrase: (optional) if encrypted PEM format, this can be
2170 either the passphrase to use, or a callback for
2171 providing the passphrase.
2172
2173 :return: The PKey object
2174 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002175 if isinstance(buffer, _text_type):
2176 buffer = buffer.encode("ascii")
2177
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002178 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002179
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002180 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002181 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002182 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2183 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002184 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002185 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002186 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002187 else:
2188 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2189
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002190 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002191 _raise_current_error()
2192
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002193 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002194 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002195 return pkey
2196
2197
2198
2199def dump_certificate_request(type, req):
2200 """
2201 Dump a certificate request to a buffer
2202
2203 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2204 :param req: The certificate request to dump
2205 :return: The buffer with the dumped certificate request in
2206 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002207 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002208
2209 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002210 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002211 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002212 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002213 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002214 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002215 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002216 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002217
2218 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002219 # TODO: This is untested.
2220 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002221
2222 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002223
2224
2225
2226def load_certificate_request(type, buffer):
2227 """
2228 Load a certificate request from a buffer
2229
2230 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2231 :param buffer: The buffer the certificate request is stored in
2232 :return: The X509Req object
2233 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002234 if isinstance(buffer, _text_type):
2235 buffer = buffer.encode("ascii")
2236
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002237 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002238
2239 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002240 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002241 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002242 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002243 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002244 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002245
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002246 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002247 # TODO: This is untested.
2248 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002249
2250 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002251 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002252 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002253
2254
2255
2256def sign(pkey, data, digest):
2257 """
2258 Sign data with a digest
2259
2260 :param pkey: Pkey to sign with
2261 :param data: data to be signed
2262 :param digest: message digest to use
2263 :return: signature
2264 """
Abraham Martine82326c2015-02-04 10:18:10 +00002265
2266 # Backward compatibility
2267 if isinstance(data, _text_type):
Abraham Martin82efe3e2015-03-25 10:50:09 +00002268 if version_info.major == 2:
2269 warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning)
2270 elif version_info.major == 3:
2271 warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning)
2272 else:
2273 warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martine82326c2015-02-04 10:18:10 +00002274 data = data.encode('utf-8')
2275
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002276 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002277 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002278 raise ValueError("No such digest method")
2279
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002280 md_ctx = _ffi.new("EVP_MD_CTX*")
2281 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002282
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002283 _lib.EVP_SignInit(md_ctx, digest_obj)
2284 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002285
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002286 signature_buffer = _ffi.new("unsigned char[]", 512)
2287 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002288 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002289 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002290 md_ctx, signature_buffer, signature_length, pkey._pkey)
2291
2292 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002293 # TODO: This is untested.
2294 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002295
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002296 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002297
2298
2299
2300def verify(cert, signature, data, digest):
2301 """
2302 Verify a signature
2303
2304 :param cert: signing certificate (X509 object)
2305 :param signature: signature returned by sign function
2306 :param data: data to be verified
2307 :param digest: message digest to use
2308 :return: None if the signature is correct, raise exception otherwise
2309 """
Abraham Martine82326c2015-02-04 10:18:10 +00002310
2311 # Backward compatibility
2312 if isinstance(data, _text_type):
Abraham Martin82efe3e2015-03-25 10:50:09 +00002313 if version_info.major == 2:
2314 warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning)
2315 elif version_info.major == 3:
2316 warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning)
2317 else:
2318 warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martine82326c2015-02-04 10:18:10 +00002319 data = data.encode('utf-8')
2320
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002321 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002322 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002323 raise ValueError("No such digest method")
2324
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002325 pkey = _lib.X509_get_pubkey(cert._x509)
2326 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002327 # TODO: This is untested.
2328 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002329 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002330
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002331 md_ctx = _ffi.new("EVP_MD_CTX*")
2332 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002333
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002334 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2335 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2336 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002337
2338 if verify_result != 1:
2339 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002340
2341
2342
2343def load_crl(type, buffer):
2344 """
2345 Load a certificate revocation list from a buffer
2346
2347 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2348 :param buffer: The buffer the CRL is stored in
2349
2350 :return: The PKey object
2351 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002352 if isinstance(buffer, _text_type):
2353 buffer = buffer.encode("ascii")
2354
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002355 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002356
2357 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002358 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002359 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002360 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002361 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002362 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2363
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002364 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002365 _raise_current_error()
2366
2367 result = CRL.__new__(CRL)
2368 result._crl = crl
2369 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002370
2371
2372
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002373def load_pkcs7_data(type, buffer):
2374 """
2375 Load pkcs7 data from a buffer
2376
2377 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2378 :param buffer: The buffer with the pkcs7 data.
2379 :return: The PKCS7 object
2380 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002381 if isinstance(buffer, _text_type):
2382 buffer = buffer.encode("ascii")
2383
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002384 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002385
2386 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002387 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002388 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002389 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002390 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002391 # TODO: This is untested.
2392 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002393 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2394
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002395 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002396 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002397
2398 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002399 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002400 return pypkcs7
2401
2402
2403
Stephen Holsapple38482622014-04-05 20:29:34 -07002404def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002405 """
2406 Load a PKCS12 object from a buffer
2407
2408 :param buffer: The buffer the certificate is stored in
2409 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2410 :returns: The PKCS12 object
2411 """
Abraham Martine82326c2015-02-04 10:18:10 +00002412
2413 # Backward compatibility
2414 if isinstance(passphrase, _text_type):
Abraham Martin82efe3e2015-03-25 10:50:09 +00002415 if version_info.major == 2:
2416 warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning)
2417 elif version_info.major == 3:
2418 warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning)
2419 else:
2420 warn("text in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martine82326c2015-02-04 10:18:10 +00002421 passphrase = passphrase.encode('utf-8')
2422
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002423 if isinstance(buffer, _text_type):
2424 buffer = buffer.encode("ascii")
2425
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002426 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002427
Stephen Holsapple38482622014-04-05 20:29:34 -07002428 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2429 # password based encryption no password and a zero length password are two
2430 # different things, but OpenSSL implementation will try both to figure out
2431 # which one works.
2432 if not passphrase:
2433 passphrase = _ffi.NULL
2434
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002435 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2436 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002437 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002438 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002439
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002440 pkey = _ffi.new("EVP_PKEY**")
2441 cert = _ffi.new("X509**")
2442 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002443
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002444 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002445 if not parse_result:
2446 _raise_current_error()
2447
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002448 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002449
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002450 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2451 # queue for no particular reason. This error isn't interesting to anyone
2452 # outside this function. It's not even interesting to us. Get rid of it.
2453 try:
2454 _raise_current_error()
2455 except Error:
2456 pass
2457
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002458 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002459 pykey = None
2460 else:
2461 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002462 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002463
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002464 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002465 pycert = None
2466 friendlyname = None
2467 else:
2468 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002469 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002470
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002471 friendlyname_length = _ffi.new("int*")
2472 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2473 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2474 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002475 friendlyname = None
2476
2477 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002478 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002479 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002480 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002481 pycacerts.append(pycacert)
2482 if not pycacerts:
2483 pycacerts = None
2484
2485 pkcs12 = PKCS12.__new__(PKCS12)
2486 pkcs12._pkey = pykey
2487 pkcs12._cert = pycert
2488 pkcs12._cacerts = pycacerts
2489 pkcs12._friendlyname = friendlyname
2490 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002491
2492
2493def _initialize_openssl_threads(get_ident, Lock):
2494 import _ssl
2495 return
2496
2497 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2498
2499 def locking_function(mode, index, filename, line):
2500 if mode & _lib.CRYPTO_LOCK:
2501 locks[index].acquire()
2502 else:
2503 locks[index].release()
2504
2505 _lib.CRYPTO_set_id_callback(
2506 _ffi.callback("unsigned long (*)(void)", get_ident))
2507
2508 _lib.CRYPTO_set_locking_callback(
2509 _ffi.callback(
2510 "void (*)(int, int, const char*, int)", locking_function))
2511
2512
2513try:
2514 from thread import get_ident
2515 from threading import Lock
2516except ImportError:
2517 pass
2518else:
2519 _initialize_openssl_threads(get_ident, Lock)
2520 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002521
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002522# There are no direct unit tests for this initialization. It is tested
2523# indirectly since it is necessary for functions like dump_privatekey when
2524# using encryption.
2525#
2526# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2527# and some other similar tests may fail without this (though they may not if
2528# the Python runtime has already done some initialization of the underlying
2529# OpenSSL library (and is linked against the same one that cryptography is
2530# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002531_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002532
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002533# This is similar but exercised mainly by exception_from_error_queue. It calls
2534# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2535_lib.SSL_load_error_strings()