blob: b75fa8b1af9ff9b6e2a5a7130ef66faf7364cd4f [file] [log] [blame]
Paul Kehrer5d5d28d2015-10-21 18:55:22 -05001import datetime
2from time import mktime, 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__
Jean-Paul Calderone60432792015-04-13 12:26:07 -04006from warnings import warn as _warn
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05007
8from six import (
9 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -040010 text_type as _text_type,
11 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080012
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050013from OpenSSL._util import (
14 ffi as _ffi,
15 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050016 exception_from_error_queue as _exception_from_error_queue,
17 byte_string as _byte_string,
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -040018 native as _native,
19 UNSPECIFIED as _UNSPECIFIED,
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -040020 text_to_bytes_and_warn as _text_to_bytes_and_warn,
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -040021)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080022
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050023FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
24FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080025
26# TODO This was an API mistake. OpenSSL has no such constant.
27FILETYPE_TEXT = 2 ** 16 - 1
28
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050029TYPE_RSA = _lib.EVP_PKEY_RSA
30TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080031
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080032
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050033class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050034 """
35 An error occurred in an `OpenSSL.crypto` API.
36 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050037
38
39_raise_current_error = partial(_exception_from_error_queue, Error)
40
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070041
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050042def _untested_error(where):
43 """
44 An OpenSSL API failed somehow. Additionally, the failure which was
45 encountered isn't one that's exercised by the test suite so future behavior
46 of pyOpenSSL is now somewhat less predictable.
47 """
48 raise RuntimeError("Unknown %s failure" % (where,))
49
50
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050051def _new_mem_buf(buffer=None):
52 """
53 Allocate a new OpenSSL memory BIO.
54
55 Arrange for the garbage collector to clean it up automatically.
56
57 :param buffer: None or some bytes to use to put into the BIO so that they
58 can be read out.
59 """
60 if buffer is None:
61 bio = _lib.BIO_new(_lib.BIO_s_mem())
62 free = _lib.BIO_free
63 else:
64 data = _ffi.new("char[]", buffer)
65 bio = _lib.BIO_new_mem_buf(data, len(buffer))
Alex Gaynor5945ea82015-09-05 14:59:06 -040066
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050067 # Keep the memory alive as long as the bio is alive!
68 def free(bio, ref=data):
69 return _lib.BIO_free(bio)
70
71 if bio == _ffi.NULL:
72 # TODO: This is untested.
73 _raise_current_error()
74
75 bio = _ffi.gc(bio, free)
76 return bio
77
78
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080079def _bio_to_string(bio):
80 """
81 Copy the contents of an OpenSSL BIO object into a Python byte string.
82 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050083 result_buffer = _ffi.new('char**')
84 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
85 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080086
87
Jean-Paul Calderone57122982013-02-21 08:47:05 -080088def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050089 """
90 The the time value of an ASN1 time object.
91
92 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
93 castable to that type) which will have its value set.
94 @param when: A string representation of the desired time value.
95
96 @raise TypeError: If C{when} is not a L{bytes} string.
97 @raise ValueError: If C{when} does not represent a time in the required
98 format.
99 @raise RuntimeError: If the time value cannot be set for some other
100 (unspecified) reason.
101 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800102 if not isinstance(when, bytes):
103 raise TypeError("when must be a byte string")
104
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500105 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
106 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800107 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500108 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
109 _lib.ASN1_STRING_set(dummy, when, len(when))
110 check_result = _lib.ASN1_GENERALIZEDTIME_check(
111 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800112 if not check_result:
113 raise ValueError("Invalid string")
114 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500115 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800116
117
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800118def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500119 """
120 Retrieve the time value of an ASN1 time object.
121
122 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
123 that type) from which the time value will be retrieved.
124
125 @return: The time value from C{timestamp} as a L{bytes} string in a certain
126 format. Or C{None} if the object contains no time value.
127 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500128 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
129 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800130 return None
Alex Gaynor5945ea82015-09-05 14:59:06 -0400131 elif (
132 _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME
133 ):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500134 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800135 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500136 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
137 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
138 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500139 # This may happen:
140 # - if timestamp was not an ASN1_TIME
141 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
142 # - if a copy of the time data from timestamp cannot be made for
143 # the newly allocated ASN1_GENERALIZEDTIME
144 #
145 # These are difficult to test. cffi enforces the ASN1_TIME type.
146 # Memory allocation failures are a pain to trigger
147 # deterministically.
148 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800149 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500150 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800151 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500152 string_data = _lib.ASN1_STRING_data(string_timestamp)
153 string_result = _ffi.string(string_data)
154 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800155 return string_result
156
157
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800158class PKey(object):
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200159 """
160 A class representing an DSA or RSA public key or key pair.
161 """
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800162 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800163 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800164
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800165 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500166 pkey = _lib.EVP_PKEY_new()
167 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800168 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800169
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800170 def generate_key(self, type, bits):
171 """
Laurens Van Houtven90c09142015-04-23 10:52:49 -0700172 Generate a key pair of the given type, with the given number of bits.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800173
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200174 This generates a key "into" the this object.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800175
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200176 :param type: The key type.
177 :type type: :py:data:`TYPE_RSA` or :py:data:`TYPE_DSA`
178 :param bits: The number of bits.
179 :type bits: :py:data:`int` ``>= 0``
180 :raises TypeError: If :py:data:`type` or :py:data:`bits` isn't
181 of the appropriate type.
182 :raises ValueError: If the number of bits isn't an integer of
183 the appropriate size.
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200184 :return: :py:const:`None`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800185 """
186 if not isinstance(type, int):
187 raise TypeError("type must be an integer")
188
189 if not isinstance(bits, int):
190 raise TypeError("bits must be an integer")
191
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800192 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500193 exponent = _lib.BN_new()
194 exponent = _ffi.gc(exponent, _lib.BN_free)
195 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800196
197 if type == TYPE_RSA:
198 if bits <= 0:
199 raise ValueError("Invalid number of bits")
200
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500201 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800202
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500203 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500204 if result == 0:
205 # TODO: The test for this case is commented out. Different
206 # builds of OpenSSL appear to have different failure modes that
207 # make it hard to test. Visual inspection of the OpenSSL
208 # source reveals that a return value of 0 signals an error.
209 # Manual testing on a particular build of OpenSSL suggests that
210 # this is probably the appropriate way to handle those errors.
211 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800212
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500213 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800214 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500215 # TODO: It appears as though this can fail if an engine is in
216 # use which does not support RSA.
217 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800218
219 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500220 dsa = _lib.DSA_generate_parameters(
221 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
222 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500223 # TODO: This is untested.
224 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500225 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500226 # TODO: This is untested.
227 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500228 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500229 # TODO: This is untested.
230 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800231 else:
232 raise Error("No such key type")
233
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800234 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800235
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800236 def check(self):
237 """
238 Check the consistency of an RSA private key.
239
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200240 This is the Python equivalent of OpenSSL's ``RSA_check_key``.
241
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800242 :return: True if key is consistent.
243 :raise Error: if the key is inconsistent.
244 :raise TypeError: if the key is of a type which cannot be checked.
245 Only RSA keys can currently be checked.
246 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800247 if self._only_public:
248 raise TypeError("public key only")
249
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500250 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800251 raise TypeError("key type unsupported")
252
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500253 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
254 rsa = _ffi.gc(rsa, _lib.RSA_free)
255 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800256 if result:
257 return True
258 _raise_current_error()
259
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800260 def type(self):
261 """
262 Returns the type of the key
263
264 :return: The type of the key.
265 """
266 return self._pkey.type
267
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800268 def bits(self):
269 """
270 Returns the number of bits of the key
271
272 :return: The number of bits of the key.
273 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500274 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800275PKeyType = PKey
276
277
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400278class _EllipticCurve(object):
279 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400280 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400281
282 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
283 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
284 instances each of which represents one curve supported by the system.
285 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400286 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400287 _curves = None
288
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400289 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400290 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400291 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400292 """
293 Implement cooperation with the right-hand side argument of ``!=``.
294
295 Python 3 seems to have dropped this cooperation in this very narrow
296 circumstance.
297 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400298 if isinstance(other, _EllipticCurve):
299 return super(_EllipticCurve, self).__ne__(other)
300 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400301
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400302 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400303 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400304 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400305 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400306
307 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400308
309 :return: A :py:type:`set` of ``cls`` instances giving the names of the
310 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400311 """
312 if lib.Cryptography_HAS_EC:
313 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
314 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
Alex Gaynor5945ea82015-09-05 14:59:06 -0400315 # The return value on this call should be num_curves again. We
316 # could check it to make sure but if it *isn't* then.. what could
317 # we do? Abort the whole process, I suppose...? -exarkun
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400318 lib.EC_get_builtin_curves(builtin_curves, num_curves)
319 return set(
320 cls.from_nid(lib, c.nid)
321 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400322 return set()
323
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400324 @classmethod
325 def _get_elliptic_curves(cls, lib):
326 """
327 Get, cache, and return the curves supported by OpenSSL.
328
329 :param lib: The OpenSSL library binding object.
330
331 :return: A :py:type:`set` of ``cls`` instances giving the names of the
332 elliptic curves the underlying library supports.
333 """
334 if cls._curves is None:
335 cls._curves = cls._load_elliptic_curves(lib)
336 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400337
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400338 @classmethod
339 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400340 """
341 Instantiate a new :py:class:`_EllipticCurve` associated with the given
342 OpenSSL NID.
343
344 :param lib: The OpenSSL library binding object.
345
346 :param nid: The OpenSSL NID the resulting curve object will represent.
347 This must be a curve NID (and not, for example, a hash NID) or
348 subsequent operations will fail in unpredictable ways.
349 :type nid: :py:class:`int`
350
351 :return: The curve object.
352 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400353 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
354
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400355 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400356 """
357 :param _lib: The :py:mod:`cryptography` binding instance used to
358 interface with OpenSSL.
359
360 :param _nid: The OpenSSL NID identifying the curve this object
361 represents.
362 :type _nid: :py:class:`int`
363
364 :param name: The OpenSSL short name identifying the curve this object
365 represents.
366 :type name: :py:class:`unicode`
367 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400368 self._lib = lib
369 self._nid = nid
370 self.name = name
371
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400372 def __repr__(self):
373 return "<Curve %r>" % (self.name,)
374
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400375 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400376 """
377 Create a new OpenSSL EC_KEY structure initialized to use this curve.
378
379 The structure is automatically garbage collected when the Python object
380 is garbage collected.
381 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400382 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
383 return _ffi.gc(key, _lib.EC_KEY_free)
384
385
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400386def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400387 """
388 Return a set of objects representing the elliptic curves supported in the
389 OpenSSL build in use.
390
391 The curve objects have a :py:class:`unicode` ``name`` attribute by which
392 they identify themselves.
393
394 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400395 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
396 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400397 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400398 return _EllipticCurve._get_elliptic_curves(_lib)
399
400
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400401def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400402 """
403 Return a single curve object selected by name.
404
405 See :py:func:`get_elliptic_curves` for information about curve objects.
406
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400407 :param name: The OpenSSL short name identifying the curve object to
408 retrieve.
409 :type name: :py:class:`unicode`
410
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400411 If the named curve is not supported then :py:class:`ValueError` is raised.
412 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400413 for curve in get_elliptic_curves():
414 if curve.name == name:
415 return curve
416 raise ValueError("unknown curve name", name)
417
418
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800419class X509Name(object):
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200420 """
421 An X.509 Distinguished Name.
422
423 :ivar countryName: The country of the entity.
424 :ivar C: Alias for :py:attr:`countryName`.
425
426 :ivar stateOrProvinceName: The state or province of the entity.
427 :ivar ST: Alias for :py:attr:`stateOrProvinceName`.
428
429 :ivar localityName: The locality of the entity.
430 :ivar L: Alias for :py:attr:`localityName`.
431
432 :ivar organizationName: The organization name of the entity.
433 :ivar O: Alias for :py:attr:`organizationName`.
434
435 :ivar organizationalUnitName: The organizational unit of the entity.
436 :ivar OU: Alias for :py:attr:`organizationalUnitName`
437
438 :ivar commonName: The common name of the entity.
439 :ivar CN: Alias for :py:attr:`commonName`.
440
441 :ivar emailAddress: The e-mail address of the entity.
442 """
Alex Gaynor5945ea82015-09-05 14:59:06 -0400443
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800444 def __init__(self, name):
445 """
446 Create a new X509Name, copying the given X509Name instance.
447
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200448 :param name: The name to copy.
449 :type name: :py:class:`X509Name`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800450 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500451 name = _lib.X509_NAME_dup(name._name)
452 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800453
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800454 def __setattr__(self, name, value):
455 if name.startswith('_'):
456 return super(X509Name, self).__setattr__(name, value)
457
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800458 # Note: we really do not want str subclasses here, so we do not use
459 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800460 if type(name) is not str:
461 raise TypeError("attribute name must be string, not '%.200s'" % (
Alex Gaynora738ed52015-09-05 11:17:10 -0400462 type(value).__name__,))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800463
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500464 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500465 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800466 try:
467 _raise_current_error()
468 except Error:
469 pass
470 raise AttributeError("No such attribute")
471
472 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500473 for i in range(_lib.X509_NAME_entry_count(self._name)):
474 ent = _lib.X509_NAME_get_entry(self._name, i)
475 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
476 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800477 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500478 ent = _lib.X509_NAME_delete_entry(self._name, i)
479 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800480 break
481
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500482 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800483 value = value.encode('utf-8')
484
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500485 add_result = _lib.X509_NAME_add_entry_by_NID(
486 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800487 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500488 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800489
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800490 def __getattr__(self, name):
491 """
492 Find attribute. An X509Name object has the following attributes:
493 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
Alex Gaynor5945ea82015-09-05 14:59:06 -0400494 organization (alias O), organizationalUnit (alias OU), commonName
495 (alias CN) and more...
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800496 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500497 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500498 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800499 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
500 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
501 # push something onto the error queue. If we don't clean that up
502 # now, someone else will bump into it later and be quite confused.
503 # See lp#314814.
504 try:
505 _raise_current_error()
506 except Error:
507 pass
508 return super(X509Name, self).__getattr__(name)
509
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500510 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800511 if entry_index == -1:
512 return None
513
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500514 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
515 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800516
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500517 result_buffer = _ffi.new("unsigned char**")
518 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800519 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500520 # TODO: This is untested.
521 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700523 try:
Alex Gaynor5945ea82015-09-05 14:59:06 -0400524 result = _ffi.buffer(
525 result_buffer[0], data_length
526 )[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700527 finally:
528 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500529 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800530 return result
531
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500532 def _cmp(op):
533 def f(self, other):
534 if not isinstance(other, X509Name):
535 return NotImplemented
536 result = _lib.X509_NAME_cmp(self._name, other._name)
537 return op(result, 0)
538 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800539
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500540 __eq__ = _cmp(__eq__)
541 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800542
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500543 __lt__ = _cmp(__lt__)
544 __le__ = _cmp(__le__)
545
546 __gt__ = _cmp(__gt__)
547 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800548
549 def __repr__(self):
550 """
551 String representation of an X509Name
552 """
Alex Gaynor962ac212015-09-04 08:06:42 -0400553 result_buffer = _ffi.new("char[]", 512)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500554 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800555 self._name, result_buffer, len(result_buffer))
556
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500557 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500558 # TODO: This is untested.
559 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800560
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500561 return "<X509Name object '%s'>" % (
562 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800563
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800564 def hash(self):
565 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200566 Return an integer representation of the first four bytes of the
567 MD5 digest of the DER representation of the name.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800568
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200569 This is the Python equivalent of OpenSSL's ``X509_NAME_hash``.
570
571 :return: The (integer) hash of this name.
572 :rtype: :py:class:`int`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800573 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500574 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800575
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800576 def der(self):
577 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200578 Return the DER encoding of this name.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800579
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200580 :return: The DER encoded form of this name.
581 :rtype: :py:class:`bytes`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800582 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500583 result_buffer = _ffi.new('unsigned char**')
584 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800585 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500586 # TODO: This is untested.
587 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800588
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500589 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
590 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800591 return string_result
592
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800593 def get_components(self):
594 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200595 Returns the components of this name, as a sequence of 2-tuples.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800596
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200597 :return: The components of this name.
598 :rtype: :py:class:`list` of ``name, value`` tuples.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800599 """
600 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500601 for i in range(_lib.X509_NAME_entry_count(self._name)):
602 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800603
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500604 fname = _lib.X509_NAME_ENTRY_get_object(ent)
605 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800606
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500607 nid = _lib.OBJ_obj2nid(fname)
608 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800609
610 result.append((
Alex Gaynora738ed52015-09-05 11:17:10 -0400611 _ffi.string(name),
612 _ffi.string(
613 _lib.ASN1_STRING_data(fval),
614 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800615
616 return result
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200617
618
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800619X509NameType = X509Name
620
621
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800622class X509Extension(object):
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200623 """
624 An X.509 v3 certificate extension.
625 """
Alex Gaynor5945ea82015-09-05 14:59:06 -0400626
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800627 def __init__(self, type_name, critical, value, subject=None, issuer=None):
628 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200629 Initializes an X509 extension.
630
Alex Gaynor6f719912015-09-20 09:21:29 -0400631 :param type_name: The name of the type of extension to create. See
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200632 http://openssl.org/docs/apps/x509v3_config.html#STANDARD_EXTENSIONS
Alex Gaynor6f719912015-09-20 09:21:29 -0400633 :type type_name: :py:data:`bytes`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800634
Alex Gaynor5945ea82015-09-05 14:59:06 -0400635 :param bool critical: A flag indicating whether this is a critical
636 extension.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800637
638 :param value: The value of the extension.
Maximilian Hils0de43752015-09-18 15:26:54 +0200639 :type value: :py:data:`bytes`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800640
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200641 :param subject: Optional X509 certificate to use as subject.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800642 :type subject: :py:class:`X509`
643
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200644 :param issuer: Optional X509 certificate to use as issuer.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800645 :type issuer: :py:class:`X509`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800646 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500647 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800648
Alex Gaynor5945ea82015-09-05 14:59:06 -0400649 # A context is necessary for any extension which uses the r2i
650 # conversion method. That is, X509V3_EXT_nconf may segfault if passed
651 # a NULL ctx. Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500652 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800653
654 # We have no configuration database - but perhaps we should (some
655 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500656 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800657
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800658 # Initialize the subject and issuer, if appropriate. ctx is a local,
659 # and as far as I can tell none of the X509V3_* APIs invoked here steal
Alex Gaynora738ed52015-09-05 11:17:10 -0400660 # any references, so no need to mess with reference counts or
661 # duplicates.
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800662 if issuer is not None:
663 if not isinstance(issuer, X509):
664 raise TypeError("issuer must be an X509 instance")
665 ctx.issuer_cert = issuer._x509
666 if subject is not None:
667 if not isinstance(subject, X509):
668 raise TypeError("subject must be an X509 instance")
669 ctx.subject_cert = subject._x509
670
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800671 if critical:
672 # There are other OpenSSL APIs which would let us pass in critical
673 # separately, but they're harder to use, and since value is already
674 # a pile of crappy junk smuggling a ton of utterly important
675 # structured data, what's the point of trying to avoid nasty stuff
Alex Gaynor5945ea82015-09-05 14:59:06 -0400676 # with strings? (However, X509V3_EXT_i2d in particular seems like
677 # it would be a better API to invoke. I do not know where to get
678 # the ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500679 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800680
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500681 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
682 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800683 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500684 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800685
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400686 @property
687 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500688 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400689
690 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500691 _lib.GEN_EMAIL: "email",
692 _lib.GEN_DNS: "DNS",
693 _lib.GEN_URI: "URI",
Alex Gaynora738ed52015-09-05 11:17:10 -0400694 }
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400695
696 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500697 method = _lib.X509V3_EXT_get(self._extension)
698 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500699 # TODO: This is untested.
700 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400701 payload = self._extension.value.data
702 length = self._extension.value.length
703
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500704 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400705 payloadptr[0] = payload
706
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500707 if method.it != _ffi.NULL:
708 ptr = _lib.ASN1_ITEM_ptr(method.it)
709 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
710 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400711 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500712 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400713 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500714 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400715
Paul Kehrerb7d79502015-05-04 07:43:51 -0500716 names = _ffi.gc(names, _lib.GENERAL_NAMES_free)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400717 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500718 for i in range(_lib.sk_GENERAL_NAME_num(names)):
719 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400720 try:
721 label = self._prefixes[name.type]
722 except KeyError:
723 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500724 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500725 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400726 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500727 value = _native(
728 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
729 parts.append(label + ":" + value)
730 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400731
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800732 def __str__(self):
733 """
734 :return: a nice text representation of the extension
735 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500736 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400737 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800738
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400739 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500740 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800741 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500742 # TODO: This is untested.
743 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800744
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500745 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800746
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800747 def get_critical(self):
748 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200749 Returns the critical field of this X.509 extension.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800750
751 :return: The critical field.
752 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500753 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800754
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800755 def get_short_name(self):
756 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200757 Returns the short type name of this X.509 extension.
758
759 The result is a byte string such as :py:const:`b"basicConstraints"`.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800760
761 :return: The short type name.
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200762 :rtype: :py:data:`bytes`
763
764 .. versionadded:: 0.12
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800765 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500766 obj = _lib.X509_EXTENSION_get_object(self._extension)
767 nid = _lib.OBJ_obj2nid(obj)
768 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800769
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800770 def get_data(self):
771 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200772 Returns the data of the X509 extension, encoded as ASN.1.
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800773
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200774 :return: The ASN.1 encoded data of this X509 extension.
775 :rtype: :py:data:`bytes`
776
777 .. versionadded:: 0.12
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800778 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500779 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
780 string_result = _ffi.cast('ASN1_STRING*', octet_result)
781 char_result = _lib.ASN1_STRING_data(string_result)
782 result_length = _lib.ASN1_STRING_length(string_result)
783 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800784
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200785
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800786X509ExtensionType = X509Extension
787
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800788
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800789class X509Req(object):
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200790 """
791 An X.509 certificate signing requests.
792 """
Alex Gaynora738ed52015-09-05 11:17:10 -0400793
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800794 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500795 req = _lib.X509_REQ_new()
796 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800797
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800798 def set_pubkey(self, pkey):
799 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200800 Set the public key of the certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800801
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200802 :param pkey: The public key to use.
803 :type pkey: :py:class:`PKey`
804
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200805 :return: :py:const:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800806 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500807 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800808 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500809 # TODO: This is untested.
810 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800811
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800812 def get_pubkey(self):
813 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200814 Get the public key of the certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800815
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200816 :return: The public key.
817 :rtype: :py:class:`PKey`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800818 """
819 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500820 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
821 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500822 # TODO: This is untested.
823 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500824 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800825 pkey._only_public = True
826 return pkey
827
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800828 def set_version(self, version):
829 """
830 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
831 request.
832
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200833 :param int version: The version number.
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200834 :return: :py:const:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800835 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500836 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800837 if not set_result:
838 _raise_current_error()
839
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800840 def get_version(self):
841 """
842 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
843 request.
844
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200845 :return: The value of the version subfield.
846 :rtype: :py:class:`int`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800847 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500848 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800849
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800850 def get_subject(self):
851 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200852 Return the subject of this certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800853
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200854 This creates a new :py:class:`X509Name`: modifying it does not affect
855 this request.
856
857 :return: The subject of this certificate signing request.
858 :rtype: :py:class:`X509Name`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800859 """
860 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500861 name._name = _lib.X509_REQ_get_subject_name(self._req)
862 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500863 # TODO: This is untested.
864 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800865
866 # The name is owned by the X509Req structure. As long as the X509Name
867 # Python object is alive, keep the X509Req Python object alive.
868 name._owner = self
869
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800870 return name
871
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800872 def add_extensions(self, extensions):
873 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200874 Add extensions to the certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800875
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200876 :param extensions: The X.509 extensions to add.
877 :type extensions: iterable of :py:class:`X509Extension`
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200878 :return: :py:const:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800879 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500880 stack = _lib.sk_X509_EXTENSION_new_null()
881 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500882 # TODO: This is untested.
883 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800884
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500885 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800886
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800887 for ext in extensions:
888 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800889 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800890
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800891 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500892 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800893
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500894 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800895 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500896 # TODO: This is untested.
897 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800898
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800899 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800900 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200901 Get X.509 extensions in the certificate signing request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800902
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200903 :return: The X.509 extensions in this request.
904 :rtype: :py:class:`list` of :py:class:`X509Extension` objects.
905
906 .. versionadded:: 0.15
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800907 """
908 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500909 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500910 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800911 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500912 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800913 exts.append(ext)
914 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800915
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800916 def sign(self, pkey, digest):
917 """
Laurens Van Houtven6f2e4262015-04-23 10:48:32 -0700918 Sign the certificate signing request with this key and digest type.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800919
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200920 :param pkey: The key pair to sign with.
921 :type pkey: :py:class:`PKey`
922 :param digest: The name of the message digest to use for the signature,
923 e.g. :py:data:`b"sha1"`.
924 :type digest: :py:class:`bytes`
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200925 :return: :py:const:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800926 """
927 if pkey._only_public:
928 raise ValueError("Key has only public part")
929
930 if not pkey._initialized:
931 raise ValueError("Key is uninitialized")
932
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500933 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500934 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800935 raise ValueError("No such digest method")
936
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500937 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800938 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500939 # TODO: This is untested.
940 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800941
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800942 def verify(self, pkey):
943 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200944 Verifies the signature on this certificate signing request.
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800945
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200946 :param key: A public key.
947 :type key: :py:class:`PKey`
948 :return: :py:data:`True` if the signature is correct.
949 :rtype: :py:class:`bool`
950 :raises Error: If the signature is invalid or there is a
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800951 problem verifying the signature.
952 """
953 if not isinstance(pkey, PKey):
954 raise TypeError("pkey must be a PKey instance")
955
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500956 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800957 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500958 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800959
960 return result
961
962
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800963X509ReqType = X509Req
964
965
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800966class X509(object):
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200967 """
968 An X.509 certificate.
969 """
Alex Gaynora738ed52015-09-05 11:17:10 -0400970
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800971 def __init__(self):
972 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500973 x509 = _lib.X509_new()
974 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800975
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800976 def set_version(self, version):
977 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200978 Set the version number of the certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800979
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200980 :param version: The version number of the certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800981 :type version: :py:class:`int`
982
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200983 :return: :py:const:`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800984 """
985 if not isinstance(version, int):
986 raise TypeError("version must be an integer")
987
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500988 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800989
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800990 def get_version(self):
991 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200992 Return the version number of the certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800993
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200994 :return: The version number of the certificate.
995 :rtype: :py:class:`int`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800996 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500997 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800998
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800999 def get_pubkey(self):
1000 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001001 Get the public key of the certificate.
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001002
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001003 :return: The public key.
1004 :rtype: :py:class:`PKey`
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001005 """
1006 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001007 pkey._pkey = _lib.X509_get_pubkey(self._x509)
1008 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001009 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001010 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001011 pkey._only_public = True
1012 return pkey
1013
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001014 def set_pubkey(self, pkey):
1015 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001016 Set the public key of the certificate.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001017
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001018 :param pkey: The public key.
1019 :type pkey: :py:class:`PKey`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001020
Laurens Van Houtven33fcf122015-04-23 10:50:08 -07001021 :return: :py:data:`None`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001022 """
1023 if not isinstance(pkey, PKey):
1024 raise TypeError("pkey must be a PKey instance")
1025
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001026 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001027 if not set_result:
1028 _raise_current_error()
1029
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001030 def sign(self, pkey, digest):
1031 """
Laurens Van Houtven6f2e4262015-04-23 10:48:32 -07001032 Sign the certificate with this key and digest type.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001033
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001034 :param pkey: The key to sign with.
1035 :type pkey: :py:class:`PKey`
1036
1037 :param digest: The name of the message digest to use.
1038 :type digest: :py:class:`bytes`
1039
Laurens Van Houtvena367fe82015-04-23 10:49:12 -07001040 :return: :py:data:`None`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001041 """
1042 if not isinstance(pkey, PKey):
1043 raise TypeError("pkey must be a PKey instance")
1044
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001045 if pkey._only_public:
1046 raise ValueError("Key only has public part")
1047
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -08001048 if not pkey._initialized:
1049 raise ValueError("Key is uninitialized")
1050
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001051 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001052 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001053 raise ValueError("No such digest method")
1054
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001055 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001056 if not sign_result:
1057 _raise_current_error()
1058
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001059 def get_signature_algorithm(self):
1060 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001061 Return the signature algorithm used in the certificate.
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001062
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001063 :return: The name of the algorithm.
1064 :rtype: :py:class:`bytes`
1065
1066 :raises ValueError: If the signature algorithm is undefined.
1067
Laurens Van Houtven0dd87402015-04-23 10:47:18 -07001068 .. versionadded:: 0.13
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001069 """
1070 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 nid = _lib.OBJ_obj2nid(alg)
1072 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001073 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001074 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001075
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001076 def digest(self, digest_name):
1077 """
1078 Return the digest of the X509 object.
1079
1080 :param digest_name: The name of the digest algorithm to use.
1081 :type digest_name: :py:class:`bytes`
1082
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001083 :return: The digest of the object, formatted as
1084 :py:const:`b":"`-delimited hex pairs.
1085 :rtype: :py:class:`bytes`
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001086 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001087 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001088 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001089 raise ValueError("No such digest method")
1090
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001091 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1092 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001093 result_length[0] = len(result_buffer)
1094
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001095 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001096 self._x509, digest, result_buffer, result_length)
1097
1098 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001099 # TODO: This is untested.
1100 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001101
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001102 return b":".join([
Alex Gaynora738ed52015-09-05 11:17:10 -04001103 b16encode(ch).upper() for ch
1104 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001105
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001106 def subject_name_hash(self):
1107 """
1108 Return the hash of the X509 subject.
1109
1110 :return: The hash of the subject.
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001111 :rtype: :py:class:`bytes`
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001112 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001113 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001114
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001115 def set_serial_number(self, serial):
1116 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001117 Set the serial number of the certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001118
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001119 :param serial: The new serial number.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001120 :type serial: :py:class:`int`
1121
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001122 :return: :py:data`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001123 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001124 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001125 raise TypeError("serial must be an integer")
1126
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001127 hex_serial = hex(serial)[2:]
1128 if not isinstance(hex_serial, bytes):
1129 hex_serial = hex_serial.encode('ascii')
1130
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001131 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001132
1133 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
Alex Gaynor5945ea82015-09-05 14:59:06 -04001134 # it. If bignum is still NULL after this call, then the return value
1135 # is actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001136 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001137
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001138 if bignum_serial[0] == _ffi.NULL:
1139 set_result = _lib.ASN1_INTEGER_set(
1140 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001141 if set_result:
1142 # TODO Not tested
1143 _raise_current_error()
1144 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001145 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1146 _lib.BN_free(bignum_serial[0])
1147 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001148 # TODO Not tested
1149 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001150 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1151 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001152 if not set_result:
1153 # TODO Not tested
1154 _raise_current_error()
1155
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001156 def get_serial_number(self):
1157 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001158 Return the serial number of this certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001159
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001160 :return: The serial number.
1161 :rtype: :py:class:`int`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001162 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001163 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1164 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001165 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001166 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001167 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001168 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001169 serial = int(hexstring_serial, 16)
1170 return serial
1171 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001172 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001173 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001174 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001175
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001176 def gmtime_adj_notAfter(self, amount):
1177 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001178 Adjust the time stamp on which the certificate stops being valid.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001179
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001180 :param amount: The number of seconds by which to adjust the timestamp.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001181 :type amount: :py:class:`int`
1182
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001183 :return: :py:const:`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001184 """
1185 if not isinstance(amount, int):
1186 raise TypeError("amount must be an integer")
1187
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001188 notAfter = _lib.X509_get_notAfter(self._x509)
1189 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001190
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001191 def gmtime_adj_notBefore(self, amount):
1192 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001193 Adjust the timestamp on which the certificate starts being valid.
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001194
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001195 :param amount: The number of seconds by which to adjust the timestamp.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001196 :return: :py:const:`None`
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001197 """
1198 if not isinstance(amount, int):
1199 raise TypeError("amount must be an integer")
1200
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001201 notBefore = _lib.X509_get_notBefore(self._x509)
1202 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001203
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001204 def has_expired(self):
1205 """
1206 Check whether the certificate has expired.
1207
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001208 :return: :py:const:`True` if the certificate has expired,
1209 :py:const:`False` otherwise.
1210 :rtype: :py:class:`bool`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001211 """
Paul Kehrer5d5d28d2015-10-21 18:55:22 -05001212 time_string = self.get_notAfter()
1213 timestamp = mktime(datetime.datetime.strptime(
1214 time_string, "%Y%m%d%H%M%SZ").timetuple())
1215 now = mktime(datetime.datetime.utcnow().timetuple())
1216
1217 return timestamp < now
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001218
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001219 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001220 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001221
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001222 def get_notBefore(self):
1223 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001224 Get the timestamp at which the certificate starts being valid.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001225
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001226 The timestamp is formatted as an ASN.1 GENERALIZEDTIME::
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001227
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001228 YYYYMMDDhhmmssZ
1229 YYYYMMDDhhmmss+hhmm
1230 YYYYMMDDhhmmss-hhmm
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001231
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001232 :return: A timestamp string, or :py:const:`None` if there is none.
1233 :rtype: :py:class:`bytes` or :py:const:`None`
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001234 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001235 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001236
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001237 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001238 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001239
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001240 def set_notBefore(self, when):
1241 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001242 Set the timestamp at which the certificate starts being valid.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001243
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001244 The timestamp is formatted as an ASN.1 GENERALIZEDTIME::
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001245
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001246 YYYYMMDDhhmmssZ
1247 YYYYMMDDhhmmss+hhmm
1248 YYYYMMDDhhmmss-hhmm
1249
1250 :param when: A timestamp string.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001251 :type when: :py:class:`bytes`
1252
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001253 :return: :py:const:`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001254 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001255 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001256
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001257 def get_notAfter(self):
1258 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001259 Get the timestamp at which the certificate stops being valid.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001260
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001261 The timestamp is formatted as an ASN.1 GENERALIZEDTIME::
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001262
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001263 YYYYMMDDhhmmssZ
1264 YYYYMMDDhhmmss+hhmm
1265 YYYYMMDDhhmmss-hhmm
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001266
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001267 :return: A timestamp string, or :py:const:`None` if there is none.
1268 :rtype: :py:class:`bytes` or :py:const:`None`
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001269 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001270 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001271
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001272 def set_notAfter(self, when):
1273 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001274 Set the timestamp at which the certificate stops being valid.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001275
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001276 The timestamp is formatted as an ASN.1 GENERALIZEDTIME::
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001277
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001278 YYYYMMDDhhmmssZ
1279 YYYYMMDDhhmmss+hhmm
1280 YYYYMMDDhhmmss-hhmm
1281
1282 :param when: A timestamp string.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001283 :type when: :py:class:`bytes`
1284
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001285 :return: :py:const:`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001286 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001287 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001288
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001289 def _get_name(self, which):
1290 name = X509Name.__new__(X509Name)
1291 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001292 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001293 # TODO: This is untested.
1294 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001295
1296 # The name is owned by the X509 structure. As long as the X509Name
1297 # Python object is alive, keep the X509 Python object alive.
1298 name._owner = self
1299
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001300 return name
1301
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001302 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001303 if not isinstance(name, X509Name):
1304 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001305 set_result = which(self._x509, name._name)
1306 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001307 # TODO: This is untested.
1308 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001309
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001310 def get_issuer(self):
1311 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001312 Return the issuer of this certificate.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001313
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001314 This creates a new :py:class:`X509Name`: modifying it does not affect
1315 this certificate.
1316
1317 :return: The issuer of this certificate.
1318 :rtype: :py:class:`X509Name`
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001319 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001320 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001321
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001322 def set_issuer(self, issuer):
1323 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001324 Set the issuer of this certificate.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001325
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001326 :param issuer: The issuer.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001327 :type issuer: :py:class:`X509Name`
1328
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001329 :return: :py:const:`None`
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001330 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001331 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001332
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001333 def get_subject(self):
1334 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001335 Return the subject of this certificate.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001336
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001337 This creates a new :py:class:`X509Name`: modifying it does not affect
1338 this certificate.
1339
1340 :return: The subject of this certificate.
1341 :rtype: :py:class:`X509Name`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001342 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001343 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001344
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001345 def set_subject(self, subject):
1346 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001347 Set the subject of this certificate.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001348
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001349 :param subject: The subject.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001350 :type subject: :py:class:`X509Name`
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001351
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001352 :return: :py:const:`None`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001353 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001354 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001355
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001356 def get_extension_count(self):
1357 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001358 Get the number of extensions on this certificate.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001359
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001360 :return: The number of extensions.
1361 :rtype: :py:class:`int`
1362
1363 .. versionadded:: 0.12
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001364 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001365 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001366
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001367 def add_extensions(self, extensions):
1368 """
1369 Add extensions to the certificate.
1370
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001371 :param extensions: The extensions to add.
1372 :type extensions: An iterable of :py:class:`X509Extension` objects.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001373 :return: :py:const:`None`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001374 """
1375 for ext in extensions:
1376 if not isinstance(ext, X509Extension):
1377 raise ValueError("One of the elements is not an X509Extension")
1378
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001379 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001380 if not add_result:
1381 _raise_current_error()
1382
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001383 def get_extension(self, index):
1384 """
1385 Get a specific extension of the certificate by index.
1386
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001387 Extensions on a certificate are kept in order. The index
1388 parameter selects which extension will be returned.
1389
1390 :param int index: The index of the extension to retrieve.
1391 :return: The extension at the specified index.
1392 :rtype: :py:class:`X509Extension`
1393 :raises IndexError: If the extension index was out of bounds.
1394
1395 .. versionadded:: 0.12
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001396 """
1397 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001398 ext._extension = _lib.X509_get_ext(self._x509, index)
1399 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001400 raise IndexError("extension index out of bounds")
1401
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001402 extension = _lib.X509_EXTENSION_dup(ext._extension)
1403 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001404 return ext
1405
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001406
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001407X509Type = X509
1408
1409
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001410class X509Store(object):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001411 """
1412 An X509 certificate store.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001413 """
Alex Gaynora738ed52015-09-05 11:17:10 -04001414
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001415 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001416 store = _lib.X509_STORE_new()
1417 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001418
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001419 def add_cert(self, cert):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001420 """
1421 Adds the certificate :py:data:`cert` to this store.
1422
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +02001423 This is the Python equivalent of OpenSSL's ``X509_STORE_add_cert``.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001424
1425 :param X509 cert: The certificate to add to this store.
1426 :raises TypeError: If the certificate is not an :py:class:`X509`.
1427 :raises Error: If OpenSSL was unhappy with your certificate.
Laurens Van Houtven5ee60b32015-04-23 10:51:16 -07001428 :return: :py:data:`None` if the certificate was added successfully.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001429 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001430 if not isinstance(cert, X509):
1431 raise TypeError()
1432
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001433 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001434 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001435 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001436
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001437
1438X509StoreType = X509Store
1439
1440
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001441class X509StoreContextError(Exception):
1442 """
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001443 An exception raised when an error occurred while verifying a certificate
1444 using `OpenSSL.X509StoreContext.verify_certificate`.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001445
Jean-Paul Calderonefeb17432015-03-15 15:49:45 -04001446 :ivar certificate: The certificate which caused verificate failure.
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001447 :type certificate: :class:`X509`
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001448 """
Alex Gaynora738ed52015-09-05 11:17:10 -04001449
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001450 def __init__(self, message, certificate):
1451 super(X509StoreContextError, self).__init__(message)
1452 self.certificate = certificate
1453
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001454
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001455class X509StoreContext(object):
1456 """
1457 An X.509 store context.
1458
Jean-Paul Calderone13a81682015-01-18 15:49:15 -05001459 An :py:class:`X509StoreContext` is used to define some of the criteria for
1460 certificate verification. The information encapsulated in this object
1461 includes, but is not limited to, a set of trusted certificates,
1462 verification parameters, and revoked certificates.
1463
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001464 .. note::
1465
1466 Currently, one can only set the trusted certificates on an
1467 :py:class:`X509StoreContext`. Future versions of pyOpenSSL will expose
1468 verification parameters and certificate revocation lists.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001469
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001470 :ivar _store_ctx: The underlying X509_STORE_CTX structure used by this
1471 instance. It is dynamically allocated and automatically garbage
1472 collected.
1473
Jean-Paul Calderone64b6b842015-03-15 16:08:02 -04001474 :ivar _store: See the ``store`` ``__init__`` parameter.
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001475
Jean-Paul Calderone64b6b842015-03-15 16:08:02 -04001476 :ivar _cert: See the ``certificate`` ``__init__`` parameter.
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001477
1478 :param X509Store store: The certificates which will be trusted for the
1479 purposes of any verifications.
1480
1481 :param X509 certificate: The certificate to be verified.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001482 """
1483
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001484 def __init__(self, store, certificate):
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001485 store_ctx = _lib.X509_STORE_CTX_new()
1486 self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
1487 self._store = store
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001488 self._cert = certificate
Stephen Holsapple46a09252015-02-12 14:45:43 -08001489 # Make the store context available for use after instantiating this
1490 # class by initializing it now. Per testing, subsequent calls to
1491 # :py:meth:`_init` have no adverse affect.
1492 self._init()
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001493
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001494 def _init(self):
1495 """
1496 Set up the store context for a subsequent verification operation.
1497 """
Alex Gaynor5945ea82015-09-05 14:59:06 -04001498 ret = _lib.X509_STORE_CTX_init(
1499 self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL
1500 )
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001501 if ret <= 0:
1502 _raise_current_error()
1503
1504 def _cleanup(self):
1505 """
1506 Internally cleans up the store context.
1507
1508 The store context can then be reused with a new call to
Stephen Holsapple46a09252015-02-12 14:45:43 -08001509 :py:meth:`_init`.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001510 """
1511 _lib.X509_STORE_CTX_cleanup(self._store_ctx)
1512
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001513 def _exception_from_context(self):
1514 """
1515 Convert an OpenSSL native context error failure into a Python
1516 exception.
1517
Alex Gaynor5945ea82015-09-05 14:59:06 -04001518 When a call to native OpenSSL X509_verify_cert fails, additional
1519 information about the failure can be obtained from the store context.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001520 """
1521 errors = [
1522 _lib.X509_STORE_CTX_get_error(self._store_ctx),
1523 _lib.X509_STORE_CTX_get_error_depth(self._store_ctx),
1524 _native(_ffi.string(_lib.X509_verify_cert_error_string(
Alex Gaynor5945ea82015-09-05 14:59:06 -04001525 _lib.X509_STORE_CTX_get_error(self._store_ctx)))),
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001526 ]
Stephen Holsapple1f713eb2015-02-09 19:19:44 -08001527 # A context error should always be associated with a certificate, so we
1528 # expect this call to never return :class:`None`.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001529 _x509 = _lib.X509_STORE_CTX_get_current_cert(self._store_ctx)
Stephen Holsapple1f713eb2015-02-09 19:19:44 -08001530 _cert = _lib.X509_dup(_x509)
1531 pycert = X509.__new__(X509)
1532 pycert._x509 = _ffi.gc(_cert, _lib.X509_free)
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001533 return X509StoreContextError(errors, pycert)
1534
Stephen Holsapple46a09252015-02-12 14:45:43 -08001535 def set_store(self, store):
1536 """
1537 Set the context's trust store.
1538
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001539 .. versionadded:: 0.15
1540
Stephen Holsapple46a09252015-02-12 14:45:43 -08001541 :param X509Store store: The certificates which will be trusted for the
1542 purposes of any *future* verifications.
1543 """
1544 self._store = store
1545
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001546 def verify_certificate(self):
1547 """
1548 Verify a certificate in a context.
1549
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001550 .. versionadded:: 0.15
1551
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001552 :param store_ctx: The :py:class:`X509StoreContext` to verify.
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001553
Alex Gaynorca87ff62015-09-04 23:31:03 -04001554 :raises X509StoreContextError: If an error occurred when validating a
Alex Gaynor5945ea82015-09-05 14:59:06 -04001555 certificate in the context. Sets ``certificate`` attribute to
1556 indicate which certificate caused the error.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001557 """
Stephen Holsapple46a09252015-02-12 14:45:43 -08001558 # Always re-initialize the store context in case
1559 # :py:meth:`verify_certificate` is called multiple times.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001560 self._init()
1561 ret = _lib.X509_verify_cert(self._store_ctx)
1562 self._cleanup()
1563 if ret <= 0:
1564 raise self._exception_from_context()
1565
1566
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001567def load_certificate(type, buffer):
1568 """
1569 Load a certificate from a buffer
1570
1571 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1572
1573 :param buffer: The buffer the certificate is stored in
1574 :type buffer: :py:class:`bytes`
1575
1576 :return: The X509 object
1577 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001578 if isinstance(buffer, _text_type):
1579 buffer = buffer.encode("ascii")
1580
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001581 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001582
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001583 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001584 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001585 elif type == FILETYPE_ASN1:
Alex Gaynor962ac212015-09-04 08:06:42 -04001586 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001587 else:
1588 raise ValueError(
1589 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001590
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001591 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001592 _raise_current_error()
1593
1594 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001595 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001596 return cert
1597
1598
1599def dump_certificate(type, cert):
1600 """
1601 Dump a certificate to a buffer
1602
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001603 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1604 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001605 :param cert: The certificate to dump
1606 :return: The buffer with the dumped certificate in
1607 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001608 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001609
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001610 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001611 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001612 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001613 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001614 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001615 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001616 else:
1617 raise ValueError(
1618 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1619 "FILETYPE_TEXT")
1620
Alex Gaynorc7a9eb52015-09-05 16:57:49 -04001621 assert result_code == 1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001622 return _bio_to_string(bio)
1623
1624
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001625def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1626 """
1627 Dump a private key to a buffer
1628
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001629 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1630 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001631 :param pkey: The PKey to dump
1632 :param cipher: (optional) if encrypted PEM format, the cipher to
1633 use
1634 :param passphrase: (optional) if encrypted PEM format, this can be either
1635 the passphrase to use, or a callback for providing the
1636 passphrase.
1637 :return: The buffer with the dumped key in
Maximilian Hils0de43752015-09-18 15:26:54 +02001638 :rtype: :py:data:`bytes`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001639 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001640 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001641
1642 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001643 if passphrase is None:
1644 raise TypeError(
1645 "if a value is given for cipher "
1646 "one must also be given for passphrase")
1647 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001648 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001649 raise ValueError("Invalid cipher name")
1650 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001651 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001652
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001653 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001654 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001655 result_code = _lib.PEM_write_bio_PrivateKey(
1656 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001657 helper.callback, helper.callback_args)
1658 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001659 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001660 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001661 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001662 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1663 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001664 # TODO RSA_free(rsa)?
1665 else:
1666 raise ValueError(
1667 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1668 "FILETYPE_TEXT")
1669
1670 if result_code == 0:
1671 _raise_current_error()
1672
1673 return _bio_to_string(bio)
1674
1675
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001676def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001677 copy = _lib.X509_REVOKED_new()
1678 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001679 # TODO: This is untested.
1680 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001681
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001682 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001683 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001684 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001685
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001686 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001687 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001688 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001689
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001690 if original.extensions != _ffi.NULL:
1691 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1692 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1693 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1694 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1695 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001696 copy.extensions = extension_stack
1697
1698 copy.sequence = original.sequence
1699 return copy
1700
1701
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001702class Revoked(object):
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001703 """
1704 A certificate revocation.
1705 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001706 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1707 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1708 # OCSP_crl_reason_str. We use the latter, just like the command line
1709 # program.
1710 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001711 b"unspecified",
1712 b"keyCompromise",
1713 b"CACompromise",
1714 b"affiliationChanged",
1715 b"superseded",
1716 b"cessationOfOperation",
1717 b"certificateHold",
1718 # b"removeFromCRL",
Alex Gaynorca87ff62015-09-04 23:31:03 -04001719 ]
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001720
1721 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001722 revoked = _lib.X509_REVOKED_new()
1723 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001724
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001725 def set_serial(self, hex_str):
1726 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001727 Set the serial number.
1728
1729 The serial number is formatted as a hexadecimal number encoded in
1730 ASCII.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001731
1732 :param hex_str: The new serial number.
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001733 :type hex_str: :py:class:`bytes`
1734
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001735 :return: :py:const:`None`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001736 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001737 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1738 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001739 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001740 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001741 if not bn_result:
1742 raise ValueError("bad hex string")
1743
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001744 asn1_serial = _ffi.gc(
1745 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1746 _lib.ASN1_INTEGER_free)
1747 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001748
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001749 def get_serial(self):
1750 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001751 Get the serial number.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001752
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001753 The serial number is formatted as a hexadecimal number encoded in
1754 ASCII.
1755
1756 :return: The serial number.
1757 :rtype: :py:class:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001758 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001759 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001760
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001761 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001762 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001763 # TODO: This is untested.
1764 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001765
1766 return _bio_to_string(bio)
1767
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001768 def _delete_reason(self):
1769 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001770 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1771 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1772 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1773 _lib.X509_EXTENSION_free(ext)
1774 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001775 break
1776
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001777 def set_reason(self, reason):
1778 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001779 Set the reason of this revocation.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001780
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001781 If :py:data:`reason` is :py:const:`None`, delete the reason instead.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001782
1783 :param reason: The reason string.
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001784 :type reason: :py:class:`bytes` or :py:class:`NoneType`
1785
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001786 :return: :py:const:`None`
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001787
1788 .. seealso::
1789
1790 :py:meth:`all_reasons`, which gives you a list of all supported
1791 reasons which you might pass to this method.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001792 """
1793 if reason is None:
1794 self._delete_reason()
1795 elif not isinstance(reason, bytes):
1796 raise TypeError("reason must be None or a byte string")
1797 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001798 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001799 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1800
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001801 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1802 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001803 # TODO: This is untested.
1804 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001805 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001806
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001807 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1808 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001809 # TODO: This is untested.
1810 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001811
1812 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001813 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1814 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001815
1816 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001817 # TODO: This is untested.
1818 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001819
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001820 def get_reason(self):
1821 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001822 Set the reason of this revocation.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001823
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001824 :return: The reason, or :py:const:`None` if there is none.
1825 :rtype: :py:class:`bytes` or :py:class:`NoneType`
1826
1827 .. seealso::
1828
1829 :py:meth:`all_reasons`, which gives you a list of all supported
1830 reasons this method might return.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001831 """
1832 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001833 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1834 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1835 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001836 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001837
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001838 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001839 if not print_result:
Alex Gaynor5945ea82015-09-05 14:59:06 -04001840 print_result = _lib.M_ASN1_OCTET_STRING_print(
1841 bio, ext.value
1842 )
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001843 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001844 # TODO: This is untested.
1845 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001846
1847 return _bio_to_string(bio)
1848
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001849 def all_reasons(self):
1850 """
1851 Return a list of all the supported reason strings.
1852
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001853 This list is a copy; modifying it does not change the supported reason
1854 strings.
1855
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001856 :return: A list of reason strings.
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001857 :rtype: :py:class:`list` of :py:class:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001858 """
1859 return self._crl_reasons[:]
1860
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001861 def set_rev_date(self, when):
1862 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001863 Set the revocation timestamp.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001864
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001865 :param when: The timestamp of the revocation, as ASN.1 GENERALIZEDTIME.
1866 :type when: :py:class:`bytes`
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001867 :return: :py:const:`None`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001868 """
1869 return _set_asn1_time(self._revoked.revocationDate, when)
1870
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001871 def get_rev_date(self):
1872 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001873 Get the revocation timestamp.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001874
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001875 :return: The timestamp of the revocation, as ASN.1 GENERALIZEDTIME.
1876 :rtype: :py:class:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001877 """
1878 return _get_asn1_time(self._revoked.revocationDate)
1879
1880
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001881class CRL(object):
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001882 """
1883 A certificate revocation list.
1884 """
Alex Gaynora738ed52015-09-05 11:17:10 -04001885
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001886 def __init__(self):
1887 """
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001888 Create a new empty certificate revocation list.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001889 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001890 crl = _lib.X509_CRL_new()
1891 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001892
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001893 def get_revoked(self):
1894 """
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001895 Return the revocations in this certificate revocation list.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001896
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001897 These revocations will be provided by value, not by reference.
1898 That means it's okay to mutate them: it won't affect this CRL.
1899
1900 :return: The revocations in this CRL.
1901 :rtype: :py:class:`tuple` of :py:class:`Revocation`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001902 """
1903 results = []
1904 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001905 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1906 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001907 revoked_copy = _X509_REVOKED_dup(revoked)
1908 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001909 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001910 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001911 if results:
1912 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001913
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001914 def add_revoked(self, revoked):
1915 """
1916 Add a revoked (by value not reference) to the CRL structure
1917
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001918 This revocation will be added by value, not by reference. That
1919 means it's okay to mutate it after adding: it won't affect
1920 this CRL.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001921
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001922 :param revoked: The new revocation.
1923 :type revoked: :class:`Revoked`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001924
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001925 :return: :py:const:`None`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001926 """
1927 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001928 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001929 # TODO: This is untested.
1930 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001931
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001932 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001933 if add_result == 0:
1934 # TODO: This is untested.
1935 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001936
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001937 def export(self, cert, key, type=FILETYPE_PEM, days=100,
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -04001938 digest=_UNSPECIFIED):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001939 """
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001940 Export a CRL as a string.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001941
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001942 :param cert: The certificate used to sign the CRL.
1943 :type cert: :py:class:`X509`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001944
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001945 :param key: The key used to sign the CRL.
1946 :type key: :py:class:`PKey`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001947
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001948 :param type: The export format, either :py:data:`FILETYPE_PEM`,
1949 :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001950
Jean-Paul Calderonedf514012015-04-13 21:45:18 -04001951 :param int days: The number of days until the next update of this CRL.
Bulat Gaifullin5f9eea42014-09-23 19:35:15 +04001952
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001953 :param bytes digest: The name of the message digest to use (eg
1954 ``b"sha1"``).
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001955
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001956 :return: :py:data:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001957 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001958 if not isinstance(cert, X509):
1959 raise TypeError("cert must be an X509 instance")
1960 if not isinstance(key, PKey):
1961 raise TypeError("key must be a PKey instance")
1962 if not isinstance(type, int):
1963 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001964
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -04001965 if digest is _UNSPECIFIED:
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001966 _warn(
1967 "The default message digest (md5) is deprecated. "
1968 "Pass the name of a message digest explicitly.",
1969 category=DeprecationWarning,
1970 stacklevel=2,
1971 )
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001972 digest = b"md5"
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001973
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001974 digest_obj = _lib.EVP_get_digestbyname(digest)
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04001975 if digest_obj == _ffi.NULL:
1976 raise ValueError("No such digest method")
1977
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001978 bio = _lib.BIO_new(_lib.BIO_s_mem())
1979 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001980 # TODO: This is untested.
1981 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001982
Alex Gaynora738ed52015-09-05 11:17:10 -04001983 # A scratch time object to give different values to different CRL
1984 # fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001985 sometime = _lib.ASN1_TIME_new()
1986 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001987 # TODO: This is untested.
1988 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001989
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001990 _lib.X509_gmtime_adj(sometime, 0)
1991 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001992
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001993 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1994 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001995
Alex Gaynor5945ea82015-09-05 14:59:06 -04001996 _lib.X509_CRL_set_issuer_name(
1997 self._crl, _lib.X509_get_subject_name(cert._x509)
1998 )
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001999
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04002000 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, digest_obj)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002001 if not sign_result:
2002 _raise_current_error()
2003
Dominic Chenf05b2122015-10-13 16:32:35 +00002004 return dump_crl(type, self)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002005
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002006
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002007CRLType = CRL
2008
2009
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002010class PKCS7(object):
2011 def type_is_signed(self):
2012 """
2013 Check if this NID_pkcs7_signed object
2014
2015 :return: True if the PKCS7 is of type signed
2016 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002017 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002018 return True
2019 return False
2020
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002021 def type_is_enveloped(self):
2022 """
2023 Check if this NID_pkcs7_enveloped object
2024
2025 :returns: True if the PKCS7 is of type enveloped
2026 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002027 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002028 return True
2029 return False
2030
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002031 def type_is_signedAndEnveloped(self):
2032 """
2033 Check if this NID_pkcs7_signedAndEnveloped object
2034
2035 :returns: True if the PKCS7 is of type signedAndEnveloped
2036 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002037 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002038 return True
2039 return False
2040
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002041 def type_is_data(self):
2042 """
2043 Check if this NID_pkcs7_data object
2044
2045 :return: True if the PKCS7 is of type data
2046 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002047 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002048 return True
2049 return False
2050
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002051 def get_type_name(self):
2052 """
2053 Returns the type name of the PKCS7 structure
2054
2055 :return: A string with the typename
2056 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002057 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
2058 string_type = _lib.OBJ_nid2sn(nid)
2059 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002060
2061PKCS7Type = PKCS7
2062
2063
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002064class PKCS12(object):
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002065 """
2066 A PKCS #12 archive.
2067 """
Alex Gaynora738ed52015-09-05 11:17:10 -04002068
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002069 def __init__(self):
2070 self._pkey = None
2071 self._cert = None
2072 self._cacerts = None
2073 self._friendlyname = None
2074
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002075 def get_certificate(self):
2076 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002077 Get the certificate in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002078
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002079 :return: The certificate, or :py:const:`None` if there is none.
2080 :rtype: :py:class:`X509` or :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002081 """
2082 return self._cert
2083
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002084 def set_certificate(self, cert):
2085 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002086 Set the certificate in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002087
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002088 :param cert: The new certificate, or :py:const:`None` to unset it.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002089 :type cert: :py:class:`X509` or :py:const:`None`
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002090
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002091 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002092 """
2093 if not isinstance(cert, X509):
2094 raise TypeError("cert must be an X509 instance")
2095 self._cert = cert
2096
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002097 def get_privatekey(self):
2098 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002099 Get the private key in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002100
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002101 :return: The private key, or :py:const:`None` if there is none.
2102 :rtype: :py:class:`PKey`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002103 """
2104 return self._pkey
2105
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002106 def set_privatekey(self, pkey):
2107 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002108 Set the certificate portion of the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002109
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002110 :param pkey: The new private key, or :py:const:`None` to unset it.
2111 :type pkey: :py:class:`PKey` or :py:const:`None`
2112
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002113 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002114 """
2115 if not isinstance(pkey, PKey):
2116 raise TypeError("pkey must be a PKey instance")
2117 self._pkey = pkey
2118
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002119 def get_ca_certificates(self):
2120 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002121 Get the CA certificates in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002122
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002123 :return: A tuple with the CA certificates in the chain, or
2124 :py:const:`None` if there are none.
2125 :rtype: :py:class:`tuple` of :py:class:`X509` or :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002126 """
2127 if self._cacerts is not None:
2128 return tuple(self._cacerts)
2129
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002130 def set_ca_certificates(self, cacerts):
2131 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08002132 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002133
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002134 :param cacerts: The new CA certificates, or :py:const:`None` to unset
2135 them.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002136 :type cacerts: An iterable of :py:class:`X509` or :py:const:`None`
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002137
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002138 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002139 """
2140 if cacerts is None:
2141 self._cacerts = None
2142 else:
2143 cacerts = list(cacerts)
2144 for cert in cacerts:
2145 if not isinstance(cert, X509):
Alex Gaynor5945ea82015-09-05 14:59:06 -04002146 raise TypeError(
2147 "iterable must only contain X509 instances"
2148 )
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002149 self._cacerts = cacerts
2150
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002151 def set_friendlyname(self, name):
2152 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002153 Set the friendly name in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002154
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002155 :param name: The new friendly name, or :py:const:`None` to unset.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002156 :type name: :py:class:`bytes` or :py:const:`None`
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002157
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002158 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002159 """
2160 if name is None:
2161 self._friendlyname = None
2162 elif not isinstance(name, bytes):
Alex Gaynor5945ea82015-09-05 14:59:06 -04002163 raise TypeError(
2164 "name must be a byte string or None (not %r)" % (name,)
2165 )
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002166 self._friendlyname = name
2167
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002168 def get_friendlyname(self):
2169 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002170 Get the friendly name in the PKCS# 12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002171
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002172 :returns: The friendly name, or :py:const:`None` if there is none.
2173 :rtype: :py:class:`bytes` or :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002174 """
2175 return self._friendlyname
2176
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002177 def export(self, passphrase=None, iter=2048, maciter=1):
2178 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002179 Dump a PKCS12 object as a string.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002180
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002181 For more information, see the :c:func:`PKCS12_create` man page.
2182
2183 :param passphrase: The passphrase used to encrypt the structure. Unlike
2184 some other passphrase arguments, this *must* be a string, not a
2185 callback.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002186 :type passphrase: :py:data:`bytes`
2187
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002188 :param iter: Number of times to repeat the encryption step.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002189 :type iter: :py:data:`int`
2190
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002191 :param maciter: Number of times to repeat the MAC step.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002192 :type maciter: :py:data:`int`
2193
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002194 :return: The string representation of the PKCS #12 structure.
2195 :rtype:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002196 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002197 passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
Abraham Martine82326c2015-02-04 10:18:10 +00002198
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002199 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002200 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002201 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002202 cacerts = _lib.sk_X509_new_null()
2203 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002204 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002205 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002206
2207 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002208 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002209
2210 friendlyname = self._friendlyname
2211 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002212 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002213
2214 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002215 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002216 else:
2217 pkey = self._pkey._pkey
2218
2219 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002220 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002221 else:
2222 cert = self._cert._x509
2223
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002224 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002225 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002226 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2227 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002228 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002229 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002230 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002231 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002232
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002233 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002234 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002235 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002236
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002237
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002238PKCS12Type = PKCS12
2239
2240
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002241class NetscapeSPKI(object):
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002242 """
2243 A Netscape SPKI object.
2244 """
Alex Gaynora738ed52015-09-05 11:17:10 -04002245
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002246 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002247 spki = _lib.NETSCAPE_SPKI_new()
2248 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002249
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002250 def sign(self, pkey, digest):
2251 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002252 Sign the certificate request with this key and digest type.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002253
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002254 :param pkey: The private key to sign with.
2255 :type pkey: :py:class:`PKey`
2256
2257 :param digest: The message digest to use.
2258 :type digest: :py:class:`bytes`
2259
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002260 :return: :py:const:`None`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002261 """
2262 if pkey._only_public:
2263 raise ValueError("Key has only public part")
2264
2265 if not pkey._initialized:
2266 raise ValueError("Key is uninitialized")
2267
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002268 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002269 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002270 raise ValueError("No such digest method")
2271
Alex Gaynor5945ea82015-09-05 14:59:06 -04002272 sign_result = _lib.NETSCAPE_SPKI_sign(
2273 self._spki, pkey._pkey, digest_obj
2274 )
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002275 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002276 # TODO: This is untested.
2277 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002278
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002279 def verify(self, key):
2280 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002281 Verifies a signature on a certificate request.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002282
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002283 :param key: The public key that signature is supposedly from.
2284 :type pkey: :py:class:`PKey`
2285
2286 :return: :py:const:`True` if the signature is correct.
2287 :rtype: :py:class:`bool`
2288
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02002289 :raises Error: If the signature is invalid, or there was a problem
2290 verifying the signature.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002291 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002292 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002293 if answer <= 0:
2294 _raise_current_error()
2295 return True
2296
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002297 def b64_encode(self):
2298 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002299 Generate a base64 encoded representation of this SPKI object.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002300
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002301 :return: The base64 encoded string.
2302 :rtype: :py:class:`bytes`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002303 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002304 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2305 result = _ffi.string(encoded)
2306 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002307 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002308
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002309 def get_pubkey(self):
2310 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002311 Get the public key of this certificate.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002312
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002313 :return: The public key.
2314 :rtype: :py:class:`PKey`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002315 """
2316 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002317 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2318 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002319 # TODO: This is untested.
2320 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002321 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002322 pkey._only_public = True
2323 return pkey
2324
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002325 def set_pubkey(self, pkey):
2326 """
2327 Set the public key of the certificate
2328
2329 :param pkey: The public key
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002330 :return: :py:const:`None`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002331 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002332 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002333 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002334 # TODO: This is untested.
2335 _raise_current_error()
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002336
2337
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002338NetscapeSPKIType = NetscapeSPKI
2339
2340
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002341class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002342 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002343 if type != FILETYPE_PEM and passphrase is not None:
Alex Gaynor5945ea82015-09-05 14:59:06 -04002344 raise ValueError(
2345 "only FILETYPE_PEM key format supports encryption"
2346 )
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002347 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002348 self._more_args = more_args
2349 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002350 self._problems = []
2351
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002352 @property
2353 def callback(self):
2354 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002355 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002356 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002357 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002358 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002359 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002360 else:
2361 raise TypeError("Last argument must be string or callable")
2362
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002363 @property
2364 def callback_args(self):
2365 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002366 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002367 elif isinstance(self._passphrase, bytes):
2368 return self._passphrase
2369 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002370 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002371 else:
2372 raise TypeError("Last argument must be string or callable")
2373
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002374 def raise_if_problem(self, exceptionType=Error):
2375 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002376 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002377 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002378 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002379 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002380 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002381 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002382
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002383 def _read_passphrase(self, buf, size, rwflag, userdata):
2384 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002385 if self._more_args:
2386 result = self._passphrase(size, rwflag, userdata)
2387 else:
2388 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002389 if not isinstance(result, bytes):
2390 raise ValueError("String expected")
2391 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002392 if self._truncate:
2393 result = result[:size]
2394 else:
Alex Gaynor5945ea82015-09-05 14:59:06 -04002395 raise ValueError(
2396 "passphrase returned by callback is too long"
2397 )
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002398 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002399 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002400 return len(result)
2401 except Exception as e:
2402 self._problems.append(e)
2403 return 0
2404
2405
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002406def load_privatekey(type, buffer, passphrase=None):
2407 """
2408 Load a private key from a buffer
2409
2410 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2411 :param buffer: The buffer the key is stored in
2412 :param passphrase: (optional) if encrypted PEM format, this can be
2413 either the passphrase to use, or a callback for
2414 providing the passphrase.
2415
2416 :return: The PKey object
2417 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002418 if isinstance(buffer, _text_type):
2419 buffer = buffer.encode("ascii")
2420
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002421 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002422
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002423 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002424 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002425 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2426 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002427 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002428 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002429 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002430 else:
2431 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2432
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002433 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002434 _raise_current_error()
2435
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002436 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002437 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002438 return pkey
2439
2440
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002441def dump_certificate_request(type, req):
2442 """
2443 Dump a certificate request to a buffer
2444
2445 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2446 :param req: The certificate request to dump
2447 :return: The buffer with the dumped certificate request in
2448 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002449 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002450
2451 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002452 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002453 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002454 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002455 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002456 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002457 else:
Alex Gaynor5945ea82015-09-05 14:59:06 -04002458 raise ValueError(
2459 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
2460 "FILETYPE_TEXT"
2461 )
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002462
2463 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002464 # TODO: This is untested.
2465 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002466
2467 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002468
2469
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002470def load_certificate_request(type, buffer):
2471 """
2472 Load a certificate request from a buffer
2473
2474 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2475 :param buffer: The buffer the certificate request is stored in
2476 :return: The X509Req object
2477 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002478 if isinstance(buffer, _text_type):
2479 buffer = buffer.encode("ascii")
2480
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002481 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002482
2483 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002484 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002485 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002486 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002487 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002488 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002489
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002490 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002491 # TODO: This is untested.
2492 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002493
2494 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002495 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002496 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002497
2498
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002499def sign(pkey, data, digest):
2500 """
2501 Sign data with a digest
2502
2503 :param pkey: Pkey to sign with
2504 :param data: data to be signed
2505 :param digest: message digest to use
2506 :return: signature
2507 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002508 data = _text_to_bytes_and_warn("data", data)
Abraham Martine82326c2015-02-04 10:18:10 +00002509
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002510 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002511 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002512 raise ValueError("No such digest method")
2513
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002514 md_ctx = _ffi.new("EVP_MD_CTX*")
2515 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002516
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002517 _lib.EVP_SignInit(md_ctx, digest_obj)
2518 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002519
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002520 signature_buffer = _ffi.new("unsigned char[]", 512)
2521 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002522 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002523 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002524 md_ctx, signature_buffer, signature_length, pkey._pkey)
2525
2526 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002527 # TODO: This is untested.
2528 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002529
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002530 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002531
2532
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002533def verify(cert, signature, data, digest):
2534 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02002535 Verify a signature.
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002536
2537 :param cert: signing certificate (X509 object)
2538 :param signature: signature returned by sign function
2539 :param data: data to be verified
2540 :param digest: message digest to use
Alex Gaynor5945ea82015-09-05 14:59:06 -04002541 :return: :py:const:`None` if the signature is correct, raise exception
2542 otherwise
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002543 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002544 data = _text_to_bytes_and_warn("data", data)
Abraham Martine82326c2015-02-04 10:18:10 +00002545
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002546 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002547 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002548 raise ValueError("No such digest method")
2549
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002550 pkey = _lib.X509_get_pubkey(cert._x509)
2551 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002552 # TODO: This is untested.
2553 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002554 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002555
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002556 md_ctx = _ffi.new("EVP_MD_CTX*")
2557 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002558
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002559 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2560 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
Alex Gaynor5945ea82015-09-05 14:59:06 -04002561 verify_result = _lib.EVP_VerifyFinal(
2562 md_ctx, signature, len(signature), pkey
2563 )
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002564
2565 if verify_result != 1:
2566 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002567
2568
Dominic Chenf05b2122015-10-13 16:32:35 +00002569def dump_crl(type, crl):
2570 """
2571 Dump a certificate revocation list to a buffer.
2572
2573 :param type: The file type (one of ``FILETYPE_PEM``, ``FILETYPE_ASN1``, or
2574 ``FILETYPE_TEXT``).
Hynek Schlawack0a3cd6d2015-10-21 16:39:22 +02002575 :param CRL crl: The CRL to dump.
2576
Dominic Chenf05b2122015-10-13 16:32:35 +00002577 :return: The buffer with the CRL.
Hynek Schlawack0a3cd6d2015-10-21 16:39:22 +02002578 :rtype: :data:`bytes`
Dominic Chenf05b2122015-10-13 16:32:35 +00002579 """
2580 bio = _new_mem_buf()
2581
2582 if type == FILETYPE_PEM:
2583 ret = _lib.PEM_write_bio_X509_CRL(bio, crl._crl)
2584 elif type == FILETYPE_ASN1:
2585 ret = _lib.i2d_X509_CRL_bio(bio, crl._crl)
2586 elif type == FILETYPE_TEXT:
2587 ret = _lib.X509_CRL_print(bio, crl._crl)
2588 else:
2589 raise ValueError(
2590 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
2591 "FILETYPE_TEXT")
2592
2593 assert ret == 1
2594 return _bio_to_string(bio)
2595
2596
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002597def load_crl(type, buffer):
2598 """
2599 Load a certificate revocation list from a buffer
2600
2601 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2602 :param buffer: The buffer the CRL is stored in
2603
2604 :return: The PKey object
2605 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002606 if isinstance(buffer, _text_type):
2607 buffer = buffer.encode("ascii")
2608
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002609 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002610
2611 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002612 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002613 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002614 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002615 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002616 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2617
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002618 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002619 _raise_current_error()
2620
2621 result = CRL.__new__(CRL)
2622 result._crl = crl
2623 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002624
2625
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002626def load_pkcs7_data(type, buffer):
2627 """
2628 Load pkcs7 data from a buffer
2629
2630 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2631 :param buffer: The buffer with the pkcs7 data.
2632 :return: The PKCS7 object
2633 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002634 if isinstance(buffer, _text_type):
2635 buffer = buffer.encode("ascii")
2636
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002637 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002638
2639 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002640 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002641 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002642 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002643 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002644 # TODO: This is untested.
2645 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002646 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2647
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002648 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002649 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002650
2651 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002652 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002653 return pypkcs7
2654
2655
Stephen Holsapple38482622014-04-05 20:29:34 -07002656def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002657 """
2658 Load a PKCS12 object from a buffer
2659
2660 :param buffer: The buffer the certificate is stored in
2661 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2662 :returns: The PKCS12 object
2663 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002664 passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
Abraham Martine82326c2015-02-04 10:18:10 +00002665
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002666 if isinstance(buffer, _text_type):
2667 buffer = buffer.encode("ascii")
2668
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002669 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002670
Stephen Holsapple38482622014-04-05 20:29:34 -07002671 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2672 # password based encryption no password and a zero length password are two
2673 # different things, but OpenSSL implementation will try both to figure out
2674 # which one works.
2675 if not passphrase:
2676 passphrase = _ffi.NULL
2677
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002678 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2679 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002680 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002681 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002682
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002683 pkey = _ffi.new("EVP_PKEY**")
2684 cert = _ffi.new("X509**")
2685 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002686
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002687 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002688 if not parse_result:
2689 _raise_current_error()
2690
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002691 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002692
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002693 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2694 # queue for no particular reason. This error isn't interesting to anyone
2695 # outside this function. It's not even interesting to us. Get rid of it.
2696 try:
2697 _raise_current_error()
2698 except Error:
2699 pass
2700
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002701 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002702 pykey = None
2703 else:
2704 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002705 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002706
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002707 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002708 pycert = None
2709 friendlyname = None
2710 else:
2711 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002712 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002713
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002714 friendlyname_length = _ffi.new("int*")
Alex Gaynor5945ea82015-09-05 14:59:06 -04002715 friendlyname_buffer = _lib.X509_alias_get0(
2716 cert[0], friendlyname_length
2717 )
2718 friendlyname = _ffi.buffer(
2719 friendlyname_buffer, friendlyname_length[0]
2720 )[:]
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002721 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002722 friendlyname = None
2723
2724 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002725 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002726 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002727 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002728 pycacerts.append(pycacert)
2729 if not pycacerts:
2730 pycacerts = None
2731
2732 pkcs12 = PKCS12.__new__(PKCS12)
2733 pkcs12._pkey = pykey
2734 pkcs12._cert = pycert
2735 pkcs12._cacerts = pycacerts
2736 pkcs12._friendlyname = friendlyname
2737 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002738
2739
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002740# There are no direct unit tests for this initialization. It is tested
2741# indirectly since it is necessary for functions like dump_privatekey when
2742# using encryption.
2743#
2744# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2745# and some other similar tests may fail without this (though they may not if
2746# the Python runtime has already done some initialization of the underlying
2747# OpenSSL library (and is linked against the same one that cryptography is
2748# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002749_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002750
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002751# This is similar but exercised mainly by exception_from_error_queue. It calls
2752# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2753_lib.SSL_load_error_strings()
D.S. Ljungmark349e1362014-05-31 18:40:38 +02002754
2755
D.S. Ljungmark349e1362014-05-31 18:40:38 +02002756# Set the default string mask to match OpenSSL upstream (since 2005) and
2757# RFC5280 recommendations.
2758_lib.ASN1_STRING_set_default_mask_asc(b'utf8only')