blob: 0909e6e28b79a6fddd0713bd3acdbb24c9b1dbda [file] [log] [blame]
Abraham Martin82efe3e2015-03-25 10:50:09 +00001from warnings import warn
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05003from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05004from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05005from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
6
7from six import (
8 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -04009 text_type as _text_type,
10 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080011
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050012from OpenSSL._util import (
13 ffi as _ffi,
14 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050015 exception_from_error_queue as _exception_from_error_queue,
16 byte_string as _byte_string,
17 native as _native)
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 """
Abraham Martine82326c2015-02-04 10:18:10 +00001956
1957 # Backward compatibility
1958 if isinstance(passphrase, _text_type):
Abraham Martin9778f412015-03-25 14:02:37 +00001959 if _PY3:
Abraham Martind2f0b072015-03-25 13:56:25 +00001960 warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martin9778f412015-03-25 14:02:37 +00001961 else:
1962 warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martine82326c2015-02-04 10:18:10 +00001963 passphrase = passphrase.encode('utf-8')
1964
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001965 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001966 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001967 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001968 cacerts = _lib.sk_X509_new_null()
1969 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001970 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001971 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001972
1973 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001974 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001975
1976 friendlyname = self._friendlyname
1977 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001978 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001979
1980 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001981 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001982 else:
1983 pkey = self._pkey._pkey
1984
1985 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001986 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001987 else:
1988 cert = self._cert._x509
1989
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001990 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001991 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001992 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1993 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001994 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001995 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001996 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001997 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001998
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001999 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002000 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002001 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002002
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002003PKCS12Type = PKCS12
2004
2005
2006
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002007class NetscapeSPKI(object):
2008 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002009 spki = _lib.NETSCAPE_SPKI_new()
2010 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002011
2012
2013 def sign(self, pkey, digest):
2014 """
2015 Sign the certificate request using the supplied key and digest
2016
2017 :param pkey: The key to sign with
2018 :param digest: The message digest to use
2019 :return: None
2020 """
2021 if pkey._only_public:
2022 raise ValueError("Key has only public part")
2023
2024 if not pkey._initialized:
2025 raise ValueError("Key is uninitialized")
2026
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002027 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002028 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002029 raise ValueError("No such digest method")
2030
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002031 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002032 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002033 # TODO: This is untested.
2034 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002035
2036
2037 def verify(self, key):
2038 """
2039 Verifies a certificate request using the supplied public key
2040
2041 :param key: a public key
2042 :return: True if the signature is correct.
2043 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2044 problem verifying the signature.
2045 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002046 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002047 if answer <= 0:
2048 _raise_current_error()
2049 return True
2050
2051
2052 def b64_encode(self):
2053 """
2054 Generate a base64 encoded string from an SPKI
2055
2056 :return: The base64 encoded string
2057 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002058 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2059 result = _ffi.string(encoded)
2060 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002061 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002062
2063
2064 def get_pubkey(self):
2065 """
2066 Get the public key of the certificate
2067
2068 :return: The public key
2069 """
2070 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002071 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2072 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002073 # TODO: This is untested.
2074 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002075 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002076 pkey._only_public = True
2077 return pkey
2078
2079
2080 def set_pubkey(self, pkey):
2081 """
2082 Set the public key of the certificate
2083
2084 :param pkey: The public key
2085 :return: None
2086 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002087 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002088 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002089 # TODO: This is untested.
2090 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002091NetscapeSPKIType = NetscapeSPKI
2092
2093
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002094class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002095 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002096 if type != FILETYPE_PEM and passphrase is not None:
2097 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002098 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002099 self._more_args = more_args
2100 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002101 self._problems = []
2102
2103
2104 @property
2105 def callback(self):
2106 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002107 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002108 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002109 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002110 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002111 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002112 else:
2113 raise TypeError("Last argument must be string or callable")
2114
2115
2116 @property
2117 def callback_args(self):
2118 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002119 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002120 elif isinstance(self._passphrase, bytes):
2121 return self._passphrase
2122 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002123 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002124 else:
2125 raise TypeError("Last argument must be string or callable")
2126
2127
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002128 def raise_if_problem(self, exceptionType=Error):
2129 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002130 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002131 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002132 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002133 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002134 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002135 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002136
2137
2138 def _read_passphrase(self, buf, size, rwflag, userdata):
2139 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002140 if self._more_args:
2141 result = self._passphrase(size, rwflag, userdata)
2142 else:
2143 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002144 if not isinstance(result, bytes):
2145 raise ValueError("String expected")
2146 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002147 if self._truncate:
2148 result = result[:size]
2149 else:
2150 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002151 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002152 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002153 return len(result)
2154 except Exception as e:
2155 self._problems.append(e)
2156 return 0
2157
2158
2159
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002160def load_privatekey(type, buffer, passphrase=None):
2161 """
2162 Load a private key from a buffer
2163
2164 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2165 :param buffer: The buffer the key is stored in
2166 :param passphrase: (optional) if encrypted PEM format, this can be
2167 either the passphrase to use, or a callback for
2168 providing the passphrase.
2169
2170 :return: The PKey object
2171 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002172 if isinstance(buffer, _text_type):
2173 buffer = buffer.encode("ascii")
2174
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002175 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002176
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002177 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002178 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002179 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2180 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002181 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002182 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002183 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002184 else:
2185 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2186
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002187 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002188 _raise_current_error()
2189
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002190 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002191 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002192 return pkey
2193
2194
2195
2196def dump_certificate_request(type, req):
2197 """
2198 Dump a certificate request to a buffer
2199
2200 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2201 :param req: The certificate request to dump
2202 :return: The buffer with the dumped certificate request in
2203 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002204 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002205
2206 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002207 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002208 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002209 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002210 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002211 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002212 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002213 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002214
2215 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002216 # TODO: This is untested.
2217 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002218
2219 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002220
2221
2222
2223def load_certificate_request(type, buffer):
2224 """
2225 Load a certificate request from a buffer
2226
2227 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2228 :param buffer: The buffer the certificate request is stored in
2229 :return: The X509Req object
2230 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002231 if isinstance(buffer, _text_type):
2232 buffer = buffer.encode("ascii")
2233
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002234 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002235
2236 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002237 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002238 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002239 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002240 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002241 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002242
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002243 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002244 # TODO: This is untested.
2245 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002246
2247 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002248 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002249 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002250
2251
2252
2253def sign(pkey, data, digest):
2254 """
2255 Sign data with a digest
2256
2257 :param pkey: Pkey to sign with
2258 :param data: data to be signed
2259 :param digest: message digest to use
2260 :return: signature
2261 """
Abraham Martine82326c2015-02-04 10:18:10 +00002262
2263 # Backward compatibility
2264 if isinstance(data, _text_type):
Abraham Martin9778f412015-03-25 14:02:37 +00002265 if _PY3:
Abraham Martind2f0b072015-03-25 13:56:25 +00002266 warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martin9778f412015-03-25 14:02:37 +00002267 else:
2268 warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martine82326c2015-02-04 10:18:10 +00002269 data = data.encode('utf-8')
2270
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002271 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002272 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002273 raise ValueError("No such digest method")
2274
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002275 md_ctx = _ffi.new("EVP_MD_CTX*")
2276 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002277
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002278 _lib.EVP_SignInit(md_ctx, digest_obj)
2279 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002280
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002281 signature_buffer = _ffi.new("unsigned char[]", 512)
2282 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002283 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002284 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002285 md_ctx, signature_buffer, signature_length, pkey._pkey)
2286
2287 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002288 # TODO: This is untested.
2289 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002290
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002291 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002292
2293
2294
2295def verify(cert, signature, data, digest):
2296 """
2297 Verify a signature
2298
2299 :param cert: signing certificate (X509 object)
2300 :param signature: signature returned by sign function
2301 :param data: data to be verified
2302 :param digest: message digest to use
2303 :return: None if the signature is correct, raise exception otherwise
2304 """
Abraham Martine82326c2015-02-04 10:18:10 +00002305
2306 # Backward compatibility
2307 if isinstance(data, _text_type):
Abraham Martin9778f412015-03-25 14:02:37 +00002308 if _PY3:
Abraham Martind2f0b072015-03-25 13:56:25 +00002309 warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martin9778f412015-03-25 14:02:37 +00002310 else:
2311 warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martine82326c2015-02-04 10:18:10 +00002312 data = data.encode('utf-8')
2313
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002314 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002315 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002316 raise ValueError("No such digest method")
2317
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002318 pkey = _lib.X509_get_pubkey(cert._x509)
2319 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002320 # TODO: This is untested.
2321 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002322 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002323
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002324 md_ctx = _ffi.new("EVP_MD_CTX*")
2325 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002326
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002327 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2328 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2329 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002330
2331 if verify_result != 1:
2332 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002333
2334
2335
2336def load_crl(type, buffer):
2337 """
2338 Load a certificate revocation list from a buffer
2339
2340 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2341 :param buffer: The buffer the CRL is stored in
2342
2343 :return: The PKey object
2344 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002345 if isinstance(buffer, _text_type):
2346 buffer = buffer.encode("ascii")
2347
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002348 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002349
2350 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002351 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002352 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002353 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002354 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002355 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2356
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002357 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002358 _raise_current_error()
2359
2360 result = CRL.__new__(CRL)
2361 result._crl = crl
2362 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002363
2364
2365
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002366def load_pkcs7_data(type, buffer):
2367 """
2368 Load pkcs7 data from a buffer
2369
2370 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2371 :param buffer: The buffer with the pkcs7 data.
2372 :return: The PKCS7 object
2373 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002374 if isinstance(buffer, _text_type):
2375 buffer = buffer.encode("ascii")
2376
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002377 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002378
2379 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002380 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002381 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002382 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002383 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002384 # TODO: This is untested.
2385 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002386 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2387
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002388 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002389 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002390
2391 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002392 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002393 return pypkcs7
2394
2395
2396
Stephen Holsapple38482622014-04-05 20:29:34 -07002397def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002398 """
2399 Load a PKCS12 object from a buffer
2400
2401 :param buffer: The buffer the certificate is stored in
2402 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2403 :returns: The PKCS12 object
2404 """
Abraham Martine82326c2015-02-04 10:18:10 +00002405
2406 # Backward compatibility
2407 if isinstance(passphrase, _text_type):
Abraham Martin9778f412015-03-25 14:02:37 +00002408 if _PY3:
Abraham Martind2f0b072015-03-25 13:56:25 +00002409 warn("str in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martin9778f412015-03-25 14:02:37 +00002410 else:
2411 warn("unicode in passphrase is no longer accepted, use bytes", DeprecationWarning)
Abraham Martine82326c2015-02-04 10:18:10 +00002412 passphrase = passphrase.encode('utf-8')
2413
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002414 if isinstance(buffer, _text_type):
2415 buffer = buffer.encode("ascii")
2416
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002417 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002418
Stephen Holsapple38482622014-04-05 20:29:34 -07002419 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2420 # password based encryption no password and a zero length password are two
2421 # different things, but OpenSSL implementation will try both to figure out
2422 # which one works.
2423 if not passphrase:
2424 passphrase = _ffi.NULL
2425
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002426 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2427 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002428 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002429 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002430
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002431 pkey = _ffi.new("EVP_PKEY**")
2432 cert = _ffi.new("X509**")
2433 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002434
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002435 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002436 if not parse_result:
2437 _raise_current_error()
2438
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002439 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002440
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002441 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2442 # queue for no particular reason. This error isn't interesting to anyone
2443 # outside this function. It's not even interesting to us. Get rid of it.
2444 try:
2445 _raise_current_error()
2446 except Error:
2447 pass
2448
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002449 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002450 pykey = None
2451 else:
2452 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002453 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002454
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002455 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002456 pycert = None
2457 friendlyname = None
2458 else:
2459 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002460 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002461
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002462 friendlyname_length = _ffi.new("int*")
2463 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2464 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2465 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002466 friendlyname = None
2467
2468 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002469 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002470 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002471 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002472 pycacerts.append(pycacert)
2473 if not pycacerts:
2474 pycacerts = None
2475
2476 pkcs12 = PKCS12.__new__(PKCS12)
2477 pkcs12._pkey = pykey
2478 pkcs12._cert = pycert
2479 pkcs12._cacerts = pycacerts
2480 pkcs12._friendlyname = friendlyname
2481 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002482
2483
2484def _initialize_openssl_threads(get_ident, Lock):
2485 import _ssl
2486 return
2487
2488 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2489
2490 def locking_function(mode, index, filename, line):
2491 if mode & _lib.CRYPTO_LOCK:
2492 locks[index].acquire()
2493 else:
2494 locks[index].release()
2495
2496 _lib.CRYPTO_set_id_callback(
2497 _ffi.callback("unsigned long (*)(void)", get_ident))
2498
2499 _lib.CRYPTO_set_locking_callback(
2500 _ffi.callback(
2501 "void (*)(int, int, const char*, int)", locking_function))
2502
2503
2504try:
2505 from thread import get_ident
2506 from threading import Lock
2507except ImportError:
2508 pass
2509else:
2510 _initialize_openssl_threads(get_ident, Lock)
2511 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002512
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002513# There are no direct unit tests for this initialization. It is tested
2514# indirectly since it is necessary for functions like dump_privatekey when
2515# using encryption.
2516#
2517# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2518# and some other similar tests may fail without this (though they may not if
2519# the Python runtime has already done some initialization of the underlying
2520# OpenSSL library (and is linked against the same one that cryptography is
2521# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002522_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002523
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002524# This is similar but exercised mainly by exception_from_error_queue. It calls
2525# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2526_lib.SSL_load_error_strings()