blob: efa1d91039e3fc8f9b02baf1a4f1d0ba843278d4 [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
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001337X509Type = X509
1338
1339
1340
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001341class X509Store(object):
1342 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001343 store = _lib.X509_STORE_new()
1344 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001345
1346
1347 def add_cert(self, cert):
1348 if not isinstance(cert, X509):
1349 raise TypeError()
1350
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001351 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001352 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001353 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001354
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001355
1356X509StoreType = X509Store
1357
1358
1359
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001360def load_certificate(type, buffer):
1361 """
1362 Load a certificate from a buffer
1363
1364 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1365
1366 :param buffer: The buffer the certificate is stored in
1367 :type buffer: :py:class:`bytes`
1368
1369 :return: The X509 object
1370 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001371 if isinstance(buffer, _text_type):
1372 buffer = buffer.encode("ascii")
1373
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001374 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001375
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001376 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001377 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001378 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001379 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001380 else:
1381 raise ValueError(
1382 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001383
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001384 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001385 _raise_current_error()
1386
1387 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001388 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001389 return cert
1390
1391
1392def dump_certificate(type, cert):
1393 """
1394 Dump a certificate to a buffer
1395
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001396 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1397 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001398 :param cert: The certificate to dump
1399 :return: The buffer with the dumped certificate in
1400 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001401 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001402
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001403 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001404 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001405 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001406 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001407 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001408 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001409 else:
1410 raise ValueError(
1411 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1412 "FILETYPE_TEXT")
1413
1414 return _bio_to_string(bio)
1415
1416
1417
1418def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1419 """
1420 Dump a private key to a buffer
1421
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001422 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1423 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001424 :param pkey: The PKey to dump
1425 :param cipher: (optional) if encrypted PEM format, the cipher to
1426 use
1427 :param passphrase: (optional) if encrypted PEM format, this can be either
1428 the passphrase to use, or a callback for providing the
1429 passphrase.
1430 :return: The buffer with the dumped key in
1431 :rtype: :py:data:`str`
1432 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001433 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001434
1435 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001436 if passphrase is None:
1437 raise TypeError(
1438 "if a value is given for cipher "
1439 "one must also be given for passphrase")
1440 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001441 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001442 raise ValueError("Invalid cipher name")
1443 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001444 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001445
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001446 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001447 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001448 result_code = _lib.PEM_write_bio_PrivateKey(
1449 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001450 helper.callback, helper.callback_args)
1451 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001452 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001453 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001454 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001455 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1456 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001457 # TODO RSA_free(rsa)?
1458 else:
1459 raise ValueError(
1460 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1461 "FILETYPE_TEXT")
1462
1463 if result_code == 0:
1464 _raise_current_error()
1465
1466 return _bio_to_string(bio)
1467
1468
1469
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001470def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001471 copy = _lib.X509_REVOKED_new()
1472 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001473 # TODO: This is untested.
1474 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001475
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001476 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001477 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001478 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001479
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001480 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001481 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001482 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001483
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001484 if original.extensions != _ffi.NULL:
1485 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1486 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1487 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1488 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1489 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001490 copy.extensions = extension_stack
1491
1492 copy.sequence = original.sequence
1493 return copy
1494
1495
1496
1497class Revoked(object):
1498 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1499 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1500 # OCSP_crl_reason_str. We use the latter, just like the command line
1501 # program.
1502 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001503 b"unspecified",
1504 b"keyCompromise",
1505 b"CACompromise",
1506 b"affiliationChanged",
1507 b"superseded",
1508 b"cessationOfOperation",
1509 b"certificateHold",
1510 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001511 ]
1512
1513 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001514 revoked = _lib.X509_REVOKED_new()
1515 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001516
1517
1518 def set_serial(self, hex_str):
1519 """
1520 Set the serial number of a revoked Revoked structure
1521
1522 :param hex_str: The new serial number.
1523 :type hex_str: :py:data:`str`
1524 :return: None
1525 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001526 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1527 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001528 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001529 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001530 if not bn_result:
1531 raise ValueError("bad hex string")
1532
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001533 asn1_serial = _ffi.gc(
1534 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1535 _lib.ASN1_INTEGER_free)
1536 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001537
1538
1539 def get_serial(self):
1540 """
1541 Return the serial number of a Revoked structure
1542
1543 :return: The serial number as a string
1544 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001545 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001546
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001547 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001548 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001549 # TODO: This is untested.
1550 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001551
1552 return _bio_to_string(bio)
1553
1554
1555 def _delete_reason(self):
1556 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001557 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1558 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1559 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1560 _lib.X509_EXTENSION_free(ext)
1561 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001562 break
1563
1564
1565 def set_reason(self, reason):
1566 """
1567 Set the reason of a Revoked object.
1568
1569 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1570
1571 :param reason: The reason string.
1572 :type reason: :py:class:`str` or :py:class:`NoneType`
1573 :return: None
1574 """
1575 if reason is None:
1576 self._delete_reason()
1577 elif not isinstance(reason, bytes):
1578 raise TypeError("reason must be None or a byte string")
1579 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001580 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001581 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1582
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001583 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1584 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001585 # TODO: This is untested.
1586 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001587 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001588
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001589 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1590 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001591 # TODO: This is untested.
1592 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001593
1594 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001595 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1596 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001597
1598 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001599 # TODO: This is untested.
1600 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001601
1602
1603 def get_reason(self):
1604 """
1605 Return the reason of a Revoked object.
1606
1607 :return: The reason as a string
1608 """
1609 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001610 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1611 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1612 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001613 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001614
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001615 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001616 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001617 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001618 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001619 # TODO: This is untested.
1620 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001621
1622 return _bio_to_string(bio)
1623
1624
1625 def all_reasons(self):
1626 """
1627 Return a list of all the supported reason strings.
1628
1629 :return: A list of reason strings.
1630 """
1631 return self._crl_reasons[:]
1632
1633
1634 def set_rev_date(self, when):
1635 """
1636 Set the revocation timestamp
1637
1638 :param when: A string giving the timestamp, in the format:
1639
1640 YYYYMMDDhhmmssZ
1641 YYYYMMDDhhmmss+hhmm
1642 YYYYMMDDhhmmss-hhmm
1643
1644 :return: None
1645 """
1646 return _set_asn1_time(self._revoked.revocationDate, when)
1647
1648
1649 def get_rev_date(self):
1650 """
1651 Retrieve the revocation date
1652
1653 :return: A string giving the timestamp, in the format:
1654
1655 YYYYMMDDhhmmssZ
1656 YYYYMMDDhhmmss+hhmm
1657 YYYYMMDDhhmmss-hhmm
1658 """
1659 return _get_asn1_time(self._revoked.revocationDate)
1660
1661
1662
1663class CRL(object):
1664 def __init__(self):
1665 """
1666 Create a new empty CRL object.
1667 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001668 crl = _lib.X509_CRL_new()
1669 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001670
1671
1672 def get_revoked(self):
1673 """
1674 Return revoked portion of the CRL structure (by value not reference).
1675
1676 :return: A tuple of Revoked objects.
1677 """
1678 results = []
1679 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001680 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1681 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001682 revoked_copy = _X509_REVOKED_dup(revoked)
1683 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001684 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001685 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001686 if results:
1687 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001688
1689
1690 def add_revoked(self, revoked):
1691 """
1692 Add a revoked (by value not reference) to the CRL structure
1693
1694 :param revoked: The new revoked.
1695 :type revoked: :class:`X509`
1696
1697 :return: None
1698 """
1699 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001700 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001701 # TODO: This is untested.
1702 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001703
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001704 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001705 if add_result == 0:
1706 # TODO: This is untested.
1707 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001708
1709
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04001710 def export(self, cert, key, type=FILETYPE_PEM, days=100, digest="md5"):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001711 """
1712 export a CRL as a string
1713
1714 :param cert: Used to sign CRL.
1715 :type cert: :class:`X509`
1716
1717 :param key: Used to sign CRL.
1718 :type key: :class:`PKey`
1719
1720 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1721
1722 :param days: The number of days until the next update of this CRL.
1723 :type days: :py:data:`int`
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04001724 :param digest: The message digest to use
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001725
1726 :return: :py:data:`str`
1727 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001728 if not isinstance(cert, X509):
1729 raise TypeError("cert must be an X509 instance")
1730 if not isinstance(key, PKey):
1731 raise TypeError("key must be a PKey instance")
1732 if not isinstance(type, int):
1733 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001734
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04001735 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
1736 if digest_obj == _ffi.NULL:
1737 raise ValueError("No such digest method")
1738
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001739 bio = _lib.BIO_new(_lib.BIO_s_mem())
1740 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001741 # TODO: This is untested.
1742 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001743
1744 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001745 sometime = _lib.ASN1_TIME_new()
1746 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001747 # TODO: This is untested.
1748 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001749
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001750 _lib.X509_gmtime_adj(sometime, 0)
1751 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001752
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001753 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1754 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001755
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001756 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001757
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04001758 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, digest_obj)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001759 if not sign_result:
1760 _raise_current_error()
1761
1762 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001763 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001764 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001765 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001766 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001767 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001768 else:
1769 raise ValueError(
1770 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1771
1772 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001773 # TODO: This is untested.
1774 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001775
1776 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001777CRLType = CRL
1778
1779
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001780
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001781class PKCS7(object):
1782 def type_is_signed(self):
1783 """
1784 Check if this NID_pkcs7_signed object
1785
1786 :return: True if the PKCS7 is of type signed
1787 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001788 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001789 return True
1790 return False
1791
1792
1793 def type_is_enveloped(self):
1794 """
1795 Check if this NID_pkcs7_enveloped object
1796
1797 :returns: True if the PKCS7 is of type enveloped
1798 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001799 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001800 return True
1801 return False
1802
1803
1804 def type_is_signedAndEnveloped(self):
1805 """
1806 Check if this NID_pkcs7_signedAndEnveloped object
1807
1808 :returns: True if the PKCS7 is of type signedAndEnveloped
1809 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001810 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001811 return True
1812 return False
1813
1814
1815 def type_is_data(self):
1816 """
1817 Check if this NID_pkcs7_data object
1818
1819 :return: True if the PKCS7 is of type data
1820 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001821 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001822 return True
1823 return False
1824
1825
1826 def get_type_name(self):
1827 """
1828 Returns the type name of the PKCS7 structure
1829
1830 :return: A string with the typename
1831 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001832 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1833 string_type = _lib.OBJ_nid2sn(nid)
1834 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001835
1836PKCS7Type = PKCS7
1837
1838
1839
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001840class PKCS12(object):
1841 def __init__(self):
1842 self._pkey = None
1843 self._cert = None
1844 self._cacerts = None
1845 self._friendlyname = None
1846
1847
1848 def get_certificate(self):
1849 """
1850 Return certificate portion of the PKCS12 structure
1851
1852 :return: X509 object containing the certificate
1853 """
1854 return self._cert
1855
1856
1857 def set_certificate(self, cert):
1858 """
1859 Replace the certificate portion of the PKCS12 structure
1860
1861 :param cert: The new certificate.
1862 :type cert: :py:class:`X509` or :py:data:`None`
1863 :return: None
1864 """
1865 if not isinstance(cert, X509):
1866 raise TypeError("cert must be an X509 instance")
1867 self._cert = cert
1868
1869
1870 def get_privatekey(self):
1871 """
1872 Return private key portion of the PKCS12 structure
1873
1874 :returns: PKey object containing the private key
1875 """
1876 return self._pkey
1877
1878
1879 def set_privatekey(self, pkey):
1880 """
1881 Replace or set the certificate portion of the PKCS12 structure
1882
1883 :param pkey: The new private key.
1884 :type pkey: :py:class:`PKey`
1885 :return: None
1886 """
1887 if not isinstance(pkey, PKey):
1888 raise TypeError("pkey must be a PKey instance")
1889 self._pkey = pkey
1890
1891
1892 def get_ca_certificates(self):
1893 """
1894 Return CA certificates within of the PKCS12 object
1895
1896 :return: A newly created tuple containing the CA certificates in the chain,
1897 if any are present, or None if no CA certificates are present.
1898 """
1899 if self._cacerts is not None:
1900 return tuple(self._cacerts)
1901
1902
1903 def set_ca_certificates(self, cacerts):
1904 """
1905 Replace or set the CA certificates withing the PKCS12 object.
1906
1907 :param cacerts: The new CA certificates.
1908 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1909 :return: None
1910 """
1911 if cacerts is None:
1912 self._cacerts = None
1913 else:
1914 cacerts = list(cacerts)
1915 for cert in cacerts:
1916 if not isinstance(cert, X509):
1917 raise TypeError("iterable must only contain X509 instances")
1918 self._cacerts = cacerts
1919
1920
1921 def set_friendlyname(self, name):
1922 """
1923 Replace or set the certificate portion of the PKCS12 structure
1924
1925 :param name: The new friendly name.
1926 :type name: :py:class:`bytes`
1927 :return: None
1928 """
1929 if name is None:
1930 self._friendlyname = None
1931 elif not isinstance(name, bytes):
1932 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1933 self._friendlyname = name
1934
1935
1936 def get_friendlyname(self):
1937 """
1938 Return friendly name portion of the PKCS12 structure
1939
1940 :returns: String containing the friendlyname
1941 """
1942 return self._friendlyname
1943
1944
1945 def export(self, passphrase=None, iter=2048, maciter=1):
1946 """
1947 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1948
1949 :param passphrase: used to encrypt the PKCS12
1950 :type passphrase: :py:data:`bytes`
1951
1952 :param iter: How many times to repeat the encryption
1953 :type iter: :py:data:`int`
1954
1955 :param maciter: How many times to repeat the MAC
1956 :type maciter: :py:data:`int`
1957
1958 :return: The string containing the PKCS12
1959 """
1960 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001961 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001962 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001963 cacerts = _lib.sk_X509_new_null()
1964 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001965 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001966 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001967
1968 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001969 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001970
1971 friendlyname = self._friendlyname
1972 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001973 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001974
1975 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001976 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001977 else:
1978 pkey = self._pkey._pkey
1979
1980 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001981 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001982 else:
1983 cert = self._cert._x509
1984
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001985 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001986 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001987 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1988 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001989 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001990 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001991 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001992 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001993
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001994 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001995 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001996 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001997
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001998PKCS12Type = PKCS12
1999
2000
2001
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002002class NetscapeSPKI(object):
2003 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002004 spki = _lib.NETSCAPE_SPKI_new()
2005 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002006
2007
2008 def sign(self, pkey, digest):
2009 """
2010 Sign the certificate request using the supplied key and digest
2011
2012 :param pkey: The key to sign with
2013 :param digest: The message digest to use
2014 :return: None
2015 """
2016 if pkey._only_public:
2017 raise ValueError("Key has only public part")
2018
2019 if not pkey._initialized:
2020 raise ValueError("Key is uninitialized")
2021
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002022 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002023 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002024 raise ValueError("No such digest method")
2025
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002026 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002027 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002028 # TODO: This is untested.
2029 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002030
2031
2032 def verify(self, key):
2033 """
2034 Verifies a certificate request using the supplied public key
2035
2036 :param key: a public key
2037 :return: True if the signature is correct.
2038 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2039 problem verifying the signature.
2040 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002041 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002042 if answer <= 0:
2043 _raise_current_error()
2044 return True
2045
2046
2047 def b64_encode(self):
2048 """
2049 Generate a base64 encoded string from an SPKI
2050
2051 :return: The base64 encoded string
2052 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002053 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2054 result = _ffi.string(encoded)
2055 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002056 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002057
2058
2059 def get_pubkey(self):
2060 """
2061 Get the public key of the certificate
2062
2063 :return: The public key
2064 """
2065 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002066 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2067 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002068 # TODO: This is untested.
2069 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002070 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002071 pkey._only_public = True
2072 return pkey
2073
2074
2075 def set_pubkey(self, pkey):
2076 """
2077 Set the public key of the certificate
2078
2079 :param pkey: The public key
2080 :return: None
2081 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002082 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002083 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002084 # TODO: This is untested.
2085 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002086NetscapeSPKIType = NetscapeSPKI
2087
2088
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002089class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002090 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002091 if type != FILETYPE_PEM and passphrase is not None:
2092 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002093 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002094 self._more_args = more_args
2095 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002096 self._problems = []
2097
2098
2099 @property
2100 def callback(self):
2101 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002102 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002103 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002104 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002105 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002106 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002107 else:
2108 raise TypeError("Last argument must be string or callable")
2109
2110
2111 @property
2112 def callback_args(self):
2113 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002114 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002115 elif isinstance(self._passphrase, bytes):
2116 return self._passphrase
2117 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002118 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002119 else:
2120 raise TypeError("Last argument must be string or callable")
2121
2122
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002123 def raise_if_problem(self, exceptionType=Error):
2124 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002125 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002126 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002127 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002128 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002129 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002130 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002131
2132
2133 def _read_passphrase(self, buf, size, rwflag, userdata):
2134 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002135 if self._more_args:
2136 result = self._passphrase(size, rwflag, userdata)
2137 else:
2138 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002139 if not isinstance(result, bytes):
2140 raise ValueError("String expected")
2141 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002142 if self._truncate:
2143 result = result[:size]
2144 else:
2145 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002146 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002147 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002148 return len(result)
2149 except Exception as e:
2150 self._problems.append(e)
2151 return 0
2152
2153
2154
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002155def load_privatekey(type, buffer, passphrase=None):
2156 """
2157 Load a private key from a buffer
2158
2159 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2160 :param buffer: The buffer the key is stored in
2161 :param passphrase: (optional) if encrypted PEM format, this can be
2162 either the passphrase to use, or a callback for
2163 providing the passphrase.
2164
2165 :return: The PKey object
2166 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002167 if isinstance(buffer, _text_type):
2168 buffer = buffer.encode("ascii")
2169
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002170 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002171
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002172 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002173 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002174 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2175 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002176 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002177 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002178 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002179 else:
2180 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2181
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002182 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002183 _raise_current_error()
2184
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002185 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002186 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002187 return pkey
2188
2189
2190
2191def dump_certificate_request(type, req):
2192 """
2193 Dump a certificate request to a buffer
2194
2195 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2196 :param req: The certificate request to dump
2197 :return: The buffer with the dumped certificate request in
2198 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002199 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002200
2201 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002202 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002203 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002204 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002205 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002206 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002207 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002208 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002209
2210 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002211 # TODO: This is untested.
2212 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002213
2214 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002215
2216
2217
2218def load_certificate_request(type, buffer):
2219 """
2220 Load a certificate request from a buffer
2221
2222 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2223 :param buffer: The buffer the certificate request is stored in
2224 :return: The X509Req object
2225 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002226 if isinstance(buffer, _text_type):
2227 buffer = buffer.encode("ascii")
2228
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002229 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002230
2231 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002232 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002233 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002234 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002235 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002236 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002237
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002238 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002239 # TODO: This is untested.
2240 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002241
2242 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002243 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002244 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002245
2246
2247
2248def sign(pkey, data, digest):
2249 """
2250 Sign data with a digest
2251
2252 :param pkey: Pkey to sign with
2253 :param data: data to be signed
2254 :param digest: message digest to use
2255 :return: signature
2256 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002257 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002258 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002259 raise ValueError("No such digest method")
2260
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002261 md_ctx = _ffi.new("EVP_MD_CTX*")
2262 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002263
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002264 _lib.EVP_SignInit(md_ctx, digest_obj)
2265 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002266
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002267 signature_buffer = _ffi.new("unsigned char[]", 512)
2268 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002269 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002270 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002271 md_ctx, signature_buffer, signature_length, pkey._pkey)
2272
2273 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002274 # TODO: This is untested.
2275 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002276
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002277 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002278
2279
2280
2281def verify(cert, signature, data, digest):
2282 """
2283 Verify a signature
2284
2285 :param cert: signing certificate (X509 object)
2286 :param signature: signature returned by sign function
2287 :param data: data to be verified
2288 :param digest: message digest to use
2289 :return: None if the signature is correct, raise exception otherwise
2290 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002291 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002292 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002293 raise ValueError("No such digest method")
2294
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002295 pkey = _lib.X509_get_pubkey(cert._x509)
2296 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002297 # TODO: This is untested.
2298 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002299 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002300
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002301 md_ctx = _ffi.new("EVP_MD_CTX*")
2302 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002303
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002304 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2305 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2306 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002307
2308 if verify_result != 1:
2309 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002310
2311
2312
2313def load_crl(type, buffer):
2314 """
2315 Load a certificate revocation list from a buffer
2316
2317 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2318 :param buffer: The buffer the CRL is stored in
2319
2320 :return: The PKey object
2321 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002322 if isinstance(buffer, _text_type):
2323 buffer = buffer.encode("ascii")
2324
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002325 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002326
2327 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002328 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002329 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002330 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002331 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002332 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2333
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002334 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002335 _raise_current_error()
2336
2337 result = CRL.__new__(CRL)
2338 result._crl = crl
2339 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002340
2341
2342
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002343def load_pkcs7_data(type, buffer):
2344 """
2345 Load pkcs7 data from a buffer
2346
2347 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2348 :param buffer: The buffer with the pkcs7 data.
2349 :return: The PKCS7 object
2350 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002351 if isinstance(buffer, _text_type):
2352 buffer = buffer.encode("ascii")
2353
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002354 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002355
2356 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002357 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002358 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002359 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002360 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002361 # TODO: This is untested.
2362 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002363 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2364
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002365 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002366 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002367
2368 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002369 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002370 return pypkcs7
2371
2372
2373
Stephen Holsapple38482622014-04-05 20:29:34 -07002374def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002375 """
2376 Load a PKCS12 object from a buffer
2377
2378 :param buffer: The buffer the certificate is stored in
2379 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2380 :returns: The PKCS12 object
2381 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002382 if isinstance(buffer, _text_type):
2383 buffer = buffer.encode("ascii")
2384
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002385 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002386
Stephen Holsapple38482622014-04-05 20:29:34 -07002387 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2388 # password based encryption no password and a zero length password are two
2389 # different things, but OpenSSL implementation will try both to figure out
2390 # which one works.
2391 if not passphrase:
2392 passphrase = _ffi.NULL
2393
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002394 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2395 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002396 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002397 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002398
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002399 pkey = _ffi.new("EVP_PKEY**")
2400 cert = _ffi.new("X509**")
2401 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002402
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002403 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002404 if not parse_result:
2405 _raise_current_error()
2406
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002407 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002408
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002409 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2410 # queue for no particular reason. This error isn't interesting to anyone
2411 # outside this function. It's not even interesting to us. Get rid of it.
2412 try:
2413 _raise_current_error()
2414 except Error:
2415 pass
2416
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002417 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002418 pykey = None
2419 else:
2420 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002421 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002422
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002423 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002424 pycert = None
2425 friendlyname = None
2426 else:
2427 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002428 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002429
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002430 friendlyname_length = _ffi.new("int*")
2431 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2432 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2433 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002434 friendlyname = None
2435
2436 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002437 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002438 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002439 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002440 pycacerts.append(pycacert)
2441 if not pycacerts:
2442 pycacerts = None
2443
2444 pkcs12 = PKCS12.__new__(PKCS12)
2445 pkcs12._pkey = pykey
2446 pkcs12._cert = pycert
2447 pkcs12._cacerts = pycacerts
2448 pkcs12._friendlyname = friendlyname
2449 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002450
2451
2452def _initialize_openssl_threads(get_ident, Lock):
2453 import _ssl
2454 return
2455
2456 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2457
2458 def locking_function(mode, index, filename, line):
2459 if mode & _lib.CRYPTO_LOCK:
2460 locks[index].acquire()
2461 else:
2462 locks[index].release()
2463
2464 _lib.CRYPTO_set_id_callback(
2465 _ffi.callback("unsigned long (*)(void)", get_ident))
2466
2467 _lib.CRYPTO_set_locking_callback(
2468 _ffi.callback(
2469 "void (*)(int, int, const char*, int)", locking_function))
2470
2471
2472try:
2473 from thread import get_ident
2474 from threading import Lock
2475except ImportError:
2476 pass
2477else:
2478 _initialize_openssl_threads(get_ident, Lock)
2479 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002480
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002481# There are no direct unit tests for this initialization. It is tested
2482# indirectly since it is necessary for functions like dump_privatekey when
2483# using encryption.
2484#
2485# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2486# and some other similar tests may fail without this (though they may not if
2487# the Python runtime has already done some initialization of the underlying
2488# OpenSSL library (and is linked against the same one that cryptography is
2489# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002490_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002491
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002492# This is similar but exercised mainly by exception_from_error_queue. It calls
2493# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2494_lib.SSL_load_error_strings()