blob: 06b7422d40da1596af17ace2ca61c027604d92d0 [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05003from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
5
6from six import (
7 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -04008 text_type as _text_type,
9 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080010
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050011from OpenSSL._util import (
12 ffi as _ffi,
13 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050014 exception_from_error_queue as _exception_from_error_queue,
15 byte_string as _byte_string,
16 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080017
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050018FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
19FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080020
21# TODO This was an API mistake. OpenSSL has no such constant.
22FILETYPE_TEXT = 2 ** 16 - 1
23
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050024TYPE_RSA = _lib.EVP_PKEY_RSA
25TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080026
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080027
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050028class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050029 """
30 An error occurred in an `OpenSSL.crypto` API.
31 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050032
33
34_raise_current_error = partial(_exception_from_error_queue, Error)
35
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050036def _untested_error(where):
37 """
38 An OpenSSL API failed somehow. Additionally, the failure which was
39 encountered isn't one that's exercised by the test suite so future behavior
40 of pyOpenSSL is now somewhat less predictable.
41 """
42 raise RuntimeError("Unknown %s failure" % (where,))
43
44
45
46def _new_mem_buf(buffer=None):
47 """
48 Allocate a new OpenSSL memory BIO.
49
50 Arrange for the garbage collector to clean it up automatically.
51
52 :param buffer: None or some bytes to use to put into the BIO so that they
53 can be read out.
54 """
55 if buffer is None:
56 bio = _lib.BIO_new(_lib.BIO_s_mem())
57 free = _lib.BIO_free
58 else:
59 data = _ffi.new("char[]", buffer)
60 bio = _lib.BIO_new_mem_buf(data, len(buffer))
61 # Keep the memory alive as long as the bio is alive!
62 def free(bio, ref=data):
63 return _lib.BIO_free(bio)
64
65 if bio == _ffi.NULL:
66 # TODO: This is untested.
67 _raise_current_error()
68
69 bio = _ffi.gc(bio, free)
70 return bio
71
72
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050073
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080074def _bio_to_string(bio):
75 """
76 Copy the contents of an OpenSSL BIO object into a Python byte string.
77 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050078 result_buffer = _ffi.new('char**')
79 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
80 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080081
82
83
Jean-Paul Calderone57122982013-02-21 08:47:05 -080084def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050085 """
86 The the time value of an ASN1 time object.
87
88 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
89 castable to that type) which will have its value set.
90 @param when: A string representation of the desired time value.
91
92 @raise TypeError: If C{when} is not a L{bytes} string.
93 @raise ValueError: If C{when} does not represent a time in the required
94 format.
95 @raise RuntimeError: If the time value cannot be set for some other
96 (unspecified) reason.
97 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080098 if not isinstance(when, bytes):
99 raise TypeError("when must be a byte string")
100
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500101 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
102 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800103 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500104 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
105 _lib.ASN1_STRING_set(dummy, when, len(when))
106 check_result = _lib.ASN1_GENERALIZEDTIME_check(
107 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800108 if not check_result:
109 raise ValueError("Invalid string")
110 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500111 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800112
113
114
115def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500116 """
117 Retrieve the time value of an ASN1 time object.
118
119 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
120 that type) from which the time value will be retrieved.
121
122 @return: The time value from C{timestamp} as a L{bytes} string in a certain
123 format. Or C{None} if the object contains no time value.
124 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500125 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
126 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800127 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500128 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
129 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800130 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500131 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
132 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
133 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500134 # This may happen:
135 # - if timestamp was not an ASN1_TIME
136 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
137 # - if a copy of the time data from timestamp cannot be made for
138 # the newly allocated ASN1_GENERALIZEDTIME
139 #
140 # These are difficult to test. cffi enforces the ASN1_TIME type.
141 # Memory allocation failures are a pain to trigger
142 # deterministically.
143 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800144 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500145 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800146 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500147 string_data = _lib.ASN1_STRING_data(string_timestamp)
148 string_result = _ffi.string(string_data)
149 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800150 return string_result
151
152
153
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800154class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800155 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800156 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800157
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800158 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500159 pkey = _lib.EVP_PKEY_new()
160 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800161 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800162
163
164 def generate_key(self, type, bits):
165 """
166 Generate a key of a given type, with a given number of a bits
167
168 :param type: The key type (TYPE_RSA or TYPE_DSA)
169 :param bits: The number of bits
170
171 :return: None
172 """
173 if not isinstance(type, int):
174 raise TypeError("type must be an integer")
175
176 if not isinstance(bits, int):
177 raise TypeError("bits must be an integer")
178
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800179 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500180 exponent = _lib.BN_new()
181 exponent = _ffi.gc(exponent, _lib.BN_free)
182 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800183
184 if type == TYPE_RSA:
185 if bits <= 0:
186 raise ValueError("Invalid number of bits")
187
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500188 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800189
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500190 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500191 if result == 0:
192 # TODO: The test for this case is commented out. Different
193 # builds of OpenSSL appear to have different failure modes that
194 # make it hard to test. Visual inspection of the OpenSSL
195 # source reveals that a return value of 0 signals an error.
196 # Manual testing on a particular build of OpenSSL suggests that
197 # this is probably the appropriate way to handle those errors.
198 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800199
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500200 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800201 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500202 # TODO: It appears as though this can fail if an engine is in
203 # use which does not support RSA.
204 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800205
206 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500207 dsa = _lib.DSA_generate_parameters(
208 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
209 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500210 # TODO: This is untested.
211 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500212 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500213 # TODO: This is untested.
214 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500215 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500216 # TODO: This is untested.
217 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800218 else:
219 raise Error("No such key type")
220
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800221 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800222
223
224 def check(self):
225 """
226 Check the consistency of an RSA private key.
227
228 :return: True if key is consistent.
229 :raise Error: if the key is inconsistent.
230 :raise TypeError: if the key is of a type which cannot be checked.
231 Only RSA keys can currently be checked.
232 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800233 if self._only_public:
234 raise TypeError("public key only")
235
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500236 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800237 raise TypeError("key type unsupported")
238
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500239 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
240 rsa = _ffi.gc(rsa, _lib.RSA_free)
241 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800242 if result:
243 return True
244 _raise_current_error()
245
246
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800247 def type(self):
248 """
249 Returns the type of the key
250
251 :return: The type of the key.
252 """
253 return self._pkey.type
254
255
256 def bits(self):
257 """
258 Returns the number of bits of the key
259
260 :return: The number of bits of the key.
261 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500262 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800263PKeyType = PKey
264
265
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800266
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400267class _EllipticCurve(object):
268 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400269 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400270
271 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
272 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
273 instances each of which represents one curve supported by the system.
274 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400275 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400276 _curves = None
277
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400278 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400279 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400280 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400281 """
282 Implement cooperation with the right-hand side argument of ``!=``.
283
284 Python 3 seems to have dropped this cooperation in this very narrow
285 circumstance.
286 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400287 if isinstance(other, _EllipticCurve):
288 return super(_EllipticCurve, self).__ne__(other)
289 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400290
291
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400292 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400293 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400294 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400295 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400296
297 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400298
299 :return: A :py:type:`set` of ``cls`` instances giving the names of the
300 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400301 """
302 if lib.Cryptography_HAS_EC:
303 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
304 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
305 # The return value on this call should be num_curves again. We could
306 # check it to make sure but if it *isn't* then.. what could we do?
307 # Abort the whole process, I suppose...? -exarkun
308 lib.EC_get_builtin_curves(builtin_curves, num_curves)
309 return set(
310 cls.from_nid(lib, c.nid)
311 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400312 return set()
313
314
315 @classmethod
316 def _get_elliptic_curves(cls, lib):
317 """
318 Get, cache, and return the curves supported by OpenSSL.
319
320 :param lib: The OpenSSL library binding object.
321
322 :return: A :py:type:`set` of ``cls`` instances giving the names of the
323 elliptic curves the underlying library supports.
324 """
325 if cls._curves is None:
326 cls._curves = cls._load_elliptic_curves(lib)
327 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400328
329
330 @classmethod
331 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400332 """
333 Instantiate a new :py:class:`_EllipticCurve` associated with the given
334 OpenSSL NID.
335
336 :param lib: The OpenSSL library binding object.
337
338 :param nid: The OpenSSL NID the resulting curve object will represent.
339 This must be a curve NID (and not, for example, a hash NID) or
340 subsequent operations will fail in unpredictable ways.
341 :type nid: :py:class:`int`
342
343 :return: The curve object.
344 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400345 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
346
347
348 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400349 """
350 :param _lib: The :py:mod:`cryptography` binding instance used to
351 interface with OpenSSL.
352
353 :param _nid: The OpenSSL NID identifying the curve this object
354 represents.
355 :type _nid: :py:class:`int`
356
357 :param name: The OpenSSL short name identifying the curve this object
358 represents.
359 :type name: :py:class:`unicode`
360 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400361 self._lib = lib
362 self._nid = nid
363 self.name = name
364
365
366 def __repr__(self):
367 return "<Curve %r>" % (self.name,)
368
369
370 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400371 """
372 Create a new OpenSSL EC_KEY structure initialized to use this curve.
373
374 The structure is automatically garbage collected when the Python object
375 is garbage collected.
376 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400377 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
378 return _ffi.gc(key, _lib.EC_KEY_free)
379
380
381
382def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400383 """
384 Return a set of objects representing the elliptic curves supported in the
385 OpenSSL build in use.
386
387 The curve objects have a :py:class:`unicode` ``name`` attribute by which
388 they identify themselves.
389
390 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400391 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
392 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400393 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400394 return _EllipticCurve._get_elliptic_curves(_lib)
395
396
397
398def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400399 """
400 Return a single curve object selected by name.
401
402 See :py:func:`get_elliptic_curves` for information about curve objects.
403
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400404 :param name: The OpenSSL short name identifying the curve object to
405 retrieve.
406 :type name: :py:class:`unicode`
407
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400408 If the named curve is not supported then :py:class:`ValueError` is raised.
409 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400410 for curve in get_elliptic_curves():
411 if curve.name == name:
412 return curve
413 raise ValueError("unknown curve name", name)
414
415
416
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800417class X509Name(object):
418 def __init__(self, name):
419 """
420 Create a new X509Name, copying the given X509Name instance.
421
422 :param name: An X509Name object to copy
423 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500424 name = _lib.X509_NAME_dup(name._name)
425 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800426
427
428 def __setattr__(self, name, value):
429 if name.startswith('_'):
430 return super(X509Name, self).__setattr__(name, value)
431
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800432 # Note: we really do not want str subclasses here, so we do not use
433 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800434 if type(name) is not str:
435 raise TypeError("attribute name must be string, not '%.200s'" % (
436 type(value).__name__,))
437
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500438 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500439 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800440 try:
441 _raise_current_error()
442 except Error:
443 pass
444 raise AttributeError("No such attribute")
445
446 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500447 for i in range(_lib.X509_NAME_entry_count(self._name)):
448 ent = _lib.X509_NAME_get_entry(self._name, i)
449 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
450 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800451 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500452 ent = _lib.X509_NAME_delete_entry(self._name, i)
453 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800454 break
455
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500456 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800457 value = value.encode('utf-8')
458
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500459 add_result = _lib.X509_NAME_add_entry_by_NID(
460 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800461 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500462 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800463
464
465 def __getattr__(self, name):
466 """
467 Find attribute. An X509Name object has the following attributes:
468 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
469 organization (alias O), organizationalUnit (alias OU), commonName (alias
470 CN) and more...
471 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500472 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500473 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800474 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
475 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
476 # push something onto the error queue. If we don't clean that up
477 # now, someone else will bump into it later and be quite confused.
478 # See lp#314814.
479 try:
480 _raise_current_error()
481 except Error:
482 pass
483 return super(X509Name, self).__getattr__(name)
484
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500485 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800486 if entry_index == -1:
487 return None
488
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500489 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
490 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800491
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500492 result_buffer = _ffi.new("unsigned char**")
493 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800494 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500495 # TODO: This is untested.
496 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800497
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700498 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500499 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700500 finally:
501 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500502 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800503 return result
504
505
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500506 def _cmp(op):
507 def f(self, other):
508 if not isinstance(other, X509Name):
509 return NotImplemented
510 result = _lib.X509_NAME_cmp(self._name, other._name)
511 return op(result, 0)
512 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800513
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500514 __eq__ = _cmp(__eq__)
515 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800516
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500517 __lt__ = _cmp(__lt__)
518 __le__ = _cmp(__le__)
519
520 __gt__ = _cmp(__gt__)
521 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522
523 def __repr__(self):
524 """
525 String representation of an X509Name
526 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500527 result_buffer = _ffi.new("char[]", 512);
528 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800529 self._name, result_buffer, len(result_buffer))
530
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500531 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500532 # TODO: This is untested.
533 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800534
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500535 return "<X509Name object '%s'>" % (
536 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800537
538
539 def hash(self):
540 """
541 Return the hash value of this name
542
543 :return: None
544 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500545 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800546
547
548 def der(self):
549 """
550 Return the DER encoding of this name
551
552 :return: A :py:class:`bytes` instance giving the DER encoded form of
553 this name.
554 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500555 result_buffer = _ffi.new('unsigned char**')
556 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800557 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500558 # TODO: This is untested.
559 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800560
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500561 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
562 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800563 return string_result
564
565
566 def get_components(self):
567 """
568 Returns the split-up components of this name.
569
570 :return: List of tuples (name, value).
571 """
572 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500573 for i in range(_lib.X509_NAME_entry_count(self._name)):
574 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800575
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500576 fname = _lib.X509_NAME_ENTRY_get_object(ent)
577 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800578
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500579 nid = _lib.OBJ_obj2nid(fname)
580 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800581
582 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500583 _ffi.string(name),
584 _ffi.string(
585 _lib.ASN1_STRING_data(fval),
586 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800587
588 return result
589X509NameType = X509Name
590
591
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800592class X509Extension(object):
593 def __init__(self, type_name, critical, value, subject=None, issuer=None):
594 """
595 :param typename: The name of the extension to create.
596 :type typename: :py:data:`str`
597
598 :param critical: A flag indicating whether this is a critical extension.
599
600 :param value: The value of the extension.
601 :type value: :py:data:`str`
602
603 :param subject: Optional X509 cert to use as subject.
604 :type subject: :py:class:`X509`
605
606 :param issuer: Optional X509 cert to use as issuer.
607 :type issuer: :py:class:`X509`
608
609 :return: The X509Extension object
610 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500611 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800612
613 # A context is necessary for any extension which uses the r2i conversion
614 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
615 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500616 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800617
618 # We have no configuration database - but perhaps we should (some
619 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500620 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800621
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800622 # Initialize the subject and issuer, if appropriate. ctx is a local,
623 # and as far as I can tell none of the X509V3_* APIs invoked here steal
624 # any references, so no need to mess with reference counts or duplicates.
625 if issuer is not None:
626 if not isinstance(issuer, X509):
627 raise TypeError("issuer must be an X509 instance")
628 ctx.issuer_cert = issuer._x509
629 if subject is not None:
630 if not isinstance(subject, X509):
631 raise TypeError("subject must be an X509 instance")
632 ctx.subject_cert = subject._x509
633
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800634 if critical:
635 # There are other OpenSSL APIs which would let us pass in critical
636 # separately, but they're harder to use, and since value is already
637 # a pile of crappy junk smuggling a ton of utterly important
638 # structured data, what's the point of trying to avoid nasty stuff
639 # with strings? (However, X509V3_EXT_i2d in particular seems like it
640 # would be a better API to invoke. I do not know where to get the
641 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500642 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800643
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500644 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
645 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800646 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500647 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800648
649
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400650 @property
651 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500652 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400653
654 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500655 _lib.GEN_EMAIL: "email",
656 _lib.GEN_DNS: "DNS",
657 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400658 }
659
660 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500661 method = _lib.X509V3_EXT_get(self._extension)
662 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500663 # TODO: This is untested.
664 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400665 payload = self._extension.value.data
666 length = self._extension.value.length
667
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500668 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400669 payloadptr[0] = payload
670
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500671 if method.it != _ffi.NULL:
672 ptr = _lib.ASN1_ITEM_ptr(method.it)
673 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
674 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400675 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500676 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400677 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500678 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400679
680 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500681 for i in range(_lib.sk_GENERAL_NAME_num(names)):
682 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400683 try:
684 label = self._prefixes[name.type]
685 except KeyError:
686 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500687 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500688 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400689 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500690 value = _native(
691 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
692 parts.append(label + ":" + value)
693 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400694
695
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800696 def __str__(self):
697 """
698 :return: a nice text representation of the extension
699 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500700 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400701 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800702
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400703 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500704 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800705 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500706 # TODO: This is untested.
707 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800708
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500709 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800710
711
712 def get_critical(self):
713 """
714 Returns the critical field of the X509Extension
715
716 :return: The critical field.
717 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500718 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800719
720
721 def get_short_name(self):
722 """
723 Returns the short version of the type name of the X509Extension
724
725 :return: The short type name.
726 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500727 obj = _lib.X509_EXTENSION_get_object(self._extension)
728 nid = _lib.OBJ_obj2nid(obj)
729 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800730
731
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800732 def get_data(self):
733 """
734 Returns the data of the X509Extension
735
736 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
737 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500738 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
739 string_result = _ffi.cast('ASN1_STRING*', octet_result)
740 char_result = _lib.ASN1_STRING_data(string_result)
741 result_length = _lib.ASN1_STRING_length(string_result)
742 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800743
744X509ExtensionType = X509Extension
745
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800746
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800747class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800748 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500749 req = _lib.X509_REQ_new()
750 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800751
752
753 def set_pubkey(self, pkey):
754 """
755 Set the public key of the certificate request
756
757 :param pkey: The public key to use
758 :return: None
759 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500760 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800761 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500762 # TODO: This is untested.
763 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800764
765
766 def get_pubkey(self):
767 """
768 Get the public key from the certificate request
769
770 :return: The public key
771 """
772 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500773 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
774 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500775 # TODO: This is untested.
776 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500777 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800778 pkey._only_public = True
779 return pkey
780
781
782 def set_version(self, version):
783 """
784 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
785 request.
786
787 :param version: The version number
788 :return: None
789 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500790 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800791 if not set_result:
792 _raise_current_error()
793
794
795 def get_version(self):
796 """
797 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
798 request.
799
800 :return: an integer giving the value of the version subfield
801 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500802 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800803
804
805 def get_subject(self):
806 """
807 Create an X509Name object for the subject of the certificate request
808
809 :return: An X509Name object
810 """
811 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500812 name._name = _lib.X509_REQ_get_subject_name(self._req)
813 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500814 # TODO: This is untested.
815 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800816
817 # The name is owned by the X509Req structure. As long as the X509Name
818 # Python object is alive, keep the X509Req Python object alive.
819 name._owner = self
820
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800821 return name
822
823
824 def add_extensions(self, extensions):
825 """
826 Add extensions to the request.
827
828 :param extensions: a sequence of X509Extension objects
829 :return: None
830 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500831 stack = _lib.sk_X509_EXTENSION_new_null()
832 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500833 # TODO: This is untested.
834 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800835
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500836 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800837
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800838 for ext in extensions:
839 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800840 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800841
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800842 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500843 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800844
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500845 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800846 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500847 # TODO: This is untested.
848 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800849
850
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800851 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800852 """
853 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800854
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500855 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800856 """
857 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500858 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500859 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800860 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500861 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800862 exts.append(ext)
863 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800864
865
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800866 def sign(self, pkey, digest):
867 """
868 Sign the certificate request using the supplied key and digest
869
870 :param pkey: The key to sign with
871 :param digest: The message digest to use
872 :return: None
873 """
874 if pkey._only_public:
875 raise ValueError("Key has only public part")
876
877 if not pkey._initialized:
878 raise ValueError("Key is uninitialized")
879
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500880 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500881 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800882 raise ValueError("No such digest method")
883
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500884 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800885 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500886 # TODO: This is untested.
887 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800888
889
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800890 def verify(self, pkey):
891 """
892 Verifies a certificate request using the supplied public key
893
894 :param key: a public key
895 :return: True if the signature is correct.
896
897 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
898 problem verifying the signature.
899 """
900 if not isinstance(pkey, PKey):
901 raise TypeError("pkey must be a PKey instance")
902
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500903 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800904 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500905 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800906
907 return result
908
909
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800910X509ReqType = X509Req
911
912
913
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800914class X509(object):
915 def __init__(self):
916 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500917 x509 = _lib.X509_new()
918 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800919
920
921 def set_version(self, version):
922 """
923 Set version number of the certificate
924
925 :param version: The version number
926 :type version: :py:class:`int`
927
928 :return: None
929 """
930 if not isinstance(version, int):
931 raise TypeError("version must be an integer")
932
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500933 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800934
935
936 def get_version(self):
937 """
938 Return version number of the certificate
939
940 :return: Version number as a Python integer
941 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500942 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800943
944
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800945 def get_pubkey(self):
946 """
947 Get the public key of the certificate
948
949 :return: The public key
950 """
951 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500952 pkey._pkey = _lib.X509_get_pubkey(self._x509)
953 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800954 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500955 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800956 pkey._only_public = True
957 return pkey
958
959
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800960 def set_pubkey(self, pkey):
961 """
962 Set the public key of the certificate
963
964 :param pkey: The public key
965
966 :return: None
967 """
968 if not isinstance(pkey, PKey):
969 raise TypeError("pkey must be a PKey instance")
970
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500971 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800972 if not set_result:
973 _raise_current_error()
974
975
976 def sign(self, pkey, digest):
977 """
978 Sign the certificate using the supplied key and digest
979
980 :param pkey: The key to sign with
981 :param digest: The message digest to use
982 :return: None
983 """
984 if not isinstance(pkey, PKey):
985 raise TypeError("pkey must be a PKey instance")
986
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800987 if pkey._only_public:
988 raise ValueError("Key only has public part")
989
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800990 if not pkey._initialized:
991 raise ValueError("Key is uninitialized")
992
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500993 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500994 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800995 raise ValueError("No such digest method")
996
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500997 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800998 if not sign_result:
999 _raise_current_error()
1000
1001
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001002 def get_signature_algorithm(self):
1003 """
1004 Retrieve the signature algorithm used in the certificate
1005
1006 :return: A byte string giving the name of the signature algorithm used in
1007 the certificate.
1008 :raise ValueError: If the signature algorithm is undefined.
1009 """
1010 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001011 nid = _lib.OBJ_obj2nid(alg)
1012 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001013 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001014 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001015
1016
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001017 def digest(self, digest_name):
1018 """
1019 Return the digest of the X509 object.
1020
1021 :param digest_name: The name of the digest algorithm to use.
1022 :type digest_name: :py:class:`bytes`
1023
1024 :return: The digest of the object
1025 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001026 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001027 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001028 raise ValueError("No such digest method")
1029
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001030 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1031 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001032 result_length[0] = len(result_buffer)
1033
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001034 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001035 self._x509, digest, result_buffer, result_length)
1036
1037 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001038 # TODO: This is untested.
1039 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001040
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001041 return b":".join([
1042 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001043 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001044
1045
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001046 def subject_name_hash(self):
1047 """
1048 Return the hash of the X509 subject.
1049
1050 :return: The hash of the subject.
1051 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001052 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001053
1054
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001055 def set_serial_number(self, serial):
1056 """
1057 Set serial number of the certificate
1058
1059 :param serial: The serial number
1060 :type serial: :py:class:`int`
1061
1062 :return: None
1063 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001064 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001065 raise TypeError("serial must be an integer")
1066
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001067 hex_serial = hex(serial)[2:]
1068 if not isinstance(hex_serial, bytes):
1069 hex_serial = hex_serial.encode('ascii')
1070
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001072
1073 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1074 # it. If bignum is still NULL after this call, then the return value is
1075 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001076 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001077
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001078 if bignum_serial[0] == _ffi.NULL:
1079 set_result = _lib.ASN1_INTEGER_set(
1080 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001081 if set_result:
1082 # TODO Not tested
1083 _raise_current_error()
1084 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001085 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1086 _lib.BN_free(bignum_serial[0])
1087 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001088 # TODO Not tested
1089 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001090 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1091 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001092 if not set_result:
1093 # TODO Not tested
1094 _raise_current_error()
1095
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001096
1097 def get_serial_number(self):
1098 """
1099 Return serial number of the certificate
1100
1101 :return: Serial number as a Python integer
1102 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001103 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1104 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001105 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001106 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001107 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001108 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001109 serial = int(hexstring_serial, 16)
1110 return serial
1111 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001112 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001113 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001114 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001115
1116
1117 def gmtime_adj_notAfter(self, amount):
1118 """
1119 Adjust the time stamp for when the certificate stops being valid
1120
1121 :param amount: The number of seconds by which to adjust the ending
1122 validity time.
1123 :type amount: :py:class:`int`
1124
1125 :return: None
1126 """
1127 if not isinstance(amount, int):
1128 raise TypeError("amount must be an integer")
1129
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001130 notAfter = _lib.X509_get_notAfter(self._x509)
1131 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001132
1133
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001134 def gmtime_adj_notBefore(self, amount):
1135 """
1136 Change the timestamp for when the certificate starts being valid to the current
1137 time plus an offset.
1138
1139 :param amount: The number of seconds by which to adjust the starting validity
1140 time.
1141 :return: None
1142 """
1143 if not isinstance(amount, int):
1144 raise TypeError("amount must be an integer")
1145
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001146 notBefore = _lib.X509_get_notBefore(self._x509)
1147 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001148
1149
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001150 def has_expired(self):
1151 """
1152 Check whether the certificate has expired.
1153
1154 :return: True if the certificate has expired, false otherwise
1155 """
1156 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001157 notAfter = _lib.X509_get_notAfter(self._x509)
1158 return _lib.ASN1_UTCTIME_cmp_time_t(
1159 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001160
1161
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001162 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001163 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001164
1165
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001166 def get_notBefore(self):
1167 """
1168 Retrieve the time stamp for when the certificate starts being valid
1169
1170 :return: A string giving the timestamp, in the format::
1171
1172 YYYYMMDDhhmmssZ
1173 YYYYMMDDhhmmss+hhmm
1174 YYYYMMDDhhmmss-hhmm
1175
1176 or None if there is no value set.
1177 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001178 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001179
1180
1181 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001182 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001183
1184
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001185 def set_notBefore(self, when):
1186 """
1187 Set the time stamp for when the certificate starts being valid
1188
1189 :param when: A string giving the timestamp, in the format:
1190
1191 YYYYMMDDhhmmssZ
1192 YYYYMMDDhhmmss+hhmm
1193 YYYYMMDDhhmmss-hhmm
1194 :type when: :py:class:`bytes`
1195
1196 :return: None
1197 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001198 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001199
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001200
1201 def get_notAfter(self):
1202 """
1203 Retrieve the time stamp for when the certificate stops being valid
1204
1205 :return: A string giving the timestamp, in the format::
1206
1207 YYYYMMDDhhmmssZ
1208 YYYYMMDDhhmmss+hhmm
1209 YYYYMMDDhhmmss-hhmm
1210
1211 or None if there is no value set.
1212 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001213 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001214
1215
1216 def set_notAfter(self, when):
1217 """
1218 Set the time stamp for when the certificate stops being valid
1219
1220 :param when: A string giving the timestamp, in the format:
1221
1222 YYYYMMDDhhmmssZ
1223 YYYYMMDDhhmmss+hhmm
1224 YYYYMMDDhhmmss-hhmm
1225 :type when: :py:class:`bytes`
1226
1227 :return: None
1228 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001229 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001230
1231
1232 def _get_name(self, which):
1233 name = X509Name.__new__(X509Name)
1234 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001235 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001236 # TODO: This is untested.
1237 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001238
1239 # The name is owned by the X509 structure. As long as the X509Name
1240 # Python object is alive, keep the X509 Python object alive.
1241 name._owner = self
1242
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001243 return name
1244
1245
1246 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001247 if not isinstance(name, X509Name):
1248 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001249 set_result = which(self._x509, name._name)
1250 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001251 # TODO: This is untested.
1252 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001253
1254
1255 def get_issuer(self):
1256 """
1257 Create an X509Name object for the issuer of the certificate
1258
1259 :return: An X509Name object
1260 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001261 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001262
1263
1264 def set_issuer(self, issuer):
1265 """
1266 Set the issuer of the certificate
1267
1268 :param issuer: The issuer name
1269 :type issuer: :py:class:`X509Name`
1270
1271 :return: None
1272 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001273 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001274
1275
1276 def get_subject(self):
1277 """
1278 Create an X509Name object for the subject of the certificate
1279
1280 :return: An X509Name object
1281 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001282 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001283
1284
1285 def set_subject(self, subject):
1286 """
1287 Set the subject of the certificate
1288
1289 :param subject: The subject name
1290 :type subject: :py:class:`X509Name`
1291 :return: None
1292 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001293 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001294
1295
1296 def get_extension_count(self):
1297 """
1298 Get the number of extensions on the certificate.
1299
1300 :return: The number of extensions as an integer.
1301 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001302 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001303
1304
1305 def add_extensions(self, extensions):
1306 """
1307 Add extensions to the certificate.
1308
1309 :param extensions: a sequence of X509Extension objects
1310 :return: None
1311 """
1312 for ext in extensions:
1313 if not isinstance(ext, X509Extension):
1314 raise ValueError("One of the elements is not an X509Extension")
1315
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001316 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001317 if not add_result:
1318 _raise_current_error()
1319
1320
1321 def get_extension(self, index):
1322 """
1323 Get a specific extension of the certificate by index.
1324
1325 :param index: The index of the extension to retrieve.
1326 :return: The X509Extension object at the specified index.
1327 """
1328 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001329 ext._extension = _lib.X509_get_ext(self._x509, index)
1330 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001331 raise IndexError("extension index out of bounds")
1332
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001333 extension = _lib.X509_EXTENSION_dup(ext._extension)
1334 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001335 return ext
1336
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001337
1338
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001339X509Type = X509
1340
1341
1342
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001343class X509Store(object):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001344 """
1345 An X509 certificate store.
1346
1347 .. note:: The type of :py:class:`X509Store` objects was formerly
1348 :py:class:`X509StoreType`. This name is maintained for
1349 backwards compatibility, although you can just use
1350 :py:class:`X509Store` now.
1351
1352 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001353 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001354 store = _lib.X509_STORE_new()
1355 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001356
1357
1358 def add_cert(self, cert):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001359 """
1360 Adds the certificate :py:data:`cert` to this store.
1361
1362 This is the Python equivalent of OpenSSL's X509_STORE_add_cert.
1363
1364 :param X509 cert: The certificate to add to this store.
1365 :raises TypeError: If the certificate is not an :py:class:`X509`.
1366 :raises Error: If OpenSSL was unhappy with your certificate.
1367 :return: py:data:`None` if the certificate was added successfully.
1368 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001369 if not isinstance(cert, X509):
1370 raise TypeError()
1371
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001372 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001373 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001374 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001375
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001376
1377X509StoreType = X509Store
1378
1379
1380
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001381def load_certificate(type, buffer):
1382 """
1383 Load a certificate from a buffer
1384
1385 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1386
1387 :param buffer: The buffer the certificate is stored in
1388 :type buffer: :py:class:`bytes`
1389
1390 :return: The X509 object
1391 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001392 if isinstance(buffer, _text_type):
1393 buffer = buffer.encode("ascii")
1394
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001395 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001396
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001397 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001398 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001399 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001400 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001401 else:
1402 raise ValueError(
1403 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001404
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001405 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001406 _raise_current_error()
1407
1408 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001409 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001410 return cert
1411
1412
1413def dump_certificate(type, cert):
1414 """
1415 Dump a certificate to a buffer
1416
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001417 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1418 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001419 :param cert: The certificate to dump
1420 :return: The buffer with the dumped certificate in
1421 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001422 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001423
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001424 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001425 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001426 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001427 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001428 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001429 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001430 else:
1431 raise ValueError(
1432 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1433 "FILETYPE_TEXT")
1434
1435 return _bio_to_string(bio)
1436
1437
1438
1439def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1440 """
1441 Dump a private key to a buffer
1442
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001443 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1444 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001445 :param pkey: The PKey to dump
1446 :param cipher: (optional) if encrypted PEM format, the cipher to
1447 use
1448 :param passphrase: (optional) if encrypted PEM format, this can be either
1449 the passphrase to use, or a callback for providing the
1450 passphrase.
1451 :return: The buffer with the dumped key in
1452 :rtype: :py:data:`str`
1453 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001454 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001455
1456 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001457 if passphrase is None:
1458 raise TypeError(
1459 "if a value is given for cipher "
1460 "one must also be given for passphrase")
1461 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001462 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001463 raise ValueError("Invalid cipher name")
1464 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001465 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001466
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001467 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001468 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001469 result_code = _lib.PEM_write_bio_PrivateKey(
1470 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001471 helper.callback, helper.callback_args)
1472 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001473 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001474 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001475 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001476 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1477 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001478 # TODO RSA_free(rsa)?
1479 else:
1480 raise ValueError(
1481 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1482 "FILETYPE_TEXT")
1483
1484 if result_code == 0:
1485 _raise_current_error()
1486
1487 return _bio_to_string(bio)
1488
1489
1490
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001491def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001492 copy = _lib.X509_REVOKED_new()
1493 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001494 # TODO: This is untested.
1495 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001496
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001497 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001498 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001499 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001500
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001501 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001502 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001503 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001504
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001505 if original.extensions != _ffi.NULL:
1506 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1507 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1508 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1509 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1510 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001511 copy.extensions = extension_stack
1512
1513 copy.sequence = original.sequence
1514 return copy
1515
1516
1517
1518class Revoked(object):
1519 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1520 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1521 # OCSP_crl_reason_str. We use the latter, just like the command line
1522 # program.
1523 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001524 b"unspecified",
1525 b"keyCompromise",
1526 b"CACompromise",
1527 b"affiliationChanged",
1528 b"superseded",
1529 b"cessationOfOperation",
1530 b"certificateHold",
1531 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001532 ]
1533
1534 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001535 revoked = _lib.X509_REVOKED_new()
1536 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001537
1538
1539 def set_serial(self, hex_str):
1540 """
1541 Set the serial number of a revoked Revoked structure
1542
1543 :param hex_str: The new serial number.
1544 :type hex_str: :py:data:`str`
1545 :return: None
1546 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001547 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1548 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001549 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001550 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001551 if not bn_result:
1552 raise ValueError("bad hex string")
1553
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001554 asn1_serial = _ffi.gc(
1555 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1556 _lib.ASN1_INTEGER_free)
1557 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001558
1559
1560 def get_serial(self):
1561 """
1562 Return the serial number of a Revoked structure
1563
1564 :return: The serial number as a string
1565 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001566 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001567
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001568 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001569 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001570 # TODO: This is untested.
1571 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001572
1573 return _bio_to_string(bio)
1574
1575
1576 def _delete_reason(self):
1577 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001578 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1579 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1580 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1581 _lib.X509_EXTENSION_free(ext)
1582 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001583 break
1584
1585
1586 def set_reason(self, reason):
1587 """
1588 Set the reason of a Revoked object.
1589
1590 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1591
1592 :param reason: The reason string.
1593 :type reason: :py:class:`str` or :py:class:`NoneType`
1594 :return: None
1595 """
1596 if reason is None:
1597 self._delete_reason()
1598 elif not isinstance(reason, bytes):
1599 raise TypeError("reason must be None or a byte string")
1600 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001601 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001602 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1603
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001604 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1605 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001606 # TODO: This is untested.
1607 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001608 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001609
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001610 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1611 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001612 # TODO: This is untested.
1613 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001614
1615 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001616 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1617 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001618
1619 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001620 # TODO: This is untested.
1621 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001622
1623
1624 def get_reason(self):
1625 """
1626 Return the reason of a Revoked object.
1627
1628 :return: The reason as a string
1629 """
1630 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001631 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1632 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1633 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001634 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001635
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001636 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001637 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001638 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001639 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001640 # TODO: This is untested.
1641 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001642
1643 return _bio_to_string(bio)
1644
1645
1646 def all_reasons(self):
1647 """
1648 Return a list of all the supported reason strings.
1649
1650 :return: A list of reason strings.
1651 """
1652 return self._crl_reasons[:]
1653
1654
1655 def set_rev_date(self, when):
1656 """
1657 Set the revocation timestamp
1658
1659 :param when: A string giving the timestamp, in the format:
1660
1661 YYYYMMDDhhmmssZ
1662 YYYYMMDDhhmmss+hhmm
1663 YYYYMMDDhhmmss-hhmm
1664
1665 :return: None
1666 """
1667 return _set_asn1_time(self._revoked.revocationDate, when)
1668
1669
1670 def get_rev_date(self):
1671 """
1672 Retrieve the revocation date
1673
1674 :return: A string giving the timestamp, in the format:
1675
1676 YYYYMMDDhhmmssZ
1677 YYYYMMDDhhmmss+hhmm
1678 YYYYMMDDhhmmss-hhmm
1679 """
1680 return _get_asn1_time(self._revoked.revocationDate)
1681
1682
1683
1684class CRL(object):
1685 def __init__(self):
1686 """
1687 Create a new empty CRL object.
1688 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001689 crl = _lib.X509_CRL_new()
1690 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001691
1692
1693 def get_revoked(self):
1694 """
1695 Return revoked portion of the CRL structure (by value not reference).
1696
1697 :return: A tuple of Revoked objects.
1698 """
1699 results = []
1700 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001701 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1702 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001703 revoked_copy = _X509_REVOKED_dup(revoked)
1704 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001705 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001706 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001707 if results:
1708 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001709
1710
1711 def add_revoked(self, revoked):
1712 """
1713 Add a revoked (by value not reference) to the CRL structure
1714
1715 :param revoked: The new revoked.
1716 :type revoked: :class:`X509`
1717
1718 :return: None
1719 """
1720 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001721 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001722 # TODO: This is untested.
1723 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001724
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001725 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001726 if add_result == 0:
1727 # TODO: This is untested.
1728 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001729
1730
1731 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1732 """
1733 export a CRL as a string
1734
1735 :param cert: Used to sign CRL.
1736 :type cert: :class:`X509`
1737
1738 :param key: Used to sign CRL.
1739 :type key: :class:`PKey`
1740
1741 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1742
1743 :param days: The number of days until the next update of this CRL.
1744 :type days: :py:data:`int`
1745
1746 :return: :py:data:`str`
1747 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001748 if not isinstance(cert, X509):
1749 raise TypeError("cert must be an X509 instance")
1750 if not isinstance(key, PKey):
1751 raise TypeError("key must be a PKey instance")
1752 if not isinstance(type, int):
1753 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001754
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001755 bio = _lib.BIO_new(_lib.BIO_s_mem())
1756 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001757 # TODO: This is untested.
1758 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001759
1760 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001761 sometime = _lib.ASN1_TIME_new()
1762 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001763 # TODO: This is untested.
1764 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001765
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001766 _lib.X509_gmtime_adj(sometime, 0)
1767 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001768
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001769 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1770 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001771
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001772 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001773
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001774 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001775 if not sign_result:
1776 _raise_current_error()
1777
1778 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001779 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001780 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001781 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001782 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001783 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001784 else:
1785 raise ValueError(
1786 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1787
1788 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001789 # TODO: This is untested.
1790 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001791
1792 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001793CRLType = CRL
1794
1795
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001796
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001797class PKCS7(object):
1798 def type_is_signed(self):
1799 """
1800 Check if this NID_pkcs7_signed object
1801
1802 :return: True if the PKCS7 is of type signed
1803 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001804 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001805 return True
1806 return False
1807
1808
1809 def type_is_enveloped(self):
1810 """
1811 Check if this NID_pkcs7_enveloped object
1812
1813 :returns: True if the PKCS7 is of type enveloped
1814 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001815 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001816 return True
1817 return False
1818
1819
1820 def type_is_signedAndEnveloped(self):
1821 """
1822 Check if this NID_pkcs7_signedAndEnveloped object
1823
1824 :returns: True if the PKCS7 is of type signedAndEnveloped
1825 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001826 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001827 return True
1828 return False
1829
1830
1831 def type_is_data(self):
1832 """
1833 Check if this NID_pkcs7_data object
1834
1835 :return: True if the PKCS7 is of type data
1836 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001837 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001838 return True
1839 return False
1840
1841
1842 def get_type_name(self):
1843 """
1844 Returns the type name of the PKCS7 structure
1845
1846 :return: A string with the typename
1847 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001848 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1849 string_type = _lib.OBJ_nid2sn(nid)
1850 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001851
1852PKCS7Type = PKCS7
1853
1854
1855
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001856class PKCS12(object):
1857 def __init__(self):
1858 self._pkey = None
1859 self._cert = None
1860 self._cacerts = None
1861 self._friendlyname = None
1862
1863
1864 def get_certificate(self):
1865 """
1866 Return certificate portion of the PKCS12 structure
1867
1868 :return: X509 object containing the certificate
1869 """
1870 return self._cert
1871
1872
1873 def set_certificate(self, cert):
1874 """
1875 Replace the certificate portion of the PKCS12 structure
1876
1877 :param cert: The new certificate.
1878 :type cert: :py:class:`X509` or :py:data:`None`
1879 :return: None
1880 """
1881 if not isinstance(cert, X509):
1882 raise TypeError("cert must be an X509 instance")
1883 self._cert = cert
1884
1885
1886 def get_privatekey(self):
1887 """
1888 Return private key portion of the PKCS12 structure
1889
1890 :returns: PKey object containing the private key
1891 """
1892 return self._pkey
1893
1894
1895 def set_privatekey(self, pkey):
1896 """
1897 Replace or set the certificate portion of the PKCS12 structure
1898
1899 :param pkey: The new private key.
1900 :type pkey: :py:class:`PKey`
1901 :return: None
1902 """
1903 if not isinstance(pkey, PKey):
1904 raise TypeError("pkey must be a PKey instance")
1905 self._pkey = pkey
1906
1907
1908 def get_ca_certificates(self):
1909 """
1910 Return CA certificates within of the PKCS12 object
1911
1912 :return: A newly created tuple containing the CA certificates in the chain,
1913 if any are present, or None if no CA certificates are present.
1914 """
1915 if self._cacerts is not None:
1916 return tuple(self._cacerts)
1917
1918
1919 def set_ca_certificates(self, cacerts):
1920 """
1921 Replace or set the CA certificates withing the PKCS12 object.
1922
1923 :param cacerts: The new CA certificates.
1924 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1925 :return: None
1926 """
1927 if cacerts is None:
1928 self._cacerts = None
1929 else:
1930 cacerts = list(cacerts)
1931 for cert in cacerts:
1932 if not isinstance(cert, X509):
1933 raise TypeError("iterable must only contain X509 instances")
1934 self._cacerts = cacerts
1935
1936
1937 def set_friendlyname(self, name):
1938 """
1939 Replace or set the certificate portion of the PKCS12 structure
1940
1941 :param name: The new friendly name.
1942 :type name: :py:class:`bytes`
1943 :return: None
1944 """
1945 if name is None:
1946 self._friendlyname = None
1947 elif not isinstance(name, bytes):
1948 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1949 self._friendlyname = name
1950
1951
1952 def get_friendlyname(self):
1953 """
1954 Return friendly name portion of the PKCS12 structure
1955
1956 :returns: String containing the friendlyname
1957 """
1958 return self._friendlyname
1959
1960
1961 def export(self, passphrase=None, iter=2048, maciter=1):
1962 """
1963 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1964
1965 :param passphrase: used to encrypt the PKCS12
1966 :type passphrase: :py:data:`bytes`
1967
1968 :param iter: How many times to repeat the encryption
1969 :type iter: :py:data:`int`
1970
1971 :param maciter: How many times to repeat the MAC
1972 :type maciter: :py:data:`int`
1973
1974 :return: The string containing the PKCS12
1975 """
1976 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001977 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001978 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001979 cacerts = _lib.sk_X509_new_null()
1980 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001981 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001982 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001983
1984 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001985 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001986
1987 friendlyname = self._friendlyname
1988 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001989 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001990
1991 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001992 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001993 else:
1994 pkey = self._pkey._pkey
1995
1996 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001997 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001998 else:
1999 cert = self._cert._x509
2000
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002001 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002002 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002003 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2004 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002005 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002006 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002007 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002008 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002009
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002010 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002011 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002012 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002013
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002014PKCS12Type = PKCS12
2015
2016
2017
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002018class NetscapeSPKI(object):
2019 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002020 spki = _lib.NETSCAPE_SPKI_new()
2021 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002022
2023
2024 def sign(self, pkey, digest):
2025 """
2026 Sign the certificate request using the supplied key and digest
2027
2028 :param pkey: The key to sign with
2029 :param digest: The message digest to use
2030 :return: None
2031 """
2032 if pkey._only_public:
2033 raise ValueError("Key has only public part")
2034
2035 if not pkey._initialized:
2036 raise ValueError("Key is uninitialized")
2037
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002038 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002039 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002040 raise ValueError("No such digest method")
2041
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002042 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002043 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002044 # TODO: This is untested.
2045 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002046
2047
2048 def verify(self, key):
2049 """
2050 Verifies a certificate request using the supplied public key
2051
2052 :param key: a public key
2053 :return: True if the signature is correct.
2054 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2055 problem verifying the signature.
2056 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002057 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002058 if answer <= 0:
2059 _raise_current_error()
2060 return True
2061
2062
2063 def b64_encode(self):
2064 """
2065 Generate a base64 encoded string from an SPKI
2066
2067 :return: The base64 encoded string
2068 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002069 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2070 result = _ffi.string(encoded)
2071 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002072 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002073
2074
2075 def get_pubkey(self):
2076 """
2077 Get the public key of the certificate
2078
2079 :return: The public key
2080 """
2081 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002082 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2083 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002084 # TODO: This is untested.
2085 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002086 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002087 pkey._only_public = True
2088 return pkey
2089
2090
2091 def set_pubkey(self, pkey):
2092 """
2093 Set the public key of the certificate
2094
2095 :param pkey: The public key
2096 :return: None
2097 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002098 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002099 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002100 # TODO: This is untested.
2101 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002102NetscapeSPKIType = NetscapeSPKI
2103
2104
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002105class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002106 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002107 if type != FILETYPE_PEM and passphrase is not None:
2108 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002109 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002110 self._more_args = more_args
2111 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002112 self._problems = []
2113
2114
2115 @property
2116 def callback(self):
2117 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002118 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002119 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002120 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002121 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002122 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002123 else:
2124 raise TypeError("Last argument must be string or callable")
2125
2126
2127 @property
2128 def callback_args(self):
2129 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002130 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002131 elif isinstance(self._passphrase, bytes):
2132 return self._passphrase
2133 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002134 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002135 else:
2136 raise TypeError("Last argument must be string or callable")
2137
2138
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002139 def raise_if_problem(self, exceptionType=Error):
2140 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002141 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002142 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002143 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002144 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002145 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002146 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002147
2148
2149 def _read_passphrase(self, buf, size, rwflag, userdata):
2150 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002151 if self._more_args:
2152 result = self._passphrase(size, rwflag, userdata)
2153 else:
2154 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002155 if not isinstance(result, bytes):
2156 raise ValueError("String expected")
2157 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002158 if self._truncate:
2159 result = result[:size]
2160 else:
2161 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002162 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002163 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002164 return len(result)
2165 except Exception as e:
2166 self._problems.append(e)
2167 return 0
2168
2169
2170
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002171def load_privatekey(type, buffer, passphrase=None):
2172 """
2173 Load a private key from a buffer
2174
2175 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2176 :param buffer: The buffer the key is stored in
2177 :param passphrase: (optional) if encrypted PEM format, this can be
2178 either the passphrase to use, or a callback for
2179 providing the passphrase.
2180
2181 :return: The PKey object
2182 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002183 if isinstance(buffer, _text_type):
2184 buffer = buffer.encode("ascii")
2185
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002186 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002187
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002188 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002189 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002190 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2191 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002192 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002193 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002194 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002195 else:
2196 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2197
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002198 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002199 _raise_current_error()
2200
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002201 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002202 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002203 return pkey
2204
2205
2206
2207def dump_certificate_request(type, req):
2208 """
2209 Dump a certificate request to a buffer
2210
2211 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2212 :param req: The certificate request to dump
2213 :return: The buffer with the dumped certificate request in
2214 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002215 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002216
2217 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002218 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002219 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002220 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002221 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002222 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002223 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002224 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002225
2226 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002227 # TODO: This is untested.
2228 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002229
2230 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002231
2232
2233
2234def load_certificate_request(type, buffer):
2235 """
2236 Load a certificate request from a buffer
2237
2238 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2239 :param buffer: The buffer the certificate request is stored in
2240 :return: The X509Req object
2241 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002242 if isinstance(buffer, _text_type):
2243 buffer = buffer.encode("ascii")
2244
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002245 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002246
2247 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002248 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002249 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002250 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002251 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002252 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002253
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002254 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002255 # TODO: This is untested.
2256 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002257
2258 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002259 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002260 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002261
2262
2263
2264def sign(pkey, data, digest):
2265 """
2266 Sign data with a digest
2267
2268 :param pkey: Pkey to sign with
2269 :param data: data to be signed
2270 :param digest: message digest to use
2271 :return: signature
2272 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002273 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002274 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002275 raise ValueError("No such digest method")
2276
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002277 md_ctx = _ffi.new("EVP_MD_CTX*")
2278 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002279
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002280 _lib.EVP_SignInit(md_ctx, digest_obj)
2281 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002282
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002283 signature_buffer = _ffi.new("unsigned char[]", 512)
2284 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002285 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002286 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002287 md_ctx, signature_buffer, signature_length, pkey._pkey)
2288
2289 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002290 # TODO: This is untested.
2291 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002292
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002293 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002294
2295
2296
2297def verify(cert, signature, data, digest):
2298 """
2299 Verify a signature
2300
2301 :param cert: signing certificate (X509 object)
2302 :param signature: signature returned by sign function
2303 :param data: data to be verified
2304 :param digest: message digest to use
2305 :return: None if the signature is correct, raise exception otherwise
2306 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002307 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002308 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002309 raise ValueError("No such digest method")
2310
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002311 pkey = _lib.X509_get_pubkey(cert._x509)
2312 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002313 # TODO: This is untested.
2314 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002315 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002316
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002317 md_ctx = _ffi.new("EVP_MD_CTX*")
2318 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002319
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002320 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2321 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2322 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002323
2324 if verify_result != 1:
2325 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002326
2327
2328
2329def load_crl(type, buffer):
2330 """
2331 Load a certificate revocation list from a buffer
2332
2333 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2334 :param buffer: The buffer the CRL is stored in
2335
2336 :return: The PKey object
2337 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002338 if isinstance(buffer, _text_type):
2339 buffer = buffer.encode("ascii")
2340
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002341 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002342
2343 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002344 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002345 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002346 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002347 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002348 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2349
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002350 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002351 _raise_current_error()
2352
2353 result = CRL.__new__(CRL)
2354 result._crl = crl
2355 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002356
2357
2358
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002359def load_pkcs7_data(type, buffer):
2360 """
2361 Load pkcs7 data from a buffer
2362
2363 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2364 :param buffer: The buffer with the pkcs7 data.
2365 :return: The PKCS7 object
2366 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002367 if isinstance(buffer, _text_type):
2368 buffer = buffer.encode("ascii")
2369
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002370 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002371
2372 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002373 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002374 elif type == FILETYPE_ASN1:
2375 pass
2376 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002377 # TODO: This is untested.
2378 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002379 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2380
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002381 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002382 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002383
2384 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002385 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002386 return pypkcs7
2387
2388
2389
Stephen Holsapple38482622014-04-05 20:29:34 -07002390def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002391 """
2392 Load a PKCS12 object from a buffer
2393
2394 :param buffer: The buffer the certificate is stored in
2395 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2396 :returns: The PKCS12 object
2397 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002398 if isinstance(buffer, _text_type):
2399 buffer = buffer.encode("ascii")
2400
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002401 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002402
Stephen Holsapple38482622014-04-05 20:29:34 -07002403 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2404 # password based encryption no password and a zero length password are two
2405 # different things, but OpenSSL implementation will try both to figure out
2406 # which one works.
2407 if not passphrase:
2408 passphrase = _ffi.NULL
2409
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002410 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2411 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002412 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002413 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002414
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002415 pkey = _ffi.new("EVP_PKEY**")
2416 cert = _ffi.new("X509**")
2417 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002418
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002419 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002420 if not parse_result:
2421 _raise_current_error()
2422
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002423 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002424
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002425 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2426 # queue for no particular reason. This error isn't interesting to anyone
2427 # outside this function. It's not even interesting to us. Get rid of it.
2428 try:
2429 _raise_current_error()
2430 except Error:
2431 pass
2432
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002433 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002434 pykey = None
2435 else:
2436 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002437 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002438
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002439 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002440 pycert = None
2441 friendlyname = None
2442 else:
2443 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002444 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002445
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002446 friendlyname_length = _ffi.new("int*")
2447 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2448 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2449 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002450 friendlyname = None
2451
2452 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002453 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002454 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002455 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002456 pycacerts.append(pycacert)
2457 if not pycacerts:
2458 pycacerts = None
2459
2460 pkcs12 = PKCS12.__new__(PKCS12)
2461 pkcs12._pkey = pykey
2462 pkcs12._cert = pycert
2463 pkcs12._cacerts = pycacerts
2464 pkcs12._friendlyname = friendlyname
2465 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002466
2467
2468def _initialize_openssl_threads(get_ident, Lock):
2469 import _ssl
2470 return
2471
2472 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2473
2474 def locking_function(mode, index, filename, line):
2475 if mode & _lib.CRYPTO_LOCK:
2476 locks[index].acquire()
2477 else:
2478 locks[index].release()
2479
2480 _lib.CRYPTO_set_id_callback(
2481 _ffi.callback("unsigned long (*)(void)", get_ident))
2482
2483 _lib.CRYPTO_set_locking_callback(
2484 _ffi.callback(
2485 "void (*)(int, int, const char*, int)", locking_function))
2486
2487
2488try:
2489 from thread import get_ident
2490 from threading import Lock
2491except ImportError:
2492 pass
2493else:
2494 _initialize_openssl_threads(get_ident, Lock)
2495 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002496
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002497# There are no direct unit tests for this initialization. It is tested
2498# indirectly since it is necessary for functions like dump_privatekey when
2499# using encryption.
2500#
2501# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2502# and some other similar tests may fail without this (though they may not if
2503# the Python runtime has already done some initialization of the underlying
2504# OpenSSL library (and is linked against the same one that cryptography is
2505# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002506_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002507
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002508# This is similar but exercised mainly by exception_from_error_queue. It calls
2509# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2510_lib.SSL_load_error_strings()