blob: 52320f30ca1defbd52642ffe1c0f870e2591fc24 [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,
Jean-Paul Calderone6462b072015-03-29 07:03:11 -040016 native as _native,
17 warn_text as _warn_text)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080018
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050019FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
20FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080021
22# TODO This was an API mistake. OpenSSL has no such constant.
23FILETYPE_TEXT = 2 ** 16 - 1
24
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050025TYPE_RSA = _lib.EVP_PKEY_RSA
26TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080027
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080028
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050029class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050030 """
31 An error occurred in an `OpenSSL.crypto` API.
32 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050033
34
35_raise_current_error = partial(_exception_from_error_queue, Error)
36
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050037def _untested_error(where):
38 """
39 An OpenSSL API failed somehow. Additionally, the failure which was
40 encountered isn't one that's exercised by the test suite so future behavior
41 of pyOpenSSL is now somewhat less predictable.
42 """
43 raise RuntimeError("Unknown %s failure" % (where,))
44
45
46
47def _new_mem_buf(buffer=None):
48 """
49 Allocate a new OpenSSL memory BIO.
50
51 Arrange for the garbage collector to clean it up automatically.
52
53 :param buffer: None or some bytes to use to put into the BIO so that they
54 can be read out.
55 """
56 if buffer is None:
57 bio = _lib.BIO_new(_lib.BIO_s_mem())
58 free = _lib.BIO_free
59 else:
60 data = _ffi.new("char[]", buffer)
61 bio = _lib.BIO_new_mem_buf(data, len(buffer))
62 # Keep the memory alive as long as the bio is alive!
63 def free(bio, ref=data):
64 return _lib.BIO_free(bio)
65
66 if bio == _ffi.NULL:
67 # TODO: This is untested.
68 _raise_current_error()
69
70 bio = _ffi.gc(bio, free)
71 return bio
72
73
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050074
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080075def _bio_to_string(bio):
76 """
77 Copy the contents of an OpenSSL BIO object into a Python byte string.
78 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050079 result_buffer = _ffi.new('char**')
80 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
81 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080082
83
84
Jean-Paul Calderone57122982013-02-21 08:47:05 -080085def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050086 """
87 The the time value of an ASN1 time object.
88
89 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
90 castable to that type) which will have its value set.
91 @param when: A string representation of the desired time value.
92
93 @raise TypeError: If C{when} is not a L{bytes} string.
94 @raise ValueError: If C{when} does not represent a time in the required
95 format.
96 @raise RuntimeError: If the time value cannot be set for some other
97 (unspecified) reason.
98 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080099 if not isinstance(when, bytes):
100 raise TypeError("when must be a byte string")
101
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500102 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
103 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800104 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500105 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
106 _lib.ASN1_STRING_set(dummy, when, len(when))
107 check_result = _lib.ASN1_GENERALIZEDTIME_check(
108 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800109 if not check_result:
110 raise ValueError("Invalid string")
111 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500112 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800113
114
115
116def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500117 """
118 Retrieve the time value of an ASN1 time object.
119
120 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
121 that type) from which the time value will be retrieved.
122
123 @return: The time value from C{timestamp} as a L{bytes} string in a certain
124 format. Or C{None} if the object contains no time value.
125 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500126 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
127 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800128 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500129 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
130 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800131 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500132 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
133 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
134 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500135 # This may happen:
136 # - if timestamp was not an ASN1_TIME
137 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
138 # - if a copy of the time data from timestamp cannot be made for
139 # the newly allocated ASN1_GENERALIZEDTIME
140 #
141 # These are difficult to test. cffi enforces the ASN1_TIME type.
142 # Memory allocation failures are a pain to trigger
143 # deterministically.
144 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800145 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500146 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800147 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500148 string_data = _lib.ASN1_STRING_data(string_timestamp)
149 string_result = _ffi.string(string_data)
150 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800151 return string_result
152
153
154
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800155class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800156 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800157 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800158
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800159 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500160 pkey = _lib.EVP_PKEY_new()
161 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800162 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800163
164
165 def generate_key(self, type, bits):
166 """
167 Generate a key of a given type, with a given number of a bits
168
169 :param type: The key type (TYPE_RSA or TYPE_DSA)
170 :param bits: The number of bits
171
172 :return: None
173 """
174 if not isinstance(type, int):
175 raise TypeError("type must be an integer")
176
177 if not isinstance(bits, int):
178 raise TypeError("bits must be an integer")
179
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800180 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500181 exponent = _lib.BN_new()
182 exponent = _ffi.gc(exponent, _lib.BN_free)
183 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800184
185 if type == TYPE_RSA:
186 if bits <= 0:
187 raise ValueError("Invalid number of bits")
188
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500189 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800190
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500191 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500192 if result == 0:
193 # TODO: The test for this case is commented out. Different
194 # builds of OpenSSL appear to have different failure modes that
195 # make it hard to test. Visual inspection of the OpenSSL
196 # source reveals that a return value of 0 signals an error.
197 # Manual testing on a particular build of OpenSSL suggests that
198 # this is probably the appropriate way to handle those errors.
199 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800200
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500201 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800202 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500203 # TODO: It appears as though this can fail if an engine is in
204 # use which does not support RSA.
205 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800206
207 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500208 dsa = _lib.DSA_generate_parameters(
209 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
210 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500211 # TODO: This is untested.
212 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500213 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500214 # TODO: This is untested.
215 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500216 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500217 # TODO: This is untested.
218 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800219 else:
220 raise Error("No such key type")
221
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800222 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800223
224
225 def check(self):
226 """
227 Check the consistency of an RSA private key.
228
229 :return: True if key is consistent.
230 :raise Error: if the key is inconsistent.
231 :raise TypeError: if the key is of a type which cannot be checked.
232 Only RSA keys can currently be checked.
233 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800234 if self._only_public:
235 raise TypeError("public key only")
236
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500237 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800238 raise TypeError("key type unsupported")
239
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500240 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
241 rsa = _ffi.gc(rsa, _lib.RSA_free)
242 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800243 if result:
244 return True
245 _raise_current_error()
246
247
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800248 def type(self):
249 """
250 Returns the type of the key
251
252 :return: The type of the key.
253 """
254 return self._pkey.type
255
256
257 def bits(self):
258 """
259 Returns the number of bits of the key
260
261 :return: The number of bits of the key.
262 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500263 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800264PKeyType = PKey
265
266
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800267
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400268class _EllipticCurve(object):
269 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400270 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400271
272 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
273 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
274 instances each of which represents one curve supported by the system.
275 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400276 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400277 _curves = None
278
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400279 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400280 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400281 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400282 """
283 Implement cooperation with the right-hand side argument of ``!=``.
284
285 Python 3 seems to have dropped this cooperation in this very narrow
286 circumstance.
287 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400288 if isinstance(other, _EllipticCurve):
289 return super(_EllipticCurve, self).__ne__(other)
290 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400291
292
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400293 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400294 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400295 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400296 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400297
298 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400299
300 :return: A :py:type:`set` of ``cls`` instances giving the names of the
301 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400302 """
303 if lib.Cryptography_HAS_EC:
304 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
305 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
306 # The return value on this call should be num_curves again. We could
307 # check it to make sure but if it *isn't* then.. what could we do?
308 # Abort the whole process, I suppose...? -exarkun
309 lib.EC_get_builtin_curves(builtin_curves, num_curves)
310 return set(
311 cls.from_nid(lib, c.nid)
312 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400313 return set()
314
315
316 @classmethod
317 def _get_elliptic_curves(cls, lib):
318 """
319 Get, cache, and return the curves supported by OpenSSL.
320
321 :param lib: The OpenSSL library binding object.
322
323 :return: A :py:type:`set` of ``cls`` instances giving the names of the
324 elliptic curves the underlying library supports.
325 """
326 if cls._curves is None:
327 cls._curves = cls._load_elliptic_curves(lib)
328 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400329
330
331 @classmethod
332 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400333 """
334 Instantiate a new :py:class:`_EllipticCurve` associated with the given
335 OpenSSL NID.
336
337 :param lib: The OpenSSL library binding object.
338
339 :param nid: The OpenSSL NID the resulting curve object will represent.
340 This must be a curve NID (and not, for example, a hash NID) or
341 subsequent operations will fail in unpredictable ways.
342 :type nid: :py:class:`int`
343
344 :return: The curve object.
345 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400346 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
347
348
349 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400350 """
351 :param _lib: The :py:mod:`cryptography` binding instance used to
352 interface with OpenSSL.
353
354 :param _nid: The OpenSSL NID identifying the curve this object
355 represents.
356 :type _nid: :py:class:`int`
357
358 :param name: The OpenSSL short name identifying the curve this object
359 represents.
360 :type name: :py:class:`unicode`
361 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400362 self._lib = lib
363 self._nid = nid
364 self.name = name
365
366
367 def __repr__(self):
368 return "<Curve %r>" % (self.name,)
369
370
371 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400372 """
373 Create a new OpenSSL EC_KEY structure initialized to use this curve.
374
375 The structure is automatically garbage collected when the Python object
376 is garbage collected.
377 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400378 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
379 return _ffi.gc(key, _lib.EC_KEY_free)
380
381
382
383def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400384 """
385 Return a set of objects representing the elliptic curves supported in the
386 OpenSSL build in use.
387
388 The curve objects have a :py:class:`unicode` ``name`` attribute by which
389 they identify themselves.
390
391 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400392 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
393 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400394 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400395 return _EllipticCurve._get_elliptic_curves(_lib)
396
397
398
399def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400400 """
401 Return a single curve object selected by name.
402
403 See :py:func:`get_elliptic_curves` for information about curve objects.
404
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400405 :param name: The OpenSSL short name identifying the curve object to
406 retrieve.
407 :type name: :py:class:`unicode`
408
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400409 If the named curve is not supported then :py:class:`ValueError` is raised.
410 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400411 for curve in get_elliptic_curves():
412 if curve.name == name:
413 return curve
414 raise ValueError("unknown curve name", name)
415
416
417
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800418class X509Name(object):
419 def __init__(self, name):
420 """
421 Create a new X509Name, copying the given X509Name instance.
422
423 :param name: An X509Name object to copy
424 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500425 name = _lib.X509_NAME_dup(name._name)
426 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800427
428
429 def __setattr__(self, name, value):
430 if name.startswith('_'):
431 return super(X509Name, self).__setattr__(name, value)
432
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800433 # Note: we really do not want str subclasses here, so we do not use
434 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800435 if type(name) is not str:
436 raise TypeError("attribute name must be string, not '%.200s'" % (
437 type(value).__name__,))
438
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500439 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500440 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800441 try:
442 _raise_current_error()
443 except Error:
444 pass
445 raise AttributeError("No such attribute")
446
447 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500448 for i in range(_lib.X509_NAME_entry_count(self._name)):
449 ent = _lib.X509_NAME_get_entry(self._name, i)
450 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
451 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800452 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500453 ent = _lib.X509_NAME_delete_entry(self._name, i)
454 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800455 break
456
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500457 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800458 value = value.encode('utf-8')
459
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500460 add_result = _lib.X509_NAME_add_entry_by_NID(
461 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800462 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500463 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800464
465
466 def __getattr__(self, name):
467 """
468 Find attribute. An X509Name object has the following attributes:
469 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
470 organization (alias O), organizationalUnit (alias OU), commonName (alias
471 CN) and more...
472 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500473 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500474 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800475 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
476 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
477 # push something onto the error queue. If we don't clean that up
478 # now, someone else will bump into it later and be quite confused.
479 # See lp#314814.
480 try:
481 _raise_current_error()
482 except Error:
483 pass
484 return super(X509Name, self).__getattr__(name)
485
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500486 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800487 if entry_index == -1:
488 return None
489
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500490 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
491 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800492
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500493 result_buffer = _ffi.new("unsigned char**")
494 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800495 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500496 # TODO: This is untested.
497 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800498
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700499 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500500 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700501 finally:
502 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500503 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800504 return result
505
506
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500507 def _cmp(op):
508 def f(self, other):
509 if not isinstance(other, X509Name):
510 return NotImplemented
511 result = _lib.X509_NAME_cmp(self._name, other._name)
512 return op(result, 0)
513 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800514
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500515 __eq__ = _cmp(__eq__)
516 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800517
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500518 __lt__ = _cmp(__lt__)
519 __le__ = _cmp(__le__)
520
521 __gt__ = _cmp(__gt__)
522 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800523
524 def __repr__(self):
525 """
526 String representation of an X509Name
527 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500528 result_buffer = _ffi.new("char[]", 512);
529 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800530 self._name, result_buffer, len(result_buffer))
531
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500532 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500533 # TODO: This is untested.
534 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800535
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500536 return "<X509Name object '%s'>" % (
537 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800538
539
540 def hash(self):
541 """
542 Return the hash value of this name
543
544 :return: None
545 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500546 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800547
548
549 def der(self):
550 """
551 Return the DER encoding of this name
552
553 :return: A :py:class:`bytes` instance giving the DER encoded form of
554 this name.
555 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500556 result_buffer = _ffi.new('unsigned char**')
557 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800558 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500559 # TODO: This is untested.
560 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800561
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500562 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
563 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800564 return string_result
565
566
567 def get_components(self):
568 """
569 Returns the split-up components of this name.
570
571 :return: List of tuples (name, value).
572 """
573 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500574 for i in range(_lib.X509_NAME_entry_count(self._name)):
575 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800576
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500577 fname = _lib.X509_NAME_ENTRY_get_object(ent)
578 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800579
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500580 nid = _lib.OBJ_obj2nid(fname)
581 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800582
583 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500584 _ffi.string(name),
585 _ffi.string(
586 _lib.ASN1_STRING_data(fval),
587 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800588
589 return result
590X509NameType = X509Name
591
592
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800593class X509Extension(object):
594 def __init__(self, type_name, critical, value, subject=None, issuer=None):
595 """
596 :param typename: The name of the extension to create.
597 :type typename: :py:data:`str`
598
599 :param critical: A flag indicating whether this is a critical extension.
600
601 :param value: The value of the extension.
602 :type value: :py:data:`str`
603
604 :param subject: Optional X509 cert to use as subject.
605 :type subject: :py:class:`X509`
606
607 :param issuer: Optional X509 cert to use as issuer.
608 :type issuer: :py:class:`X509`
609
610 :return: The X509Extension object
611 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500612 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800613
614 # A context is necessary for any extension which uses the r2i conversion
615 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
616 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500617 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800618
619 # We have no configuration database - but perhaps we should (some
620 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500621 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800622
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800623 # Initialize the subject and issuer, if appropriate. ctx is a local,
624 # and as far as I can tell none of the X509V3_* APIs invoked here steal
625 # any references, so no need to mess with reference counts or duplicates.
626 if issuer is not None:
627 if not isinstance(issuer, X509):
628 raise TypeError("issuer must be an X509 instance")
629 ctx.issuer_cert = issuer._x509
630 if subject is not None:
631 if not isinstance(subject, X509):
632 raise TypeError("subject must be an X509 instance")
633 ctx.subject_cert = subject._x509
634
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800635 if critical:
636 # There are other OpenSSL APIs which would let us pass in critical
637 # separately, but they're harder to use, and since value is already
638 # a pile of crappy junk smuggling a ton of utterly important
639 # structured data, what's the point of trying to avoid nasty stuff
640 # with strings? (However, X509V3_EXT_i2d in particular seems like it
641 # would be a better API to invoke. I do not know where to get the
642 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500643 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800644
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500645 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
646 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800647 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500648 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800649
650
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400651 @property
652 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500653 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400654
655 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500656 _lib.GEN_EMAIL: "email",
657 _lib.GEN_DNS: "DNS",
658 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400659 }
660
661 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500662 method = _lib.X509V3_EXT_get(self._extension)
663 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500664 # TODO: This is untested.
665 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400666 payload = self._extension.value.data
667 length = self._extension.value.length
668
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500669 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400670 payloadptr[0] = payload
671
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500672 if method.it != _ffi.NULL:
673 ptr = _lib.ASN1_ITEM_ptr(method.it)
674 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
675 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400676 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500677 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400678 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500679 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400680
681 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500682 for i in range(_lib.sk_GENERAL_NAME_num(names)):
683 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400684 try:
685 label = self._prefixes[name.type]
686 except KeyError:
687 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500688 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500689 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400690 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500691 value = _native(
692 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
693 parts.append(label + ":" + value)
694 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400695
696
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800697 def __str__(self):
698 """
699 :return: a nice text representation of the extension
700 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500701 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400702 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800703
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400704 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500705 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800706 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500707 # TODO: This is untested.
708 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800709
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500710 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800711
712
713 def get_critical(self):
714 """
715 Returns the critical field of the X509Extension
716
717 :return: The critical field.
718 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500719 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800720
721
722 def get_short_name(self):
723 """
724 Returns the short version of the type name of the X509Extension
725
726 :return: The short type name.
727 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500728 obj = _lib.X509_EXTENSION_get_object(self._extension)
729 nid = _lib.OBJ_obj2nid(obj)
730 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800731
732
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800733 def get_data(self):
734 """
735 Returns the data of the X509Extension
736
737 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
738 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500739 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
740 string_result = _ffi.cast('ASN1_STRING*', octet_result)
741 char_result = _lib.ASN1_STRING_data(string_result)
742 result_length = _lib.ASN1_STRING_length(string_result)
743 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800744
745X509ExtensionType = X509Extension
746
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800747
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800748class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800749 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500750 req = _lib.X509_REQ_new()
751 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800752
753
754 def set_pubkey(self, pkey):
755 """
756 Set the public key of the certificate request
757
758 :param pkey: The public key to use
759 :return: None
760 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500761 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800762 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500763 # TODO: This is untested.
764 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800765
766
767 def get_pubkey(self):
768 """
769 Get the public key from the certificate request
770
771 :return: The public key
772 """
773 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500774 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
775 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500776 # TODO: This is untested.
777 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500778 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800779 pkey._only_public = True
780 return pkey
781
782
783 def set_version(self, version):
784 """
785 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
786 request.
787
788 :param version: The version number
789 :return: None
790 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500791 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800792 if not set_result:
793 _raise_current_error()
794
795
796 def get_version(self):
797 """
798 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
799 request.
800
801 :return: an integer giving the value of the version subfield
802 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500803 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800804
805
806 def get_subject(self):
807 """
808 Create an X509Name object for the subject of the certificate request
809
810 :return: An X509Name object
811 """
812 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500813 name._name = _lib.X509_REQ_get_subject_name(self._req)
814 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500815 # TODO: This is untested.
816 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800817
818 # The name is owned by the X509Req structure. As long as the X509Name
819 # Python object is alive, keep the X509Req Python object alive.
820 name._owner = self
821
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800822 return name
823
824
825 def add_extensions(self, extensions):
826 """
827 Add extensions to the request.
828
829 :param extensions: a sequence of X509Extension objects
830 :return: None
831 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500832 stack = _lib.sk_X509_EXTENSION_new_null()
833 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500834 # TODO: This is untested.
835 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800836
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500837 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800838
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800839 for ext in extensions:
840 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800841 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800842
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800843 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500844 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800845
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500846 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800847 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500848 # TODO: This is untested.
849 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800850
851
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800852 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800853 """
854 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800855
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500856 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800857 """
858 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500859 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500860 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800861 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500862 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800863 exts.append(ext)
864 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800865
866
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800867 def sign(self, pkey, digest):
868 """
869 Sign the certificate request using the supplied key and digest
870
871 :param pkey: The key to sign with
872 :param digest: The message digest to use
873 :return: None
874 """
875 if pkey._only_public:
876 raise ValueError("Key has only public part")
877
878 if not pkey._initialized:
879 raise ValueError("Key is uninitialized")
880
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500881 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500882 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800883 raise ValueError("No such digest method")
884
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500885 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800886 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500887 # TODO: This is untested.
888 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800889
890
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800891 def verify(self, pkey):
892 """
893 Verifies a certificate request using the supplied public key
894
895 :param key: a public key
896 :return: True if the signature is correct.
897
898 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
899 problem verifying the signature.
900 """
901 if not isinstance(pkey, PKey):
902 raise TypeError("pkey must be a PKey instance")
903
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500904 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800905 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500906 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800907
908 return result
909
910
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800911X509ReqType = X509Req
912
913
914
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800915class X509(object):
916 def __init__(self):
917 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500918 x509 = _lib.X509_new()
919 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800920
921
922 def set_version(self, version):
923 """
924 Set version number of the certificate
925
926 :param version: The version number
927 :type version: :py:class:`int`
928
929 :return: None
930 """
931 if not isinstance(version, int):
932 raise TypeError("version must be an integer")
933
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500934 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800935
936
937 def get_version(self):
938 """
939 Return version number of the certificate
940
941 :return: Version number as a Python integer
942 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500943 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800944
945
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800946 def get_pubkey(self):
947 """
948 Get the public key of the certificate
949
950 :return: The public key
951 """
952 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500953 pkey._pkey = _lib.X509_get_pubkey(self._x509)
954 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800955 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500956 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800957 pkey._only_public = True
958 return pkey
959
960
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800961 def set_pubkey(self, pkey):
962 """
963 Set the public key of the certificate
964
965 :param pkey: The public key
966
967 :return: None
968 """
969 if not isinstance(pkey, PKey):
970 raise TypeError("pkey must be a PKey instance")
971
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500972 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800973 if not set_result:
974 _raise_current_error()
975
976
977 def sign(self, pkey, digest):
978 """
979 Sign the certificate using the supplied key and digest
980
981 :param pkey: The key to sign with
982 :param digest: The message digest to use
983 :return: None
984 """
985 if not isinstance(pkey, PKey):
986 raise TypeError("pkey must be a PKey instance")
987
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800988 if pkey._only_public:
989 raise ValueError("Key only has public part")
990
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800991 if not pkey._initialized:
992 raise ValueError("Key is uninitialized")
993
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500994 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500995 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800996 raise ValueError("No such digest method")
997
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500998 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800999 if not sign_result:
1000 _raise_current_error()
1001
1002
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001003 def get_signature_algorithm(self):
1004 """
1005 Retrieve the signature algorithm used in the certificate
1006
1007 :return: A byte string giving the name of the signature algorithm used in
1008 the certificate.
1009 :raise ValueError: If the signature algorithm is undefined.
1010 """
1011 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001012 nid = _lib.OBJ_obj2nid(alg)
1013 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001014 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001015 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001016
1017
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001018 def digest(self, digest_name):
1019 """
1020 Return the digest of the X509 object.
1021
1022 :param digest_name: The name of the digest algorithm to use.
1023 :type digest_name: :py:class:`bytes`
1024
1025 :return: The digest of the object
1026 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001027 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001028 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001029 raise ValueError("No such digest method")
1030
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001031 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1032 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001033 result_length[0] = len(result_buffer)
1034
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001035 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001036 self._x509, digest, result_buffer, result_length)
1037
1038 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001039 # TODO: This is untested.
1040 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001041
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001042 return b":".join([
1043 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001044 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001045
1046
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001047 def subject_name_hash(self):
1048 """
1049 Return the hash of the X509 subject.
1050
1051 :return: The hash of the subject.
1052 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001053 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001054
1055
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001056 def set_serial_number(self, serial):
1057 """
1058 Set serial number of the certificate
1059
1060 :param serial: The serial number
1061 :type serial: :py:class:`int`
1062
1063 :return: None
1064 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001065 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001066 raise TypeError("serial must be an integer")
1067
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001068 hex_serial = hex(serial)[2:]
1069 if not isinstance(hex_serial, bytes):
1070 hex_serial = hex_serial.encode('ascii')
1071
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001072 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001073
1074 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1075 # it. If bignum is still NULL after this call, then the return value is
1076 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001077 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001078
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001079 if bignum_serial[0] == _ffi.NULL:
1080 set_result = _lib.ASN1_INTEGER_set(
1081 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001082 if set_result:
1083 # TODO Not tested
1084 _raise_current_error()
1085 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001086 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1087 _lib.BN_free(bignum_serial[0])
1088 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001089 # TODO Not tested
1090 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001091 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1092 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001093 if not set_result:
1094 # TODO Not tested
1095 _raise_current_error()
1096
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001097
1098 def get_serial_number(self):
1099 """
1100 Return serial number of the certificate
1101
1102 :return: Serial number as a Python integer
1103 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001104 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1105 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001106 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001107 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001108 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001109 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001110 serial = int(hexstring_serial, 16)
1111 return serial
1112 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001113 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001114 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001115 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001116
1117
1118 def gmtime_adj_notAfter(self, amount):
1119 """
1120 Adjust the time stamp for when the certificate stops being valid
1121
1122 :param amount: The number of seconds by which to adjust the ending
1123 validity time.
1124 :type amount: :py:class:`int`
1125
1126 :return: None
1127 """
1128 if not isinstance(amount, int):
1129 raise TypeError("amount must be an integer")
1130
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001131 notAfter = _lib.X509_get_notAfter(self._x509)
1132 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001133
1134
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001135 def gmtime_adj_notBefore(self, amount):
1136 """
1137 Change the timestamp for when the certificate starts being valid to the current
1138 time plus an offset.
1139
1140 :param amount: The number of seconds by which to adjust the starting validity
1141 time.
1142 :return: None
1143 """
1144 if not isinstance(amount, int):
1145 raise TypeError("amount must be an integer")
1146
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001147 notBefore = _lib.X509_get_notBefore(self._x509)
1148 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001149
1150
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001151 def has_expired(self):
1152 """
1153 Check whether the certificate has expired.
1154
1155 :return: True if the certificate has expired, false otherwise
1156 """
1157 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001158 notAfter = _lib.X509_get_notAfter(self._x509)
1159 return _lib.ASN1_UTCTIME_cmp_time_t(
1160 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001161
1162
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001163 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001164 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001165
1166
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001167 def get_notBefore(self):
1168 """
1169 Retrieve the time stamp for when the certificate starts being valid
1170
1171 :return: A string giving the timestamp, in the format::
1172
1173 YYYYMMDDhhmmssZ
1174 YYYYMMDDhhmmss+hhmm
1175 YYYYMMDDhhmmss-hhmm
1176
1177 or None if there is no value set.
1178 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001179 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001180
1181
1182 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001183 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001184
1185
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001186 def set_notBefore(self, when):
1187 """
1188 Set the time stamp for when the certificate starts being valid
1189
1190 :param when: A string giving the timestamp, in the format:
1191
1192 YYYYMMDDhhmmssZ
1193 YYYYMMDDhhmmss+hhmm
1194 YYYYMMDDhhmmss-hhmm
1195 :type when: :py:class:`bytes`
1196
1197 :return: None
1198 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001199 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001200
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001201
1202 def get_notAfter(self):
1203 """
1204 Retrieve the time stamp for when the certificate stops being valid
1205
1206 :return: A string giving the timestamp, in the format::
1207
1208 YYYYMMDDhhmmssZ
1209 YYYYMMDDhhmmss+hhmm
1210 YYYYMMDDhhmmss-hhmm
1211
1212 or None if there is no value set.
1213 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001214 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001215
1216
1217 def set_notAfter(self, when):
1218 """
1219 Set the time stamp for when the certificate stops being valid
1220
1221 :param when: A string giving the timestamp, in the format:
1222
1223 YYYYMMDDhhmmssZ
1224 YYYYMMDDhhmmss+hhmm
1225 YYYYMMDDhhmmss-hhmm
1226 :type when: :py:class:`bytes`
1227
1228 :return: None
1229 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001230 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001231
1232
1233 def _get_name(self, which):
1234 name = X509Name.__new__(X509Name)
1235 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001236 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001237 # TODO: This is untested.
1238 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001239
1240 # The name is owned by the X509 structure. As long as the X509Name
1241 # Python object is alive, keep the X509 Python object alive.
1242 name._owner = self
1243
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001244 return name
1245
1246
1247 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001248 if not isinstance(name, X509Name):
1249 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001250 set_result = which(self._x509, name._name)
1251 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001252 # TODO: This is untested.
1253 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001254
1255
1256 def get_issuer(self):
1257 """
1258 Create an X509Name object for the issuer of the certificate
1259
1260 :return: An X509Name object
1261 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001262 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001263
1264
1265 def set_issuer(self, issuer):
1266 """
1267 Set the issuer of the certificate
1268
1269 :param issuer: The issuer name
1270 :type issuer: :py:class:`X509Name`
1271
1272 :return: None
1273 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001274 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001275
1276
1277 def get_subject(self):
1278 """
1279 Create an X509Name object for the subject of the certificate
1280
1281 :return: An X509Name object
1282 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001283 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001284
1285
1286 def set_subject(self, subject):
1287 """
1288 Set the subject of the certificate
1289
1290 :param subject: The subject name
1291 :type subject: :py:class:`X509Name`
1292 :return: None
1293 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001294 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001295
1296
1297 def get_extension_count(self):
1298 """
1299 Get the number of extensions on the certificate.
1300
1301 :return: The number of extensions as an integer.
1302 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001303 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001304
1305
1306 def add_extensions(self, extensions):
1307 """
1308 Add extensions to the certificate.
1309
1310 :param extensions: a sequence of X509Extension objects
1311 :return: None
1312 """
1313 for ext in extensions:
1314 if not isinstance(ext, X509Extension):
1315 raise ValueError("One of the elements is not an X509Extension")
1316
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001317 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001318 if not add_result:
1319 _raise_current_error()
1320
1321
1322 def get_extension(self, index):
1323 """
1324 Get a specific extension of the certificate by index.
1325
1326 :param index: The index of the extension to retrieve.
1327 :return: The X509Extension object at the specified index.
1328 """
1329 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001330 ext._extension = _lib.X509_get_ext(self._x509, index)
1331 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001332 raise IndexError("extension index out of bounds")
1333
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001334 extension = _lib.X509_EXTENSION_dup(ext._extension)
1335 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001336 return ext
1337
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001338X509Type = X509
1339
1340
1341
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001342class X509Store(object):
1343 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001344 store = _lib.X509_STORE_new()
1345 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001346
1347
1348 def add_cert(self, cert):
1349 if not isinstance(cert, X509):
1350 raise TypeError()
1351
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001352 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001353 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001354 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001355
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001356
1357X509StoreType = X509Store
1358
1359
1360
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001361def load_certificate(type, buffer):
1362 """
1363 Load a certificate from a buffer
1364
1365 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1366
1367 :param buffer: The buffer the certificate is stored in
1368 :type buffer: :py:class:`bytes`
1369
1370 :return: The X509 object
1371 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001372 if isinstance(buffer, _text_type):
1373 buffer = buffer.encode("ascii")
1374
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001375 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001376
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001377 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001378 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001379 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001380 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001381 else:
1382 raise ValueError(
1383 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001384
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001385 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001386 _raise_current_error()
1387
1388 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001389 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001390 return cert
1391
1392
1393def dump_certificate(type, cert):
1394 """
1395 Dump a certificate to a buffer
1396
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001397 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1398 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001399 :param cert: The certificate to dump
1400 :return: The buffer with the dumped certificate in
1401 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001402 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001403
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001404 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001405 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001406 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001407 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001408 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001409 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001410 else:
1411 raise ValueError(
1412 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1413 "FILETYPE_TEXT")
1414
1415 return _bio_to_string(bio)
1416
1417
1418
1419def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1420 """
1421 Dump a private key to a buffer
1422
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001423 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1424 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001425 :param pkey: The PKey to dump
1426 :param cipher: (optional) if encrypted PEM format, the cipher to
1427 use
1428 :param passphrase: (optional) if encrypted PEM format, this can be either
1429 the passphrase to use, or a callback for providing the
1430 passphrase.
1431 :return: The buffer with the dumped key in
1432 :rtype: :py:data:`str`
1433 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001434 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001435
1436 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001437 if passphrase is None:
1438 raise TypeError(
1439 "if a value is given for cipher "
1440 "one must also be given for passphrase")
1441 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001442 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001443 raise ValueError("Invalid cipher name")
1444 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001445 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001446
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001447 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001448 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001449 result_code = _lib.PEM_write_bio_PrivateKey(
1450 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001451 helper.callback, helper.callback_args)
1452 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001453 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001454 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001455 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001456 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1457 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001458 # TODO RSA_free(rsa)?
1459 else:
1460 raise ValueError(
1461 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1462 "FILETYPE_TEXT")
1463
1464 if result_code == 0:
1465 _raise_current_error()
1466
1467 return _bio_to_string(bio)
1468
1469
1470
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001471def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001472 copy = _lib.X509_REVOKED_new()
1473 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001474 # TODO: This is untested.
1475 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001476
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001477 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001478 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001479 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001480
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001481 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001482 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001483 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001484
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001485 if original.extensions != _ffi.NULL:
1486 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1487 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1488 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1489 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1490 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001491 copy.extensions = extension_stack
1492
1493 copy.sequence = original.sequence
1494 return copy
1495
1496
1497
1498class Revoked(object):
1499 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1500 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1501 # OCSP_crl_reason_str. We use the latter, just like the command line
1502 # program.
1503 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001504 b"unspecified",
1505 b"keyCompromise",
1506 b"CACompromise",
1507 b"affiliationChanged",
1508 b"superseded",
1509 b"cessationOfOperation",
1510 b"certificateHold",
1511 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001512 ]
1513
1514 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001515 revoked = _lib.X509_REVOKED_new()
1516 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001517
1518
1519 def set_serial(self, hex_str):
1520 """
1521 Set the serial number of a revoked Revoked structure
1522
1523 :param hex_str: The new serial number.
1524 :type hex_str: :py:data:`str`
1525 :return: None
1526 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001527 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1528 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001529 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001530 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001531 if not bn_result:
1532 raise ValueError("bad hex string")
1533
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001534 asn1_serial = _ffi.gc(
1535 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1536 _lib.ASN1_INTEGER_free)
1537 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001538
1539
1540 def get_serial(self):
1541 """
1542 Return the serial number of a Revoked structure
1543
1544 :return: The serial number as a string
1545 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001546 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001547
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001548 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001549 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001550 # TODO: This is untested.
1551 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001552
1553 return _bio_to_string(bio)
1554
1555
1556 def _delete_reason(self):
1557 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001558 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1559 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1560 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1561 _lib.X509_EXTENSION_free(ext)
1562 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001563 break
1564
1565
1566 def set_reason(self, reason):
1567 """
1568 Set the reason of a Revoked object.
1569
1570 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1571
1572 :param reason: The reason string.
1573 :type reason: :py:class:`str` or :py:class:`NoneType`
1574 :return: None
1575 """
1576 if reason is None:
1577 self._delete_reason()
1578 elif not isinstance(reason, bytes):
1579 raise TypeError("reason must be None or a byte string")
1580 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001581 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001582 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1583
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001584 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1585 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001586 # TODO: This is untested.
1587 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001588 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001589
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001590 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1591 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001592 # TODO: This is untested.
1593 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001594
1595 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001596 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1597 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001598
1599 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001600 # TODO: This is untested.
1601 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001602
1603
1604 def get_reason(self):
1605 """
1606 Return the reason of a Revoked object.
1607
1608 :return: The reason as a string
1609 """
1610 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001611 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1612 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1613 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001614 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001615
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001616 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001617 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001618 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001619 if print_result == 0:
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 return _bio_to_string(bio)
1624
1625
1626 def all_reasons(self):
1627 """
1628 Return a list of all the supported reason strings.
1629
1630 :return: A list of reason strings.
1631 """
1632 return self._crl_reasons[:]
1633
1634
1635 def set_rev_date(self, when):
1636 """
1637 Set the revocation timestamp
1638
1639 :param when: A string giving the timestamp, in the format:
1640
1641 YYYYMMDDhhmmssZ
1642 YYYYMMDDhhmmss+hhmm
1643 YYYYMMDDhhmmss-hhmm
1644
1645 :return: None
1646 """
1647 return _set_asn1_time(self._revoked.revocationDate, when)
1648
1649
1650 def get_rev_date(self):
1651 """
1652 Retrieve the revocation date
1653
1654 :return: A string giving the timestamp, in the format:
1655
1656 YYYYMMDDhhmmssZ
1657 YYYYMMDDhhmmss+hhmm
1658 YYYYMMDDhhmmss-hhmm
1659 """
1660 return _get_asn1_time(self._revoked.revocationDate)
1661
1662
1663
1664class CRL(object):
1665 def __init__(self):
1666 """
1667 Create a new empty CRL object.
1668 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001669 crl = _lib.X509_CRL_new()
1670 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001671
1672
1673 def get_revoked(self):
1674 """
1675 Return revoked portion of the CRL structure (by value not reference).
1676
1677 :return: A tuple of Revoked objects.
1678 """
1679 results = []
1680 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001681 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1682 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001683 revoked_copy = _X509_REVOKED_dup(revoked)
1684 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001685 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001686 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001687 if results:
1688 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001689
1690
1691 def add_revoked(self, revoked):
1692 """
1693 Add a revoked (by value not reference) to the CRL structure
1694
1695 :param revoked: The new revoked.
1696 :type revoked: :class:`X509`
1697
1698 :return: None
1699 """
1700 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001701 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001702 # TODO: This is untested.
1703 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001704
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001705 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001706 if add_result == 0:
1707 # TODO: This is untested.
1708 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001709
1710
1711 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1712 """
1713 export a CRL as a string
1714
1715 :param cert: Used to sign CRL.
1716 :type cert: :class:`X509`
1717
1718 :param key: Used to sign CRL.
1719 :type key: :class:`PKey`
1720
1721 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1722
1723 :param days: The number of days until the next update of this CRL.
1724 :type days: :py:data:`int`
1725
1726 :return: :py:data:`str`
1727 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001728 if not isinstance(cert, X509):
1729 raise TypeError("cert must be an X509 instance")
1730 if not isinstance(key, PKey):
1731 raise TypeError("key must be a PKey instance")
1732 if not isinstance(type, int):
1733 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001734
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001735 bio = _lib.BIO_new(_lib.BIO_s_mem())
1736 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001737 # TODO: This is untested.
1738 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001739
1740 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001741 sometime = _lib.ASN1_TIME_new()
1742 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001743 # TODO: This is untested.
1744 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001745
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001746 _lib.X509_gmtime_adj(sometime, 0)
1747 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001748
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001749 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1750 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001751
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001752 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001753
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001754 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001755 if not sign_result:
1756 _raise_current_error()
1757
1758 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001759 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001760 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001761 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001762 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001763 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001764 else:
1765 raise ValueError(
1766 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1767
1768 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001769 # TODO: This is untested.
1770 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001771
1772 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001773CRLType = CRL
1774
1775
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001776
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001777class PKCS7(object):
1778 def type_is_signed(self):
1779 """
1780 Check if this NID_pkcs7_signed object
1781
1782 :return: True if the PKCS7 is of type signed
1783 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001784 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001785 return True
1786 return False
1787
1788
1789 def type_is_enveloped(self):
1790 """
1791 Check if this NID_pkcs7_enveloped object
1792
1793 :returns: True if the PKCS7 is of type enveloped
1794 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001795 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001796 return True
1797 return False
1798
1799
1800 def type_is_signedAndEnveloped(self):
1801 """
1802 Check if this NID_pkcs7_signedAndEnveloped object
1803
1804 :returns: True if the PKCS7 is of type signedAndEnveloped
1805 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001806 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001807 return True
1808 return False
1809
1810
1811 def type_is_data(self):
1812 """
1813 Check if this NID_pkcs7_data object
1814
1815 :return: True if the PKCS7 is of type data
1816 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001817 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001818 return True
1819 return False
1820
1821
1822 def get_type_name(self):
1823 """
1824 Returns the type name of the PKCS7 structure
1825
1826 :return: A string with the typename
1827 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001828 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1829 string_type = _lib.OBJ_nid2sn(nid)
1830 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001831
1832PKCS7Type = PKCS7
1833
1834
1835
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001836class PKCS12(object):
1837 def __init__(self):
1838 self._pkey = None
1839 self._cert = None
1840 self._cacerts = None
1841 self._friendlyname = None
1842
1843
1844 def get_certificate(self):
1845 """
1846 Return certificate portion of the PKCS12 structure
1847
1848 :return: X509 object containing the certificate
1849 """
1850 return self._cert
1851
1852
1853 def set_certificate(self, cert):
1854 """
1855 Replace the certificate portion of the PKCS12 structure
1856
1857 :param cert: The new certificate.
1858 :type cert: :py:class:`X509` or :py:data:`None`
1859 :return: None
1860 """
1861 if not isinstance(cert, X509):
1862 raise TypeError("cert must be an X509 instance")
1863 self._cert = cert
1864
1865
1866 def get_privatekey(self):
1867 """
1868 Return private key portion of the PKCS12 structure
1869
1870 :returns: PKey object containing the private key
1871 """
1872 return self._pkey
1873
1874
1875 def set_privatekey(self, pkey):
1876 """
1877 Replace or set the certificate portion of the PKCS12 structure
1878
1879 :param pkey: The new private key.
1880 :type pkey: :py:class:`PKey`
1881 :return: None
1882 """
1883 if not isinstance(pkey, PKey):
1884 raise TypeError("pkey must be a PKey instance")
1885 self._pkey = pkey
1886
1887
1888 def get_ca_certificates(self):
1889 """
1890 Return CA certificates within of the PKCS12 object
1891
1892 :return: A newly created tuple containing the CA certificates in the chain,
1893 if any are present, or None if no CA certificates are present.
1894 """
1895 if self._cacerts is not None:
1896 return tuple(self._cacerts)
1897
1898
1899 def set_ca_certificates(self, cacerts):
1900 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08001901 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001902
1903 :param cacerts: The new CA certificates.
1904 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1905 :return: None
1906 """
1907 if cacerts is None:
1908 self._cacerts = None
1909 else:
1910 cacerts = list(cacerts)
1911 for cert in cacerts:
1912 if not isinstance(cert, X509):
1913 raise TypeError("iterable must only contain X509 instances")
1914 self._cacerts = cacerts
1915
1916
1917 def set_friendlyname(self, name):
1918 """
1919 Replace or set the certificate portion of the PKCS12 structure
1920
1921 :param name: The new friendly name.
1922 :type name: :py:class:`bytes`
1923 :return: None
1924 """
1925 if name is None:
1926 self._friendlyname = None
1927 elif not isinstance(name, bytes):
1928 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1929 self._friendlyname = name
1930
1931
1932 def get_friendlyname(self):
1933 """
1934 Return friendly name portion of the PKCS12 structure
1935
1936 :returns: String containing the friendlyname
1937 """
1938 return self._friendlyname
1939
1940
1941 def export(self, passphrase=None, iter=2048, maciter=1):
1942 """
1943 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1944
1945 :param passphrase: used to encrypt the PKCS12
1946 :type passphrase: :py:data:`bytes`
1947
1948 :param iter: How many times to repeat the encryption
1949 :type iter: :py:data:`int`
1950
1951 :param maciter: How many times to repeat the MAC
1952 :type maciter: :py:data:`int`
1953
1954 :return: The string containing the PKCS12
1955 """
Jean-Paul Calderone6462b072015-03-29 07:03:11 -04001956 passphrase = _warn_text("passphrase", passphrase)
Abraham Martine82326c2015-02-04 10:18:10 +00001957
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001958 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001959 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001960 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001961 cacerts = _lib.sk_X509_new_null()
1962 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001963 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001964 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001965
1966 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001967 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001968
1969 friendlyname = self._friendlyname
1970 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001971 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001972
1973 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001974 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001975 else:
1976 pkey = self._pkey._pkey
1977
1978 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001979 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001980 else:
1981 cert = self._cert._x509
1982
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001983 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001984 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001985 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1986 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001987 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001988 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001989 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001990 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001991
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001992 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001993 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001994 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001995
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001996PKCS12Type = PKCS12
1997
1998
1999
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002000class NetscapeSPKI(object):
2001 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002002 spki = _lib.NETSCAPE_SPKI_new()
2003 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002004
2005
2006 def sign(self, pkey, digest):
2007 """
2008 Sign the certificate request using the supplied key and digest
2009
2010 :param pkey: The key to sign with
2011 :param digest: The message digest to use
2012 :return: None
2013 """
2014 if pkey._only_public:
2015 raise ValueError("Key has only public part")
2016
2017 if not pkey._initialized:
2018 raise ValueError("Key is uninitialized")
2019
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002020 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002021 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002022 raise ValueError("No such digest method")
2023
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002024 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002025 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002026 # TODO: This is untested.
2027 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002028
2029
2030 def verify(self, key):
2031 """
2032 Verifies a certificate request using the supplied public key
2033
2034 :param key: a public key
2035 :return: True if the signature is correct.
2036 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2037 problem verifying the signature.
2038 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002039 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002040 if answer <= 0:
2041 _raise_current_error()
2042 return True
2043
2044
2045 def b64_encode(self):
2046 """
2047 Generate a base64 encoded string from an SPKI
2048
2049 :return: The base64 encoded string
2050 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002051 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2052 result = _ffi.string(encoded)
2053 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002054 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002055
2056
2057 def get_pubkey(self):
2058 """
2059 Get the public key of the certificate
2060
2061 :return: The public key
2062 """
2063 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002064 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2065 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002066 # TODO: This is untested.
2067 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002068 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002069 pkey._only_public = True
2070 return pkey
2071
2072
2073 def set_pubkey(self, pkey):
2074 """
2075 Set the public key of the certificate
2076
2077 :param pkey: The public key
2078 :return: None
2079 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002080 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002081 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002082 # TODO: This is untested.
2083 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002084NetscapeSPKIType = NetscapeSPKI
2085
2086
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002087class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002088 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002089 if type != FILETYPE_PEM and passphrase is not None:
2090 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002091 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002092 self._more_args = more_args
2093 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002094 self._problems = []
2095
2096
2097 @property
2098 def callback(self):
2099 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002100 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002101 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002102 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002103 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002104 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002105 else:
2106 raise TypeError("Last argument must be string or callable")
2107
2108
2109 @property
2110 def callback_args(self):
2111 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002112 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002113 elif isinstance(self._passphrase, bytes):
2114 return self._passphrase
2115 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002116 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002117 else:
2118 raise TypeError("Last argument must be string or callable")
2119
2120
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002121 def raise_if_problem(self, exceptionType=Error):
2122 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002123 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002124 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002125 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002126 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002127 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002128 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002129
2130
2131 def _read_passphrase(self, buf, size, rwflag, userdata):
2132 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002133 if self._more_args:
2134 result = self._passphrase(size, rwflag, userdata)
2135 else:
2136 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002137 if not isinstance(result, bytes):
2138 raise ValueError("String expected")
2139 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002140 if self._truncate:
2141 result = result[:size]
2142 else:
2143 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002144 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002145 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002146 return len(result)
2147 except Exception as e:
2148 self._problems.append(e)
2149 return 0
2150
2151
2152
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002153def load_privatekey(type, buffer, passphrase=None):
2154 """
2155 Load a private key from a buffer
2156
2157 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2158 :param buffer: The buffer the key is stored in
2159 :param passphrase: (optional) if encrypted PEM format, this can be
2160 either the passphrase to use, or a callback for
2161 providing the passphrase.
2162
2163 :return: The PKey object
2164 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002165 if isinstance(buffer, _text_type):
2166 buffer = buffer.encode("ascii")
2167
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002168 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002169
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002170 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002171 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002172 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2173 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002174 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002175 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002176 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002177 else:
2178 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2179
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002180 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002181 _raise_current_error()
2182
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002183 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002184 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002185 return pkey
2186
2187
2188
2189def dump_certificate_request(type, req):
2190 """
2191 Dump a certificate request to a buffer
2192
2193 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2194 :param req: The certificate request to dump
2195 :return: The buffer with the dumped certificate request in
2196 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002197 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002198
2199 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002200 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002201 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002202 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002203 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002204 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002205 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002206 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002207
2208 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002209 # TODO: This is untested.
2210 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002211
2212 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002213
2214
2215
2216def load_certificate_request(type, buffer):
2217 """
2218 Load a certificate request from a buffer
2219
2220 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2221 :param buffer: The buffer the certificate request is stored in
2222 :return: The X509Req object
2223 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002224 if isinstance(buffer, _text_type):
2225 buffer = buffer.encode("ascii")
2226
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002227 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002228
2229 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002230 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002231 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002232 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002233 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002234 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002235
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002236 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002237 # TODO: This is untested.
2238 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002239
2240 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002241 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002242 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002243
2244
2245
2246def sign(pkey, data, digest):
2247 """
2248 Sign data with a digest
2249
2250 :param pkey: Pkey to sign with
2251 :param data: data to be signed
2252 :param digest: message digest to use
2253 :return: signature
2254 """
Jean-Paul Calderone6462b072015-03-29 07:03:11 -04002255 data = _warn_text("data", data)
Abraham Martine82326c2015-02-04 10:18:10 +00002256
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002257 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002258 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002259 raise ValueError("No such digest method")
2260
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002261 md_ctx = _ffi.new("EVP_MD_CTX*")
2262 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002263
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002264 _lib.EVP_SignInit(md_ctx, digest_obj)
2265 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002266
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002267 signature_buffer = _ffi.new("unsigned char[]", 512)
2268 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002269 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002270 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002271 md_ctx, signature_buffer, signature_length, pkey._pkey)
2272
2273 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002274 # TODO: This is untested.
2275 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002276
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002277 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002278
2279
2280
2281def verify(cert, signature, data, digest):
2282 """
2283 Verify a signature
2284
2285 :param cert: signing certificate (X509 object)
2286 :param signature: signature returned by sign function
2287 :param data: data to be verified
2288 :param digest: message digest to use
2289 :return: None if the signature is correct, raise exception otherwise
2290 """
Jean-Paul Calderone6462b072015-03-29 07:03:11 -04002291 data = _warn_text("data", data)
Abraham Martine82326c2015-02-04 10:18:10 +00002292
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002293 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002294 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002295 raise ValueError("No such digest method")
2296
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002297 pkey = _lib.X509_get_pubkey(cert._x509)
2298 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002299 # TODO: This is untested.
2300 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002301 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002302
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002303 md_ctx = _ffi.new("EVP_MD_CTX*")
2304 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002305
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002306 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2307 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2308 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002309
2310 if verify_result != 1:
2311 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002312
2313
2314
2315def load_crl(type, buffer):
2316 """
2317 Load a certificate revocation list from a buffer
2318
2319 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2320 :param buffer: The buffer the CRL is stored in
2321
2322 :return: The PKey object
2323 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002324 if isinstance(buffer, _text_type):
2325 buffer = buffer.encode("ascii")
2326
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002327 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002328
2329 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002330 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002331 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002332 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002333 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002334 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2335
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002336 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002337 _raise_current_error()
2338
2339 result = CRL.__new__(CRL)
2340 result._crl = crl
2341 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002342
2343
2344
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002345def load_pkcs7_data(type, buffer):
2346 """
2347 Load pkcs7 data from a buffer
2348
2349 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2350 :param buffer: The buffer with the pkcs7 data.
2351 :return: The PKCS7 object
2352 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002353 if isinstance(buffer, _text_type):
2354 buffer = buffer.encode("ascii")
2355
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002356 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002357
2358 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002359 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002360 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002361 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002362 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002363 # TODO: This is untested.
2364 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002365 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2366
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002367 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002368 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002369
2370 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002371 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002372 return pypkcs7
2373
2374
2375
Stephen Holsapple38482622014-04-05 20:29:34 -07002376def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002377 """
2378 Load a PKCS12 object from a buffer
2379
2380 :param buffer: The buffer the certificate is stored in
2381 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2382 :returns: The PKCS12 object
2383 """
Jean-Paul Calderone6462b072015-03-29 07:03:11 -04002384 passphrase = _warn_text("passphrase", passphrase)
Abraham Martine82326c2015-02-04 10:18:10 +00002385
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002386 if isinstance(buffer, _text_type):
2387 buffer = buffer.encode("ascii")
2388
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002389 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002390
Stephen Holsapple38482622014-04-05 20:29:34 -07002391 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2392 # password based encryption no password and a zero length password are two
2393 # different things, but OpenSSL implementation will try both to figure out
2394 # which one works.
2395 if not passphrase:
2396 passphrase = _ffi.NULL
2397
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002398 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2399 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002400 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002401 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002402
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002403 pkey = _ffi.new("EVP_PKEY**")
2404 cert = _ffi.new("X509**")
2405 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002406
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002407 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002408 if not parse_result:
2409 _raise_current_error()
2410
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002411 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002412
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002413 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2414 # queue for no particular reason. This error isn't interesting to anyone
2415 # outside this function. It's not even interesting to us. Get rid of it.
2416 try:
2417 _raise_current_error()
2418 except Error:
2419 pass
2420
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002421 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002422 pykey = None
2423 else:
2424 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002425 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002426
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002427 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002428 pycert = None
2429 friendlyname = None
2430 else:
2431 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002432 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002433
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002434 friendlyname_length = _ffi.new("int*")
2435 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2436 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2437 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002438 friendlyname = None
2439
2440 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002441 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002442 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002443 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002444 pycacerts.append(pycacert)
2445 if not pycacerts:
2446 pycacerts = None
2447
2448 pkcs12 = PKCS12.__new__(PKCS12)
2449 pkcs12._pkey = pykey
2450 pkcs12._cert = pycert
2451 pkcs12._cacerts = pycacerts
2452 pkcs12._friendlyname = friendlyname
2453 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002454
2455
2456def _initialize_openssl_threads(get_ident, Lock):
2457 import _ssl
2458 return
2459
2460 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2461
2462 def locking_function(mode, index, filename, line):
2463 if mode & _lib.CRYPTO_LOCK:
2464 locks[index].acquire()
2465 else:
2466 locks[index].release()
2467
2468 _lib.CRYPTO_set_id_callback(
2469 _ffi.callback("unsigned long (*)(void)", get_ident))
2470
2471 _lib.CRYPTO_set_locking_callback(
2472 _ffi.callback(
2473 "void (*)(int, int, const char*, int)", locking_function))
2474
2475
2476try:
2477 from thread import get_ident
2478 from threading import Lock
2479except ImportError:
2480 pass
2481else:
2482 _initialize_openssl_threads(get_ident, Lock)
2483 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002484
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002485# There are no direct unit tests for this initialization. It is tested
2486# indirectly since it is necessary for functions like dump_privatekey when
2487# using encryption.
2488#
2489# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2490# and some other similar tests may fail without this (though they may not if
2491# the Python runtime has already done some initialization of the underlying
2492# OpenSSL library (and is linked against the same one that cryptography is
2493# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002494_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002495
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002496# This is similar but exercised mainly by exception_from_error_queue. It calls
2497# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2498_lib.SSL_load_error_strings()