blob: 8e837498e2c363c4ca035967fc0f7283a4ca7f29 [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)
1645 if result_code != 1:
1646 # TODO: This is untested.
1647 _raise_current_error()
1648
1649 return _bio_to_string(bio)
1650
1651
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001652def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1653 """
1654 Dump a private key to a buffer
1655
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001656 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1657 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001658 :param pkey: The PKey to dump
1659 :param cipher: (optional) if encrypted PEM format, the cipher to
1660 use
1661 :param passphrase: (optional) if encrypted PEM format, this can be either
1662 the passphrase to use, or a callback for providing the
1663 passphrase.
1664 :return: The buffer with the dumped key in
Maximilian Hils0de43752015-09-18 15:26:54 +02001665 :rtype: :py:data:`bytes`
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001666 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001667 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001668
1669 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001670 if passphrase is None:
1671 raise TypeError(
1672 "if a value is given for cipher "
1673 "one must also be given for passphrase")
1674 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001675 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001676 raise ValueError("Invalid cipher name")
1677 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001678 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001679
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001680 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001681 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001682 result_code = _lib.PEM_write_bio_PrivateKey(
1683 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001684 helper.callback, helper.callback_args)
1685 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001686 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001687 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001688 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001689 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1690 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001691 # TODO RSA_free(rsa)?
1692 else:
1693 raise ValueError(
1694 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1695 "FILETYPE_TEXT")
1696
1697 if result_code == 0:
1698 _raise_current_error()
1699
1700 return _bio_to_string(bio)
1701
1702
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001703def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001704 copy = _lib.X509_REVOKED_new()
1705 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001706 # TODO: This is untested.
1707 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001708
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001709 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001710 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001711 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001712
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001713 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001714 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001715 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001716
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001717 if original.extensions != _ffi.NULL:
1718 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1719 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1720 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1721 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1722 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001723 copy.extensions = extension_stack
1724
1725 copy.sequence = original.sequence
1726 return copy
1727
1728
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001729class Revoked(object):
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001730 """
1731 A certificate revocation.
1732 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001733 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1734 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1735 # OCSP_crl_reason_str. We use the latter, just like the command line
1736 # program.
1737 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001738 b"unspecified",
1739 b"keyCompromise",
1740 b"CACompromise",
1741 b"affiliationChanged",
1742 b"superseded",
1743 b"cessationOfOperation",
1744 b"certificateHold",
1745 # b"removeFromCRL",
Alex Gaynorca87ff62015-09-04 23:31:03 -04001746 ]
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001747
1748 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001749 revoked = _lib.X509_REVOKED_new()
1750 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001751
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001752 def set_serial(self, hex_str):
1753 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001754 Set the serial number.
1755
1756 The serial number is formatted as a hexadecimal number encoded in
1757 ASCII.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001758
1759 :param hex_str: The new serial number.
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001760 :type hex_str: :py:class:`bytes`
1761
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001762 :return: :py:const:`None`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001763 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001764 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1765 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001766 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001767 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001768 if not bn_result:
1769 raise ValueError("bad hex string")
1770
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001771 asn1_serial = _ffi.gc(
1772 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1773 _lib.ASN1_INTEGER_free)
1774 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001775
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001776 def get_serial(self):
1777 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001778 Get the serial number.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001779
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001780 The serial number is formatted as a hexadecimal number encoded in
1781 ASCII.
1782
1783 :return: The serial number.
1784 :rtype: :py:class:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001785 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001786 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001787
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001788 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001789 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001790 # TODO: This is untested.
1791 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001792
1793 return _bio_to_string(bio)
1794
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001795 def _delete_reason(self):
1796 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001797 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1798 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1799 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1800 _lib.X509_EXTENSION_free(ext)
1801 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001802 break
1803
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001804 def set_reason(self, reason):
1805 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001806 Set the reason of this revocation.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001807
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001808 If :py:data:`reason` is :py:const:`None`, delete the reason instead.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001809
1810 :param reason: The reason string.
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001811 :type reason: :py:class:`bytes` or :py:class:`NoneType`
1812
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001813 :return: :py:const:`None`
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001814
1815 .. seealso::
1816
1817 :py:meth:`all_reasons`, which gives you a list of all supported
1818 reasons which you might pass to this method.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001819 """
1820 if reason is None:
1821 self._delete_reason()
1822 elif not isinstance(reason, bytes):
1823 raise TypeError("reason must be None or a byte string")
1824 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001825 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001826 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1827
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001828 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1829 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001830 # TODO: This is untested.
1831 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001832 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001833
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001834 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1835 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001836 # TODO: This is untested.
1837 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001838
1839 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001840 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1841 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001842
1843 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001844 # TODO: This is untested.
1845 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001846
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001847 def get_reason(self):
1848 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001849 Set the reason of this revocation.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001850
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001851 :return: The reason, or :py:const:`None` if there is none.
1852 :rtype: :py:class:`bytes` or :py:class:`NoneType`
1853
1854 .. seealso::
1855
1856 :py:meth:`all_reasons`, which gives you a list of all supported
1857 reasons this method might return.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001858 """
1859 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001860 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1861 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1862 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001863 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001864
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001865 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001866 if not print_result:
Alex Gaynor5945ea82015-09-05 14:59:06 -04001867 print_result = _lib.M_ASN1_OCTET_STRING_print(
1868 bio, ext.value
1869 )
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001870 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001871 # TODO: This is untested.
1872 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001873
1874 return _bio_to_string(bio)
1875
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001876 def all_reasons(self):
1877 """
1878 Return a list of all the supported reason strings.
1879
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001880 This list is a copy; modifying it does not change the supported reason
1881 strings.
1882
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001883 :return: A list of reason strings.
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001884 :rtype: :py:class:`list` of :py:class:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001885 """
1886 return self._crl_reasons[:]
1887
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001888 def set_rev_date(self, when):
1889 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001890 Set the revocation timestamp.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001891
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001892 :param when: The timestamp of the revocation, as ASN.1 GENERALIZEDTIME.
1893 :type when: :py:class:`bytes`
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001894 :return: :py:const:`None`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001895 """
1896 return _set_asn1_time(self._revoked.revocationDate, when)
1897
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001898 def get_rev_date(self):
1899 """
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001900 Get the revocation timestamp.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001901
Laurens Van Houtvend92f55c2014-06-19 17:08:41 +02001902 :return: The timestamp of the revocation, as ASN.1 GENERALIZEDTIME.
1903 :rtype: :py:class:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001904 """
1905 return _get_asn1_time(self._revoked.revocationDate)
1906
1907
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001908class CRL(object):
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001909 """
1910 A certificate revocation list.
1911 """
Alex Gaynora738ed52015-09-05 11:17:10 -04001912
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001913 def __init__(self):
1914 """
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001915 Create a new empty certificate revocation list.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001916 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001917 crl = _lib.X509_CRL_new()
1918 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001919
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001920 def get_revoked(self):
1921 """
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001922 Return the revocations in this certificate revocation list.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001923
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001924 These revocations will be provided by value, not by reference.
1925 That means it's okay to mutate them: it won't affect this CRL.
1926
1927 :return: The revocations in this CRL.
1928 :rtype: :py:class:`tuple` of :py:class:`Revocation`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001929 """
1930 results = []
1931 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001932 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1933 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001934 revoked_copy = _X509_REVOKED_dup(revoked)
1935 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001936 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001937 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001938 if results:
1939 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001940
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001941 def add_revoked(self, revoked):
1942 """
1943 Add a revoked (by value not reference) to the CRL structure
1944
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001945 This revocation will be added by value, not by reference. That
1946 means it's okay to mutate it after adding: it won't affect
1947 this CRL.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001948
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001949 :param revoked: The new revocation.
1950 :type revoked: :class:`Revoked`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001951
Laurens Van Houtvena7904582014-06-19 12:33:04 +02001952 :return: :py:const:`None`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001953 """
1954 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001955 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001956 # TODO: This is untested.
1957 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001958
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001959 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001960 if add_result == 0:
1961 # TODO: This is untested.
1962 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001963
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001964 def export(self, cert, key, type=FILETYPE_PEM, days=100,
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -04001965 digest=_UNSPECIFIED):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001966 """
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001967 Export a CRL as a string.
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001968
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001969 :param cert: The certificate used to sign the CRL.
1970 :type cert: :py:class:`X509`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001971
Laurens Van Houtvencb32e852014-06-19 17:36:28 +02001972 :param key: The key used to sign the CRL.
1973 :type key: :py:class:`PKey`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001974
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001975 :param type: The export format, either :py:data:`FILETYPE_PEM`,
1976 :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001977
Jean-Paul Calderonedf514012015-04-13 21:45:18 -04001978 :param int days: The number of days until the next update of this CRL.
Bulat Gaifullin5f9eea42014-09-23 19:35:15 +04001979
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001980 :param bytes digest: The name of the message digest to use (eg
1981 ``b"sha1"``).
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001982
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001983 :return: :py:data:`bytes`
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001984 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001985 if not isinstance(cert, X509):
1986 raise TypeError("cert must be an X509 instance")
1987 if not isinstance(key, PKey):
1988 raise TypeError("key must be a PKey instance")
1989 if not isinstance(type, int):
1990 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001991
Jean-Paul Calderone00f84eb2015-04-13 12:47:21 -04001992 if digest is _UNSPECIFIED:
Jean-Paul Calderone60432792015-04-13 12:26:07 -04001993 _warn(
1994 "The default message digest (md5) is deprecated. "
1995 "Pass the name of a message digest explicitly.",
1996 category=DeprecationWarning,
1997 stacklevel=2,
1998 )
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04001999 digest = b"md5"
Jean-Paul Calderone60432792015-04-13 12:26:07 -04002000
Jean-Paul Calderonecce22d02015-04-13 13:56:09 -04002001 digest_obj = _lib.EVP_get_digestbyname(digest)
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04002002 if digest_obj == _ffi.NULL:
2003 raise ValueError("No such digest method")
2004
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002005 bio = _lib.BIO_new(_lib.BIO_s_mem())
2006 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002007 # TODO: This is untested.
2008 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002009
Alex Gaynora738ed52015-09-05 11:17:10 -04002010 # A scratch time object to give different values to different CRL
2011 # fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002012 sometime = _lib.ASN1_TIME_new()
2013 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002014 # TODO: This is untested.
2015 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002016
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002017 _lib.X509_gmtime_adj(sometime, 0)
2018 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002019
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002020 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
2021 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002022
Alex Gaynor5945ea82015-09-05 14:59:06 -04002023 _lib.X509_CRL_set_issuer_name(
2024 self._crl, _lib.X509_get_subject_name(cert._x509)
2025 )
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002026
Bulat Gaifullin2923dc02014-09-21 22:36:48 +04002027 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, digest_obj)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002028 if not sign_result:
2029 _raise_current_error()
2030
Dominic Chenf05b2122015-10-13 16:32:35 +00002031 return dump_crl(type, self)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002032
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08002033
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002034CRLType = CRL
2035
2036
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002037class PKCS7(object):
2038 def type_is_signed(self):
2039 """
2040 Check if this NID_pkcs7_signed object
2041
2042 :return: True if the PKCS7 is of type signed
2043 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002044 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002045 return True
2046 return False
2047
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002048 def type_is_enveloped(self):
2049 """
2050 Check if this NID_pkcs7_enveloped object
2051
2052 :returns: True if the PKCS7 is of type enveloped
2053 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002054 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002055 return True
2056 return False
2057
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002058 def type_is_signedAndEnveloped(self):
2059 """
2060 Check if this NID_pkcs7_signedAndEnveloped object
2061
2062 :returns: True if the PKCS7 is of type signedAndEnveloped
2063 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002064 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002065 return True
2066 return False
2067
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002068 def type_is_data(self):
2069 """
2070 Check if this NID_pkcs7_data object
2071
2072 :return: True if the PKCS7 is of type data
2073 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002074 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002075 return True
2076 return False
2077
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002078 def get_type_name(self):
2079 """
2080 Returns the type name of the PKCS7 structure
2081
2082 :return: A string with the typename
2083 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002084 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
2085 string_type = _lib.OBJ_nid2sn(nid)
2086 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002087
2088PKCS7Type = PKCS7
2089
2090
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002091class PKCS12(object):
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002092 """
2093 A PKCS #12 archive.
2094 """
Alex Gaynora738ed52015-09-05 11:17:10 -04002095
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002096 def __init__(self):
2097 self._pkey = None
2098 self._cert = None
2099 self._cacerts = None
2100 self._friendlyname = None
2101
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002102 def get_certificate(self):
2103 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002104 Get the certificate in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002105
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002106 :return: The certificate, or :py:const:`None` if there is none.
2107 :rtype: :py:class:`X509` or :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002108 """
2109 return self._cert
2110
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002111 def set_certificate(self, cert):
2112 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002113 Set the certificate in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002114
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002115 :param cert: The new certificate, or :py:const:`None` to unset it.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002116 :type cert: :py:class:`X509` or :py:const:`None`
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002117
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002118 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002119 """
2120 if not isinstance(cert, X509):
2121 raise TypeError("cert must be an X509 instance")
2122 self._cert = cert
2123
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002124 def get_privatekey(self):
2125 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002126 Get the private key in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002127
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002128 :return: The private key, or :py:const:`None` if there is none.
2129 :rtype: :py:class:`PKey`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002130 """
2131 return self._pkey
2132
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002133 def set_privatekey(self, pkey):
2134 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002135 Set the certificate portion of the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002136
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002137 :param pkey: The new private key, or :py:const:`None` to unset it.
2138 :type pkey: :py:class:`PKey` or :py:const:`None`
2139
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002140 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002141 """
2142 if not isinstance(pkey, PKey):
2143 raise TypeError("pkey must be a PKey instance")
2144 self._pkey = pkey
2145
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002146 def get_ca_certificates(self):
2147 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002148 Get the CA certificates in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002149
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002150 :return: A tuple with the CA certificates in the chain, or
2151 :py:const:`None` if there are none.
2152 :rtype: :py:class:`tuple` of :py:class:`X509` or :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002153 """
2154 if self._cacerts is not None:
2155 return tuple(self._cacerts)
2156
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002157 def set_ca_certificates(self, cacerts):
2158 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08002159 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002160
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002161 :param cacerts: The new CA certificates, or :py:const:`None` to unset
2162 them.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002163 :type cacerts: An iterable of :py:class:`X509` or :py:const:`None`
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002164
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002165 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002166 """
2167 if cacerts is None:
2168 self._cacerts = None
2169 else:
2170 cacerts = list(cacerts)
2171 for cert in cacerts:
2172 if not isinstance(cert, X509):
Alex Gaynor5945ea82015-09-05 14:59:06 -04002173 raise TypeError(
2174 "iterable must only contain X509 instances"
2175 )
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002176 self._cacerts = cacerts
2177
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002178 def set_friendlyname(self, name):
2179 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002180 Set the friendly name in the PKCS #12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002181
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002182 :param name: The new friendly name, or :py:const:`None` to unset.
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002183 :type name: :py:class:`bytes` or :py:const:`None`
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002184
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002185 :return: :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002186 """
2187 if name is None:
2188 self._friendlyname = None
2189 elif not isinstance(name, bytes):
Alex Gaynor5945ea82015-09-05 14:59:06 -04002190 raise TypeError(
2191 "name must be a byte string or None (not %r)" % (name,)
2192 )
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002193 self._friendlyname = name
2194
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002195 def get_friendlyname(self):
2196 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002197 Get the friendly name in the PKCS# 12 structure.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002198
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002199 :returns: The friendly name, or :py:const:`None` if there is none.
2200 :rtype: :py:class:`bytes` or :py:const:`None`
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002201 """
2202 return self._friendlyname
2203
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002204 def export(self, passphrase=None, iter=2048, maciter=1):
2205 """
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002206 Dump a PKCS12 object as a string.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002207
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002208 For more information, see the :c:func:`PKCS12_create` man page.
2209
2210 :param passphrase: The passphrase used to encrypt the structure. Unlike
2211 some other passphrase arguments, this *must* be a string, not a
2212 callback.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002213 :type passphrase: :py:data:`bytes`
2214
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002215 :param iter: Number of times to repeat the encryption step.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002216 :type iter: :py:data:`int`
2217
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002218 :param maciter: Number of times to repeat the MAC step.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002219 :type maciter: :py:data:`int`
2220
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002221 :return: The string representation of the PKCS #12 structure.
2222 :rtype:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002223 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002224 passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
Abraham Martine82326c2015-02-04 10:18:10 +00002225
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002226 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002227 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002228 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002229 cacerts = _lib.sk_X509_new_null()
2230 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002231 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002232 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002233
2234 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002235 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002236
2237 friendlyname = self._friendlyname
2238 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002239 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002240
2241 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002242 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002243 else:
2244 pkey = self._pkey._pkey
2245
2246 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002247 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002248 else:
2249 cert = self._cert._x509
2250
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002251 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002252 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002253 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2254 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002255 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002256 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002257 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002258 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002259
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002260 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002261 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002262 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002263
Laurens Van Houtvenbb503a32014-06-19 12:28:08 +02002264
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002265PKCS12Type = PKCS12
2266
2267
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002268class NetscapeSPKI(object):
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002269 """
2270 A Netscape SPKI object.
2271 """
Alex Gaynora738ed52015-09-05 11:17:10 -04002272
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002273 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002274 spki = _lib.NETSCAPE_SPKI_new()
2275 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002276
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002277 def sign(self, pkey, digest):
2278 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002279 Sign the certificate request with this key and digest type.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002280
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002281 :param pkey: The private key to sign with.
2282 :type pkey: :py:class:`PKey`
2283
2284 :param digest: The message digest to use.
2285 :type digest: :py:class:`bytes`
2286
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002287 :return: :py:const:`None`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002288 """
2289 if pkey._only_public:
2290 raise ValueError("Key has only public part")
2291
2292 if not pkey._initialized:
2293 raise ValueError("Key is uninitialized")
2294
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002295 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002296 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002297 raise ValueError("No such digest method")
2298
Alex Gaynor5945ea82015-09-05 14:59:06 -04002299 sign_result = _lib.NETSCAPE_SPKI_sign(
2300 self._spki, pkey._pkey, digest_obj
2301 )
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002302 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002303 # TODO: This is untested.
2304 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002305
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002306 def verify(self, key):
2307 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002308 Verifies a signature on a certificate request.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002309
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002310 :param key: The public key that signature is supposedly from.
2311 :type pkey: :py:class:`PKey`
2312
2313 :return: :py:const:`True` if the signature is correct.
2314 :rtype: :py:class:`bool`
2315
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02002316 :raises Error: If the signature is invalid, or there was a problem
2317 verifying the signature.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002318 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002319 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002320 if answer <= 0:
2321 _raise_current_error()
2322 return True
2323
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002324 def b64_encode(self):
2325 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002326 Generate a base64 encoded representation of this SPKI object.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002327
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002328 :return: The base64 encoded string.
2329 :rtype: :py:class:`bytes`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002330 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002331 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2332 result = _ffi.string(encoded)
2333 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002334 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002335
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002336 def get_pubkey(self):
2337 """
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002338 Get the public key of this certificate.
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002339
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002340 :return: The public key.
2341 :rtype: :py:class:`PKey`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002342 """
2343 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002344 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2345 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002346 # TODO: This is untested.
2347 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002348 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002349 pkey._only_public = True
2350 return pkey
2351
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002352 def set_pubkey(self, pkey):
2353 """
2354 Set the public key of the certificate
2355
2356 :param pkey: The public key
Laurens Van Houtvena7904582014-06-19 12:33:04 +02002357 :return: :py:const:`None`
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002358 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002359 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002360 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002361 # TODO: This is untested.
2362 _raise_current_error()
Laurens Van Houtven59152b52014-06-19 16:42:30 +02002363
2364
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002365NetscapeSPKIType = NetscapeSPKI
2366
2367
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002368class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002369 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002370 if type != FILETYPE_PEM and passphrase is not None:
Alex Gaynor5945ea82015-09-05 14:59:06 -04002371 raise ValueError(
2372 "only FILETYPE_PEM key format supports encryption"
2373 )
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002374 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002375 self._more_args = more_args
2376 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002377 self._problems = []
2378
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002379 @property
2380 def callback(self):
2381 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002382 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002383 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002384 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002385 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002386 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002387 else:
2388 raise TypeError("Last argument must be string or callable")
2389
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002390 @property
2391 def callback_args(self):
2392 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002393 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002394 elif isinstance(self._passphrase, bytes):
2395 return self._passphrase
2396 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002397 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002398 else:
2399 raise TypeError("Last argument must be string or callable")
2400
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002401 def raise_if_problem(self, exceptionType=Error):
2402 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002403 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002404 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002405 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002406 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002407 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002408 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002409
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002410 def _read_passphrase(self, buf, size, rwflag, userdata):
2411 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002412 if self._more_args:
2413 result = self._passphrase(size, rwflag, userdata)
2414 else:
2415 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002416 if not isinstance(result, bytes):
2417 raise ValueError("String expected")
2418 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002419 if self._truncate:
2420 result = result[:size]
2421 else:
Alex Gaynor5945ea82015-09-05 14:59:06 -04002422 raise ValueError(
2423 "passphrase returned by callback is too long"
2424 )
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002425 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002426 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002427 return len(result)
2428 except Exception as e:
2429 self._problems.append(e)
2430 return 0
2431
2432
Cory Benfield6492f7c2015-10-27 16:57:58 +09002433def load_publickey(type, buffer):
2434 """
Cory Benfield11c10192015-10-27 17:23:03 +09002435 Load a public key from a buffer.
Cory Benfield6492f7c2015-10-27 16:57:58 +09002436
Cory Benfield11c10192015-10-27 17:23:03 +09002437 :param type: The file type (one of :py:data:`FILETYPE_PEM`,
Cory Benfielde813cec2015-10-28 08:57:08 +09002438 :data:`FILETYPE_ASN1`).
Cory Benfield11c10192015-10-27 17:23:03 +09002439 :param buffer: The buffer the key is stored in.
Cory Benfielde813cec2015-10-28 08:57:08 +09002440 :return: The :class:`PKey` object.
Cory Benfield6492f7c2015-10-27 16:57:58 +09002441 """
2442 if isinstance(buffer, _text_type):
2443 buffer = buffer.encode("ascii")
2444
2445 bio = _new_mem_buf(buffer)
2446
2447 if type == FILETYPE_PEM:
2448 evp_pkey = _lib.PEM_read_bio_PUBKEY(
2449 bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
2450 elif type == FILETYPE_ASN1:
2451 evp_pkey = _lib.d2i_PUBKEY_bio(bio, _ffi.NULL)
2452 else:
2453 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2454
2455 if evp_pkey == _ffi.NULL:
2456 _raise_current_error()
2457
2458 pkey = PKey.__new__(PKey)
2459 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
2460 return pkey
2461
2462
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002463def load_privatekey(type, buffer, passphrase=None):
2464 """
2465 Load a private key from a buffer
2466
2467 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2468 :param buffer: The buffer the key is stored in
2469 :param passphrase: (optional) if encrypted PEM format, this can be
2470 either the passphrase to use, or a callback for
2471 providing the passphrase.
2472
2473 :return: The PKey object
2474 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002475 if isinstance(buffer, _text_type):
2476 buffer = buffer.encode("ascii")
2477
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002478 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002479
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002480 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002481 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002482 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2483 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002484 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002485 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002486 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002487 else:
2488 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2489
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002490 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002491 _raise_current_error()
2492
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002493 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002494 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002495 return pkey
2496
2497
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002498def dump_certificate_request(type, req):
2499 """
2500 Dump a certificate request to a buffer
2501
2502 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2503 :param req: The certificate request to dump
2504 :return: The buffer with the dumped certificate request in
2505 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002506 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002507
2508 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002509 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002510 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002511 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002512 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002513 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002514 else:
Alex Gaynor5945ea82015-09-05 14:59:06 -04002515 raise ValueError(
2516 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
2517 "FILETYPE_TEXT"
2518 )
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002519
2520 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002521 # TODO: This is untested.
2522 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002523
2524 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002525
2526
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002527def load_certificate_request(type, buffer):
2528 """
2529 Load a certificate request from a buffer
2530
2531 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2532 :param buffer: The buffer the certificate request is stored in
2533 :return: The X509Req object
2534 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002535 if isinstance(buffer, _text_type):
2536 buffer = buffer.encode("ascii")
2537
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002538 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002539
2540 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002541 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002542 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002543 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002544 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002545 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002546
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002547 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002548 # TODO: This is untested.
2549 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002550
2551 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002552 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002553 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002554
2555
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002556def sign(pkey, data, digest):
2557 """
2558 Sign data with a digest
2559
2560 :param pkey: Pkey to sign with
2561 :param data: data to be signed
2562 :param digest: message digest to use
2563 :return: signature
2564 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002565 data = _text_to_bytes_and_warn("data", data)
Abraham Martine82326c2015-02-04 10:18:10 +00002566
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002567 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002568 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002569 raise ValueError("No such digest method")
2570
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002571 md_ctx = _ffi.new("EVP_MD_CTX*")
2572 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002573
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002574 _lib.EVP_SignInit(md_ctx, digest_obj)
2575 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002576
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002577 signature_buffer = _ffi.new("unsigned char[]", 512)
2578 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002579 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002580 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002581 md_ctx, signature_buffer, signature_length, pkey._pkey)
2582
2583 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002584 # TODO: This is untested.
2585 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002586
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002587 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002588
2589
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002590def verify(cert, signature, data, digest):
2591 """
Laurens Van Houtvenc3baa7b2014-06-18 22:06:56 +02002592 Verify a signature.
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002593
2594 :param cert: signing certificate (X509 object)
2595 :param signature: signature returned by sign function
2596 :param data: data to be verified
2597 :param digest: message digest to use
Alex Gaynor5945ea82015-09-05 14:59:06 -04002598 :return: :py:const:`None` if the signature is correct, raise exception
2599 otherwise
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002600 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002601 data = _text_to_bytes_and_warn("data", data)
Abraham Martine82326c2015-02-04 10:18:10 +00002602
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002603 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002604 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002605 raise ValueError("No such digest method")
2606
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002607 pkey = _lib.X509_get_pubkey(cert._x509)
2608 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002609 # TODO: This is untested.
2610 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002611 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002612
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002613 md_ctx = _ffi.new("EVP_MD_CTX*")
2614 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002615
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002616 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2617 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
Alex Gaynor5945ea82015-09-05 14:59:06 -04002618 verify_result = _lib.EVP_VerifyFinal(
2619 md_ctx, signature, len(signature), pkey
2620 )
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002621
2622 if verify_result != 1:
2623 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002624
2625
Dominic Chenf05b2122015-10-13 16:32:35 +00002626def dump_crl(type, crl):
2627 """
2628 Dump a certificate revocation list to a buffer.
2629
2630 :param type: The file type (one of ``FILETYPE_PEM``, ``FILETYPE_ASN1``, or
2631 ``FILETYPE_TEXT``).
Hynek Schlawack0a3cd6d2015-10-21 16:39:22 +02002632 :param CRL crl: The CRL to dump.
2633
Dominic Chenf05b2122015-10-13 16:32:35 +00002634 :return: The buffer with the CRL.
Hynek Schlawack0a3cd6d2015-10-21 16:39:22 +02002635 :rtype: :data:`bytes`
Dominic Chenf05b2122015-10-13 16:32:35 +00002636 """
2637 bio = _new_mem_buf()
2638
2639 if type == FILETYPE_PEM:
2640 ret = _lib.PEM_write_bio_X509_CRL(bio, crl._crl)
2641 elif type == FILETYPE_ASN1:
2642 ret = _lib.i2d_X509_CRL_bio(bio, crl._crl)
2643 elif type == FILETYPE_TEXT:
2644 ret = _lib.X509_CRL_print(bio, crl._crl)
2645 else:
2646 raise ValueError(
2647 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
2648 "FILETYPE_TEXT")
2649
2650 assert ret == 1
2651 return _bio_to_string(bio)
2652
2653
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002654def load_crl(type, buffer):
2655 """
2656 Load a certificate revocation list from a buffer
2657
2658 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2659 :param buffer: The buffer the CRL is stored in
2660
2661 :return: The PKey object
2662 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002663 if isinstance(buffer, _text_type):
2664 buffer = buffer.encode("ascii")
2665
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002666 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002667
2668 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002669 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002670 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002671 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002672 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002673 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2674
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002675 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002676 _raise_current_error()
2677
2678 result = CRL.__new__(CRL)
2679 result._crl = crl
2680 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002681
2682
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002683def load_pkcs7_data(type, buffer):
2684 """
2685 Load pkcs7 data from a buffer
2686
2687 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2688 :param buffer: The buffer with the pkcs7 data.
2689 :return: The PKCS7 object
2690 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002691 if isinstance(buffer, _text_type):
2692 buffer = buffer.encode("ascii")
2693
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002694 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002695
2696 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002697 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002698 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002699 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002700 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002701 # TODO: This is untested.
2702 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002703 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2704
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002705 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002706 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002707
2708 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002709 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002710 return pypkcs7
2711
2712
Stephen Holsapple38482622014-04-05 20:29:34 -07002713def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002714 """
2715 Load a PKCS12 object from a buffer
2716
2717 :param buffer: The buffer the certificate is stored in
2718 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2719 :returns: The PKCS12 object
2720 """
Jean-Paul Calderone39a8d592015-04-13 20:49:50 -04002721 passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
Abraham Martine82326c2015-02-04 10:18:10 +00002722
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002723 if isinstance(buffer, _text_type):
2724 buffer = buffer.encode("ascii")
2725
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002726 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002727
Stephen Holsapple38482622014-04-05 20:29:34 -07002728 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2729 # password based encryption no password and a zero length password are two
2730 # different things, but OpenSSL implementation will try both to figure out
2731 # which one works.
2732 if not passphrase:
2733 passphrase = _ffi.NULL
2734
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002735 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2736 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002737 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002738 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002739
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002740 pkey = _ffi.new("EVP_PKEY**")
2741 cert = _ffi.new("X509**")
2742 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002743
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002744 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002745 if not parse_result:
2746 _raise_current_error()
2747
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002748 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002749
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002750 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2751 # queue for no particular reason. This error isn't interesting to anyone
2752 # outside this function. It's not even interesting to us. Get rid of it.
2753 try:
2754 _raise_current_error()
2755 except Error:
2756 pass
2757
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002758 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002759 pykey = None
2760 else:
2761 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002762 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002763
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002764 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002765 pycert = None
2766 friendlyname = None
2767 else:
2768 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002769 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002770
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002771 friendlyname_length = _ffi.new("int*")
Alex Gaynor5945ea82015-09-05 14:59:06 -04002772 friendlyname_buffer = _lib.X509_alias_get0(
2773 cert[0], friendlyname_length
2774 )
2775 friendlyname = _ffi.buffer(
2776 friendlyname_buffer, friendlyname_length[0]
2777 )[:]
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002778 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002779 friendlyname = None
2780
2781 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002782 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002783 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002784 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002785 pycacerts.append(pycacert)
2786 if not pycacerts:
2787 pycacerts = None
2788
2789 pkcs12 = PKCS12.__new__(PKCS12)
2790 pkcs12._pkey = pykey
2791 pkcs12._cert = pycert
2792 pkcs12._cacerts = pycacerts
2793 pkcs12._friendlyname = friendlyname
2794 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002795
2796
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002797# There are no direct unit tests for this initialization. It is tested
2798# indirectly since it is necessary for functions like dump_privatekey when
2799# using encryption.
2800#
2801# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2802# and some other similar tests may fail without this (though they may not if
2803# the Python runtime has already done some initialization of the underlying
2804# OpenSSL library (and is linked against the same one that cryptography is
2805# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002806_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002807
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002808# This is similar but exercised mainly by exception_from_error_queue. It calls
2809# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2810_lib.SSL_load_error_strings()
D.S. Ljungmark349e1362014-05-31 18:40:38 +02002811
2812
D.S. Ljungmark349e1362014-05-31 18:40:38 +02002813# Set the default string mask to match OpenSSL upstream (since 2005) and
2814# RFC5280 recommendations.
2815_lib.ASN1_STRING_set_default_mask_asc(b'utf8only')