blob: 3a69beaa1759115320572fbe8124ce4ee4326efb [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05003from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
5
6from six import (
7 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -04008 text_type as _text_type,
9 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080010
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050011from OpenSSL._util import (
12 ffi as _ffi,
13 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050014 exception_from_error_queue as _exception_from_error_queue,
15 byte_string as _byte_string,
16 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080017
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050018FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
19FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080020
21# TODO This was an API mistake. OpenSSL has no such constant.
22FILETYPE_TEXT = 2 ** 16 - 1
23
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050024TYPE_RSA = _lib.EVP_PKEY_RSA
25TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080026
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080027
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050028class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050029 """
30 An error occurred in an `OpenSSL.crypto` API.
31 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050032
33
34_raise_current_error = partial(_exception_from_error_queue, Error)
35
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050036def _untested_error(where):
37 """
38 An OpenSSL API failed somehow. Additionally, the failure which was
39 encountered isn't one that's exercised by the test suite so future behavior
40 of pyOpenSSL is now somewhat less predictable.
41 """
42 raise RuntimeError("Unknown %s failure" % (where,))
43
44
45
46def _new_mem_buf(buffer=None):
47 """
48 Allocate a new OpenSSL memory BIO.
49
50 Arrange for the garbage collector to clean it up automatically.
51
52 :param buffer: None or some bytes to use to put into the BIO so that they
53 can be read out.
54 """
55 if buffer is None:
56 bio = _lib.BIO_new(_lib.BIO_s_mem())
57 free = _lib.BIO_free
58 else:
59 data = _ffi.new("char[]", buffer)
60 bio = _lib.BIO_new_mem_buf(data, len(buffer))
61 # Keep the memory alive as long as the bio is alive!
62 def free(bio, ref=data):
63 return _lib.BIO_free(bio)
64
65 if bio == _ffi.NULL:
66 # TODO: This is untested.
67 _raise_current_error()
68
69 bio = _ffi.gc(bio, free)
70 return bio
71
72
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050073
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080074def _bio_to_string(bio):
75 """
76 Copy the contents of an OpenSSL BIO object into a Python byte string.
77 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050078 result_buffer = _ffi.new('char**')
79 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
80 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080081
82
83
Jean-Paul Calderone57122982013-02-21 08:47:05 -080084def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050085 """
86 The the time value of an ASN1 time object.
87
88 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
89 castable to that type) which will have its value set.
90 @param when: A string representation of the desired time value.
91
92 @raise TypeError: If C{when} is not a L{bytes} string.
93 @raise ValueError: If C{when} does not represent a time in the required
94 format.
95 @raise RuntimeError: If the time value cannot be set for some other
96 (unspecified) reason.
97 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080098 if not isinstance(when, bytes):
99 raise TypeError("when must be a byte string")
100
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500101 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
102 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800103 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500104 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
105 _lib.ASN1_STRING_set(dummy, when, len(when))
106 check_result = _lib.ASN1_GENERALIZEDTIME_check(
107 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800108 if not check_result:
109 raise ValueError("Invalid string")
110 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500111 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800112
113
114
115def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500116 """
117 Retrieve the time value of an ASN1 time object.
118
119 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
120 that type) from which the time value will be retrieved.
121
122 @return: The time value from C{timestamp} as a L{bytes} string in a certain
123 format. Or C{None} if the object contains no time value.
124 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500125 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
126 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800127 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500128 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
129 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800130 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500131 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
132 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
133 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500134 # This may happen:
135 # - if timestamp was not an ASN1_TIME
136 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
137 # - if a copy of the time data from timestamp cannot be made for
138 # the newly allocated ASN1_GENERALIZEDTIME
139 #
140 # These are difficult to test. cffi enforces the ASN1_TIME type.
141 # Memory allocation failures are a pain to trigger
142 # deterministically.
143 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800144 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500145 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800146 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500147 string_data = _lib.ASN1_STRING_data(string_timestamp)
148 string_result = _ffi.string(string_data)
149 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800150 return string_result
151
152
153
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800154class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800155 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800156 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800157
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800158 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500159 pkey = _lib.EVP_PKEY_new()
160 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800161 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800162
163
164 def generate_key(self, type, bits):
165 """
166 Generate a key of a given type, with a given number of a bits
167
168 :param type: The key type (TYPE_RSA or TYPE_DSA)
169 :param bits: The number of bits
170
171 :return: None
172 """
173 if not isinstance(type, int):
174 raise TypeError("type must be an integer")
175
176 if not isinstance(bits, int):
177 raise TypeError("bits must be an integer")
178
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800179 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500180 exponent = _lib.BN_new()
181 exponent = _ffi.gc(exponent, _lib.BN_free)
182 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800183
184 if type == TYPE_RSA:
185 if bits <= 0:
186 raise ValueError("Invalid number of bits")
187
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500188 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800189
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500190 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500191 if result == 0:
192 # TODO: The test for this case is commented out. Different
193 # builds of OpenSSL appear to have different failure modes that
194 # make it hard to test. Visual inspection of the OpenSSL
195 # source reveals that a return value of 0 signals an error.
196 # Manual testing on a particular build of OpenSSL suggests that
197 # this is probably the appropriate way to handle those errors.
198 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800199
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500200 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800201 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500202 # TODO: It appears as though this can fail if an engine is in
203 # use which does not support RSA.
204 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800205
206 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500207 dsa = _lib.DSA_generate_parameters(
208 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
209 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500210 # TODO: This is untested.
211 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500212 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500213 # TODO: This is untested.
214 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500215 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500216 # TODO: This is untested.
217 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800218 else:
219 raise Error("No such key type")
220
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800221 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800222
223
224 def check(self):
225 """
226 Check the consistency of an RSA private key.
227
228 :return: True if key is consistent.
229 :raise Error: if the key is inconsistent.
230 :raise TypeError: if the key is of a type which cannot be checked.
231 Only RSA keys can currently be checked.
232 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800233 if self._only_public:
234 raise TypeError("public key only")
235
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500236 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800237 raise TypeError("key type unsupported")
238
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500239 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
240 rsa = _ffi.gc(rsa, _lib.RSA_free)
241 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800242 if result:
243 return True
244 _raise_current_error()
245
246
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800247 def type(self):
248 """
249 Returns the type of the key
250
251 :return: The type of the key.
252 """
253 return self._pkey.type
254
255
256 def bits(self):
257 """
258 Returns the number of bits of the key
259
260 :return: The number of bits of the key.
261 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500262 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800263PKeyType = PKey
264
265
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800266
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400267class _EllipticCurve(object):
268 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400269 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400270
271 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
272 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
273 instances each of which represents one curve supported by the system.
274 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400275 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400276 _curves = None
277
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400278 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400279 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400280 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400281 """
282 Implement cooperation with the right-hand side argument of ``!=``.
283
284 Python 3 seems to have dropped this cooperation in this very narrow
285 circumstance.
286 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400287 if isinstance(other, _EllipticCurve):
288 return super(_EllipticCurve, self).__ne__(other)
289 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400290
291
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400292 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400293 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400294 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400295 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400296
297 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400298
299 :return: A :py:type:`set` of ``cls`` instances giving the names of the
300 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400301 """
302 if lib.Cryptography_HAS_EC:
303 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
304 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
305 # The return value on this call should be num_curves again. We could
306 # check it to make sure but if it *isn't* then.. what could we do?
307 # Abort the whole process, I suppose...? -exarkun
308 lib.EC_get_builtin_curves(builtin_curves, num_curves)
309 return set(
310 cls.from_nid(lib, c.nid)
311 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400312 return set()
313
314
315 @classmethod
316 def _get_elliptic_curves(cls, lib):
317 """
318 Get, cache, and return the curves supported by OpenSSL.
319
320 :param lib: The OpenSSL library binding object.
321
322 :return: A :py:type:`set` of ``cls`` instances giving the names of the
323 elliptic curves the underlying library supports.
324 """
325 if cls._curves is None:
326 cls._curves = cls._load_elliptic_curves(lib)
327 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400328
329
330 @classmethod
331 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400332 """
333 Instantiate a new :py:class:`_EllipticCurve` associated with the given
334 OpenSSL NID.
335
336 :param lib: The OpenSSL library binding object.
337
338 :param nid: The OpenSSL NID the resulting curve object will represent.
339 This must be a curve NID (and not, for example, a hash NID) or
340 subsequent operations will fail in unpredictable ways.
341 :type nid: :py:class:`int`
342
343 :return: The curve object.
344 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400345 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
346
347
348 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400349 """
350 :param _lib: The :py:mod:`cryptography` binding instance used to
351 interface with OpenSSL.
352
353 :param _nid: The OpenSSL NID identifying the curve this object
354 represents.
355 :type _nid: :py:class:`int`
356
357 :param name: The OpenSSL short name identifying the curve this object
358 represents.
359 :type name: :py:class:`unicode`
360 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400361 self._lib = lib
362 self._nid = nid
363 self.name = name
364
365
366 def __repr__(self):
367 return "<Curve %r>" % (self.name,)
368
369
370 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400371 """
372 Create a new OpenSSL EC_KEY structure initialized to use this curve.
373
374 The structure is automatically garbage collected when the Python object
375 is garbage collected.
376 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400377 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
378 return _ffi.gc(key, _lib.EC_KEY_free)
379
380
381
382def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400383 """
384 Return a set of objects representing the elliptic curves supported in the
385 OpenSSL build in use.
386
387 The curve objects have a :py:class:`unicode` ``name`` attribute by which
388 they identify themselves.
389
390 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400391 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
392 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400393 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400394 return _EllipticCurve._get_elliptic_curves(_lib)
395
396
397
398def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400399 """
400 Return a single curve object selected by name.
401
402 See :py:func:`get_elliptic_curves` for information about curve objects.
403
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400404 :param name: The OpenSSL short name identifying the curve object to
405 retrieve.
406 :type name: :py:class:`unicode`
407
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400408 If the named curve is not supported then :py:class:`ValueError` is raised.
409 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400410 for curve in get_elliptic_curves():
411 if curve.name == name:
412 return curve
413 raise ValueError("unknown curve name", name)
414
415
416
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800417class X509Name(object):
418 def __init__(self, name):
419 """
420 Create a new X509Name, copying the given X509Name instance.
421
422 :param name: An X509Name object to copy
423 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500424 name = _lib.X509_NAME_dup(name._name)
425 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800426
427
428 def __setattr__(self, name, value):
429 if name.startswith('_'):
430 return super(X509Name, self).__setattr__(name, value)
431
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800432 # Note: we really do not want str subclasses here, so we do not use
433 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800434 if type(name) is not str:
435 raise TypeError("attribute name must be string, not '%.200s'" % (
436 type(value).__name__,))
437
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500438 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500439 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800440 try:
441 _raise_current_error()
442 except Error:
443 pass
444 raise AttributeError("No such attribute")
445
446 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500447 for i in range(_lib.X509_NAME_entry_count(self._name)):
448 ent = _lib.X509_NAME_get_entry(self._name, i)
449 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
450 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800451 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500452 ent = _lib.X509_NAME_delete_entry(self._name, i)
453 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800454 break
455
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500456 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800457 value = value.encode('utf-8')
458
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500459 add_result = _lib.X509_NAME_add_entry_by_NID(
460 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800461 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500462 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800463
464
465 def __getattr__(self, name):
466 """
467 Find attribute. An X509Name object has the following attributes:
468 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
469 organization (alias O), organizationalUnit (alias OU), commonName (alias
470 CN) and more...
471 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500472 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500473 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800474 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
475 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
476 # push something onto the error queue. If we don't clean that up
477 # now, someone else will bump into it later and be quite confused.
478 # See lp#314814.
479 try:
480 _raise_current_error()
481 except Error:
482 pass
483 return super(X509Name, self).__getattr__(name)
484
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500485 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800486 if entry_index == -1:
487 return None
488
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500489 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
490 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800491
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500492 result_buffer = _ffi.new("unsigned char**")
493 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800494 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500495 # TODO: This is untested.
496 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800497
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700498 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500499 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700500 finally:
501 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500502 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800503 return result
504
505
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500506 def _cmp(op):
507 def f(self, other):
508 if not isinstance(other, X509Name):
509 return NotImplemented
510 result = _lib.X509_NAME_cmp(self._name, other._name)
511 return op(result, 0)
512 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800513
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500514 __eq__ = _cmp(__eq__)
515 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800516
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500517 __lt__ = _cmp(__lt__)
518 __le__ = _cmp(__le__)
519
520 __gt__ = _cmp(__gt__)
521 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522
523 def __repr__(self):
524 """
525 String representation of an X509Name
526 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500527 result_buffer = _ffi.new("char[]", 512);
528 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800529 self._name, result_buffer, len(result_buffer))
530
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500531 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500532 # TODO: This is untested.
533 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800534
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500535 return "<X509Name object '%s'>" % (
536 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800537
538
539 def hash(self):
540 """
541 Return the hash value of this name
542
543 :return: None
544 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500545 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800546
547
548 def der(self):
549 """
550 Return the DER encoding of this name
551
552 :return: A :py:class:`bytes` instance giving the DER encoded form of
553 this name.
554 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500555 result_buffer = _ffi.new('unsigned char**')
556 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800557 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500558 # TODO: This is untested.
559 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800560
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500561 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
562 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800563 return string_result
564
565
566 def get_components(self):
567 """
568 Returns the split-up components of this name.
569
570 :return: List of tuples (name, value).
571 """
572 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500573 for i in range(_lib.X509_NAME_entry_count(self._name)):
574 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800575
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500576 fname = _lib.X509_NAME_ENTRY_get_object(ent)
577 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800578
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500579 nid = _lib.OBJ_obj2nid(fname)
580 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800581
582 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500583 _ffi.string(name),
584 _ffi.string(
585 _lib.ASN1_STRING_data(fval),
586 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800587
588 return result
589X509NameType = X509Name
590
591
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800592class X509Extension(object):
593 def __init__(self, type_name, critical, value, subject=None, issuer=None):
594 """
595 :param typename: The name of the extension to create.
596 :type typename: :py:data:`str`
597
598 :param critical: A flag indicating whether this is a critical extension.
599
600 :param value: The value of the extension.
601 :type value: :py:data:`str`
602
603 :param subject: Optional X509 cert to use as subject.
604 :type subject: :py:class:`X509`
605
606 :param issuer: Optional X509 cert to use as issuer.
607 :type issuer: :py:class:`X509`
608
609 :return: The X509Extension object
610 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500611 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800612
613 # A context is necessary for any extension which uses the r2i conversion
614 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
615 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500616 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800617
618 # We have no configuration database - but perhaps we should (some
619 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500620 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800621
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800622 # Initialize the subject and issuer, if appropriate. ctx is a local,
623 # and as far as I can tell none of the X509V3_* APIs invoked here steal
624 # any references, so no need to mess with reference counts or duplicates.
625 if issuer is not None:
626 if not isinstance(issuer, X509):
627 raise TypeError("issuer must be an X509 instance")
628 ctx.issuer_cert = issuer._x509
629 if subject is not None:
630 if not isinstance(subject, X509):
631 raise TypeError("subject must be an X509 instance")
632 ctx.subject_cert = subject._x509
633
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800634 if critical:
635 # There are other OpenSSL APIs which would let us pass in critical
636 # separately, but they're harder to use, and since value is already
637 # a pile of crappy junk smuggling a ton of utterly important
638 # structured data, what's the point of trying to avoid nasty stuff
639 # with strings? (However, X509V3_EXT_i2d in particular seems like it
640 # would be a better API to invoke. I do not know where to get the
641 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500642 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800643
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500644 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
645 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800646 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500647 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800648
649
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400650 @property
651 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500652 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400653
654 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500655 _lib.GEN_EMAIL: "email",
656 _lib.GEN_DNS: "DNS",
657 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400658 }
659
660 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500661 method = _lib.X509V3_EXT_get(self._extension)
662 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500663 # TODO: This is untested.
664 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400665 payload = self._extension.value.data
666 length = self._extension.value.length
667
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500668 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400669 payloadptr[0] = payload
670
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500671 if method.it != _ffi.NULL:
672 ptr = _lib.ASN1_ITEM_ptr(method.it)
673 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
674 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400675 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500676 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400677 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500678 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400679
680 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500681 for i in range(_lib.sk_GENERAL_NAME_num(names)):
682 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400683 try:
684 label = self._prefixes[name.type]
685 except KeyError:
686 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500687 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500688 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400689 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500690 value = _native(
691 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
692 parts.append(label + ":" + value)
693 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400694
695
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800696 def __str__(self):
697 """
698 :return: a nice text representation of the extension
699 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500700 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400701 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800702
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400703 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500704 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800705 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500706 # TODO: This is untested.
707 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800708
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500709 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800710
711
712 def get_critical(self):
713 """
714 Returns the critical field of the X509Extension
715
716 :return: The critical field.
717 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500718 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800719
720
721 def get_short_name(self):
722 """
723 Returns the short version of the type name of the X509Extension
724
725 :return: The short type name.
726 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500727 obj = _lib.X509_EXTENSION_get_object(self._extension)
728 nid = _lib.OBJ_obj2nid(obj)
729 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800730
731
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800732 def get_data(self):
733 """
734 Returns the data of the X509Extension
735
736 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
737 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500738 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
739 string_result = _ffi.cast('ASN1_STRING*', octet_result)
740 char_result = _lib.ASN1_STRING_data(string_result)
741 result_length = _lib.ASN1_STRING_length(string_result)
742 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800743
744X509ExtensionType = X509Extension
745
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800746
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800747class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800748 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500749 req = _lib.X509_REQ_new()
750 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800751
752
753 def set_pubkey(self, pkey):
754 """
755 Set the public key of the certificate request
756
757 :param pkey: The public key to use
758 :return: None
759 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500760 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800761 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500762 # TODO: This is untested.
763 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800764
765
766 def get_pubkey(self):
767 """
768 Get the public key from the certificate request
769
770 :return: The public key
771 """
772 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500773 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
774 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500775 # TODO: This is untested.
776 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500777 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800778 pkey._only_public = True
779 return pkey
780
781
782 def set_version(self, version):
783 """
784 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
785 request.
786
787 :param version: The version number
788 :return: None
789 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500790 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800791 if not set_result:
792 _raise_current_error()
793
794
795 def get_version(self):
796 """
797 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
798 request.
799
800 :return: an integer giving the value of the version subfield
801 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500802 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800803
804
805 def get_subject(self):
806 """
807 Create an X509Name object for the subject of the certificate request
808
809 :return: An X509Name object
810 """
811 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500812 name._name = _lib.X509_REQ_get_subject_name(self._req)
813 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500814 # TODO: This is untested.
815 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800816
817 # The name is owned by the X509Req structure. As long as the X509Name
818 # Python object is alive, keep the X509Req Python object alive.
819 name._owner = self
820
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800821 return name
822
823
824 def add_extensions(self, extensions):
825 """
826 Add extensions to the request.
827
828 :param extensions: a sequence of X509Extension objects
829 :return: None
830 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500831 stack = _lib.sk_X509_EXTENSION_new_null()
832 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500833 # TODO: This is untested.
834 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800835
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500836 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800837
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800838 for ext in extensions:
839 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800840 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800841
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800842 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500843 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800844
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500845 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800846 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500847 # TODO: This is untested.
848 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800849
850
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800851 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800852 """
853 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800854
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500855 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800856 """
857 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500858 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500859 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800860 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500861 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800862 exts.append(ext)
863 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800864
865
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800866 def sign(self, pkey, digest):
867 """
868 Sign the certificate request using the supplied key and digest
869
870 :param pkey: The key to sign with
871 :param digest: The message digest to use
872 :return: None
873 """
874 if pkey._only_public:
875 raise ValueError("Key has only public part")
876
877 if not pkey._initialized:
878 raise ValueError("Key is uninitialized")
879
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500880 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500881 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800882 raise ValueError("No such digest method")
883
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500884 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800885 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500886 # TODO: This is untested.
887 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800888
889
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800890 def verify(self, pkey):
891 """
892 Verifies a certificate request using the supplied public key
893
894 :param key: a public key
895 :return: True if the signature is correct.
896
897 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
898 problem verifying the signature.
899 """
900 if not isinstance(pkey, PKey):
901 raise TypeError("pkey must be a PKey instance")
902
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500903 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800904 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500905 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800906
907 return result
908
909
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800910X509ReqType = X509Req
911
912
913
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800914class X509(object):
915 def __init__(self):
916 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500917 x509 = _lib.X509_new()
918 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800919
920
921 def set_version(self, version):
922 """
923 Set version number of the certificate
924
925 :param version: The version number
926 :type version: :py:class:`int`
927
928 :return: None
929 """
930 if not isinstance(version, int):
931 raise TypeError("version must be an integer")
932
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500933 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800934
935
936 def get_version(self):
937 """
938 Return version number of the certificate
939
940 :return: Version number as a Python integer
941 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500942 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800943
944
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800945 def get_pubkey(self):
946 """
947 Get the public key of the certificate
948
949 :return: The public key
950 """
951 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500952 pkey._pkey = _lib.X509_get_pubkey(self._x509)
953 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800954 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500955 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800956 pkey._only_public = True
957 return pkey
958
959
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800960 def set_pubkey(self, pkey):
961 """
962 Set the public key of the certificate
963
964 :param pkey: The public key
965
966 :return: None
967 """
968 if not isinstance(pkey, PKey):
969 raise TypeError("pkey must be a PKey instance")
970
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500971 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800972 if not set_result:
973 _raise_current_error()
974
975
976 def sign(self, pkey, digest):
977 """
978 Sign the certificate using the supplied key and digest
979
980 :param pkey: The key to sign with
981 :param digest: The message digest to use
982 :return: None
983 """
984 if not isinstance(pkey, PKey):
985 raise TypeError("pkey must be a PKey instance")
986
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800987 if pkey._only_public:
988 raise ValueError("Key only has public part")
989
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800990 if not pkey._initialized:
991 raise ValueError("Key is uninitialized")
992
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500993 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500994 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800995 raise ValueError("No such digest method")
996
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500997 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800998 if not sign_result:
999 _raise_current_error()
1000
1001
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001002 def get_signature_algorithm(self):
1003 """
1004 Retrieve the signature algorithm used in the certificate
1005
1006 :return: A byte string giving the name of the signature algorithm used in
1007 the certificate.
1008 :raise ValueError: If the signature algorithm is undefined.
1009 """
1010 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001011 nid = _lib.OBJ_obj2nid(alg)
1012 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001013 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001014 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001015
1016
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001017 def digest(self, digest_name):
1018 """
1019 Return the digest of the X509 object.
1020
1021 :param digest_name: The name of the digest algorithm to use.
1022 :type digest_name: :py:class:`bytes`
1023
1024 :return: The digest of the object
1025 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001026 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001027 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001028 raise ValueError("No such digest method")
1029
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001030 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1031 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001032 result_length[0] = len(result_buffer)
1033
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001034 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001035 self._x509, digest, result_buffer, result_length)
1036
1037 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001038 # TODO: This is untested.
1039 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001040
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001041 return b":".join([
1042 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001043 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001044
1045
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001046 def subject_name_hash(self):
1047 """
1048 Return the hash of the X509 subject.
1049
1050 :return: The hash of the subject.
1051 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001052 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001053
1054
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001055 def set_serial_number(self, serial):
1056 """
1057 Set serial number of the certificate
1058
1059 :param serial: The serial number
1060 :type serial: :py:class:`int`
1061
1062 :return: None
1063 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001064 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001065 raise TypeError("serial must be an integer")
1066
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001067 hex_serial = hex(serial)[2:]
1068 if not isinstance(hex_serial, bytes):
1069 hex_serial = hex_serial.encode('ascii')
1070
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001072
1073 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1074 # it. If bignum is still NULL after this call, then the return value is
1075 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001076 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001077
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001078 if bignum_serial[0] == _ffi.NULL:
1079 set_result = _lib.ASN1_INTEGER_set(
1080 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001081 if set_result:
1082 # TODO Not tested
1083 _raise_current_error()
1084 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001085 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1086 _lib.BN_free(bignum_serial[0])
1087 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001088 # TODO Not tested
1089 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001090 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1091 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001092 if not set_result:
1093 # TODO Not tested
1094 _raise_current_error()
1095
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001096
1097 def get_serial_number(self):
1098 """
1099 Return serial number of the certificate
1100
1101 :return: Serial number as a Python integer
1102 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001103 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1104 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001105 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001106 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001107 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001108 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001109 serial = int(hexstring_serial, 16)
1110 return serial
1111 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001112 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001113 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001114 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001115
1116
1117 def gmtime_adj_notAfter(self, amount):
1118 """
1119 Adjust the time stamp for when the certificate stops being valid
1120
1121 :param amount: The number of seconds by which to adjust the ending
1122 validity time.
1123 :type amount: :py:class:`int`
1124
1125 :return: None
1126 """
1127 if not isinstance(amount, int):
1128 raise TypeError("amount must be an integer")
1129
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001130 notAfter = _lib.X509_get_notAfter(self._x509)
1131 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001132
1133
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001134 def gmtime_adj_notBefore(self, amount):
1135 """
1136 Change the timestamp for when the certificate starts being valid to the current
1137 time plus an offset.
1138
1139 :param amount: The number of seconds by which to adjust the starting validity
1140 time.
1141 :return: None
1142 """
1143 if not isinstance(amount, int):
1144 raise TypeError("amount must be an integer")
1145
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001146 notBefore = _lib.X509_get_notBefore(self._x509)
1147 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001148
1149
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001150 def has_expired(self):
1151 """
1152 Check whether the certificate has expired.
1153
1154 :return: True if the certificate has expired, false otherwise
1155 """
1156 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001157 notAfter = _lib.X509_get_notAfter(self._x509)
1158 return _lib.ASN1_UTCTIME_cmp_time_t(
1159 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001160
1161
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001162 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001163 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001164
1165
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001166 def get_notBefore(self):
1167 """
1168 Retrieve the time stamp for when the certificate starts being valid
1169
1170 :return: A string giving the timestamp, in the format::
1171
1172 YYYYMMDDhhmmssZ
1173 YYYYMMDDhhmmss+hhmm
1174 YYYYMMDDhhmmss-hhmm
1175
1176 or None if there is no value set.
1177 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001178 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001179
1180
1181 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001182 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001183
1184
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001185 def set_notBefore(self, when):
1186 """
1187 Set the time stamp for when the certificate starts being valid
1188
1189 :param when: A string giving the timestamp, in the format:
1190
1191 YYYYMMDDhhmmssZ
1192 YYYYMMDDhhmmss+hhmm
1193 YYYYMMDDhhmmss-hhmm
1194 :type when: :py:class:`bytes`
1195
1196 :return: None
1197 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001198 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001199
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001200
1201 def get_notAfter(self):
1202 """
1203 Retrieve the time stamp for when the certificate stops being valid
1204
1205 :return: A string giving the timestamp, in the format::
1206
1207 YYYYMMDDhhmmssZ
1208 YYYYMMDDhhmmss+hhmm
1209 YYYYMMDDhhmmss-hhmm
1210
1211 or None if there is no value set.
1212 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001213 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001214
1215
1216 def set_notAfter(self, when):
1217 """
1218 Set the time stamp for when the certificate stops being valid
1219
1220 :param when: A string giving the timestamp, in the format:
1221
1222 YYYYMMDDhhmmssZ
1223 YYYYMMDDhhmmss+hhmm
1224 YYYYMMDDhhmmss-hhmm
1225 :type when: :py:class:`bytes`
1226
1227 :return: None
1228 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001229 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001230
1231
1232 def _get_name(self, which):
1233 name = X509Name.__new__(X509Name)
1234 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001235 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001236 # TODO: This is untested.
1237 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001238
1239 # The name is owned by the X509 structure. As long as the X509Name
1240 # Python object is alive, keep the X509 Python object alive.
1241 name._owner = self
1242
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001243 return name
1244
1245
1246 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001247 if not isinstance(name, X509Name):
1248 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001249 set_result = which(self._x509, name._name)
1250 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001251 # TODO: This is untested.
1252 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001253
1254
1255 def get_issuer(self):
1256 """
1257 Create an X509Name object for the issuer of the certificate
1258
1259 :return: An X509Name object
1260 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001261 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001262
1263
1264 def set_issuer(self, issuer):
1265 """
1266 Set the issuer of the certificate
1267
1268 :param issuer: The issuer name
1269 :type issuer: :py:class:`X509Name`
1270
1271 :return: None
1272 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001273 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001274
1275
1276 def get_subject(self):
1277 """
1278 Create an X509Name object for the subject of the certificate
1279
1280 :return: An X509Name object
1281 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001282 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001283
1284
1285 def set_subject(self, subject):
1286 """
1287 Set the subject of the certificate
1288
1289 :param subject: The subject name
1290 :type subject: :py:class:`X509Name`
1291 :return: None
1292 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001293 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001294
1295
1296 def get_extension_count(self):
1297 """
1298 Get the number of extensions on the certificate.
1299
1300 :return: The number of extensions as an integer.
1301 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001302 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001303
1304
1305 def add_extensions(self, extensions):
1306 """
1307 Add extensions to the certificate.
1308
1309 :param extensions: a sequence of X509Extension objects
1310 :return: None
1311 """
1312 for ext in extensions:
1313 if not isinstance(ext, X509Extension):
1314 raise ValueError("One of the elements is not an X509Extension")
1315
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001316 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001317 if not add_result:
1318 _raise_current_error()
1319
1320
1321 def get_extension(self, index):
1322 """
1323 Get a specific extension of the certificate by index.
1324
1325 :param index: The index of the extension to retrieve.
1326 :return: The X509Extension object at the specified index.
1327 """
1328 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001329 ext._extension = _lib.X509_get_ext(self._x509, index)
1330 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001331 raise IndexError("extension index out of bounds")
1332
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001333 extension = _lib.X509_EXTENSION_dup(ext._extension)
1334 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001335 return ext
1336
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001337
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):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001344 """
1345 An X509 certificate store.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001346 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001347 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001348 store = _lib.X509_STORE_new()
1349 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001350
1351
1352 def add_cert(self, cert):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001353 """
1354 Adds the certificate :py:data:`cert` to this store.
1355
1356 This is the Python equivalent of OpenSSL's X509_STORE_add_cert.
1357
1358 :param X509 cert: The certificate to add to this store.
1359 :raises TypeError: If the certificate is not an :py:class:`X509`.
1360 :raises Error: If OpenSSL was unhappy with your certificate.
1361 :return: py:data:`None` if the certificate was added successfully.
1362 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001363 if not isinstance(cert, X509):
1364 raise TypeError()
1365
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001366 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001367 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001368 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001369
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001370
1371X509StoreType = X509Store
1372
1373
1374
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001375def load_certificate(type, buffer):
1376 """
1377 Load a certificate from a buffer
1378
1379 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1380
1381 :param buffer: The buffer the certificate is stored in
1382 :type buffer: :py:class:`bytes`
1383
1384 :return: The X509 object
1385 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001386 if isinstance(buffer, _text_type):
1387 buffer = buffer.encode("ascii")
1388
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001389 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001390
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001391 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001392 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001393 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001394 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001395 else:
1396 raise ValueError(
1397 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001398
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001399 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001400 _raise_current_error()
1401
1402 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001403 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001404 return cert
1405
1406
1407def dump_certificate(type, cert):
1408 """
1409 Dump a certificate to a buffer
1410
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001411 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1412 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001413 :param cert: The certificate to dump
1414 :return: The buffer with the dumped certificate in
1415 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001416 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001417
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001418 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001419 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001420 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001421 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001422 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001423 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001424 else:
1425 raise ValueError(
1426 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1427 "FILETYPE_TEXT")
1428
1429 return _bio_to_string(bio)
1430
1431
1432
1433def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1434 """
1435 Dump a private key to a buffer
1436
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001437 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1438 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001439 :param pkey: The PKey to dump
1440 :param cipher: (optional) if encrypted PEM format, the cipher to
1441 use
1442 :param passphrase: (optional) if encrypted PEM format, this can be either
1443 the passphrase to use, or a callback for providing the
1444 passphrase.
1445 :return: The buffer with the dumped key in
1446 :rtype: :py:data:`str`
1447 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001448 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001449
1450 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001451 if passphrase is None:
1452 raise TypeError(
1453 "if a value is given for cipher "
1454 "one must also be given for passphrase")
1455 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001456 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001457 raise ValueError("Invalid cipher name")
1458 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001459 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001460
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001461 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001462 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001463 result_code = _lib.PEM_write_bio_PrivateKey(
1464 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001465 helper.callback, helper.callback_args)
1466 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001467 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001468 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001469 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001470 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1471 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001472 # TODO RSA_free(rsa)?
1473 else:
1474 raise ValueError(
1475 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1476 "FILETYPE_TEXT")
1477
1478 if result_code == 0:
1479 _raise_current_error()
1480
1481 return _bio_to_string(bio)
1482
1483
1484
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001485def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001486 copy = _lib.X509_REVOKED_new()
1487 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001488 # TODO: This is untested.
1489 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001490
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001491 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001492 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001493 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001494
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001495 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001496 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001497 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001498
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001499 if original.extensions != _ffi.NULL:
1500 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1501 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1502 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1503 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1504 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001505 copy.extensions = extension_stack
1506
1507 copy.sequence = original.sequence
1508 return copy
1509
1510
1511
1512class Revoked(object):
1513 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1514 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1515 # OCSP_crl_reason_str. We use the latter, just like the command line
1516 # program.
1517 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001518 b"unspecified",
1519 b"keyCompromise",
1520 b"CACompromise",
1521 b"affiliationChanged",
1522 b"superseded",
1523 b"cessationOfOperation",
1524 b"certificateHold",
1525 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001526 ]
1527
1528 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001529 revoked = _lib.X509_REVOKED_new()
1530 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001531
1532
1533 def set_serial(self, hex_str):
1534 """
1535 Set the serial number of a revoked Revoked structure
1536
1537 :param hex_str: The new serial number.
1538 :type hex_str: :py:data:`str`
1539 :return: None
1540 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001541 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1542 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001543 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001544 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001545 if not bn_result:
1546 raise ValueError("bad hex string")
1547
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001548 asn1_serial = _ffi.gc(
1549 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1550 _lib.ASN1_INTEGER_free)
1551 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001552
1553
1554 def get_serial(self):
1555 """
1556 Return the serial number of a Revoked structure
1557
1558 :return: The serial number as a string
1559 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001560 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001561
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001562 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001563 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001564 # TODO: This is untested.
1565 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001566
1567 return _bio_to_string(bio)
1568
1569
1570 def _delete_reason(self):
1571 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001572 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1573 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1574 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1575 _lib.X509_EXTENSION_free(ext)
1576 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001577 break
1578
1579
1580 def set_reason(self, reason):
1581 """
1582 Set the reason of a Revoked object.
1583
1584 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1585
1586 :param reason: The reason string.
1587 :type reason: :py:class:`str` or :py:class:`NoneType`
1588 :return: None
1589 """
1590 if reason is None:
1591 self._delete_reason()
1592 elif not isinstance(reason, bytes):
1593 raise TypeError("reason must be None or a byte string")
1594 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001595 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001596 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1597
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001598 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1599 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001600 # TODO: This is untested.
1601 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001602 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001603
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001604 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1605 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001606 # TODO: This is untested.
1607 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001608
1609 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001610 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1611 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001612
1613 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001614 # TODO: This is untested.
1615 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001616
1617
1618 def get_reason(self):
1619 """
1620 Return the reason of a Revoked object.
1621
1622 :return: The reason as a string
1623 """
1624 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001625 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1626 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1627 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001628 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001629
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001630 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001631 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001632 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001633 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001634 # TODO: This is untested.
1635 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001636
1637 return _bio_to_string(bio)
1638
1639
1640 def all_reasons(self):
1641 """
1642 Return a list of all the supported reason strings.
1643
1644 :return: A list of reason strings.
1645 """
1646 return self._crl_reasons[:]
1647
1648
1649 def set_rev_date(self, when):
1650 """
1651 Set the revocation timestamp
1652
1653 :param when: A string giving the timestamp, in the format:
1654
1655 YYYYMMDDhhmmssZ
1656 YYYYMMDDhhmmss+hhmm
1657 YYYYMMDDhhmmss-hhmm
1658
1659 :return: None
1660 """
1661 return _set_asn1_time(self._revoked.revocationDate, when)
1662
1663
1664 def get_rev_date(self):
1665 """
1666 Retrieve the revocation date
1667
1668 :return: A string giving the timestamp, in the format:
1669
1670 YYYYMMDDhhmmssZ
1671 YYYYMMDDhhmmss+hhmm
1672 YYYYMMDDhhmmss-hhmm
1673 """
1674 return _get_asn1_time(self._revoked.revocationDate)
1675
1676
1677
1678class CRL(object):
1679 def __init__(self):
1680 """
1681 Create a new empty CRL object.
1682 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001683 crl = _lib.X509_CRL_new()
1684 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001685
1686
1687 def get_revoked(self):
1688 """
1689 Return revoked portion of the CRL structure (by value not reference).
1690
1691 :return: A tuple of Revoked objects.
1692 """
1693 results = []
1694 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001695 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1696 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001697 revoked_copy = _X509_REVOKED_dup(revoked)
1698 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001699 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001700 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001701 if results:
1702 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001703
1704
1705 def add_revoked(self, revoked):
1706 """
1707 Add a revoked (by value not reference) to the CRL structure
1708
1709 :param revoked: The new revoked.
1710 :type revoked: :class:`X509`
1711
1712 :return: None
1713 """
1714 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001715 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001716 # TODO: This is untested.
1717 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001718
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001719 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001720 if add_result == 0:
1721 # TODO: This is untested.
1722 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001723
1724
1725 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1726 """
1727 export a CRL as a string
1728
1729 :param cert: Used to sign CRL.
1730 :type cert: :class:`X509`
1731
1732 :param key: Used to sign CRL.
1733 :type key: :class:`PKey`
1734
1735 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1736
1737 :param days: The number of days until the next update of this CRL.
1738 :type days: :py:data:`int`
1739
1740 :return: :py:data:`str`
1741 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001742 if not isinstance(cert, X509):
1743 raise TypeError("cert must be an X509 instance")
1744 if not isinstance(key, PKey):
1745 raise TypeError("key must be a PKey instance")
1746 if not isinstance(type, int):
1747 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001748
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001749 bio = _lib.BIO_new(_lib.BIO_s_mem())
1750 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001751 # TODO: This is untested.
1752 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001753
1754 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001755 sometime = _lib.ASN1_TIME_new()
1756 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001757 # TODO: This is untested.
1758 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001759
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001760 _lib.X509_gmtime_adj(sometime, 0)
1761 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001762
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001763 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1764 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001765
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001766 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001767
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001768 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001769 if not sign_result:
1770 _raise_current_error()
1771
1772 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001773 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001774 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001775 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001776 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001777 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001778 else:
1779 raise ValueError(
1780 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1781
1782 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001783 # TODO: This is untested.
1784 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001785
1786 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001787CRLType = CRL
1788
1789
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001790
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001791class PKCS7(object):
1792 def type_is_signed(self):
1793 """
1794 Check if this NID_pkcs7_signed object
1795
1796 :return: True if the PKCS7 is of type signed
1797 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001798 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001799 return True
1800 return False
1801
1802
1803 def type_is_enveloped(self):
1804 """
1805 Check if this NID_pkcs7_enveloped object
1806
1807 :returns: True if the PKCS7 is of type enveloped
1808 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001809 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001810 return True
1811 return False
1812
1813
1814 def type_is_signedAndEnveloped(self):
1815 """
1816 Check if this NID_pkcs7_signedAndEnveloped object
1817
1818 :returns: True if the PKCS7 is of type signedAndEnveloped
1819 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001820 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001821 return True
1822 return False
1823
1824
1825 def type_is_data(self):
1826 """
1827 Check if this NID_pkcs7_data object
1828
1829 :return: True if the PKCS7 is of type data
1830 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001831 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001832 return True
1833 return False
1834
1835
1836 def get_type_name(self):
1837 """
1838 Returns the type name of the PKCS7 structure
1839
1840 :return: A string with the typename
1841 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001842 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1843 string_type = _lib.OBJ_nid2sn(nid)
1844 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001845
1846PKCS7Type = PKCS7
1847
1848
1849
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001850class PKCS12(object):
1851 def __init__(self):
1852 self._pkey = None
1853 self._cert = None
1854 self._cacerts = None
1855 self._friendlyname = None
1856
1857
1858 def get_certificate(self):
1859 """
1860 Return certificate portion of the PKCS12 structure
1861
1862 :return: X509 object containing the certificate
1863 """
1864 return self._cert
1865
1866
1867 def set_certificate(self, cert):
1868 """
1869 Replace the certificate portion of the PKCS12 structure
1870
1871 :param cert: The new certificate.
1872 :type cert: :py:class:`X509` or :py:data:`None`
1873 :return: None
1874 """
1875 if not isinstance(cert, X509):
1876 raise TypeError("cert must be an X509 instance")
1877 self._cert = cert
1878
1879
1880 def get_privatekey(self):
1881 """
1882 Return private key portion of the PKCS12 structure
1883
1884 :returns: PKey object containing the private key
1885 """
1886 return self._pkey
1887
1888
1889 def set_privatekey(self, pkey):
1890 """
1891 Replace or set the certificate portion of the PKCS12 structure
1892
1893 :param pkey: The new private key.
1894 :type pkey: :py:class:`PKey`
1895 :return: None
1896 """
1897 if not isinstance(pkey, PKey):
1898 raise TypeError("pkey must be a PKey instance")
1899 self._pkey = pkey
1900
1901
1902 def get_ca_certificates(self):
1903 """
1904 Return CA certificates within of the PKCS12 object
1905
1906 :return: A newly created tuple containing the CA certificates in the chain,
1907 if any are present, or None if no CA certificates are present.
1908 """
1909 if self._cacerts is not None:
1910 return tuple(self._cacerts)
1911
1912
1913 def set_ca_certificates(self, cacerts):
1914 """
1915 Replace or set the CA certificates withing the PKCS12 object.
1916
1917 :param cacerts: The new CA certificates.
1918 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1919 :return: None
1920 """
1921 if cacerts is None:
1922 self._cacerts = None
1923 else:
1924 cacerts = list(cacerts)
1925 for cert in cacerts:
1926 if not isinstance(cert, X509):
1927 raise TypeError("iterable must only contain X509 instances")
1928 self._cacerts = cacerts
1929
1930
1931 def set_friendlyname(self, name):
1932 """
1933 Replace or set the certificate portion of the PKCS12 structure
1934
1935 :param name: The new friendly name.
1936 :type name: :py:class:`bytes`
1937 :return: None
1938 """
1939 if name is None:
1940 self._friendlyname = None
1941 elif not isinstance(name, bytes):
1942 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1943 self._friendlyname = name
1944
1945
1946 def get_friendlyname(self):
1947 """
1948 Return friendly name portion of the PKCS12 structure
1949
1950 :returns: String containing the friendlyname
1951 """
1952 return self._friendlyname
1953
1954
1955 def export(self, passphrase=None, iter=2048, maciter=1):
1956 """
1957 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1958
1959 :param passphrase: used to encrypt the PKCS12
1960 :type passphrase: :py:data:`bytes`
1961
1962 :param iter: How many times to repeat the encryption
1963 :type iter: :py:data:`int`
1964
1965 :param maciter: How many times to repeat the MAC
1966 :type maciter: :py:data:`int`
1967
1968 :return: The string containing the PKCS12
1969 """
1970 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001971 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001972 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001973 cacerts = _lib.sk_X509_new_null()
1974 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001975 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001976 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001977
1978 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001979 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001980
1981 friendlyname = self._friendlyname
1982 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001983 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001984
1985 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001986 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001987 else:
1988 pkey = self._pkey._pkey
1989
1990 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001991 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001992 else:
1993 cert = self._cert._x509
1994
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001995 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001996 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001997 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1998 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001999 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002000 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002001 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002002 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002003
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002004 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002005 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002006 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002007
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002008PKCS12Type = PKCS12
2009
2010
2011
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002012class NetscapeSPKI(object):
2013 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002014 spki = _lib.NETSCAPE_SPKI_new()
2015 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002016
2017
2018 def sign(self, pkey, digest):
2019 """
2020 Sign the certificate request using the supplied key and digest
2021
2022 :param pkey: The key to sign with
2023 :param digest: The message digest to use
2024 :return: None
2025 """
2026 if pkey._only_public:
2027 raise ValueError("Key has only public part")
2028
2029 if not pkey._initialized:
2030 raise ValueError("Key is uninitialized")
2031
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002032 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002033 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002034 raise ValueError("No such digest method")
2035
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002036 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002037 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002038 # TODO: This is untested.
2039 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002040
2041
2042 def verify(self, key):
2043 """
2044 Verifies a certificate request using the supplied public key
2045
2046 :param key: a public key
2047 :return: True if the signature is correct.
2048 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2049 problem verifying the signature.
2050 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002051 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002052 if answer <= 0:
2053 _raise_current_error()
2054 return True
2055
2056
2057 def b64_encode(self):
2058 """
2059 Generate a base64 encoded string from an SPKI
2060
2061 :return: The base64 encoded string
2062 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002063 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2064 result = _ffi.string(encoded)
2065 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002066 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002067
2068
2069 def get_pubkey(self):
2070 """
2071 Get the public key of the certificate
2072
2073 :return: The public key
2074 """
2075 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002076 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2077 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002078 # TODO: This is untested.
2079 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002080 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002081 pkey._only_public = True
2082 return pkey
2083
2084
2085 def set_pubkey(self, pkey):
2086 """
2087 Set the public key of the certificate
2088
2089 :param pkey: The public key
2090 :return: None
2091 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002092 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002093 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002094 # TODO: This is untested.
2095 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002096NetscapeSPKIType = NetscapeSPKI
2097
2098
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002099class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002100 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002101 if type != FILETYPE_PEM and passphrase is not None:
2102 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002103 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002104 self._more_args = more_args
2105 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002106 self._problems = []
2107
2108
2109 @property
2110 def callback(self):
2111 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002112 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002113 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002114 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002115 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002116 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002117 else:
2118 raise TypeError("Last argument must be string or callable")
2119
2120
2121 @property
2122 def callback_args(self):
2123 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002124 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002125 elif isinstance(self._passphrase, bytes):
2126 return self._passphrase
2127 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002128 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002129 else:
2130 raise TypeError("Last argument must be string or callable")
2131
2132
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002133 def raise_if_problem(self, exceptionType=Error):
2134 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002135 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002136 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002137 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002138 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002139 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002140 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002141
2142
2143 def _read_passphrase(self, buf, size, rwflag, userdata):
2144 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002145 if self._more_args:
2146 result = self._passphrase(size, rwflag, userdata)
2147 else:
2148 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002149 if not isinstance(result, bytes):
2150 raise ValueError("String expected")
2151 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002152 if self._truncate:
2153 result = result[:size]
2154 else:
2155 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002156 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002157 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002158 return len(result)
2159 except Exception as e:
2160 self._problems.append(e)
2161 return 0
2162
2163
2164
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002165def load_privatekey(type, buffer, passphrase=None):
2166 """
2167 Load a private key from a buffer
2168
2169 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2170 :param buffer: The buffer the key is stored in
2171 :param passphrase: (optional) if encrypted PEM format, this can be
2172 either the passphrase to use, or a callback for
2173 providing the passphrase.
2174
2175 :return: The PKey object
2176 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002177 if isinstance(buffer, _text_type):
2178 buffer = buffer.encode("ascii")
2179
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002180 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002181
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002182 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002183 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002184 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2185 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002186 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002187 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002188 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002189 else:
2190 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2191
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002192 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002193 _raise_current_error()
2194
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002195 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002196 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002197 return pkey
2198
2199
2200
2201def dump_certificate_request(type, req):
2202 """
2203 Dump a certificate request to a buffer
2204
2205 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2206 :param req: The certificate request to dump
2207 :return: The buffer with the dumped certificate request in
2208 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002209 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002210
2211 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002212 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002213 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002214 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002215 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002216 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002217 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002218 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002219
2220 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002221 # TODO: This is untested.
2222 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002223
2224 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002225
2226
2227
2228def load_certificate_request(type, buffer):
2229 """
2230 Load a certificate request from a buffer
2231
2232 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2233 :param buffer: The buffer the certificate request is stored in
2234 :return: The X509Req object
2235 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002236 if isinstance(buffer, _text_type):
2237 buffer = buffer.encode("ascii")
2238
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002239 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002240
2241 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002242 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002243 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002244 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002245 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002246 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002247
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002248 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002249 # TODO: This is untested.
2250 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002251
2252 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002253 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002254 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002255
2256
2257
2258def sign(pkey, data, digest):
2259 """
2260 Sign data with a digest
2261
2262 :param pkey: Pkey to sign with
2263 :param data: data to be signed
2264 :param digest: message digest to use
2265 :return: signature
2266 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002267 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002268 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002269 raise ValueError("No such digest method")
2270
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002271 md_ctx = _ffi.new("EVP_MD_CTX*")
2272 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002273
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002274 _lib.EVP_SignInit(md_ctx, digest_obj)
2275 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002276
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002277 signature_buffer = _ffi.new("unsigned char[]", 512)
2278 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002279 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002280 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002281 md_ctx, signature_buffer, signature_length, pkey._pkey)
2282
2283 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002284 # TODO: This is untested.
2285 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002286
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002287 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002288
2289
2290
2291def verify(cert, signature, data, digest):
2292 """
2293 Verify a signature
2294
2295 :param cert: signing certificate (X509 object)
2296 :param signature: signature returned by sign function
2297 :param data: data to be verified
2298 :param digest: message digest to use
2299 :return: None if the signature is correct, raise exception otherwise
2300 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002301 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002302 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002303 raise ValueError("No such digest method")
2304
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002305 pkey = _lib.X509_get_pubkey(cert._x509)
2306 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002307 # TODO: This is untested.
2308 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002309 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002310
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002311 md_ctx = _ffi.new("EVP_MD_CTX*")
2312 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002313
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002314 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2315 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2316 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002317
2318 if verify_result != 1:
2319 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002320
2321
2322
2323def load_crl(type, buffer):
2324 """
2325 Load a certificate revocation list from a buffer
2326
2327 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2328 :param buffer: The buffer the CRL is stored in
2329
2330 :return: The PKey object
2331 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002332 if isinstance(buffer, _text_type):
2333 buffer = buffer.encode("ascii")
2334
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002335 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002336
2337 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002338 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002339 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002340 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002341 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002342 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2343
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002344 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002345 _raise_current_error()
2346
2347 result = CRL.__new__(CRL)
2348 result._crl = crl
2349 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002350
2351
2352
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002353def load_pkcs7_data(type, buffer):
2354 """
2355 Load pkcs7 data from a buffer
2356
2357 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2358 :param buffer: The buffer with the pkcs7 data.
2359 :return: The PKCS7 object
2360 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002361 if isinstance(buffer, _text_type):
2362 buffer = buffer.encode("ascii")
2363
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002364 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002365
2366 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002367 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002368 elif type == FILETYPE_ASN1:
2369 pass
2370 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002371 # TODO: This is untested.
2372 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002373 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2374
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002375 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002376 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002377
2378 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002379 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002380 return pypkcs7
2381
2382
2383
Stephen Holsapple38482622014-04-05 20:29:34 -07002384def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002385 """
2386 Load a PKCS12 object from a buffer
2387
2388 :param buffer: The buffer the certificate is stored in
2389 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2390 :returns: The PKCS12 object
2391 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002392 if isinstance(buffer, _text_type):
2393 buffer = buffer.encode("ascii")
2394
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002395 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002396
Stephen Holsapple38482622014-04-05 20:29:34 -07002397 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2398 # password based encryption no password and a zero length password are two
2399 # different things, but OpenSSL implementation will try both to figure out
2400 # which one works.
2401 if not passphrase:
2402 passphrase = _ffi.NULL
2403
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002404 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2405 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002406 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002407 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002408
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002409 pkey = _ffi.new("EVP_PKEY**")
2410 cert = _ffi.new("X509**")
2411 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002412
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002413 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002414 if not parse_result:
2415 _raise_current_error()
2416
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002417 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002418
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002419 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2420 # queue for no particular reason. This error isn't interesting to anyone
2421 # outside this function. It's not even interesting to us. Get rid of it.
2422 try:
2423 _raise_current_error()
2424 except Error:
2425 pass
2426
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002427 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002428 pykey = None
2429 else:
2430 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002431 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002432
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002433 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002434 pycert = None
2435 friendlyname = None
2436 else:
2437 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002438 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002439
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002440 friendlyname_length = _ffi.new("int*")
2441 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2442 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2443 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002444 friendlyname = None
2445
2446 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002447 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002448 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002449 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002450 pycacerts.append(pycacert)
2451 if not pycacerts:
2452 pycacerts = None
2453
2454 pkcs12 = PKCS12.__new__(PKCS12)
2455 pkcs12._pkey = pykey
2456 pkcs12._cert = pycert
2457 pkcs12._cacerts = pycacerts
2458 pkcs12._friendlyname = friendlyname
2459 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002460
2461
2462def _initialize_openssl_threads(get_ident, Lock):
2463 import _ssl
2464 return
2465
2466 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2467
2468 def locking_function(mode, index, filename, line):
2469 if mode & _lib.CRYPTO_LOCK:
2470 locks[index].acquire()
2471 else:
2472 locks[index].release()
2473
2474 _lib.CRYPTO_set_id_callback(
2475 _ffi.callback("unsigned long (*)(void)", get_ident))
2476
2477 _lib.CRYPTO_set_locking_callback(
2478 _ffi.callback(
2479 "void (*)(int, int, const char*, int)", locking_function))
2480
2481
2482try:
2483 from thread import get_ident
2484 from threading import Lock
2485except ImportError:
2486 pass
2487else:
2488 _initialize_openssl_threads(get_ident, Lock)
2489 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002490
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002491# There are no direct unit tests for this initialization. It is tested
2492# indirectly since it is necessary for functions like dump_privatekey when
2493# using encryption.
2494#
2495# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2496# and some other similar tests may fail without this (though they may not if
2497# the Python runtime has already done some initialization of the underlying
2498# OpenSSL library (and is linked against the same one that cryptography is
2499# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002500_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002501
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002502# This is similar but exercised mainly by exception_from_error_queue. It calls
2503# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2504_lib.SSL_load_error_strings()