blob: e72e857c19e1cc213a15638e6dac803a02509746 [file] [log] [blame]
Paul Kehrer5d5d28d2015-10-21 18:55:22 -05001import datetime
Paul Kehrer8d887e12015-10-24 09:09:55 -05002
3from time import mktime
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05005from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05006from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
Jean-Paul Calderone60432792015-04-13 12:26:07 -04007from warnings import warn as _warn
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05008
9from six import (
10 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -040011 text_type as _text_type,
12 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080013
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050014from OpenSSL._util import (
15 ffi as _ffi,
16 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050017 exception_from_error_queue as _exception_from_error_queue,
18 byte_string as _byte_string,
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -040019 native as _native,
20 UNSPECIFIED as _UNSPECIFIED,
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -040021 text_to_bytes_and_warn as _text_to_bytes_and_warn,
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -040022)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080023
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050024FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
25FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080026
27# TODO This was an API mistake. OpenSSL has no such constant.
28FILETYPE_TEXT = 2 ** 16 - 1
29
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050030TYPE_RSA = _lib.EVP_PKEY_RSA
31TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080032
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080033
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050034class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050035 """
36 An error occurred in an `OpenSSL.crypto` API.
37 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050038
39
40_raise_current_error = partial(_exception_from_error_queue, Error)
41
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070042
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050043def _untested_error(where):
44 """
45 An OpenSSL API failed somehow. Additionally, the failure which was
46 encountered isn't one that's exercised by the test suite so future behavior
47 of pyOpenSSL is now somewhat less predictable.
48 """
49 raise RuntimeError("Unknown %s failure" % (where,))
50
51
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050052def _new_mem_buf(buffer=None):
53 """
54 Allocate a new OpenSSL memory BIO.
55
56 Arrange for the garbage collector to clean it up automatically.
57
58 :param buffer: None or some bytes to use to put into the BIO so that they
59 can be read out.
60 """
61 if buffer is None:
62 bio = _lib.BIO_new(_lib.BIO_s_mem())
63 free = _lib.BIO_free
64 else:
65 data = _ffi.new("char[]", buffer)
66 bio = _lib.BIO_new_mem_buf(data, len(buffer))
Alex Gaynor5945ea82015-09-05 14:59:06 -040067
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050068 # Keep the memory alive as long as the bio is alive!
69 def free(bio, ref=data):
70 return _lib.BIO_free(bio)
71
72 if bio == _ffi.NULL:
73 # TODO: This is untested.
74 _raise_current_error()
75
76 bio = _ffi.gc(bio, free)
77 return bio
78
79
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080080def _bio_to_string(bio):
81 """
82 Copy the contents of an OpenSSL BIO object into a Python byte string.
83 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050084 result_buffer = _ffi.new('char**')
85 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
86 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080087
88
Jean-Paul Calderone57122982013-02-21 08:47:05 -080089def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050090 """
91 The the time value of an ASN1 time object.
92
93 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
94 castable to that type) which will have its value set.
95 @param when: A string representation of the desired time value.
96
97 @raise TypeError: If C{when} is not a L{bytes} string.
98 @raise ValueError: If C{when} does not represent a time in the required
99 format.
100 @raise RuntimeError: If the time value cannot be set for some other
101 (unspecified) reason.
102 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800103 if not isinstance(when, bytes):
104 raise TypeError("when must be a byte string")
105
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500106 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
107 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800108 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500109 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
110 _lib.ASN1_STRING_set(dummy, when, len(when))
111 check_result = _lib.ASN1_GENERALIZEDTIME_check(
112 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800113 if not check_result:
114 raise ValueError("Invalid string")
115 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500116 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800117
118
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800119def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500120 """
121 Retrieve the time value of an ASN1 time object.
122
123 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
124 that type) from which the time value will be retrieved.
125
126 @return: The time value from C{timestamp} as a L{bytes} string in a certain
127 format. Or C{None} if the object contains no time value.
128 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500129 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
130 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800131 return None
Alex Gaynor5945ea82015-09-05 14:59:06 -0400132 elif (
133 _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME
134 ):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500135 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800136 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500137 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
138 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
139 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500140 # This may happen:
141 # - if timestamp was not an ASN1_TIME
142 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
143 # - if a copy of the time data from timestamp cannot be made for
144 # the newly allocated ASN1_GENERALIZEDTIME
145 #
146 # These are difficult to test. cffi enforces the ASN1_TIME type.
147 # Memory allocation failures are a pain to trigger
148 # deterministically.
149 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800150 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500151 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800152 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500153 string_data = _lib.ASN1_STRING_data(string_timestamp)
154 string_result = _ffi.string(string_data)
155 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800156 return string_result
157
158
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800159class PKey(object):
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200160 """
161 A class representing an DSA or RSA public key or key pair.
162 """
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800163 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800164 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800165
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800166 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500167 pkey = _lib.EVP_PKEY_new()
168 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800169 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800170
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800171 def generate_key(self, type, bits):
172 """
Laurens Van Houtven90c09142015-04-23 10:52:49 -0700173 Generate a key pair of the given type, with the given number of bits.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800174
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200175 This generates a key "into" the this object.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800176
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200177 :param type: The key type.
178 :type type: :py:data:`TYPE_RSA` or :py:data:`TYPE_DSA`
179 :param bits: The number of bits.
180 :type bits: :py:data:`int` ``>= 0``
181 :raises TypeError: If :py:data:`type` or :py:data:`bits` isn't
182 of the appropriate type.
183 :raises ValueError: If the number of bits isn't an integer of
184 the appropriate size.
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200185 :return: :py:const:`None`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800186 """
187 if not isinstance(type, int):
188 raise TypeError("type must be an integer")
189
190 if not isinstance(bits, int):
191 raise TypeError("bits must be an integer")
192
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800193 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500194 exponent = _lib.BN_new()
195 exponent = _ffi.gc(exponent, _lib.BN_free)
196 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800197
198 if type == TYPE_RSA:
199 if bits <= 0:
200 raise ValueError("Invalid number of bits")
201
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500202 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800203
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500204 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500205 if result == 0:
206 # TODO: The test for this case is commented out. Different
207 # builds of OpenSSL appear to have different failure modes that
208 # make it hard to test. Visual inspection of the OpenSSL
209 # source reveals that a return value of 0 signals an error.
210 # Manual testing on a particular build of OpenSSL suggests that
211 # this is probably the appropriate way to handle those errors.
212 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800213
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500214 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800215 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500216 # TODO: It appears as though this can fail if an engine is in
217 # use which does not support RSA.
218 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800219
220 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500221 dsa = _lib.DSA_generate_parameters(
222 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
223 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500224 # TODO: This is untested.
225 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500226 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500227 # TODO: This is untested.
228 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500229 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500230 # TODO: This is untested.
231 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800232 else:
233 raise Error("No such key type")
234
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800235 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800236
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800237 def check(self):
238 """
239 Check the consistency of an RSA private key.
240
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200241 This is the Python equivalent of OpenSSL's ``RSA_check_key``.
242
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800243 :return: True if key is consistent.
244 :raise Error: if the key is inconsistent.
245 :raise TypeError: if the key is of a type which cannot be checked.
246 Only RSA keys can currently be checked.
247 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800248 if self._only_public:
249 raise TypeError("public key only")
250
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500251 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800252 raise TypeError("key type unsupported")
253
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500254 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
255 rsa = _ffi.gc(rsa, _lib.RSA_free)
256 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800257 if result:
258 return True
259 _raise_current_error()
260
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800261 def type(self):
262 """
263 Returns the type of the key
264
265 :return: The type of the key.
266 """
267 return self._pkey.type
268
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800269 def bits(self):
270 """
271 Returns the number of bits of the key
272
273 :return: The number of bits of the key.
274 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500275 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800276PKeyType = PKey
277
278
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400279class _EllipticCurve(object):
280 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400281 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400282
283 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
284 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
285 instances each of which represents one curve supported by the system.
286 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400287 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400288 _curves = None
289
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400290 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400291 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400292 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400293 """
294 Implement cooperation with the right-hand side argument of ``!=``.
295
296 Python 3 seems to have dropped this cooperation in this very narrow
297 circumstance.
298 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400299 if isinstance(other, _EllipticCurve):
300 return super(_EllipticCurve, self).__ne__(other)
301 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400302
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400303 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400304 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400305 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400306 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400307
308 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400309
310 :return: A :py:type:`set` of ``cls`` instances giving the names of the
311 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400312 """
313 if lib.Cryptography_HAS_EC:
314 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
315 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
Alex Gaynor5945ea82015-09-05 14:59:06 -0400316 # The return value on this call should be num_curves again. We
317 # could check it to make sure but if it *isn't* then.. what could
318 # we do? Abort the whole process, I suppose...? -exarkun
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400319 lib.EC_get_builtin_curves(builtin_curves, num_curves)
320 return set(
321 cls.from_nid(lib, c.nid)
322 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400323 return set()
324
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400325 @classmethod
326 def _get_elliptic_curves(cls, lib):
327 """
328 Get, cache, and return the curves supported by OpenSSL.
329
330 :param lib: The OpenSSL library binding object.
331
332 :return: A :py:type:`set` of ``cls`` instances giving the names of the
333 elliptic curves the underlying library supports.
334 """
335 if cls._curves is None:
336 cls._curves = cls._load_elliptic_curves(lib)
337 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400338
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400339 @classmethod
340 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400341 """
342 Instantiate a new :py:class:`_EllipticCurve` associated with the given
343 OpenSSL NID.
344
345 :param lib: The OpenSSL library binding object.
346
347 :param nid: The OpenSSL NID the resulting curve object will represent.
348 This must be a curve NID (and not, for example, a hash NID) or
349 subsequent operations will fail in unpredictable ways.
350 :type nid: :py:class:`int`
351
352 :return: The curve object.
353 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400354 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
355
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400356 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400357 """
358 :param _lib: The :py:mod:`cryptography` binding instance used to
359 interface with OpenSSL.
360
361 :param _nid: The OpenSSL NID identifying the curve this object
362 represents.
363 :type _nid: :py:class:`int`
364
365 :param name: The OpenSSL short name identifying the curve this object
366 represents.
367 :type name: :py:class:`unicode`
368 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400369 self._lib = lib
370 self._nid = nid
371 self.name = name
372
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400373 def __repr__(self):
374 return "<Curve %r>" % (self.name,)
375
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400376 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400377 """
378 Create a new OpenSSL EC_KEY structure initialized to use this curve.
379
380 The structure is automatically garbage collected when the Python object
381 is garbage collected.
382 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400383 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
384 return _ffi.gc(key, _lib.EC_KEY_free)
385
386
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400387def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400388 """
389 Return a set of objects representing the elliptic curves supported in the
390 OpenSSL build in use.
391
392 The curve objects have a :py:class:`unicode` ``name`` attribute by which
393 they identify themselves.
394
395 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400396 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
397 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400398 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400399 return _EllipticCurve._get_elliptic_curves(_lib)
400
401
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400402def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400403 """
404 Return a single curve object selected by name.
405
406 See :py:func:`get_elliptic_curves` for information about curve objects.
407
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400408 :param name: The OpenSSL short name identifying the curve object to
409 retrieve.
410 :type name: :py:class:`unicode`
411
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400412 If the named curve is not supported then :py:class:`ValueError` is raised.
413 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400414 for curve in get_elliptic_curves():
415 if curve.name == name:
416 return curve
417 raise ValueError("unknown curve name", name)
418
419
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800420class X509Name(object):
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200421 """
422 An X.509 Distinguished Name.
423
424 :ivar countryName: The country of the entity.
425 :ivar C: Alias for :py:attr:`countryName`.
426
427 :ivar stateOrProvinceName: The state or province of the entity.
428 :ivar ST: Alias for :py:attr:`stateOrProvinceName`.
429
430 :ivar localityName: The locality of the entity.
431 :ivar L: Alias for :py:attr:`localityName`.
432
433 :ivar organizationName: The organization name of the entity.
434 :ivar O: Alias for :py:attr:`organizationName`.
435
436 :ivar organizationalUnitName: The organizational unit of the entity.
437 :ivar OU: Alias for :py:attr:`organizationalUnitName`
438
439 :ivar commonName: The common name of the entity.
440 :ivar CN: Alias for :py:attr:`commonName`.
441
442 :ivar emailAddress: The e-mail address of the entity.
443 """
Alex Gaynor5945ea82015-09-05 14:59:06 -0400444
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800445 def __init__(self, name):
446 """
447 Create a new X509Name, copying the given X509Name instance.
448
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200449 :param name: The name to copy.
450 :type name: :py:class:`X509Name`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800451 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500452 name = _lib.X509_NAME_dup(name._name)
453 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800454
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800455 def __setattr__(self, name, value):
456 if name.startswith('_'):
457 return super(X509Name, self).__setattr__(name, value)
458
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800459 # Note: we really do not want str subclasses here, so we do not use
460 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800461 if type(name) is not str:
462 raise TypeError("attribute name must be string, not '%.200s'" % (
Alex Gaynora738ed52015-09-05 11:17:10 -0400463 type(value).__name__,))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800464
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500465 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500466 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800467 try:
468 _raise_current_error()
469 except Error:
470 pass
471 raise AttributeError("No such attribute")
472
473 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500474 for i in range(_lib.X509_NAME_entry_count(self._name)):
475 ent = _lib.X509_NAME_get_entry(self._name, i)
476 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
477 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800478 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500479 ent = _lib.X509_NAME_delete_entry(self._name, i)
480 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800481 break
482
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500483 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800484 value = value.encode('utf-8')
485
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500486 add_result = _lib.X509_NAME_add_entry_by_NID(
487 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800488 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500489 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800490
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800491 def __getattr__(self, name):
492 """
493 Find attribute. An X509Name object has the following attributes:
494 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
Alex Gaynor5945ea82015-09-05 14:59:06 -0400495 organization (alias O), organizationalUnit (alias OU), commonName
496 (alias CN) and more...
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800497 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500498 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500499 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800500 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
501 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
502 # push something onto the error queue. If we don't clean that up
503 # now, someone else will bump into it later and be quite confused.
504 # See lp#314814.
505 try:
506 _raise_current_error()
507 except Error:
508 pass
509 return super(X509Name, self).__getattr__(name)
510
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500511 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800512 if entry_index == -1:
513 return None
514
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500515 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
516 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800517
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500518 result_buffer = _ffi.new("unsigned char**")
519 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800520 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500521 # TODO: This is untested.
522 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800523
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700524 try:
Alex Gaynor5945ea82015-09-05 14:59:06 -0400525 result = _ffi.buffer(
526 result_buffer[0], data_length
527 )[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700528 finally:
529 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500530 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800531 return result
532
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500533 def _cmp(op):
534 def f(self, other):
535 if not isinstance(other, X509Name):
536 return NotImplemented
537 result = _lib.X509_NAME_cmp(self._name, other._name)
538 return op(result, 0)
539 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800540
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500541 __eq__ = _cmp(__eq__)
542 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800543
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500544 __lt__ = _cmp(__lt__)
545 __le__ = _cmp(__le__)
546
547 __gt__ = _cmp(__gt__)
548 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800549
550 def __repr__(self):
551 """
552 String representation of an X509Name
553 """
Alex Gaynor962ac212015-09-04 08:06:42 -0400554 result_buffer = _ffi.new("char[]", 512)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500555 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800556 self._name, result_buffer, len(result_buffer))
557
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500558 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500559 # TODO: This is untested.
560 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800561
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500562 return "<X509Name object '%s'>" % (
563 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800564
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800565 def hash(self):
566 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200567 Return an integer representation of the first four bytes of the
568 MD5 digest of the DER representation of the name.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800569
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200570 This is the Python equivalent of OpenSSL's ``X509_NAME_hash``.
571
572 :return: The (integer) hash of this name.
573 :rtype: :py:class:`int`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800574 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500575 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800576
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800577 def der(self):
578 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200579 Return the DER encoding of this name.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800580
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200581 :return: The DER encoded form of this name.
582 :rtype: :py:class:`bytes`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800583 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500584 result_buffer = _ffi.new('unsigned char**')
585 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800586 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500587 # TODO: This is untested.
588 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800589
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500590 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
591 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800592 return string_result
593
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800594 def get_components(self):
595 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200596 Returns the components of this name, as a sequence of 2-tuples.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800597
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200598 :return: The components of this name.
599 :rtype: :py:class:`list` of ``name, value`` tuples.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800600 """
601 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500602 for i in range(_lib.X509_NAME_entry_count(self._name)):
603 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800604
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500605 fname = _lib.X509_NAME_ENTRY_get_object(ent)
606 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800607
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500608 nid = _lib.OBJ_obj2nid(fname)
609 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800610
611 result.append((
Alex Gaynora738ed52015-09-05 11:17:10 -0400612 _ffi.string(name),
613 _ffi.string(
614 _lib.ASN1_STRING_data(fval),
615 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800616
617 return result
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200618
619
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800620X509NameType = X509Name
621
622
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800623class X509Extension(object):
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200624 """
625 An X.509 v3 certificate extension.
626 """
Alex Gaynor5945ea82015-09-05 14:59:06 -0400627
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800628 def __init__(self, type_name, critical, value, subject=None, issuer=None):
629 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200630 Initializes an X509 extension.
631
Alex Gaynor6f719912015-09-20 09:21:29 -0400632 :param type_name: The name of the type of extension to create. See
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200633 http://openssl.org/docs/apps/x509v3_config.html#STANDARD_EXTENSIONS
Alex Gaynor6f719912015-09-20 09:21:29 -0400634 :type type_name: :py:data:`bytes`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800635
Alex Gaynor5945ea82015-09-05 14:59:06 -0400636 :param bool critical: A flag indicating whether this is a critical
637 extension.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800638
639 :param value: The value of the extension.
Maximilian Hils0de43752015-09-18 15:26:54 +0200640 :type value: :py:data:`bytes`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800641
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200642 :param subject: Optional X509 certificate to use as subject.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800643 :type subject: :py:class:`X509`
644
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200645 :param issuer: Optional X509 certificate to use as issuer.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800646 :type issuer: :py:class:`X509`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800647 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500648 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800649
Alex Gaynor5945ea82015-09-05 14:59:06 -0400650 # A context is necessary for any extension which uses the r2i
651 # conversion method. That is, X509V3_EXT_nconf may segfault if passed
652 # a NULL ctx. Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500653 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800654
655 # We have no configuration database - but perhaps we should (some
656 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500657 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800658
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800659 # Initialize the subject and issuer, if appropriate. ctx is a local,
660 # and as far as I can tell none of the X509V3_* APIs invoked here steal
Alex Gaynora738ed52015-09-05 11:17:10 -0400661 # any references, so no need to mess with reference counts or
662 # duplicates.
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800663 if issuer is not None:
664 if not isinstance(issuer, X509):
665 raise TypeError("issuer must be an X509 instance")
666 ctx.issuer_cert = issuer._x509
667 if subject is not None:
668 if not isinstance(subject, X509):
669 raise TypeError("subject must be an X509 instance")
670 ctx.subject_cert = subject._x509
671
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800672 if critical:
673 # There are other OpenSSL APIs which would let us pass in critical
674 # separately, but they're harder to use, and since value is already
675 # a pile of crappy junk smuggling a ton of utterly important
676 # structured data, what's the point of trying to avoid nasty stuff
Alex Gaynor5945ea82015-09-05 14:59:06 -0400677 # with strings? (However, X509V3_EXT_i2d in particular seems like
678 # it would be a better API to invoke. I do not know where to get
679 # the ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500680 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800681
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500682 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
683 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800684 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500685 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800686
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400687 @property
688 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500689 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400690
691 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500692 _lib.GEN_EMAIL: "email",
693 _lib.GEN_DNS: "DNS",
694 _lib.GEN_URI: "URI",
Alex Gaynora738ed52015-09-05 11:17:10 -0400695 }
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400696
697 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500698 method = _lib.X509V3_EXT_get(self._extension)
699 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500700 # TODO: This is untested.
701 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400702 payload = self._extension.value.data
703 length = self._extension.value.length
704
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500705 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400706 payloadptr[0] = payload
707
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500708 if method.it != _ffi.NULL:
709 ptr = _lib.ASN1_ITEM_ptr(method.it)
710 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
711 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400712 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500713 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400714 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500715 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400716
Paul Kehrerb7d79502015-05-04 07:43:51 -0500717 names = _ffi.gc(names, _lib.GENERAL_NAMES_free)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400718 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500719 for i in range(_lib.sk_GENERAL_NAME_num(names)):
720 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400721 try:
722 label = self._prefixes[name.type]
723 except KeyError:
724 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500725 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500726 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400727 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500728 value = _native(
729 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
730 parts.append(label + ":" + value)
731 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400732
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800733 def __str__(self):
734 """
735 :return: a nice text representation of the extension
736 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500737 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400738 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800739
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400740 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500741 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800742 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500743 # TODO: This is untested.
744 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800745
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500746 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800747
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800748 def get_critical(self):
749 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200750 Returns the critical field of this X.509 extension.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800751
752 :return: The critical field.
753 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500754 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800755
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800756 def get_short_name(self):
757 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200758 Returns the short type name of this X.509 extension.
759
760 The result is a byte string such as :py:const:`b"basicConstraints"`.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800761
762 :return: The short type name.
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200763 :rtype: :py:data:`bytes`
764
765 .. versionadded:: 0.12
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800766 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500767 obj = _lib.X509_EXTENSION_get_object(self._extension)
768 nid = _lib.OBJ_obj2nid(obj)
769 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800770
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800771 def get_data(self):
772 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200773 Returns the data of the X509 extension, encoded as ASN.1.
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800774
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200775 :return: The ASN.1 encoded data of this X509 extension.
776 :rtype: :py:data:`bytes`
777
778 .. versionadded:: 0.12
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800779 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500780 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
781 string_result = _ffi.cast('ASN1_STRING*', octet_result)
782 char_result = _lib.ASN1_STRING_data(string_result)
783 result_length = _lib.ASN1_STRING_length(string_result)
784 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800785
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200786
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800787X509ExtensionType = X509Extension
788
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800789
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800790class X509Req(object):
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200791 """
792 An X.509 certificate signing requests.
793 """
Alex Gaynora738ed52015-09-05 11:17:10 -0400794
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800795 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500796 req = _lib.X509_REQ_new()
797 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800798
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800799 def set_pubkey(self, pkey):
800 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200801 Set the public key of the certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800802
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200803 :param pkey: The public key to use.
804 :type pkey: :py:class:`PKey`
805
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200806 :return: :py:const:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800807 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500808 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800809 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500810 # TODO: This is untested.
811 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800812
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800813 def get_pubkey(self):
814 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200815 Get the public key of the certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800816
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200817 :return: The public key.
818 :rtype: :py:class:`PKey`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800819 """
820 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500821 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
822 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500823 # TODO: This is untested.
824 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500825 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800826 pkey._only_public = True
827 return pkey
828
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800829 def set_version(self, version):
830 """
831 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
832 request.
833
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200834 :param int version: The version number.
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200835 :return: :py:const:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800836 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500837 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800838 if not set_result:
839 _raise_current_error()
840
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800841 def get_version(self):
842 """
843 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
844 request.
845
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200846 :return: The value of the version subfield.
847 :rtype: :py:class:`int`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800848 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500849 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800850
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800851 def get_subject(self):
852 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200853 Return the subject of this certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800854
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200855 This creates a new :py:class:`X509Name`: modifying it does not affect
856 this request.
857
858 :return: The subject of this certificate signing request.
859 :rtype: :py:class:`X509Name`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800860 """
861 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500862 name._name = _lib.X509_REQ_get_subject_name(self._req)
863 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500864 # TODO: This is untested.
865 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800866
867 # The name is owned by the X509Req structure. As long as the X509Name
868 # Python object is alive, keep the X509Req Python object alive.
869 name._owner = self
870
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800871 return name
872
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800873 def add_extensions(self, extensions):
874 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200875 Add extensions to the certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800876
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200877 :param extensions: The X.509 extensions to add.
878 :type extensions: iterable of :py:class:`X509Extension`
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200879 :return: :py:const:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800880 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500881 stack = _lib.sk_X509_EXTENSION_new_null()
882 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500883 # TODO: This is untested.
884 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800885
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500886 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800887
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800888 for ext in extensions:
889 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800890 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800891
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800892 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500893 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800894
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500895 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800896 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500897 # TODO: This is untested.
898 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800899
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800900 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800901 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200902 Get X.509 extensions in the certificate signing request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800903
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200904 :return: The X.509 extensions in this request.
905 :rtype: :py:class:`list` of :py:class:`X509Extension` objects.
906
907 .. versionadded:: 0.15
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800908 """
909 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500910 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500911 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800912 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500913 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800914 exts.append(ext)
915 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800916
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800917 def sign(self, pkey, digest):
918 """
Laurens Van Houtven6f2e4262015-04-23 10:48:32 -0700919 Sign the certificate signing request with this key and digest type.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800920
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200921 :param pkey: The key pair to sign with.
922 :type pkey: :py:class:`PKey`
923 :param digest: The name of the message digest to use for the signature,
924 e.g. :py:data:`b"sha1"`.
925 :type digest: :py:class:`bytes`
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200926 :return: :py:const:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800927 """
928 if pkey._only_public:
929 raise ValueError("Key has only public part")
930
931 if not pkey._initialized:
932 raise ValueError("Key is uninitialized")
933
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500934 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500935 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800936 raise ValueError("No such digest method")
937
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500938 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800939 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500940 # TODO: This is untested.
941 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800942
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800943 def verify(self, pkey):
944 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200945 Verifies the signature on this certificate signing request.
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800946
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200947 :param key: A public key.
948 :type key: :py:class:`PKey`
949 :return: :py:data:`True` if the signature is correct.
950 :rtype: :py:class:`bool`
951 :raises Error: If the signature is invalid or there is a
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800952 problem verifying the signature.
953 """
954 if not isinstance(pkey, PKey):
955 raise TypeError("pkey must be a PKey instance")
956
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500957 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800958 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500959 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800960
961 return result
962
963
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800964X509ReqType = X509Req
965
966
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800967class X509(object):
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200968 """
969 An X.509 certificate.
970 """
Alex Gaynora738ed52015-09-05 11:17:10 -0400971
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800972 def __init__(self):
973 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500974 x509 = _lib.X509_new()
975 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800976
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800977 def set_version(self, version):
978 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200979 Set the version number of the certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800980
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200981 :param version: The version number of the certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800982 :type version: :py:class:`int`
983
Laurens Van Houtvena7904582014-06-19 12:33:04 +0200984 :return: :py:const:`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800985 """
986 if not isinstance(version, int):
987 raise TypeError("version must be an integer")
988
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500989 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800990
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800991 def get_version(self):
992 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200993 Return the version number of the certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800994
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +0200995 :return: The version number of the certificate.
996 :rtype: :py:class:`int`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800997 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500998 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800999
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001000 def get_pubkey(self):
1001 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001002 Get the public key of the certificate.
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001003
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001004 :return: The public key.
1005 :rtype: :py:class:`PKey`
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001006 """
1007 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001008 pkey._pkey = _lib.X509_get_pubkey(self._x509)
1009 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001010 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001011 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001012 pkey._only_public = True
1013 return pkey
1014
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001015 def set_pubkey(self, pkey):
1016 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001017 Set the public key of the certificate.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001018
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001019 :param pkey: The public key.
1020 :type pkey: :py:class:`PKey`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001021
Laurens Van Houtven33fcf122015-04-23 10:50:08 -07001022 :return: :py:data:`None`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001023 """
1024 if not isinstance(pkey, PKey):
1025 raise TypeError("pkey must be a PKey instance")
1026
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001027 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001028 if not set_result:
1029 _raise_current_error()
1030
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001031 def sign(self, pkey, digest):
1032 """
Laurens Van Houtven6f2e4262015-04-23 10:48:32 -07001033 Sign the certificate with this key and digest type.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001034
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001035 :param pkey: The key to sign with.
1036 :type pkey: :py:class:`PKey`
1037
1038 :param digest: The name of the message digest to use.
1039 :type digest: :py:class:`bytes`
1040
Laurens Van Houtvena367fe82015-04-23 10:49:12 -07001041 :return: :py:data:`None`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001042 """
1043 if not isinstance(pkey, PKey):
1044 raise TypeError("pkey must be a PKey instance")
1045
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001046 if pkey._only_public:
1047 raise ValueError("Key only has public part")
1048
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -08001049 if not pkey._initialized:
1050 raise ValueError("Key is uninitialized")
1051
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001052 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001053 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001054 raise ValueError("No such digest method")
1055
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001056 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001057 if not sign_result:
1058 _raise_current_error()
1059
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001060 def get_signature_algorithm(self):
1061 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001062 Return the signature algorithm used in the certificate.
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001063
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001064 :return: The name of the algorithm.
1065 :rtype: :py:class:`bytes`
1066
1067 :raises ValueError: If the signature algorithm is undefined.
1068
Laurens Van Houtven0dd87402015-04-23 10:47:18 -07001069 .. versionadded:: 0.13
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001070 """
1071 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001072 nid = _lib.OBJ_obj2nid(alg)
1073 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001074 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001075 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001076
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001077 def digest(self, digest_name):
1078 """
1079 Return the digest of the X509 object.
1080
1081 :param digest_name: The name of the digest algorithm to use.
1082 :type digest_name: :py:class:`bytes`
1083
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001084 :return: The digest of the object, formatted as
1085 :py:const:`b":"`-delimited hex pairs.
1086 :rtype: :py:class:`bytes`
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001087 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001088 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001089 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001090 raise ValueError("No such digest method")
1091
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001092 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1093 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001094 result_length[0] = len(result_buffer)
1095
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001096 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001097 self._x509, digest, result_buffer, result_length)
1098
1099 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001100 # TODO: This is untested.
1101 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001102
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001103 return b":".join([
Alex Gaynora738ed52015-09-05 11:17:10 -04001104 b16encode(ch).upper() for ch
1105 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001106
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001107 def subject_name_hash(self):
1108 """
1109 Return the hash of the X509 subject.
1110
1111 :return: The hash of the subject.
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001112 :rtype: :py:class:`bytes`
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001113 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001114 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001115
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001116 def set_serial_number(self, serial):
1117 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001118 Set the serial number of the certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001119
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001120 :param serial: The new serial number.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001121 :type serial: :py:class:`int`
1122
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001123 :return: :py:data`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001124 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001125 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001126 raise TypeError("serial must be an integer")
1127
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001128 hex_serial = hex(serial)[2:]
1129 if not isinstance(hex_serial, bytes):
1130 hex_serial = hex_serial.encode('ascii')
1131
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001132 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001133
1134 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
Alex Gaynor5945ea82015-09-05 14:59:06 -04001135 # it. If bignum is still NULL after this call, then the return value
1136 # is actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001137 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001138
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001139 if bignum_serial[0] == _ffi.NULL:
1140 set_result = _lib.ASN1_INTEGER_set(
1141 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001142 if set_result:
1143 # TODO Not tested
1144 _raise_current_error()
1145 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001146 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1147 _lib.BN_free(bignum_serial[0])
1148 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001149 # TODO Not tested
1150 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001151 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1152 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001153 if not set_result:
1154 # TODO Not tested
1155 _raise_current_error()
1156
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001157 def get_serial_number(self):
1158 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001159 Return the serial number of this certificate.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001160
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001161 :return: The serial number.
1162 :rtype: :py:class:`int`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001163 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001164 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1165 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001166 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001167 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001168 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001169 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001170 serial = int(hexstring_serial, 16)
1171 return serial
1172 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001173 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001174 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001175 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001176
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001177 def gmtime_adj_notAfter(self, amount):
1178 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001179 Adjust the time stamp on which the certificate stops being valid.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001180
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001181 :param amount: The number of seconds by which to adjust the timestamp.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001182 :type amount: :py:class:`int`
1183
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001184 :return: :py:const:`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001185 """
1186 if not isinstance(amount, int):
1187 raise TypeError("amount must be an integer")
1188
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001189 notAfter = _lib.X509_get_notAfter(self._x509)
1190 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001191
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001192 def gmtime_adj_notBefore(self, amount):
1193 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001194 Adjust the timestamp on which the certificate starts being valid.
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001195
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001196 :param amount: The number of seconds by which to adjust the timestamp.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001197 :return: :py:const:`None`
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001198 """
1199 if not isinstance(amount, int):
1200 raise TypeError("amount must be an integer")
1201
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001202 notBefore = _lib.X509_get_notBefore(self._x509)
1203 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001204
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001205 def has_expired(self):
1206 """
1207 Check whether the certificate has expired.
1208
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001209 :return: :py:const:`True` if the certificate has expired,
1210 :py:const:`False` otherwise.
1211 :rtype: :py:class:`bool`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001212 """
Paul Kehrer8d887e12015-10-24 09:09:55 -05001213 time_string = _native(self.get_notAfter())
Paul Kehrer5d5d28d2015-10-21 18:55:22 -05001214 timestamp = mktime(datetime.datetime.strptime(
1215 time_string, "%Y%m%d%H%M%SZ").timetuple())
1216 now = mktime(datetime.datetime.utcnow().timetuple())
1217
1218 return timestamp < now
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001219
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001220 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001221 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001222
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001223 def get_notBefore(self):
1224 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001225 Get the timestamp at which the certificate starts being valid.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001226
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001227 The timestamp is formatted as an ASN.1 GENERALIZEDTIME::
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001228
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001229 YYYYMMDDhhmmssZ
1230 YYYYMMDDhhmmss+hhmm
1231 YYYYMMDDhhmmss-hhmm
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001232
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001233 :return: A timestamp string, or :py:const:`None` if there is none.
1234 :rtype: :py:class:`bytes` or :py:const:`None`
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001235 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001236 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001237
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001238 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001239 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001240
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001241 def set_notBefore(self, when):
1242 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001243 Set the timestamp at which the certificate starts being valid.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001244
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001245 The timestamp is formatted as an ASN.1 GENERALIZEDTIME::
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001246
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001247 YYYYMMDDhhmmssZ
1248 YYYYMMDDhhmmss+hhmm
1249 YYYYMMDDhhmmss-hhmm
1250
1251 :param when: A timestamp string.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001252 :type when: :py:class:`bytes`
1253
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001254 :return: :py:const:`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001255 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001256 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001257
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001258 def get_notAfter(self):
1259 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001260 Get the timestamp at which the certificate stops being valid.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001261
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001262 The timestamp is formatted as an ASN.1 GENERALIZEDTIME::
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001263
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001264 YYYYMMDDhhmmssZ
1265 YYYYMMDDhhmmss+hhmm
1266 YYYYMMDDhhmmss-hhmm
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001267
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001268 :return: A timestamp string, or :py:const:`None` if there is none.
1269 :rtype: :py:class:`bytes` or :py:const:`None`
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001270 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001271 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001272
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001273 def set_notAfter(self, when):
1274 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001275 Set the timestamp at which the certificate stops being valid.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001276
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001277 The timestamp is formatted as an ASN.1 GENERALIZEDTIME::
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001278
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001279 YYYYMMDDhhmmssZ
1280 YYYYMMDDhhmmss+hhmm
1281 YYYYMMDDhhmmss-hhmm
1282
1283 :param when: A timestamp string.
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001284 :type when: :py:class:`bytes`
1285
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001286 :return: :py:const:`None`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001287 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001288 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001289
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001290 def _get_name(self, which):
1291 name = X509Name.__new__(X509Name)
1292 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001293 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001294 # TODO: This is untested.
1295 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001296
1297 # The name is owned by the X509 structure. As long as the X509Name
1298 # Python object is alive, keep the X509 Python object alive.
1299 name._owner = self
1300
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001301 return name
1302
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001303 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001304 if not isinstance(name, X509Name):
1305 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001306 set_result = which(self._x509, name._name)
1307 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001308 # TODO: This is untested.
1309 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001310
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001311 def get_issuer(self):
1312 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001313 Return the issuer of this certificate.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001314
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001315 This creates a new :py:class:`X509Name`: modifying it does not affect
1316 this certificate.
1317
1318 :return: The issuer of this certificate.
1319 :rtype: :py:class:`X509Name`
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001320 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001321 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001322
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001323 def set_issuer(self, issuer):
1324 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001325 Set the issuer of this certificate.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001326
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001327 :param issuer: The issuer.
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001328 :type issuer: :py:class:`X509Name`
1329
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001330 :return: :py:const:`None`
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001331 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001332 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001333
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001334 def get_subject(self):
1335 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001336 Return the subject of this certificate.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001337
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001338 This creates a new :py:class:`X509Name`: modifying it does not affect
1339 this certificate.
1340
1341 :return: The subject of this certificate.
1342 :rtype: :py:class:`X509Name`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001343 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001344 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001345
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001346 def set_subject(self, subject):
1347 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001348 Set the subject of this certificate.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001349
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001350 :param subject: The subject.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001351 :type subject: :py:class:`X509Name`
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001352
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001353 :return: :py:const:`None`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001354 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001355 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001356
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001357 def get_extension_count(self):
1358 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001359 Get the number of extensions on this certificate.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001360
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001361 :return: The number of extensions.
1362 :rtype: :py:class:`int`
1363
1364 .. versionadded:: 0.12
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001365 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001366 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001367
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001368 def add_extensions(self, extensions):
1369 """
1370 Add extensions to the certificate.
1371
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001372 :param extensions: The extensions to add.
1373 :type extensions: An iterable of :py:class:`X509Extension` objects.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001374 :return: :py:const:`None`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001375 """
1376 for ext in extensions:
1377 if not isinstance(ext, X509Extension):
1378 raise ValueError("One of the elements is not an X509Extension")
1379
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001380 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001381 if not add_result:
1382 _raise_current_error()
1383
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001384 def get_extension(self, index):
1385 """
1386 Get a specific extension of the certificate by index.
1387
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02001388 Extensions on a certificate are kept in order. The index
1389 parameter selects which extension will be returned.
1390
1391 :param int index: The index of the extension to retrieve.
1392 :return: The extension at the specified index.
1393 :rtype: :py:class:`X509Extension`
1394 :raises IndexError: If the extension index was out of bounds.
1395
1396 .. versionadded:: 0.12
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001397 """
1398 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001399 ext._extension = _lib.X509_get_ext(self._x509, index)
1400 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001401 raise IndexError("extension index out of bounds")
1402
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001403 extension = _lib.X509_EXTENSION_dup(ext._extension)
1404 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001405 return ext
1406
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001407
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001408X509Type = X509
1409
1410
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001411class X509Store(object):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001412 """
1413 An X509 certificate store.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001414 """
Alex Gaynora738ed52015-09-05 11:17:10 -04001415
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001416 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001417 store = _lib.X509_STORE_new()
1418 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001419
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001420 def add_cert(self, cert):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001421 """
1422 Adds the certificate :py:data:`cert` to this store.
1423
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +02001424 This is the Python equivalent of OpenSSL's ``X509_STORE_add_cert``.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001425
1426 :param X509 cert: The certificate to add to this store.
1427 :raises TypeError: If the certificate is not an :py:class:`X509`.
1428 :raises Error: If OpenSSL was unhappy with your certificate.
Laurens Van Houtven5ee60b32015-04-23 10:51:16 -07001429 :return: :py:data:`None` if the certificate was added successfully.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001430 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001431 if not isinstance(cert, X509):
1432 raise TypeError()
1433
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001434 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001435 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001436 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001437
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001438
1439X509StoreType = X509Store
1440
1441
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001442class X509StoreContextError(Exception):
1443 """
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001444 An exception raised when an error occurred while verifying a certificate
1445 using `OpenSSL.X509StoreContext.verify_certificate`.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001446
Jean-Paul Calderonefeb17432015-03-15 15:49:45 -04001447 :ivar certificate: The certificate which caused verificate failure.
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001448 :type certificate: :class:`X509`
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001449 """
Alex Gaynora738ed52015-09-05 11:17:10 -04001450
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001451 def __init__(self, message, certificate):
1452 super(X509StoreContextError, self).__init__(message)
1453 self.certificate = certificate
1454
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001455
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001456class X509StoreContext(object):
1457 """
1458 An X.509 store context.
1459
Jean-Paul Calderone13a81682015-01-18 15:49:15 -05001460 An :py:class:`X509StoreContext` is used to define some of the criteria for
1461 certificate verification. The information encapsulated in this object
1462 includes, but is not limited to, a set of trusted certificates,
1463 verification parameters, and revoked certificates.
1464
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001465 .. note::
1466
1467 Currently, one can only set the trusted certificates on an
1468 :py:class:`X509StoreContext`. Future versions of pyOpenSSL will expose
1469 verification parameters and certificate revocation lists.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001470
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001471 :ivar _store_ctx: The underlying X509_STORE_CTX structure used by this
1472 instance. It is dynamically allocated and automatically garbage
1473 collected.
1474
Jean-Paul Calderone64b6b842015-03-15 16:08:02 -04001475 :ivar _store: See the ``store`` ``__init__`` parameter.
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001476
Jean-Paul Calderone64b6b842015-03-15 16:08:02 -04001477 :ivar _cert: See the ``certificate`` ``__init__`` parameter.
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001478
1479 :param X509Store store: The certificates which will be trusted for the
1480 purposes of any verifications.
1481
1482 :param X509 certificate: The certificate to be verified.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001483 """
1484
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001485 def __init__(self, store, certificate):
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001486 store_ctx = _lib.X509_STORE_CTX_new()
1487 self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
1488 self._store = store
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001489 self._cert = certificate
Stephen Holsapple46a09252015-02-12 14:45:43 -08001490 # Make the store context available for use after instantiating this
1491 # class by initializing it now. Per testing, subsequent calls to
1492 # :py:meth:`_init` have no adverse affect.
1493 self._init()
Jean-Paul Calderoneb7b7fb92015-01-18 15:37:10 -05001494
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001495 def _init(self):
1496 """
1497 Set up the store context for a subsequent verification operation.
1498 """
Alex Gaynor5945ea82015-09-05 14:59:06 -04001499 ret = _lib.X509_STORE_CTX_init(
1500 self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL
1501 )
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001502 if ret <= 0:
1503 _raise_current_error()
1504
1505 def _cleanup(self):
1506 """
1507 Internally cleans up the store context.
1508
1509 The store context can then be reused with a new call to
Stephen Holsapple46a09252015-02-12 14:45:43 -08001510 :py:meth:`_init`.
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001511 """
1512 _lib.X509_STORE_CTX_cleanup(self._store_ctx)
1513
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001514 def _exception_from_context(self):
1515 """
1516 Convert an OpenSSL native context error failure into a Python
1517 exception.
1518
Alex Gaynor5945ea82015-09-05 14:59:06 -04001519 When a call to native OpenSSL X509_verify_cert fails, additional
1520 information about the failure can be obtained from the store context.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001521 """
1522 errors = [
1523 _lib.X509_STORE_CTX_get_error(self._store_ctx),
1524 _lib.X509_STORE_CTX_get_error_depth(self._store_ctx),
1525 _native(_ffi.string(_lib.X509_verify_cert_error_string(
Alex Gaynor5945ea82015-09-05 14:59:06 -04001526 _lib.X509_STORE_CTX_get_error(self._store_ctx)))),
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001527 ]
Stephen Holsapple1f713eb2015-02-09 19:19:44 -08001528 # A context error should always be associated with a certificate, so we
1529 # expect this call to never return :class:`None`.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001530 _x509 = _lib.X509_STORE_CTX_get_current_cert(self._store_ctx)
Stephen Holsapple1f713eb2015-02-09 19:19:44 -08001531 _cert = _lib.X509_dup(_x509)
1532 pycert = X509.__new__(X509)
1533 pycert._x509 = _ffi.gc(_cert, _lib.X509_free)
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001534 return X509StoreContextError(errors, pycert)
1535
Stephen Holsapple46a09252015-02-12 14:45:43 -08001536 def set_store(self, store):
1537 """
1538 Set the context's trust store.
1539
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001540 .. versionadded:: 0.15
1541
Stephen Holsapple46a09252015-02-12 14:45:43 -08001542 :param X509Store store: The certificates which will be trusted for the
1543 purposes of any *future* verifications.
1544 """
1545 self._store = store
1546
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001547 def verify_certificate(self):
1548 """
1549 Verify a certificate in a context.
1550
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001551 .. versionadded:: 0.15
1552
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001553 :param store_ctx: The :py:class:`X509StoreContext` to verify.
Stephen Holsapple8ad4a192015-06-09 22:51:43 -07001554
Alex Gaynorca87ff62015-09-04 23:31:03 -04001555 :raises X509StoreContextError: If an error occurred when validating a
Alex Gaynor5945ea82015-09-05 14:59:06 -04001556 certificate in the context. Sets ``certificate`` attribute to
1557 indicate which certificate caused the error.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001558 """
Stephen Holsapple46a09252015-02-12 14:45:43 -08001559 # Always re-initialize the store context in case
1560 # :py:meth:`verify_certificate` is called multiple times.
Stephen Holsapple08ffaa62015-01-30 17:18:40 -08001561 self._init()
1562 ret = _lib.X509_verify_cert(self._store_ctx)
1563 self._cleanup()
1564 if ret <= 0:
1565 raise self._exception_from_context()
1566
1567
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001568def load_certificate(type, buffer):
1569 """
1570 Load a certificate from a buffer
1571
1572 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1573
1574 :param buffer: The buffer the certificate is stored in
1575 :type buffer: :py:class:`bytes`
1576
1577 :return: The X509 object
1578 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001579 if isinstance(buffer, _text_type):
1580 buffer = buffer.encode("ascii")
1581
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001582 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001583
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001584 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001585 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001586 elif type == FILETYPE_ASN1:
Alex Gaynor962ac212015-09-04 08:06:42 -04001587 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001588 else:
1589 raise ValueError(
1590 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001591
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001592 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001593 _raise_current_error()
1594
1595 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001596 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001597 return cert
1598
1599
1600def dump_certificate(type, cert):
1601 """
1602 Dump a certificate to a buffer
1603
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001604 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1605 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001606 :param cert: The certificate to dump
1607 :return: The buffer with the dumped certificate in
1608 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001609 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001610
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001611 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001612 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001613 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001614 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001615 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001616 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001617 else:
1618 raise ValueError(
1619 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1620 "FILETYPE_TEXT")
1621
Alex Gaynorc7a9eb52015-09-05 16:57:49 -04001622 assert result_code == 1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001623 return _bio_to_string(bio)
1624
1625
Cory Benfield6492f7c2015-10-27 16:57:58 +09001626def dump_publickey(type, pkey):
1627 """
Cory Benfield11c10192015-10-27 17:23:03 +09001628 Dump a public key to a buffer.
Cory Benfield6492f7c2015-10-27 16:57:58 +09001629
Cory Benfield11c10192015-10-27 17:23:03 +09001630 :param type: The file type (one of :py:data:`FILETYPE_PEM` or
Cory Benfielde813cec2015-10-28 08:57:08 +09001631 :data:`FILETYPE_ASN1`).
1632 :param pkey: The :class:`PKey` to dump.
Cory Benfield6492f7c2015-10-27 16:57:58 +09001633 :return: The buffer with the dumped key in it.
Cory Benfield11c10192015-10-27 17:23:03 +09001634 :rtype: bytes
Cory Benfield6492f7c2015-10-27 16:57:58 +09001635 """
1636 bio = _new_mem_buf()
1637 if type == FILETYPE_PEM:
1638 write_bio = _lib.PEM_write_bio_PUBKEY
1639 elif type == FILETYPE_ASN1:
1640 write_bio = _lib.i2d_PUBKEY_bio
1641 else:
1642 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
1643
1644 result_code = write_bio(bio, pkey._pkey)
Cory Benfield1e9c7ab2015-10-28 08:58:31 +09001645 if result_code != 1: # pragma: no cover
Cory Benfield6492f7c2015-10-27 16:57:58 +09001646 _raise_current_error()
1647
1648 return _bio_to_string(bio)
1649
1650
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001651def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1652 """
1653 Dump a private key to a buffer
1654
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001655 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1656 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001657 :param pkey: The PKey to dump
1658 :param cipher: (optional) if encrypted PEM format, the cipher to
1659 use
1660 :param passphrase: (optional) if encrypted PEM format, this can be either
1661 the passphrase to use, or a callback for providing the
1662 passphrase.
1663 :return: The buffer with the dumped key in
Maximilian Hils0de43752015-09-18 15:26:54 +02001664 :rtype: :py:data:`bytes`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001665 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001666 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001667
1668 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001669 if passphrase is None:
1670 raise TypeError(
1671 "if a value is given for cipher "
1672 "one must also be given for passphrase")
1673 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001674 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001675 raise ValueError("Invalid cipher name")
1676 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001677 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001678
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001679 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001680 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001681 result_code = _lib.PEM_write_bio_PrivateKey(
1682 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001683 helper.callback, helper.callback_args)
1684 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001685 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001686 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001687 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001688 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1689 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001690 # TODO RSA_free(rsa)?
1691 else:
1692 raise ValueError(
1693 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1694 "FILETYPE_TEXT")
1695
1696 if result_code == 0:
1697 _raise_current_error()
1698
1699 return _bio_to_string(bio)
1700
1701
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001702def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001703 copy = _lib.X509_REVOKED_new()
1704 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001705 # TODO: This is untested.
1706 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001707
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001708 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001709 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001710 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001711
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001712 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001713 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001714 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001715
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001716 if original.extensions != _ffi.NULL:
1717 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1718 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1719 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1720 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1721 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001722 copy.extensions = extension_stack
1723
1724 copy.sequence = original.sequence
1725 return copy
1726
1727
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001728class Revoked(object):
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001729 """
1730 A certificate revocation.
1731 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001732 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1733 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1734 # OCSP_crl_reason_str. We use the latter, just like the command line
1735 # program.
1736 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001737 b"unspecified",
1738 b"keyCompromise",
1739 b"CACompromise",
1740 b"affiliationChanged",
1741 b"superseded",
1742 b"cessationOfOperation",
1743 b"certificateHold",
1744 # b"removeFromCRL",
Alex Gaynorca87ff62015-09-04 23:31:03 -04001745 ]
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001746
1747 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001748 revoked = _lib.X509_REVOKED_new()
1749 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001750
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001751 def set_serial(self, hex_str):
1752 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001753 Set the serial number.
1754
1755 The serial number is formatted as a hexadecimal number encoded in
1756 ASCII.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001757
1758 :param hex_str: The new serial number.
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001759 :type hex_str: :py:class:`bytes`
1760
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001761 :return: :py:const:`None`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001762 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001763 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1764 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001765 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001766 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001767 if not bn_result:
1768 raise ValueError("bad hex string")
1769
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001770 asn1_serial = _ffi.gc(
1771 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1772 _lib.ASN1_INTEGER_free)
1773 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001774
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001775 def get_serial(self):
1776 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001777 Get the serial number.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001778
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001779 The serial number is formatted as a hexadecimal number encoded in
1780 ASCII.
1781
1782 :return: The serial number.
1783 :rtype: :py:class:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001784 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001785 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001786
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001787 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001788 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001789 # TODO: This is untested.
1790 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001791
1792 return _bio_to_string(bio)
1793
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001794 def _delete_reason(self):
1795 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001796 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1797 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1798 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1799 _lib.X509_EXTENSION_free(ext)
1800 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001801 break
1802
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001803 def set_reason(self, reason):
1804 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001805 Set the reason of this revocation.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001806
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001807 If :py:data:`reason` is :py:const:`None`, delete the reason instead.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001808
1809 :param reason: The reason string.
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001810 :type reason: :py:class:`bytes` or :py:class:`NoneType`
1811
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001812 :return: :py:const:`None`
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001813
1814 .. seealso::
1815
1816 :py:meth:`all_reasons`, which gives you a list of all supported
1817 reasons which you might pass to this method.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001818 """
1819 if reason is None:
1820 self._delete_reason()
1821 elif not isinstance(reason, bytes):
1822 raise TypeError("reason must be None or a byte string")
1823 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001824 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001825 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1826
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001827 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1828 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001829 # TODO: This is untested.
1830 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001831 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001832
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001833 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1834 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001835 # TODO: This is untested.
1836 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001837
1838 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001839 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1840 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001841
1842 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001843 # TODO: This is untested.
1844 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001845
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001846 def get_reason(self):
1847 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001848 Set the reason of this revocation.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001849
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001850 :return: The reason, or :py:const:`None` if there is none.
1851 :rtype: :py:class:`bytes` or :py:class:`NoneType`
1852
1853 .. seealso::
1854
1855 :py:meth:`all_reasons`, which gives you a list of all supported
1856 reasons this method might return.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001857 """
1858 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001859 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1860 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1861 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001862 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001863
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001864 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001865 if not print_result:
Alex Gaynor5945ea82015-09-05 14:59:06 -04001866 print_result = _lib.M_ASN1_OCTET_STRING_print(
1867 bio, ext.value
1868 )
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001869 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001870 # TODO: This is untested.
1871 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001872
1873 return _bio_to_string(bio)
1874
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001875 def all_reasons(self):
1876 """
1877 Return a list of all the supported reason strings.
1878
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001879 This list is a copy; modifying it does not change the supported reason
1880 strings.
1881
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001882 :return: A list of reason strings.
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001883 :rtype: :py:class:`list` of :py:class:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001884 """
1885 return self._crl_reasons[:]
1886
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001887 def set_rev_date(self, when):
1888 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001889 Set the revocation timestamp.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001890
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001891 :param when: The timestamp of the revocation, as ASN.1 GENERALIZEDTIME.
1892 :type when: :py:class:`bytes`
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001893 :return: :py:const:`None`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001894 """
1895 return _set_asn1_time(self._revoked.revocationDate, when)
1896
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001897 def get_rev_date(self):
1898 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001899 Get the revocation timestamp.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001900
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001901 :return: The timestamp of the revocation, as ASN.1 GENERALIZEDTIME.
1902 :rtype: :py:class:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001903 """
1904 return _get_asn1_time(self._revoked.revocationDate)
1905
1906
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001907class CRL(object):
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001908 """
1909 A certificate revocation list.
1910 """
Alex Gaynora738ed52015-09-05 11:17:10 -04001911
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001912 def __init__(self):
1913 """
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001914 Create a new empty certificate revocation list.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001915 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001916 crl = _lib.X509_CRL_new()
1917 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001918
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001919 def get_revoked(self):
1920 """
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001921 Return the revocations in this certificate revocation list.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001922
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001923 These revocations will be provided by value, not by reference.
1924 That means it's okay to mutate them: it won't affect this CRL.
1925
1926 :return: The revocations in this CRL.
1927 :rtype: :py:class:`tuple` of :py:class:`Revocation`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001928 """
1929 results = []
1930 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001931 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1932 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001933 revoked_copy = _X509_REVOKED_dup(revoked)
1934 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001935 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001936 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001937 if results:
1938 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001939
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001940 def add_revoked(self, revoked):
1941 """
1942 Add a revoked (by value not reference) to the CRL structure
1943
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001944 This revocation will be added by value, not by reference. That
1945 means it's okay to mutate it after adding: it won't affect
1946 this CRL.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001947
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001948 :param revoked: The new revocation.
1949 :type revoked: :class:`Revoked`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001950
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001951 :return: :py:const:`None`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001952 """
1953 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001954 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001955 # TODO: This is untested.
1956 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001957
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001958 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001959 if add_result == 0:
1960 # TODO: This is untested.
1961 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001962
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001963 def export(self, cert, key, type=FILETYPE_PEM, days=100,
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -04001964 digest=_UNSPECIFIED):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001965 """
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001966 Export a CRL as a string.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001967
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001968 :param cert: The certificate used to sign the CRL.
1969 :type cert: :py:class:`X509`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001970
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001971 :param key: The key used to sign the CRL.
1972 :type key: :py:class:`PKey`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001973
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001974 :param type: The export format, either :py:data:`FILETYPE_PEM`,
1975 :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001976
Jean-Paul Calderonedf514012015-04-13 21:45:18 -04001977 :param int days: The number of days until the next update of this CRL.
Bulat Gaifullin5f9eea42014-09-23 19:35:15 +04001978
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001979 :param bytes digest: The name of the message digest to use (eg
1980 ``b"sha1"``).
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001981
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001982 :return: :py:data:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001983 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001984 if not isinstance(cert, X509):
1985 raise TypeError("cert must be an X509 instance")
1986 if not isinstance(key, PKey):
1987 raise TypeError("key must be a PKey instance")
1988 if not isinstance(type, int):
1989 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001990
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -04001991 if digest is _UNSPECIFIED:
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001992 _warn(
1993 "The default message digest (md5) is deprecated. "
1994 "Pass the name of a message digest explicitly.",
1995 category=DeprecationWarning,
1996 stacklevel=2,
1997 )
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001998 digest = b"md5"
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001999
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04002000 digest_obj = _lib.EVP_get_digestbyname(digest)
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04002001 if digest_obj == _ffi.NULL:
2002 raise ValueError("No such digest method")
2003
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002004 bio = _lib.BIO_new(_lib.BIO_s_mem())
2005 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002006 # TODO: This is untested.
2007 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002008
Alex Gaynora738ed52015-09-05 11:17:10 -04002009 # A scratch time object to give different values to different CRL
2010 # fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002011 sometime = _lib.ASN1_TIME_new()
2012 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002013 # TODO: This is untested.
2014 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002015
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002016 _lib.X509_gmtime_adj(sometime, 0)
2017 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002018
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002019 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
2020 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002021
Alex Gaynor5945ea82015-09-05 14:59:06 -04002022 _lib.X509_CRL_set_issuer_name(
2023 self._crl, _lib.X509_get_subject_name(cert._x509)
2024 )
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002025
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04002026 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, digest_obj)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002027 if not sign_result:
2028 _raise_current_error()
2029
Dominic Chenf05b2122015-10-13 16:32:35 +00002030 return dump_crl(type, self)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002031
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002032
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002033CRLType = CRL
2034
2035
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002036class PKCS7(object):
2037 def type_is_signed(self):
2038 """
2039 Check if this NID_pkcs7_signed object
2040
2041 :return: True if the PKCS7 is of type signed
2042 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002043 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002044 return True
2045 return False
2046
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002047 def type_is_enveloped(self):
2048 """
2049 Check if this NID_pkcs7_enveloped object
2050
2051 :returns: True if the PKCS7 is of type enveloped
2052 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002053 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002054 return True
2055 return False
2056
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002057 def type_is_signedAndEnveloped(self):
2058 """
2059 Check if this NID_pkcs7_signedAndEnveloped object
2060
2061 :returns: True if the PKCS7 is of type signedAndEnveloped
2062 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002063 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002064 return True
2065 return False
2066
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002067 def type_is_data(self):
2068 """
2069 Check if this NID_pkcs7_data object
2070
2071 :return: True if the PKCS7 is of type data
2072 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002073 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002074 return True
2075 return False
2076
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002077 def get_type_name(self):
2078 """
2079 Returns the type name of the PKCS7 structure
2080
2081 :return: A string with the typename
2082 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002083 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
2084 string_type = _lib.OBJ_nid2sn(nid)
2085 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002086
2087PKCS7Type = PKCS7
2088
2089
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002090class PKCS12(object):
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002091 """
2092 A PKCS #12 archive.
2093 """
Alex Gaynora738ed52015-09-05 11:17:10 -04002094
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002095 def __init__(self):
2096 self._pkey = None
2097 self._cert = None
2098 self._cacerts = None
2099 self._friendlyname = None
2100
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002101 def get_certificate(self):
2102 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002103 Get the certificate in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002104
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002105 :return: The certificate, or :py:const:`None` if there is none.
2106 :rtype: :py:class:`X509` or :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002107 """
2108 return self._cert
2109
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002110 def set_certificate(self, cert):
2111 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002112 Set the certificate in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002113
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002114 :param cert: The new certificate, or :py:const:`None` to unset it.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002115 :type cert: :py:class:`X509` or :py:const:`None`
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002116
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002117 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002118 """
2119 if not isinstance(cert, X509):
2120 raise TypeError("cert must be an X509 instance")
2121 self._cert = cert
2122
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002123 def get_privatekey(self):
2124 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002125 Get the private key in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002126
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002127 :return: The private key, or :py:const:`None` if there is none.
2128 :rtype: :py:class:`PKey`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002129 """
2130 return self._pkey
2131
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002132 def set_privatekey(self, pkey):
2133 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002134 Set the certificate portion of the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002135
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002136 :param pkey: The new private key, or :py:const:`None` to unset it.
2137 :type pkey: :py:class:`PKey` or :py:const:`None`
2138
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002139 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002140 """
2141 if not isinstance(pkey, PKey):
2142 raise TypeError("pkey must be a PKey instance")
2143 self._pkey = pkey
2144
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002145 def get_ca_certificates(self):
2146 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002147 Get the CA certificates in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002148
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002149 :return: A tuple with the CA certificates in the chain, or
2150 :py:const:`None` if there are none.
2151 :rtype: :py:class:`tuple` of :py:class:`X509` or :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002152 """
2153 if self._cacerts is not None:
2154 return tuple(self._cacerts)
2155
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002156 def set_ca_certificates(self, cacerts):
2157 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08002158 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002159
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002160 :param cacerts: The new CA certificates, or :py:const:`None` to unset
2161 them.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002162 :type cacerts: An iterable of :py:class:`X509` or :py:const:`None`
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002163
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002164 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002165 """
2166 if cacerts is None:
2167 self._cacerts = None
2168 else:
2169 cacerts = list(cacerts)
2170 for cert in cacerts:
2171 if not isinstance(cert, X509):
Alex Gaynor5945ea82015-09-05 14:59:06 -04002172 raise TypeError(
2173 "iterable must only contain X509 instances"
2174 )
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002175 self._cacerts = cacerts
2176
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002177 def set_friendlyname(self, name):
2178 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002179 Set the friendly name in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002180
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002181 :param name: The new friendly name, or :py:const:`None` to unset.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002182 :type name: :py:class:`bytes` or :py:const:`None`
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002183
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002184 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002185 """
2186 if name is None:
2187 self._friendlyname = None
2188 elif not isinstance(name, bytes):
Alex Gaynor5945ea82015-09-05 14:59:06 -04002189 raise TypeError(
2190 "name must be a byte string or None (not %r)" % (name,)
2191 )
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002192 self._friendlyname = name
2193
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002194 def get_friendlyname(self):
2195 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002196 Get the friendly name in the PKCS# 12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002197
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002198 :returns: The friendly name, or :py:const:`None` if there is none.
2199 :rtype: :py:class:`bytes` or :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002200 """
2201 return self._friendlyname
2202
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002203 def export(self, passphrase=None, iter=2048, maciter=1):
2204 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002205 Dump a PKCS12 object as a string.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002206
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002207 For more information, see the :c:func:`PKCS12_create` man page.
2208
2209 :param passphrase: The passphrase used to encrypt the structure. Unlike
2210 some other passphrase arguments, this *must* be a string, not a
2211 callback.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002212 :type passphrase: :py:data:`bytes`
2213
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002214 :param iter: Number of times to repeat the encryption step.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002215 :type iter: :py:data:`int`
2216
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002217 :param maciter: Number of times to repeat the MAC step.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002218 :type maciter: :py:data:`int`
2219
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002220 :return: The string representation of the PKCS #12 structure.
2221 :rtype:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002222 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002223 passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
Abraham Martine82326c2015-02-04 10:18:10 +00002224
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002225 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002226 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002227 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002228 cacerts = _lib.sk_X509_new_null()
2229 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002230 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002231 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002232
2233 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002234 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002235
2236 friendlyname = self._friendlyname
2237 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002238 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002239
2240 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002241 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002242 else:
2243 pkey = self._pkey._pkey
2244
2245 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002246 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002247 else:
2248 cert = self._cert._x509
2249
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002250 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002251 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002252 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2253 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002254 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002255 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002256 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002257 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002258
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002259 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002260 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002261 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002262
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002263
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002264PKCS12Type = PKCS12
2265
2266
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002267class NetscapeSPKI(object):
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002268 """
2269 A Netscape SPKI object.
2270 """
Alex Gaynora738ed52015-09-05 11:17:10 -04002271
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002272 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002273 spki = _lib.NETSCAPE_SPKI_new()
2274 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002275
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002276 def sign(self, pkey, digest):
2277 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002278 Sign the certificate request with this key and digest type.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002279
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002280 :param pkey: The private key to sign with.
2281 :type pkey: :py:class:`PKey`
2282
2283 :param digest: The message digest to use.
2284 :type digest: :py:class:`bytes`
2285
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002286 :return: :py:const:`None`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002287 """
2288 if pkey._only_public:
2289 raise ValueError("Key has only public part")
2290
2291 if not pkey._initialized:
2292 raise ValueError("Key is uninitialized")
2293
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002294 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002295 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002296 raise ValueError("No such digest method")
2297
Alex Gaynor5945ea82015-09-05 14:59:06 -04002298 sign_result = _lib.NETSCAPE_SPKI_sign(
2299 self._spki, pkey._pkey, digest_obj
2300 )
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002301 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002302 # TODO: This is untested.
2303 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002304
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002305 def verify(self, key):
2306 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002307 Verifies a signature on a certificate request.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002308
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002309 :param key: The public key that signature is supposedly from.
2310 :type pkey: :py:class:`PKey`
2311
2312 :return: :py:const:`True` if the signature is correct.
2313 :rtype: :py:class:`bool`
2314
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02002315 :raises Error: If the signature is invalid, or there was a problem
2316 verifying the signature.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002317 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002318 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002319 if answer <= 0:
2320 _raise_current_error()
2321 return True
2322
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002323 def b64_encode(self):
2324 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002325 Generate a base64 encoded representation of this SPKI object.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002326
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002327 :return: The base64 encoded string.
2328 :rtype: :py:class:`bytes`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002329 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002330 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2331 result = _ffi.string(encoded)
2332 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002333 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002334
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002335 def get_pubkey(self):
2336 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002337 Get the public key of this certificate.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002338
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002339 :return: The public key.
2340 :rtype: :py:class:`PKey`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002341 """
2342 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002343 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2344 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002345 # TODO: This is untested.
2346 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002347 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002348 pkey._only_public = True
2349 return pkey
2350
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002351 def set_pubkey(self, pkey):
2352 """
2353 Set the public key of the certificate
2354
2355 :param pkey: The public key
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002356 :return: :py:const:`None`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002357 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002358 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002359 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002360 # TODO: This is untested.
2361 _raise_current_error()
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002362
2363
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002364NetscapeSPKIType = NetscapeSPKI
2365
2366
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002367class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002368 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002369 if type != FILETYPE_PEM and passphrase is not None:
Alex Gaynor5945ea82015-09-05 14:59:06 -04002370 raise ValueError(
2371 "only FILETYPE_PEM key format supports encryption"
2372 )
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002373 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002374 self._more_args = more_args
2375 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002376 self._problems = []
2377
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002378 @property
2379 def callback(self):
2380 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002381 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002382 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002383 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002384 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002385 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002386 else:
2387 raise TypeError("Last argument must be string or callable")
2388
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002389 @property
2390 def callback_args(self):
2391 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002392 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002393 elif isinstance(self._passphrase, bytes):
2394 return self._passphrase
2395 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002396 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002397 else:
2398 raise TypeError("Last argument must be string or callable")
2399
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002400 def raise_if_problem(self, exceptionType=Error):
2401 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002402 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002403 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002404 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002405 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002406 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002407 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002408
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002409 def _read_passphrase(self, buf, size, rwflag, userdata):
2410 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002411 if self._more_args:
2412 result = self._passphrase(size, rwflag, userdata)
2413 else:
2414 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002415 if not isinstance(result, bytes):
2416 raise ValueError("String expected")
2417 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002418 if self._truncate:
2419 result = result[:size]
2420 else:
Alex Gaynor5945ea82015-09-05 14:59:06 -04002421 raise ValueError(
2422 "passphrase returned by callback is too long"
2423 )
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002424 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002425 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002426 return len(result)
2427 except Exception as e:
2428 self._problems.append(e)
2429 return 0
2430
2431
Cory Benfield6492f7c2015-10-27 16:57:58 +09002432def load_publickey(type, buffer):
2433 """
Cory Benfield11c10192015-10-27 17:23:03 +09002434 Load a public key from a buffer.
Cory Benfield6492f7c2015-10-27 16:57:58 +09002435
Cory Benfield11c10192015-10-27 17:23:03 +09002436 :param type: The file type (one of :py:data:`FILETYPE_PEM`,
Cory Benfielde813cec2015-10-28 08:57:08 +09002437 :data:`FILETYPE_ASN1`).
Cory Benfield11c10192015-10-27 17:23:03 +09002438 :param buffer: The buffer the key is stored in.
Cory Benfielde813cec2015-10-28 08:57:08 +09002439 :return: The :class:`PKey` object.
Cory Benfield6492f7c2015-10-27 16:57:58 +09002440 """
2441 if isinstance(buffer, _text_type):
2442 buffer = buffer.encode("ascii")
2443
2444 bio = _new_mem_buf(buffer)
2445
2446 if type == FILETYPE_PEM:
2447 evp_pkey = _lib.PEM_read_bio_PUBKEY(
2448 bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
2449 elif type == FILETYPE_ASN1:
2450 evp_pkey = _lib.d2i_PUBKEY_bio(bio, _ffi.NULL)
2451 else:
2452 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2453
2454 if evp_pkey == _ffi.NULL:
2455 _raise_current_error()
2456
2457 pkey = PKey.__new__(PKey)
2458 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
2459 return pkey
2460
2461
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002462def load_privatekey(type, buffer, passphrase=None):
2463 """
2464 Load a private key from a buffer
2465
2466 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2467 :param buffer: The buffer the key is stored in
2468 :param passphrase: (optional) if encrypted PEM format, this can be
2469 either the passphrase to use, or a callback for
2470 providing the passphrase.
2471
2472 :return: The PKey object
2473 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002474 if isinstance(buffer, _text_type):
2475 buffer = buffer.encode("ascii")
2476
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002477 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002478
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002479 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002480 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002481 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2482 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002483 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002484 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002485 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002486 else:
2487 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2488
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002489 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002490 _raise_current_error()
2491
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002492 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002493 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002494 return pkey
2495
2496
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002497def dump_certificate_request(type, req):
2498 """
2499 Dump a certificate request to a buffer
2500
2501 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2502 :param req: The certificate request to dump
2503 :return: The buffer with the dumped certificate request in
2504 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002505 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002506
2507 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002508 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002509 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002510 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002511 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002512 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002513 else:
Alex Gaynor5945ea82015-09-05 14:59:06 -04002514 raise ValueError(
2515 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
2516 "FILETYPE_TEXT"
2517 )
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002518
2519 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002520 # TODO: This is untested.
2521 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002522
2523 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002524
2525
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002526def load_certificate_request(type, buffer):
2527 """
2528 Load a certificate request from a buffer
2529
2530 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2531 :param buffer: The buffer the certificate request is stored in
2532 :return: The X509Req object
2533 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002534 if isinstance(buffer, _text_type):
2535 buffer = buffer.encode("ascii")
2536
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002537 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002538
2539 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002540 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002541 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002542 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002543 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002544 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002545
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002546 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002547 # TODO: This is untested.
2548 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002549
2550 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002551 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002552 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002553
2554
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002555def sign(pkey, data, digest):
2556 """
2557 Sign data with a digest
2558
2559 :param pkey: Pkey to sign with
2560 :param data: data to be signed
2561 :param digest: message digest to use
2562 :return: signature
2563 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002564 data = _text_to_bytes_and_warn("data", data)
Abraham Martine82326c2015-02-04 10:18:10 +00002565
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002566 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002567 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002568 raise ValueError("No such digest method")
2569
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002570 md_ctx = _ffi.new("EVP_MD_CTX*")
2571 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002572
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002573 _lib.EVP_SignInit(md_ctx, digest_obj)
2574 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002575
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002576 signature_buffer = _ffi.new("unsigned char[]", 512)
2577 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002578 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002579 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002580 md_ctx, signature_buffer, signature_length, pkey._pkey)
2581
2582 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002583 # TODO: This is untested.
2584 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002585
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002586 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002587
2588
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002589def verify(cert, signature, data, digest):
2590 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02002591 Verify a signature.
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002592
2593 :param cert: signing certificate (X509 object)
2594 :param signature: signature returned by sign function
2595 :param data: data to be verified
2596 :param digest: message digest to use
Alex Gaynor5945ea82015-09-05 14:59:06 -04002597 :return: :py:const:`None` if the signature is correct, raise exception
2598 otherwise
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002599 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002600 data = _text_to_bytes_and_warn("data", data)
Abraham Martine82326c2015-02-04 10:18:10 +00002601
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002602 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002603 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002604 raise ValueError("No such digest method")
2605
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002606 pkey = _lib.X509_get_pubkey(cert._x509)
2607 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002608 # TODO: This is untested.
2609 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002610 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002611
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002612 md_ctx = _ffi.new("EVP_MD_CTX*")
2613 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002614
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002615 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2616 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
Alex Gaynor5945ea82015-09-05 14:59:06 -04002617 verify_result = _lib.EVP_VerifyFinal(
2618 md_ctx, signature, len(signature), pkey
2619 )
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002620
2621 if verify_result != 1:
2622 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002623
2624
Dominic Chenf05b2122015-10-13 16:32:35 +00002625def dump_crl(type, crl):
2626 """
2627 Dump a certificate revocation list to a buffer.
2628
2629 :param type: The file type (one of ``FILETYPE_PEM``, ``FILETYPE_ASN1``, or
2630 ``FILETYPE_TEXT``).
Hynek Schlawack0a3cd6d2015-10-21 16:39:22 +02002631 :param CRL crl: The CRL to dump.
2632
Dominic Chenf05b2122015-10-13 16:32:35 +00002633 :return: The buffer with the CRL.
Hynek Schlawack0a3cd6d2015-10-21 16:39:22 +02002634 :rtype: :data:`bytes`
Dominic Chenf05b2122015-10-13 16:32:35 +00002635 """
2636 bio = _new_mem_buf()
2637
2638 if type == FILETYPE_PEM:
2639 ret = _lib.PEM_write_bio_X509_CRL(bio, crl._crl)
2640 elif type == FILETYPE_ASN1:
2641 ret = _lib.i2d_X509_CRL_bio(bio, crl._crl)
2642 elif type == FILETYPE_TEXT:
2643 ret = _lib.X509_CRL_print(bio, crl._crl)
2644 else:
2645 raise ValueError(
2646 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
2647 "FILETYPE_TEXT")
2648
2649 assert ret == 1
2650 return _bio_to_string(bio)
2651
2652
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002653def load_crl(type, buffer):
2654 """
2655 Load a certificate revocation list from a buffer
2656
2657 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2658 :param buffer: The buffer the CRL is stored in
2659
2660 :return: The PKey object
2661 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002662 if isinstance(buffer, _text_type):
2663 buffer = buffer.encode("ascii")
2664
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002665 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002666
2667 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002668 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002669 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002670 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002671 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002672 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2673
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002674 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002675 _raise_current_error()
2676
2677 result = CRL.__new__(CRL)
2678 result._crl = crl
2679 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002680
2681
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002682def load_pkcs7_data(type, buffer):
2683 """
2684 Load pkcs7 data from a buffer
2685
2686 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2687 :param buffer: The buffer with the pkcs7 data.
2688 :return: The PKCS7 object
2689 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002690 if isinstance(buffer, _text_type):
2691 buffer = buffer.encode("ascii")
2692
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002693 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002694
2695 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002696 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002697 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002698 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002699 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002700 # TODO: This is untested.
2701 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002702 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2703
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002704 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002705 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002706
2707 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002708 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002709 return pypkcs7
2710
2711
Stephen Holsapple38482622014-04-05 20:29:34 -07002712def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002713 """
2714 Load a PKCS12 object from a buffer
2715
2716 :param buffer: The buffer the certificate is stored in
2717 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2718 :returns: The PKCS12 object
2719 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002720 passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
Abraham Martine82326c2015-02-04 10:18:10 +00002721
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002722 if isinstance(buffer, _text_type):
2723 buffer = buffer.encode("ascii")
2724
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002725 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002726
Stephen Holsapple38482622014-04-05 20:29:34 -07002727 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2728 # password based encryption no password and a zero length password are two
2729 # different things, but OpenSSL implementation will try both to figure out
2730 # which one works.
2731 if not passphrase:
2732 passphrase = _ffi.NULL
2733
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002734 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2735 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002736 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002737 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002738
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002739 pkey = _ffi.new("EVP_PKEY**")
2740 cert = _ffi.new("X509**")
2741 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002742
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002743 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002744 if not parse_result:
2745 _raise_current_error()
2746
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002747 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002748
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002749 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2750 # queue for no particular reason. This error isn't interesting to anyone
2751 # outside this function. It's not even interesting to us. Get rid of it.
2752 try:
2753 _raise_current_error()
2754 except Error:
2755 pass
2756
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002757 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002758 pykey = None
2759 else:
2760 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002761 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002762
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002763 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002764 pycert = None
2765 friendlyname = None
2766 else:
2767 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002768 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002769
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002770 friendlyname_length = _ffi.new("int*")
Alex Gaynor5945ea82015-09-05 14:59:06 -04002771 friendlyname_buffer = _lib.X509_alias_get0(
2772 cert[0], friendlyname_length
2773 )
2774 friendlyname = _ffi.buffer(
2775 friendlyname_buffer, friendlyname_length[0]
2776 )[:]
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002777 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002778 friendlyname = None
2779
2780 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002781 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002782 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002783 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002784 pycacerts.append(pycacert)
2785 if not pycacerts:
2786 pycacerts = None
2787
2788 pkcs12 = PKCS12.__new__(PKCS12)
2789 pkcs12._pkey = pykey
2790 pkcs12._cert = pycert
2791 pkcs12._cacerts = pycacerts
2792 pkcs12._friendlyname = friendlyname
2793 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002794
2795
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002796# There are no direct unit tests for this initialization. It is tested
2797# indirectly since it is necessary for functions like dump_privatekey when
2798# using encryption.
2799#
2800# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2801# and some other similar tests may fail without this (though they may not if
2802# the Python runtime has already done some initialization of the underlying
2803# OpenSSL library (and is linked against the same one that cryptography is
2804# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002805_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002806
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002807# This is similar but exercised mainly by exception_from_error_queue. It calls
2808# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2809_lib.SSL_load_error_strings()
D.S. Ljungmark349e1362014-05-31 18:40:38 +02002810
2811
D.S. Ljungmark349e1362014-05-31 18:40:38 +02002812# Set the default string mask to match OpenSSL upstream (since 2005) and
2813# RFC5280 recommendations.
2814_lib.ASN1_STRING_set_default_mask_asc(b'utf8only')