blob: de009058ce35b584198a81773dc5b904d98eadc4 [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05003from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
5
6from six import (
7 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -04008 text_type as _text_type,
9 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080010
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050011from OpenSSL._util import (
12 ffi as _ffi,
13 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050014 exception_from_error_queue as _exception_from_error_queue,
15 byte_string as _byte_string,
16 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080017
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050018FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
19FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080020
21# TODO This was an API mistake. OpenSSL has no such constant.
22FILETYPE_TEXT = 2 ** 16 - 1
23
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050024TYPE_RSA = _lib.EVP_PKEY_RSA
25TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080026
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080027
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050028class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050029 """
30 An error occurred in an `OpenSSL.crypto` API.
31 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050032
33
34_raise_current_error = partial(_exception_from_error_queue, Error)
35
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050036def _untested_error(where):
37 """
38 An OpenSSL API failed somehow. Additionally, the failure which was
39 encountered isn't one that's exercised by the test suite so future behavior
40 of pyOpenSSL is now somewhat less predictable.
41 """
42 raise RuntimeError("Unknown %s failure" % (where,))
43
44
45
46def _new_mem_buf(buffer=None):
47 """
48 Allocate a new OpenSSL memory BIO.
49
50 Arrange for the garbage collector to clean it up automatically.
51
52 :param buffer: None or some bytes to use to put into the BIO so that they
53 can be read out.
54 """
55 if buffer is None:
56 bio = _lib.BIO_new(_lib.BIO_s_mem())
57 free = _lib.BIO_free
58 else:
59 data = _ffi.new("char[]", buffer)
60 bio = _lib.BIO_new_mem_buf(data, len(buffer))
61 # Keep the memory alive as long as the bio is alive!
62 def free(bio, ref=data):
63 return _lib.BIO_free(bio)
64
65 if bio == _ffi.NULL:
66 # TODO: This is untested.
67 _raise_current_error()
68
69 bio = _ffi.gc(bio, free)
70 return bio
71
72
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050073
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080074def _bio_to_string(bio):
75 """
76 Copy the contents of an OpenSSL BIO object into a Python byte string.
77 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050078 result_buffer = _ffi.new('char**')
79 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
80 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080081
82
83
Jean-Paul Calderone57122982013-02-21 08:47:05 -080084def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050085 """
86 The the time value of an ASN1 time object.
87
88 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
89 castable to that type) which will have its value set.
90 @param when: A string representation of the desired time value.
91
92 @raise TypeError: If C{when} is not a L{bytes} string.
93 @raise ValueError: If C{when} does not represent a time in the required
94 format.
95 @raise RuntimeError: If the time value cannot be set for some other
96 (unspecified) reason.
97 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080098 if not isinstance(when, bytes):
99 raise TypeError("when must be a byte string")
100
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500101 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
102 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800103 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500104 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
105 _lib.ASN1_STRING_set(dummy, when, len(when))
106 check_result = _lib.ASN1_GENERALIZEDTIME_check(
107 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800108 if not check_result:
109 raise ValueError("Invalid string")
110 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500111 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800112
113
114
115def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500116 """
117 Retrieve the time value of an ASN1 time object.
118
119 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
120 that type) from which the time value will be retrieved.
121
122 @return: The time value from C{timestamp} as a L{bytes} string in a certain
123 format. Or C{None} if the object contains no time value.
124 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500125 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
126 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800127 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500128 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
129 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800130 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500131 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
132 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
133 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500134 # This may happen:
135 # - if timestamp was not an ASN1_TIME
136 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
137 # - if a copy of the time data from timestamp cannot be made for
138 # the newly allocated ASN1_GENERALIZEDTIME
139 #
140 # These are difficult to test. cffi enforces the ASN1_TIME type.
141 # Memory allocation failures are a pain to trigger
142 # deterministically.
143 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800144 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500145 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800146 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500147 string_data = _lib.ASN1_STRING_data(string_timestamp)
148 string_result = _ffi.string(string_data)
149 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800150 return string_result
151
152
153
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800154class PKey(object):
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200155 """
156 A class representing an DSA or RSA public key or key pair.
157 """
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800158 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800159 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800160
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800161 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500162 pkey = _lib.EVP_PKEY_new()
163 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800164 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800165
166
167 def generate_key(self, type, bits):
168 """
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200169 Generate a key pair of the given type, with the given number of a bits.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800170
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200171 This generates a key "into" the this object.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800172
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200173 :param type: The key type.
174 :type type: :py:data:`TYPE_RSA` or :py:data:`TYPE_DSA`
175 :param bits: The number of bits.
176 :type bits: :py:data:`int` ``>= 0``
177 :raises TypeError: If :py:data:`type` or :py:data:`bits` isn't
178 of the appropriate type.
179 :raises ValueError: If the number of bits isn't an integer of
180 the appropriate size.
181 :return: :py:data:`None`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800182 """
183 if not isinstance(type, int):
184 raise TypeError("type must be an integer")
185
186 if not isinstance(bits, int):
187 raise TypeError("bits must be an integer")
188
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800189 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500190 exponent = _lib.BN_new()
191 exponent = _ffi.gc(exponent, _lib.BN_free)
192 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800193
194 if type == TYPE_RSA:
195 if bits <= 0:
196 raise ValueError("Invalid number of bits")
197
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500198 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800199
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500200 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500201 if result == 0:
202 # TODO: The test for this case is commented out. Different
203 # builds of OpenSSL appear to have different failure modes that
204 # make it hard to test. Visual inspection of the OpenSSL
205 # source reveals that a return value of 0 signals an error.
206 # Manual testing on a particular build of OpenSSL suggests that
207 # this is probably the appropriate way to handle those errors.
208 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800209
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500210 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800211 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500212 # TODO: It appears as though this can fail if an engine is in
213 # use which does not support RSA.
214 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800215
216 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500217 dsa = _lib.DSA_generate_parameters(
218 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
219 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500220 # TODO: This is untested.
221 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500222 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500223 # TODO: This is untested.
224 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500225 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500226 # TODO: This is untested.
227 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800228 else:
229 raise Error("No such key type")
230
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800231 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800232
233
234 def check(self):
235 """
236 Check the consistency of an RSA private key.
237
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200238 This is the Python equivalent of OpenSSL's ``RSA_check_key``.
239
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800240 :return: True if key is consistent.
241 :raise Error: if the key is inconsistent.
242 :raise TypeError: if the key is of a type which cannot be checked.
243 Only RSA keys can currently be checked.
244 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800245 if self._only_public:
246 raise TypeError("public key only")
247
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500248 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800249 raise TypeError("key type unsupported")
250
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500251 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
252 rsa = _ffi.gc(rsa, _lib.RSA_free)
253 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800254 if result:
255 return True
256 _raise_current_error()
257
258
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800259 def type(self):
260 """
261 Returns the type of the key
262
263 :return: The type of the key.
264 """
265 return self._pkey.type
266
267
268 def bits(self):
269 """
270 Returns the number of bits of the key
271
272 :return: The number of bits of the key.
273 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500274 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800275PKeyType = PKey
276
277
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800278
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
303
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400304 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400305 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400306 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400307 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400308
309 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400310
311 :return: A :py:type:`set` of ``cls`` instances giving the names of the
312 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400313 """
314 if lib.Cryptography_HAS_EC:
315 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
316 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
317 # The return value on this call should be num_curves again. We could
318 # check it to make sure but if it *isn't* then.. what could we do?
319 # Abort the whole process, I suppose...? -exarkun
320 lib.EC_get_builtin_curves(builtin_curves, num_curves)
321 return set(
322 cls.from_nid(lib, c.nid)
323 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400324 return set()
325
326
327 @classmethod
328 def _get_elliptic_curves(cls, lib):
329 """
330 Get, cache, and return the curves supported by OpenSSL.
331
332 :param lib: The OpenSSL library binding object.
333
334 :return: A :py:type:`set` of ``cls`` instances giving the names of the
335 elliptic curves the underlying library supports.
336 """
337 if cls._curves is None:
338 cls._curves = cls._load_elliptic_curves(lib)
339 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400340
341
342 @classmethod
343 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400344 """
345 Instantiate a new :py:class:`_EllipticCurve` associated with the given
346 OpenSSL NID.
347
348 :param lib: The OpenSSL library binding object.
349
350 :param nid: The OpenSSL NID the resulting curve object will represent.
351 This must be a curve NID (and not, for example, a hash NID) or
352 subsequent operations will fail in unpredictable ways.
353 :type nid: :py:class:`int`
354
355 :return: The curve object.
356 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400357 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
358
359
360 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400361 """
362 :param _lib: The :py:mod:`cryptography` binding instance used to
363 interface with OpenSSL.
364
365 :param _nid: The OpenSSL NID identifying the curve this object
366 represents.
367 :type _nid: :py:class:`int`
368
369 :param name: The OpenSSL short name identifying the curve this object
370 represents.
371 :type name: :py:class:`unicode`
372 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400373 self._lib = lib
374 self._nid = nid
375 self.name = name
376
377
378 def __repr__(self):
379 return "<Curve %r>" % (self.name,)
380
381
382 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400383 """
384 Create a new OpenSSL EC_KEY structure initialized to use this curve.
385
386 The structure is automatically garbage collected when the Python object
387 is garbage collected.
388 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400389 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
390 return _ffi.gc(key, _lib.EC_KEY_free)
391
392
393
394def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400395 """
396 Return a set of objects representing the elliptic curves supported in the
397 OpenSSL build in use.
398
399 The curve objects have a :py:class:`unicode` ``name`` attribute by which
400 they identify themselves.
401
402 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400403 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
404 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400405 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400406 return _EllipticCurve._get_elliptic_curves(_lib)
407
408
409
410def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400411 """
412 Return a single curve object selected by name.
413
414 See :py:func:`get_elliptic_curves` for information about curve objects.
415
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400416 :param name: The OpenSSL short name identifying the curve object to
417 retrieve.
418 :type name: :py:class:`unicode`
419
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400420 If the named curve is not supported then :py:class:`ValueError` is raised.
421 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400422 for curve in get_elliptic_curves():
423 if curve.name == name:
424 return curve
425 raise ValueError("unknown curve name", name)
426
427
428
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800429class X509Name(object):
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200430 """
431 An X.509 Distinguished Name.
432
433 :ivar countryName: The country of the entity.
434 :ivar C: Alias for :py:attr:`countryName`.
435
436 :ivar stateOrProvinceName: The state or province of the entity.
437 :ivar ST: Alias for :py:attr:`stateOrProvinceName`.
438
439 :ivar localityName: The locality of the entity.
440 :ivar L: Alias for :py:attr:`localityName`.
441
442 :ivar organizationName: The organization name of the entity.
443 :ivar O: Alias for :py:attr:`organizationName`.
444
445 :ivar organizationalUnitName: The organizational unit of the entity.
446 :ivar OU: Alias for :py:attr:`organizationalUnitName`
447
448 :ivar commonName: The common name of the entity.
449 :ivar CN: Alias for :py:attr:`commonName`.
450
451 :ivar emailAddress: The e-mail address of the entity.
452 """
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800453 def __init__(self, name):
454 """
455 Create a new X509Name, copying the given X509Name instance.
456
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200457 :param name: The name to copy.
458 :type name: :py:class:`X509Name`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800459 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500460 name = _lib.X509_NAME_dup(name._name)
461 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800462
463
464 def __setattr__(self, name, value):
465 if name.startswith('_'):
466 return super(X509Name, self).__setattr__(name, value)
467
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800468 # Note: we really do not want str subclasses here, so we do not use
469 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800470 if type(name) is not str:
471 raise TypeError("attribute name must be string, not '%.200s'" % (
472 type(value).__name__,))
473
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500474 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500475 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800476 try:
477 _raise_current_error()
478 except Error:
479 pass
480 raise AttributeError("No such attribute")
481
482 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500483 for i in range(_lib.X509_NAME_entry_count(self._name)):
484 ent = _lib.X509_NAME_get_entry(self._name, i)
485 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
486 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800487 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500488 ent = _lib.X509_NAME_delete_entry(self._name, i)
489 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800490 break
491
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500492 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800493 value = value.encode('utf-8')
494
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500495 add_result = _lib.X509_NAME_add_entry_by_NID(
496 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800497 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500498 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800499
500
501 def __getattr__(self, name):
502 """
503 Find attribute. An X509Name object has the following attributes:
504 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
505 organization (alias O), organizationalUnit (alias OU), commonName (alias
506 CN) and more...
507 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500508 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500509 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800510 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
511 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
512 # push something onto the error queue. If we don't clean that up
513 # now, someone else will bump into it later and be quite confused.
514 # See lp#314814.
515 try:
516 _raise_current_error()
517 except Error:
518 pass
519 return super(X509Name, self).__getattr__(name)
520
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500521 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522 if entry_index == -1:
523 return None
524
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500525 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
526 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800527
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500528 result_buffer = _ffi.new("unsigned char**")
529 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800530 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500531 # TODO: This is untested.
532 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800533
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700534 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500535 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700536 finally:
537 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500538 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800539 return result
540
541
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500542 def _cmp(op):
543 def f(self, other):
544 if not isinstance(other, X509Name):
545 return NotImplemented
546 result = _lib.X509_NAME_cmp(self._name, other._name)
547 return op(result, 0)
548 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800549
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500550 __eq__ = _cmp(__eq__)
551 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800552
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500553 __lt__ = _cmp(__lt__)
554 __le__ = _cmp(__le__)
555
556 __gt__ = _cmp(__gt__)
557 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800558
559 def __repr__(self):
560 """
561 String representation of an X509Name
562 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500563 result_buffer = _ffi.new("char[]", 512);
564 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800565 self._name, result_buffer, len(result_buffer))
566
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500567 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500568 # TODO: This is untested.
569 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800570
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500571 return "<X509Name object '%s'>" % (
572 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800573
574
575 def hash(self):
576 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200577 Return an integer representation of the first four bytes of the
578 MD5 digest of the DER representation of the name.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800579
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200580 This is the Python equivalent of OpenSSL's ``X509_NAME_hash``.
581
582 :return: The (integer) hash of this name.
583 :rtype: :py:class:`int`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800584 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500585 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800586
587
588 def der(self):
589 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200590 Return the DER encoding of this name.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800591
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200592 :return: The DER encoded form of this name.
593 :rtype: :py:class:`bytes`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800594 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500595 result_buffer = _ffi.new('unsigned char**')
596 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800597 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500598 # TODO: This is untested.
599 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800600
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500601 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
602 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800603 return string_result
604
605
606 def get_components(self):
607 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200608 Returns the components of this name, as a sequence of 2-tuples.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800609
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200610 :return: The components of this name.
611 :rtype: :py:class:`list` of ``name, value`` tuples.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800612 """
613 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500614 for i in range(_lib.X509_NAME_entry_count(self._name)):
615 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800616
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500617 fname = _lib.X509_NAME_ENTRY_get_object(ent)
618 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800619
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500620 nid = _lib.OBJ_obj2nid(fname)
621 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800622
623 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500624 _ffi.string(name),
625 _ffi.string(
626 _lib.ASN1_STRING_data(fval),
627 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800628
629 return result
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200630
631
632
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800633X509NameType = X509Name
634
635
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200636
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800637class X509Extension(object):
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200638 """
639 An X.509 v3 certificate extension.
640 """
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800641 def __init__(self, type_name, critical, value, subject=None, issuer=None):
642 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200643 Initializes an X509 extension.
644
645 :param typename: The name of the type of extension to create. See
646 http://openssl.org/docs/apps/x509v3_config.html#STANDARD_EXTENSIONS
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800647 :type typename: :py:data:`str`
648
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200649 :param bool critical: A flag indicating whether this is a critical extension.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800650
651 :param value: The value of the extension.
652 :type value: :py:data:`str`
653
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200654 :param subject: Optional X509 certificate to use as subject.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800655 :type subject: :py:class:`X509`
656
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200657 :param issuer: Optional X509 certificate to use as issuer.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800658 :type issuer: :py:class:`X509`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800659 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500660 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800661
662 # A context is necessary for any extension which uses the r2i conversion
663 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
664 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500665 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800666
667 # We have no configuration database - but perhaps we should (some
668 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500669 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800670
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800671 # Initialize the subject and issuer, if appropriate. ctx is a local,
672 # and as far as I can tell none of the X509V3_* APIs invoked here steal
673 # any references, so no need to mess with reference counts or duplicates.
674 if issuer is not None:
675 if not isinstance(issuer, X509):
676 raise TypeError("issuer must be an X509 instance")
677 ctx.issuer_cert = issuer._x509
678 if subject is not None:
679 if not isinstance(subject, X509):
680 raise TypeError("subject must be an X509 instance")
681 ctx.subject_cert = subject._x509
682
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800683 if critical:
684 # There are other OpenSSL APIs which would let us pass in critical
685 # separately, but they're harder to use, and since value is already
686 # a pile of crappy junk smuggling a ton of utterly important
687 # structured data, what's the point of trying to avoid nasty stuff
688 # with strings? (However, X509V3_EXT_i2d in particular seems like it
689 # would be a better API to invoke. I do not know where to get the
690 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500691 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800692
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500693 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
694 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800695 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500696 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800697
698
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400699 @property
700 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500701 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400702
703 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500704 _lib.GEN_EMAIL: "email",
705 _lib.GEN_DNS: "DNS",
706 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400707 }
708
709 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500710 method = _lib.X509V3_EXT_get(self._extension)
711 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500712 # TODO: This is untested.
713 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400714 payload = self._extension.value.data
715 length = self._extension.value.length
716
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500717 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400718 payloadptr[0] = payload
719
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500720 if method.it != _ffi.NULL:
721 ptr = _lib.ASN1_ITEM_ptr(method.it)
722 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
723 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400724 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500725 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400726 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500727 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400728
729 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500730 for i in range(_lib.sk_GENERAL_NAME_num(names)):
731 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400732 try:
733 label = self._prefixes[name.type]
734 except KeyError:
735 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500736 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500737 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400738 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500739 value = _native(
740 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
741 parts.append(label + ":" + value)
742 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400743
744
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800745 def __str__(self):
746 """
747 :return: a nice text representation of the extension
748 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500749 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400750 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800751
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400752 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500753 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800754 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500755 # TODO: This is untested.
756 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800757
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500758 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800759
760
761 def get_critical(self):
762 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200763 Returns the critical field of this X.509 extension.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800764
765 :return: The critical field.
766 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500767 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800768
769
770 def get_short_name(self):
771 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200772 Returns the short type name of this X.509 extension.
773
774 The result is a byte string such as :py:const:`b"basicConstraints"`.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800775
776 :return: The short type name.
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200777 :rtype: :py:data:`bytes`
778
779 .. versionadded:: 0.12
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800780 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500781 obj = _lib.X509_EXTENSION_get_object(self._extension)
782 nid = _lib.OBJ_obj2nid(obj)
783 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800784
785
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800786 def get_data(self):
787 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200788 Returns the data of the X509 extension, encoded as ASN.1.
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800789
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200790 :return: The ASN.1 encoded data of this X509 extension.
791 :rtype: :py:data:`bytes`
792
793 .. versionadded:: 0.12
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800794 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500795 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
796 string_result = _ffi.cast('ASN1_STRING*', octet_result)
797 char_result = _lib.ASN1_STRING_data(string_result)
798 result_length = _lib.ASN1_STRING_length(string_result)
799 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800800
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200801
802
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800803X509ExtensionType = X509Extension
804
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800805
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200806
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800807class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800808 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500809 req = _lib.X509_REQ_new()
810 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800811
812
813 def set_pubkey(self, pkey):
814 """
815 Set the public key of the certificate request
816
817 :param pkey: The public key to use
818 :return: None
819 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500820 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800821 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500822 # TODO: This is untested.
823 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800824
825
826 def get_pubkey(self):
827 """
828 Get the public key from the certificate request
829
830 :return: The public key
831 """
832 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500833 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
834 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500835 # TODO: This is untested.
836 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500837 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800838 pkey._only_public = True
839 return pkey
840
841
842 def set_version(self, version):
843 """
844 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
845 request.
846
847 :param version: The version number
848 :return: None
849 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500850 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800851 if not set_result:
852 _raise_current_error()
853
854
855 def get_version(self):
856 """
857 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
858 request.
859
860 :return: an integer giving the value of the version subfield
861 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500862 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800863
864
865 def get_subject(self):
866 """
867 Create an X509Name object for the subject of the certificate request
868
869 :return: An X509Name object
870 """
871 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500872 name._name = _lib.X509_REQ_get_subject_name(self._req)
873 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500874 # TODO: This is untested.
875 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800876
877 # The name is owned by the X509Req structure. As long as the X509Name
878 # Python object is alive, keep the X509Req Python object alive.
879 name._owner = self
880
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800881 return name
882
883
884 def add_extensions(self, extensions):
885 """
886 Add extensions to the request.
887
888 :param extensions: a sequence of X509Extension objects
889 :return: None
890 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500891 stack = _lib.sk_X509_EXTENSION_new_null()
892 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500893 # TODO: This is untested.
894 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800895
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500896 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800897
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800898 for ext in extensions:
899 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800900 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800901
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800902 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500903 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800904
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500905 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800906 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500907 # TODO: This is untested.
908 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800909
910
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800911 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800912 """
913 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800914
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500915 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800916 """
917 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500918 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500919 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800920 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500921 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800922 exts.append(ext)
923 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800924
925
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800926 def sign(self, pkey, digest):
927 """
928 Sign the certificate request using the supplied key and digest
929
930 :param pkey: The key to sign with
931 :param digest: The message digest to use
932 :return: None
933 """
934 if pkey._only_public:
935 raise ValueError("Key has only public part")
936
937 if not pkey._initialized:
938 raise ValueError("Key is uninitialized")
939
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500940 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500941 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800942 raise ValueError("No such digest method")
943
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500944 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800945 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500946 # TODO: This is untested.
947 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800948
949
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800950 def verify(self, pkey):
951 """
952 Verifies a certificate request using the supplied public key
953
954 :param key: a public key
955 :return: True if the signature is correct.
956
957 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
958 problem verifying the signature.
959 """
960 if not isinstance(pkey, PKey):
961 raise TypeError("pkey must be a PKey instance")
962
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500963 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800964 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500965 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800966
967 return result
968
969
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800970X509ReqType = X509Req
971
972
973
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800974class X509(object):
975 def __init__(self):
976 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500977 x509 = _lib.X509_new()
978 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800979
980
981 def set_version(self, version):
982 """
983 Set version number of the certificate
984
985 :param version: The version number
986 :type version: :py:class:`int`
987
988 :return: None
989 """
990 if not isinstance(version, int):
991 raise TypeError("version must be an integer")
992
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500993 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800994
995
996 def get_version(self):
997 """
998 Return version number of the certificate
999
1000 :return: Version number as a Python integer
1001 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001002 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001003
1004
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001005 def get_pubkey(self):
1006 """
1007 Get the public key of the certificate
1008
1009 :return: The public key
1010 """
1011 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001012 pkey._pkey = _lib.X509_get_pubkey(self._x509)
1013 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001014 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001015 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001016 pkey._only_public = True
1017 return pkey
1018
1019
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001020 def set_pubkey(self, pkey):
1021 """
1022 Set the public key of the certificate
1023
1024 :param pkey: The public key
1025
1026 :return: None
1027 """
1028 if not isinstance(pkey, PKey):
1029 raise TypeError("pkey must be a PKey instance")
1030
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001031 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001032 if not set_result:
1033 _raise_current_error()
1034
1035
1036 def sign(self, pkey, digest):
1037 """
1038 Sign the certificate using the supplied key and digest
1039
1040 :param pkey: The key to sign with
1041 :param digest: The message digest to use
1042 :return: None
1043 """
1044 if not isinstance(pkey, PKey):
1045 raise TypeError("pkey must be a PKey instance")
1046
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001047 if pkey._only_public:
1048 raise ValueError("Key only has public part")
1049
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -08001050 if not pkey._initialized:
1051 raise ValueError("Key is uninitialized")
1052
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001053 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001054 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001055 raise ValueError("No such digest method")
1056
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001057 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001058 if not sign_result:
1059 _raise_current_error()
1060
1061
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001062 def get_signature_algorithm(self):
1063 """
1064 Retrieve the signature algorithm used in the certificate
1065
1066 :return: A byte string giving the name of the signature algorithm used in
1067 the certificate.
1068 :raise ValueError: If the signature algorithm is undefined.
1069 """
1070 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 nid = _lib.OBJ_obj2nid(alg)
1072 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001073 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001074 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001075
1076
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
1084 :return: The digest of the object
1085 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001086 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001087 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001088 raise ValueError("No such digest method")
1089
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001090 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1091 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001092 result_length[0] = len(result_buffer)
1093
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001094 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001095 self._x509, digest, result_buffer, result_length)
1096
1097 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001098 # TODO: This is untested.
1099 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001100
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001101 return b":".join([
1102 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001103 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001104
1105
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001106 def subject_name_hash(self):
1107 """
1108 Return the hash of the X509 subject.
1109
1110 :return: The hash of the subject.
1111 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001112 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001113
1114
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001115 def set_serial_number(self, serial):
1116 """
1117 Set serial number of the certificate
1118
1119 :param serial: The serial number
1120 :type serial: :py:class:`int`
1121
1122 :return: None
1123 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001124 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001125 raise TypeError("serial must be an integer")
1126
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001127 hex_serial = hex(serial)[2:]
1128 if not isinstance(hex_serial, bytes):
1129 hex_serial = hex_serial.encode('ascii')
1130
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001131 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001132
1133 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1134 # it. If bignum is still NULL after this call, then the return value is
1135 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001136 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001137
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001138 if bignum_serial[0] == _ffi.NULL:
1139 set_result = _lib.ASN1_INTEGER_set(
1140 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001141 if set_result:
1142 # TODO Not tested
1143 _raise_current_error()
1144 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001145 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1146 _lib.BN_free(bignum_serial[0])
1147 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001148 # TODO Not tested
1149 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001150 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1151 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001152 if not set_result:
1153 # TODO Not tested
1154 _raise_current_error()
1155
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001156
1157 def get_serial_number(self):
1158 """
1159 Return serial number of the certificate
1160
1161 :return: Serial number as a Python integer
1162 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001163 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1164 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001165 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001166 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001167 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001168 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001169 serial = int(hexstring_serial, 16)
1170 return serial
1171 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001172 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001173 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001174 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001175
1176
1177 def gmtime_adj_notAfter(self, amount):
1178 """
1179 Adjust the time stamp for when the certificate stops being valid
1180
1181 :param amount: The number of seconds by which to adjust the ending
1182 validity time.
1183 :type amount: :py:class:`int`
1184
1185 :return: None
1186 """
1187 if not isinstance(amount, int):
1188 raise TypeError("amount must be an integer")
1189
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001190 notAfter = _lib.X509_get_notAfter(self._x509)
1191 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001192
1193
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001194 def gmtime_adj_notBefore(self, amount):
1195 """
1196 Change the timestamp for when the certificate starts being valid to the current
1197 time plus an offset.
1198
1199 :param amount: The number of seconds by which to adjust the starting validity
1200 time.
1201 :return: None
1202 """
1203 if not isinstance(amount, int):
1204 raise TypeError("amount must be an integer")
1205
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001206 notBefore = _lib.X509_get_notBefore(self._x509)
1207 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001208
1209
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001210 def has_expired(self):
1211 """
1212 Check whether the certificate has expired.
1213
1214 :return: True if the certificate has expired, false otherwise
1215 """
1216 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001217 notAfter = _lib.X509_get_notAfter(self._x509)
1218 return _lib.ASN1_UTCTIME_cmp_time_t(
1219 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001220
1221
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001222 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001223 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001224
1225
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001226 def get_notBefore(self):
1227 """
1228 Retrieve the time stamp for when the certificate starts being valid
1229
1230 :return: A string giving the timestamp, in the format::
1231
1232 YYYYMMDDhhmmssZ
1233 YYYYMMDDhhmmss+hhmm
1234 YYYYMMDDhhmmss-hhmm
1235
1236 or None if there is no value set.
1237 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001238 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001239
1240
1241 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001242 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001243
1244
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001245 def set_notBefore(self, when):
1246 """
1247 Set the time stamp for when the certificate starts being valid
1248
1249 :param when: A string giving the timestamp, in the format:
1250
1251 YYYYMMDDhhmmssZ
1252 YYYYMMDDhhmmss+hhmm
1253 YYYYMMDDhhmmss-hhmm
1254 :type when: :py:class:`bytes`
1255
1256 :return: None
1257 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001258 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001259
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001260
1261 def get_notAfter(self):
1262 """
1263 Retrieve the time stamp for when the certificate stops being valid
1264
1265 :return: A string giving the timestamp, in the format::
1266
1267 YYYYMMDDhhmmssZ
1268 YYYYMMDDhhmmss+hhmm
1269 YYYYMMDDhhmmss-hhmm
1270
1271 or None if there is no value set.
1272 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001273 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001274
1275
1276 def set_notAfter(self, when):
1277 """
1278 Set the time stamp for when the certificate stops being valid
1279
1280 :param when: A string giving the timestamp, in the format:
1281
1282 YYYYMMDDhhmmssZ
1283 YYYYMMDDhhmmss+hhmm
1284 YYYYMMDDhhmmss-hhmm
1285 :type when: :py:class:`bytes`
1286
1287 :return: None
1288 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001289 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001290
1291
1292 def _get_name(self, which):
1293 name = X509Name.__new__(X509Name)
1294 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001295 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001296 # TODO: This is untested.
1297 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001298
1299 # The name is owned by the X509 structure. As long as the X509Name
1300 # Python object is alive, keep the X509 Python object alive.
1301 name._owner = self
1302
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001303 return name
1304
1305
1306 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001307 if not isinstance(name, X509Name):
1308 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001309 set_result = which(self._x509, name._name)
1310 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001311 # TODO: This is untested.
1312 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001313
1314
1315 def get_issuer(self):
1316 """
1317 Create an X509Name object for the issuer of the certificate
1318
1319 :return: An X509Name object
1320 """
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
1323
1324 def set_issuer(self, issuer):
1325 """
1326 Set the issuer of the certificate
1327
1328 :param issuer: The issuer name
1329 :type issuer: :py:class:`X509Name`
1330
1331 :return: None
1332 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001333 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001334
1335
1336 def get_subject(self):
1337 """
1338 Create an X509Name object for the subject of the certificate
1339
1340 :return: An X509Name object
1341 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001342 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001343
1344
1345 def set_subject(self, subject):
1346 """
1347 Set the subject of the certificate
1348
1349 :param subject: The subject name
1350 :type subject: :py:class:`X509Name`
1351 :return: None
1352 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001353 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001354
1355
1356 def get_extension_count(self):
1357 """
1358 Get the number of extensions on the certificate.
1359
1360 :return: The number of extensions as an integer.
1361 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001362 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001363
1364
1365 def add_extensions(self, extensions):
1366 """
1367 Add extensions to the certificate.
1368
1369 :param extensions: a sequence of X509Extension objects
1370 :return: None
1371 """
1372 for ext in extensions:
1373 if not isinstance(ext, X509Extension):
1374 raise ValueError("One of the elements is not an X509Extension")
1375
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001376 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001377 if not add_result:
1378 _raise_current_error()
1379
1380
1381 def get_extension(self, index):
1382 """
1383 Get a specific extension of the certificate by index.
1384
1385 :param index: The index of the extension to retrieve.
1386 :return: The X509Extension object at the specified index.
1387 """
1388 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001389 ext._extension = _lib.X509_get_ext(self._x509, index)
1390 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001391 raise IndexError("extension index out of bounds")
1392
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001393 extension = _lib.X509_EXTENSION_dup(ext._extension)
1394 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001395 return ext
1396
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001397
1398
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001399X509Type = X509
1400
1401
1402
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001403class X509Store(object):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001404 """
1405 An X509 certificate store.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001406 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001407 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001408 store = _lib.X509_STORE_new()
1409 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001410
1411
1412 def add_cert(self, cert):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001413 """
1414 Adds the certificate :py:data:`cert` to this store.
1415
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +02001416 This is the Python equivalent of OpenSSL's ``X509_STORE_add_cert``.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001417
1418 :param X509 cert: The certificate to add to this store.
1419 :raises TypeError: If the certificate is not an :py:class:`X509`.
1420 :raises Error: If OpenSSL was unhappy with your certificate.
1421 :return: py:data:`None` if the certificate was added successfully.
1422 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001423 if not isinstance(cert, X509):
1424 raise TypeError()
1425
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001426 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001427 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001428 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001429
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001430
1431X509StoreType = X509Store
1432
1433
1434
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001435def load_certificate(type, buffer):
1436 """
1437 Load a certificate from a buffer
1438
1439 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1440
1441 :param buffer: The buffer the certificate is stored in
1442 :type buffer: :py:class:`bytes`
1443
1444 :return: The X509 object
1445 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001446 if isinstance(buffer, _text_type):
1447 buffer = buffer.encode("ascii")
1448
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001449 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001450
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001451 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001452 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001453 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001454 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001455 else:
1456 raise ValueError(
1457 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001458
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001459 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001460 _raise_current_error()
1461
1462 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001463 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001464 return cert
1465
1466
1467def dump_certificate(type, cert):
1468 """
1469 Dump a certificate to a buffer
1470
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001471 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1472 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001473 :param cert: The certificate to dump
1474 :return: The buffer with the dumped certificate in
1475 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001476 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001477
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001478 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001479 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001480 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001481 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001482 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001483 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001484 else:
1485 raise ValueError(
1486 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1487 "FILETYPE_TEXT")
1488
1489 return _bio_to_string(bio)
1490
1491
1492
1493def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1494 """
1495 Dump a private key to a buffer
1496
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001497 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1498 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001499 :param pkey: The PKey to dump
1500 :param cipher: (optional) if encrypted PEM format, the cipher to
1501 use
1502 :param passphrase: (optional) if encrypted PEM format, this can be either
1503 the passphrase to use, or a callback for providing the
1504 passphrase.
1505 :return: The buffer with the dumped key in
1506 :rtype: :py:data:`str`
1507 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001508 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001509
1510 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001511 if passphrase is None:
1512 raise TypeError(
1513 "if a value is given for cipher "
1514 "one must also be given for passphrase")
1515 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001516 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001517 raise ValueError("Invalid cipher name")
1518 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001519 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001520
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001521 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001522 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001523 result_code = _lib.PEM_write_bio_PrivateKey(
1524 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001525 helper.callback, helper.callback_args)
1526 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001527 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001528 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001529 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001530 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1531 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001532 # TODO RSA_free(rsa)?
1533 else:
1534 raise ValueError(
1535 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1536 "FILETYPE_TEXT")
1537
1538 if result_code == 0:
1539 _raise_current_error()
1540
1541 return _bio_to_string(bio)
1542
1543
1544
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001545def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001546 copy = _lib.X509_REVOKED_new()
1547 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001548 # TODO: This is untested.
1549 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001550
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001551 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001552 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001553 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001554
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001555 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001556 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001557 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001558
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001559 if original.extensions != _ffi.NULL:
1560 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1561 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1562 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1563 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1564 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001565 copy.extensions = extension_stack
1566
1567 copy.sequence = original.sequence
1568 return copy
1569
1570
1571
1572class Revoked(object):
1573 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1574 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1575 # OCSP_crl_reason_str. We use the latter, just like the command line
1576 # program.
1577 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001578 b"unspecified",
1579 b"keyCompromise",
1580 b"CACompromise",
1581 b"affiliationChanged",
1582 b"superseded",
1583 b"cessationOfOperation",
1584 b"certificateHold",
1585 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001586 ]
1587
1588 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001589 revoked = _lib.X509_REVOKED_new()
1590 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001591
1592
1593 def set_serial(self, hex_str):
1594 """
1595 Set the serial number of a revoked Revoked structure
1596
1597 :param hex_str: The new serial number.
1598 :type hex_str: :py:data:`str`
1599 :return: None
1600 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001601 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1602 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001603 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001604 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001605 if not bn_result:
1606 raise ValueError("bad hex string")
1607
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001608 asn1_serial = _ffi.gc(
1609 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1610 _lib.ASN1_INTEGER_free)
1611 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001612
1613
1614 def get_serial(self):
1615 """
1616 Return the serial number of a Revoked structure
1617
1618 :return: The serial number as a string
1619 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001620 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001621
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001622 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001623 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001624 # TODO: This is untested.
1625 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001626
1627 return _bio_to_string(bio)
1628
1629
1630 def _delete_reason(self):
1631 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001632 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1633 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1634 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1635 _lib.X509_EXTENSION_free(ext)
1636 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001637 break
1638
1639
1640 def set_reason(self, reason):
1641 """
1642 Set the reason of a Revoked object.
1643
1644 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1645
1646 :param reason: The reason string.
1647 :type reason: :py:class:`str` or :py:class:`NoneType`
1648 :return: None
1649 """
1650 if reason is None:
1651 self._delete_reason()
1652 elif not isinstance(reason, bytes):
1653 raise TypeError("reason must be None or a byte string")
1654 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001655 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001656 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1657
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001658 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1659 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001660 # TODO: This is untested.
1661 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001662 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001663
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001664 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1665 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001666 # TODO: This is untested.
1667 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001668
1669 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001670 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1671 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001672
1673 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001674 # TODO: This is untested.
1675 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001676
1677
1678 def get_reason(self):
1679 """
1680 Return the reason of a Revoked object.
1681
1682 :return: The reason as a string
1683 """
1684 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001685 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1686 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1687 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001688 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001689
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001690 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001691 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001692 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001693 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001694 # TODO: This is untested.
1695 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001696
1697 return _bio_to_string(bio)
1698
1699
1700 def all_reasons(self):
1701 """
1702 Return a list of all the supported reason strings.
1703
1704 :return: A list of reason strings.
1705 """
1706 return self._crl_reasons[:]
1707
1708
1709 def set_rev_date(self, when):
1710 """
1711 Set the revocation timestamp
1712
1713 :param when: A string giving the timestamp, in the format:
1714
1715 YYYYMMDDhhmmssZ
1716 YYYYMMDDhhmmss+hhmm
1717 YYYYMMDDhhmmss-hhmm
1718
1719 :return: None
1720 """
1721 return _set_asn1_time(self._revoked.revocationDate, when)
1722
1723
1724 def get_rev_date(self):
1725 """
1726 Retrieve the revocation date
1727
1728 :return: A string giving the timestamp, in the format:
1729
1730 YYYYMMDDhhmmssZ
1731 YYYYMMDDhhmmss+hhmm
1732 YYYYMMDDhhmmss-hhmm
1733 """
1734 return _get_asn1_time(self._revoked.revocationDate)
1735
1736
1737
1738class CRL(object):
1739 def __init__(self):
1740 """
1741 Create a new empty CRL object.
1742 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001743 crl = _lib.X509_CRL_new()
1744 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001745
1746
1747 def get_revoked(self):
1748 """
1749 Return revoked portion of the CRL structure (by value not reference).
1750
1751 :return: A tuple of Revoked objects.
1752 """
1753 results = []
1754 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001755 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1756 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001757 revoked_copy = _X509_REVOKED_dup(revoked)
1758 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001759 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001760 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001761 if results:
1762 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001763
1764
1765 def add_revoked(self, revoked):
1766 """
1767 Add a revoked (by value not reference) to the CRL structure
1768
1769 :param revoked: The new revoked.
1770 :type revoked: :class:`X509`
1771
1772 :return: None
1773 """
1774 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001775 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001776 # TODO: This is untested.
1777 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001778
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001779 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001780 if add_result == 0:
1781 # TODO: This is untested.
1782 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001783
1784
1785 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1786 """
1787 export a CRL as a string
1788
1789 :param cert: Used to sign CRL.
1790 :type cert: :class:`X509`
1791
1792 :param key: Used to sign CRL.
1793 :type key: :class:`PKey`
1794
1795 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1796
1797 :param days: The number of days until the next update of this CRL.
1798 :type days: :py:data:`int`
1799
1800 :return: :py:data:`str`
1801 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001802 if not isinstance(cert, X509):
1803 raise TypeError("cert must be an X509 instance")
1804 if not isinstance(key, PKey):
1805 raise TypeError("key must be a PKey instance")
1806 if not isinstance(type, int):
1807 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001808
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001809 bio = _lib.BIO_new(_lib.BIO_s_mem())
1810 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001811 # TODO: This is untested.
1812 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001813
1814 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001815 sometime = _lib.ASN1_TIME_new()
1816 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001817 # TODO: This is untested.
1818 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001819
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001820 _lib.X509_gmtime_adj(sometime, 0)
1821 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001822
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001823 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1824 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001825
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001826 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001827
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001828 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001829 if not sign_result:
1830 _raise_current_error()
1831
1832 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001833 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001834 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001835 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001836 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001837 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001838 else:
1839 raise ValueError(
1840 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1841
1842 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001843 # TODO: This is untested.
1844 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001845
1846 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001847CRLType = CRL
1848
1849
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001850
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001851class PKCS7(object):
1852 def type_is_signed(self):
1853 """
1854 Check if this NID_pkcs7_signed object
1855
1856 :return: True if the PKCS7 is of type signed
1857 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001858 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001859 return True
1860 return False
1861
1862
1863 def type_is_enveloped(self):
1864 """
1865 Check if this NID_pkcs7_enveloped object
1866
1867 :returns: True if the PKCS7 is of type enveloped
1868 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001869 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001870 return True
1871 return False
1872
1873
1874 def type_is_signedAndEnveloped(self):
1875 """
1876 Check if this NID_pkcs7_signedAndEnveloped object
1877
1878 :returns: True if the PKCS7 is of type signedAndEnveloped
1879 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001880 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001881 return True
1882 return False
1883
1884
1885 def type_is_data(self):
1886 """
1887 Check if this NID_pkcs7_data object
1888
1889 :return: True if the PKCS7 is of type data
1890 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001891 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001892 return True
1893 return False
1894
1895
1896 def get_type_name(self):
1897 """
1898 Returns the type name of the PKCS7 structure
1899
1900 :return: A string with the typename
1901 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001902 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1903 string_type = _lib.OBJ_nid2sn(nid)
1904 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001905
1906PKCS7Type = PKCS7
1907
1908
1909
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001910class PKCS12(object):
1911 def __init__(self):
1912 self._pkey = None
1913 self._cert = None
1914 self._cacerts = None
1915 self._friendlyname = None
1916
1917
1918 def get_certificate(self):
1919 """
1920 Return certificate portion of the PKCS12 structure
1921
1922 :return: X509 object containing the certificate
1923 """
1924 return self._cert
1925
1926
1927 def set_certificate(self, cert):
1928 """
1929 Replace the certificate portion of the PKCS12 structure
1930
1931 :param cert: The new certificate.
1932 :type cert: :py:class:`X509` or :py:data:`None`
1933 :return: None
1934 """
1935 if not isinstance(cert, X509):
1936 raise TypeError("cert must be an X509 instance")
1937 self._cert = cert
1938
1939
1940 def get_privatekey(self):
1941 """
1942 Return private key portion of the PKCS12 structure
1943
1944 :returns: PKey object containing the private key
1945 """
1946 return self._pkey
1947
1948
1949 def set_privatekey(self, pkey):
1950 """
1951 Replace or set the certificate portion of the PKCS12 structure
1952
1953 :param pkey: The new private key.
1954 :type pkey: :py:class:`PKey`
1955 :return: None
1956 """
1957 if not isinstance(pkey, PKey):
1958 raise TypeError("pkey must be a PKey instance")
1959 self._pkey = pkey
1960
1961
1962 def get_ca_certificates(self):
1963 """
1964 Return CA certificates within of the PKCS12 object
1965
1966 :return: A newly created tuple containing the CA certificates in the chain,
1967 if any are present, or None if no CA certificates are present.
1968 """
1969 if self._cacerts is not None:
1970 return tuple(self._cacerts)
1971
1972
1973 def set_ca_certificates(self, cacerts):
1974 """
1975 Replace or set the CA certificates withing the PKCS12 object.
1976
1977 :param cacerts: The new CA certificates.
1978 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1979 :return: None
1980 """
1981 if cacerts is None:
1982 self._cacerts = None
1983 else:
1984 cacerts = list(cacerts)
1985 for cert in cacerts:
1986 if not isinstance(cert, X509):
1987 raise TypeError("iterable must only contain X509 instances")
1988 self._cacerts = cacerts
1989
1990
1991 def set_friendlyname(self, name):
1992 """
1993 Replace or set the certificate portion of the PKCS12 structure
1994
1995 :param name: The new friendly name.
1996 :type name: :py:class:`bytes`
1997 :return: None
1998 """
1999 if name is None:
2000 self._friendlyname = None
2001 elif not isinstance(name, bytes):
2002 raise TypeError("name must be a byte string or None (not %r)" % (name,))
2003 self._friendlyname = name
2004
2005
2006 def get_friendlyname(self):
2007 """
2008 Return friendly name portion of the PKCS12 structure
2009
2010 :returns: String containing the friendlyname
2011 """
2012 return self._friendlyname
2013
2014
2015 def export(self, passphrase=None, iter=2048, maciter=1):
2016 """
2017 Dump a PKCS12 object as a string. See also "man PKCS12_create".
2018
2019 :param passphrase: used to encrypt the PKCS12
2020 :type passphrase: :py:data:`bytes`
2021
2022 :param iter: How many times to repeat the encryption
2023 :type iter: :py:data:`int`
2024
2025 :param maciter: How many times to repeat the MAC
2026 :type maciter: :py:data:`int`
2027
2028 :return: The string containing the PKCS12
2029 """
2030 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002031 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002032 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002033 cacerts = _lib.sk_X509_new_null()
2034 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002035 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002036 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002037
2038 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002039 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002040
2041 friendlyname = self._friendlyname
2042 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002043 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002044
2045 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002046 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002047 else:
2048 pkey = self._pkey._pkey
2049
2050 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002051 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002052 else:
2053 cert = self._cert._x509
2054
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002055 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002056 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002057 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2058 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002059 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002060 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002061 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002062 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002063
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002064 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002065 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002066 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002067
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002068PKCS12Type = PKCS12
2069
2070
2071
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002072class NetscapeSPKI(object):
2073 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002074 spki = _lib.NETSCAPE_SPKI_new()
2075 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002076
2077
2078 def sign(self, pkey, digest):
2079 """
2080 Sign the certificate request using the supplied key and digest
2081
2082 :param pkey: The key to sign with
2083 :param digest: The message digest to use
2084 :return: None
2085 """
2086 if pkey._only_public:
2087 raise ValueError("Key has only public part")
2088
2089 if not pkey._initialized:
2090 raise ValueError("Key is uninitialized")
2091
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002092 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002093 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002094 raise ValueError("No such digest method")
2095
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002096 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002097 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002098 # TODO: This is untested.
2099 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002100
2101
2102 def verify(self, key):
2103 """
2104 Verifies a certificate request using the supplied public key
2105
2106 :param key: a public key
2107 :return: True if the signature is correct.
2108 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2109 problem verifying the signature.
2110 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002111 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002112 if answer <= 0:
2113 _raise_current_error()
2114 return True
2115
2116
2117 def b64_encode(self):
2118 """
2119 Generate a base64 encoded string from an SPKI
2120
2121 :return: The base64 encoded string
2122 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002123 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2124 result = _ffi.string(encoded)
2125 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002126 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002127
2128
2129 def get_pubkey(self):
2130 """
2131 Get the public key of the certificate
2132
2133 :return: The public key
2134 """
2135 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002136 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2137 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002138 # TODO: This is untested.
2139 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002140 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002141 pkey._only_public = True
2142 return pkey
2143
2144
2145 def set_pubkey(self, pkey):
2146 """
2147 Set the public key of the certificate
2148
2149 :param pkey: The public key
2150 :return: None
2151 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002152 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002153 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002154 # TODO: This is untested.
2155 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002156NetscapeSPKIType = NetscapeSPKI
2157
2158
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002159class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002160 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002161 if type != FILETYPE_PEM and passphrase is not None:
2162 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002163 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002164 self._more_args = more_args
2165 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002166 self._problems = []
2167
2168
2169 @property
2170 def callback(self):
2171 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002172 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002173 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002174 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002175 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002176 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002177 else:
2178 raise TypeError("Last argument must be string or callable")
2179
2180
2181 @property
2182 def callback_args(self):
2183 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002184 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002185 elif isinstance(self._passphrase, bytes):
2186 return self._passphrase
2187 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002188 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002189 else:
2190 raise TypeError("Last argument must be string or callable")
2191
2192
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002193 def raise_if_problem(self, exceptionType=Error):
2194 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002195 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002196 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002197 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002198 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002199 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002200 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002201
2202
2203 def _read_passphrase(self, buf, size, rwflag, userdata):
2204 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002205 if self._more_args:
2206 result = self._passphrase(size, rwflag, userdata)
2207 else:
2208 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002209 if not isinstance(result, bytes):
2210 raise ValueError("String expected")
2211 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002212 if self._truncate:
2213 result = result[:size]
2214 else:
2215 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002216 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002217 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002218 return len(result)
2219 except Exception as e:
2220 self._problems.append(e)
2221 return 0
2222
2223
2224
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002225def load_privatekey(type, buffer, passphrase=None):
2226 """
2227 Load a private key from a buffer
2228
2229 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2230 :param buffer: The buffer the key is stored in
2231 :param passphrase: (optional) if encrypted PEM format, this can be
2232 either the passphrase to use, or a callback for
2233 providing the passphrase.
2234
2235 :return: The PKey object
2236 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002237 if isinstance(buffer, _text_type):
2238 buffer = buffer.encode("ascii")
2239
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002240 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002241
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002242 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002243 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002244 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2245 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002246 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002247 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002248 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002249 else:
2250 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2251
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002252 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002253 _raise_current_error()
2254
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002255 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002256 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002257 return pkey
2258
2259
2260
2261def dump_certificate_request(type, req):
2262 """
2263 Dump a certificate request to a buffer
2264
2265 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2266 :param req: The certificate request to dump
2267 :return: The buffer with the dumped certificate request in
2268 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002269 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002270
2271 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002272 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002273 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002274 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002275 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002276 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002277 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002278 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002279
2280 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002281 # TODO: This is untested.
2282 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002283
2284 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002285
2286
2287
2288def load_certificate_request(type, buffer):
2289 """
2290 Load a certificate request from a buffer
2291
2292 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2293 :param buffer: The buffer the certificate request is stored in
2294 :return: The X509Req object
2295 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002296 if isinstance(buffer, _text_type):
2297 buffer = buffer.encode("ascii")
2298
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002299 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002300
2301 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002302 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002303 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002304 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002305 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002306 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002307
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002308 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002309 # TODO: This is untested.
2310 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002311
2312 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002313 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002314 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002315
2316
2317
2318def sign(pkey, data, digest):
2319 """
2320 Sign data with a digest
2321
2322 :param pkey: Pkey to sign with
2323 :param data: data to be signed
2324 :param digest: message digest to use
2325 :return: signature
2326 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002327 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002328 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002329 raise ValueError("No such digest method")
2330
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002331 md_ctx = _ffi.new("EVP_MD_CTX*")
2332 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002333
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002334 _lib.EVP_SignInit(md_ctx, digest_obj)
2335 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002336
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002337 signature_buffer = _ffi.new("unsigned char[]", 512)
2338 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002339 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002340 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002341 md_ctx, signature_buffer, signature_length, pkey._pkey)
2342
2343 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002344 # TODO: This is untested.
2345 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002346
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002347 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002348
2349
2350
2351def verify(cert, signature, data, digest):
2352 """
2353 Verify a signature
2354
2355 :param cert: signing certificate (X509 object)
2356 :param signature: signature returned by sign function
2357 :param data: data to be verified
2358 :param digest: message digest to use
2359 :return: None if the signature is correct, raise exception otherwise
2360 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002361 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002362 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002363 raise ValueError("No such digest method")
2364
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002365 pkey = _lib.X509_get_pubkey(cert._x509)
2366 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002367 # TODO: This is untested.
2368 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002369 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002370
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002371 md_ctx = _ffi.new("EVP_MD_CTX*")
2372 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002373
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002374 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2375 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2376 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002377
2378 if verify_result != 1:
2379 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002380
2381
2382
2383def load_crl(type, buffer):
2384 """
2385 Load a certificate revocation list from a buffer
2386
2387 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2388 :param buffer: The buffer the CRL is stored in
2389
2390 :return: The PKey object
2391 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002392 if isinstance(buffer, _text_type):
2393 buffer = buffer.encode("ascii")
2394
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002395 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002396
2397 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002398 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002399 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002400 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002401 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002402 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2403
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002404 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002405 _raise_current_error()
2406
2407 result = CRL.__new__(CRL)
2408 result._crl = crl
2409 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002410
2411
2412
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002413def load_pkcs7_data(type, buffer):
2414 """
2415 Load pkcs7 data from a buffer
2416
2417 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2418 :param buffer: The buffer with the pkcs7 data.
2419 :return: The PKCS7 object
2420 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002421 if isinstance(buffer, _text_type):
2422 buffer = buffer.encode("ascii")
2423
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002424 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002425
2426 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002427 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002428 elif type == FILETYPE_ASN1:
2429 pass
2430 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002431 # TODO: This is untested.
2432 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002433 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2434
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002435 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002436 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002437
2438 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002439 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002440 return pypkcs7
2441
2442
2443
Stephen Holsapple38482622014-04-05 20:29:34 -07002444def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002445 """
2446 Load a PKCS12 object from a buffer
2447
2448 :param buffer: The buffer the certificate is stored in
2449 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2450 :returns: The PKCS12 object
2451 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002452 if isinstance(buffer, _text_type):
2453 buffer = buffer.encode("ascii")
2454
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002455 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002456
Stephen Holsapple38482622014-04-05 20:29:34 -07002457 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2458 # password based encryption no password and a zero length password are two
2459 # different things, but OpenSSL implementation will try both to figure out
2460 # which one works.
2461 if not passphrase:
2462 passphrase = _ffi.NULL
2463
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002464 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2465 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002466 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002467 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002468
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002469 pkey = _ffi.new("EVP_PKEY**")
2470 cert = _ffi.new("X509**")
2471 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002472
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002473 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002474 if not parse_result:
2475 _raise_current_error()
2476
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002477 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002478
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002479 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2480 # queue for no particular reason. This error isn't interesting to anyone
2481 # outside this function. It's not even interesting to us. Get rid of it.
2482 try:
2483 _raise_current_error()
2484 except Error:
2485 pass
2486
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002487 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002488 pykey = None
2489 else:
2490 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002491 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002492
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002493 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002494 pycert = None
2495 friendlyname = None
2496 else:
2497 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002498 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002499
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002500 friendlyname_length = _ffi.new("int*")
2501 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2502 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2503 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002504 friendlyname = None
2505
2506 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002507 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002508 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002509 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002510 pycacerts.append(pycacert)
2511 if not pycacerts:
2512 pycacerts = None
2513
2514 pkcs12 = PKCS12.__new__(PKCS12)
2515 pkcs12._pkey = pykey
2516 pkcs12._cert = pycert
2517 pkcs12._cacerts = pycacerts
2518 pkcs12._friendlyname = friendlyname
2519 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002520
2521
2522def _initialize_openssl_threads(get_ident, Lock):
2523 import _ssl
2524 return
2525
2526 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2527
2528 def locking_function(mode, index, filename, line):
2529 if mode & _lib.CRYPTO_LOCK:
2530 locks[index].acquire()
2531 else:
2532 locks[index].release()
2533
2534 _lib.CRYPTO_set_id_callback(
2535 _ffi.callback("unsigned long (*)(void)", get_ident))
2536
2537 _lib.CRYPTO_set_locking_callback(
2538 _ffi.callback(
2539 "void (*)(int, int, const char*, int)", locking_function))
2540
2541
2542try:
2543 from thread import get_ident
2544 from threading import Lock
2545except ImportError:
2546 pass
2547else:
2548 _initialize_openssl_threads(get_ident, Lock)
2549 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002550
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002551# There are no direct unit tests for this initialization. It is tested
2552# indirectly since it is necessary for functions like dump_privatekey when
2553# using encryption.
2554#
2555# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2556# and some other similar tests may fail without this (though they may not if
2557# the Python runtime has already done some initialization of the underlying
2558# OpenSSL library (and is linked against the same one that cryptography is
2559# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002560_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002561
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002562# This is similar but exercised mainly by exception_from_error_queue. It calls
2563# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2564_lib.SSL_load_error_strings()