blob: 9af7927c7ed1bbd4e32610c07a97e4c31d0d2feb [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
630X509NameType = X509Name
631
632
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800633class X509Extension(object):
634 def __init__(self, type_name, critical, value, subject=None, issuer=None):
635 """
636 :param typename: The name of the extension to create.
637 :type typename: :py:data:`str`
638
639 :param critical: A flag indicating whether this is a critical extension.
640
641 :param value: The value of the extension.
642 :type value: :py:data:`str`
643
644 :param subject: Optional X509 cert to use as subject.
645 :type subject: :py:class:`X509`
646
647 :param issuer: Optional X509 cert to use as issuer.
648 :type issuer: :py:class:`X509`
649
650 :return: The X509Extension object
651 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500652 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800653
654 # A context is necessary for any extension which uses the r2i conversion
655 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
656 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500657 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800658
659 # We have no configuration database - but perhaps we should (some
660 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500661 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800662
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800663 # Initialize the subject and issuer, if appropriate. ctx is a local,
664 # and as far as I can tell none of the X509V3_* APIs invoked here steal
665 # any references, so no need to mess with reference counts or duplicates.
666 if issuer is not None:
667 if not isinstance(issuer, X509):
668 raise TypeError("issuer must be an X509 instance")
669 ctx.issuer_cert = issuer._x509
670 if subject is not None:
671 if not isinstance(subject, X509):
672 raise TypeError("subject must be an X509 instance")
673 ctx.subject_cert = subject._x509
674
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800675 if critical:
676 # There are other OpenSSL APIs which would let us pass in critical
677 # separately, but they're harder to use, and since value is already
678 # a pile of crappy junk smuggling a ton of utterly important
679 # structured data, what's the point of trying to avoid nasty stuff
680 # with strings? (However, X509V3_EXT_i2d in particular seems like it
681 # would be a better API to invoke. I do not know where to get the
682 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500683 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800684
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500685 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
686 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800687 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500688 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800689
690
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400691 @property
692 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500693 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400694
695 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500696 _lib.GEN_EMAIL: "email",
697 _lib.GEN_DNS: "DNS",
698 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400699 }
700
701 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500702 method = _lib.X509V3_EXT_get(self._extension)
703 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500704 # TODO: This is untested.
705 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400706 payload = self._extension.value.data
707 length = self._extension.value.length
708
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500709 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400710 payloadptr[0] = payload
711
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500712 if method.it != _ffi.NULL:
713 ptr = _lib.ASN1_ITEM_ptr(method.it)
714 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
715 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400716 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500717 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400718 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500719 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400720
721 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500722 for i in range(_lib.sk_GENERAL_NAME_num(names)):
723 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400724 try:
725 label = self._prefixes[name.type]
726 except KeyError:
727 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500728 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500729 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400730 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500731 value = _native(
732 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
733 parts.append(label + ":" + value)
734 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400735
736
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800737 def __str__(self):
738 """
739 :return: a nice text representation of the extension
740 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500741 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400742 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800743
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400744 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500745 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800746 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500747 # TODO: This is untested.
748 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800749
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500750 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800751
752
753 def get_critical(self):
754 """
755 Returns the critical field of the X509Extension
756
757 :return: The critical field.
758 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500759 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800760
761
762 def get_short_name(self):
763 """
764 Returns the short version of the type name of the X509Extension
765
766 :return: The short type name.
767 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500768 obj = _lib.X509_EXTENSION_get_object(self._extension)
769 nid = _lib.OBJ_obj2nid(obj)
770 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800771
772
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800773 def get_data(self):
774 """
775 Returns the data of the X509Extension
776
777 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
778 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500779 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
780 string_result = _ffi.cast('ASN1_STRING*', octet_result)
781 char_result = _lib.ASN1_STRING_data(string_result)
782 result_length = _lib.ASN1_STRING_length(string_result)
783 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800784
785X509ExtensionType = X509Extension
786
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800787
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800788class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800789 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500790 req = _lib.X509_REQ_new()
791 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800792
793
794 def set_pubkey(self, pkey):
795 """
796 Set the public key of the certificate request
797
798 :param pkey: The public key to use
799 :return: None
800 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500801 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800802 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500803 # TODO: This is untested.
804 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800805
806
807 def get_pubkey(self):
808 """
809 Get the public key from the certificate request
810
811 :return: The public key
812 """
813 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500814 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
815 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500816 # TODO: This is untested.
817 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500818 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800819 pkey._only_public = True
820 return pkey
821
822
823 def set_version(self, version):
824 """
825 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
826 request.
827
828 :param version: The version number
829 :return: None
830 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500831 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800832 if not set_result:
833 _raise_current_error()
834
835
836 def get_version(self):
837 """
838 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
839 request.
840
841 :return: an integer giving the value of the version subfield
842 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500843 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800844
845
846 def get_subject(self):
847 """
848 Create an X509Name object for the subject of the certificate request
849
850 :return: An X509Name object
851 """
852 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500853 name._name = _lib.X509_REQ_get_subject_name(self._req)
854 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500855 # TODO: This is untested.
856 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800857
858 # The name is owned by the X509Req structure. As long as the X509Name
859 # Python object is alive, keep the X509Req Python object alive.
860 name._owner = self
861
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800862 return name
863
864
865 def add_extensions(self, extensions):
866 """
867 Add extensions to the request.
868
869 :param extensions: a sequence of X509Extension objects
870 :return: None
871 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500872 stack = _lib.sk_X509_EXTENSION_new_null()
873 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500874 # TODO: This is untested.
875 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800876
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500877 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800878
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800879 for ext in extensions:
880 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800881 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800882
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800883 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500884 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800885
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500886 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800887 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500888 # TODO: This is untested.
889 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800890
891
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800892 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800893 """
894 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800895
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500896 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800897 """
898 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500899 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500900 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800901 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500902 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800903 exts.append(ext)
904 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800905
906
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800907 def sign(self, pkey, digest):
908 """
909 Sign the certificate request using the supplied key and digest
910
911 :param pkey: The key to sign with
912 :param digest: The message digest to use
913 :return: None
914 """
915 if pkey._only_public:
916 raise ValueError("Key has only public part")
917
918 if not pkey._initialized:
919 raise ValueError("Key is uninitialized")
920
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500921 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500922 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800923 raise ValueError("No such digest method")
924
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500925 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800926 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500927 # TODO: This is untested.
928 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800929
930
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800931 def verify(self, pkey):
932 """
933 Verifies a certificate request using the supplied public key
934
935 :param key: a public key
936 :return: True if the signature is correct.
937
938 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
939 problem verifying the signature.
940 """
941 if not isinstance(pkey, PKey):
942 raise TypeError("pkey must be a PKey instance")
943
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500944 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800945 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500946 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800947
948 return result
949
950
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800951X509ReqType = X509Req
952
953
954
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800955class X509(object):
956 def __init__(self):
957 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500958 x509 = _lib.X509_new()
959 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800960
961
962 def set_version(self, version):
963 """
964 Set version number of the certificate
965
966 :param version: The version number
967 :type version: :py:class:`int`
968
969 :return: None
970 """
971 if not isinstance(version, int):
972 raise TypeError("version must be an integer")
973
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500974 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800975
976
977 def get_version(self):
978 """
979 Return version number of the certificate
980
981 :return: Version number as a Python integer
982 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500983 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800984
985
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800986 def get_pubkey(self):
987 """
988 Get the public key of the certificate
989
990 :return: The public key
991 """
992 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500993 pkey._pkey = _lib.X509_get_pubkey(self._x509)
994 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800995 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500996 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800997 pkey._only_public = True
998 return pkey
999
1000
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001001 def set_pubkey(self, pkey):
1002 """
1003 Set the public key of the certificate
1004
1005 :param pkey: The public key
1006
1007 :return: None
1008 """
1009 if not isinstance(pkey, PKey):
1010 raise TypeError("pkey must be a PKey instance")
1011
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001012 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001013 if not set_result:
1014 _raise_current_error()
1015
1016
1017 def sign(self, pkey, digest):
1018 """
1019 Sign the certificate using the supplied key and digest
1020
1021 :param pkey: The key to sign with
1022 :param digest: The message digest to use
1023 :return: None
1024 """
1025 if not isinstance(pkey, PKey):
1026 raise TypeError("pkey must be a PKey instance")
1027
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001028 if pkey._only_public:
1029 raise ValueError("Key only has public part")
1030
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -08001031 if not pkey._initialized:
1032 raise ValueError("Key is uninitialized")
1033
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001034 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001035 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001036 raise ValueError("No such digest method")
1037
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001038 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001039 if not sign_result:
1040 _raise_current_error()
1041
1042
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001043 def get_signature_algorithm(self):
1044 """
1045 Retrieve the signature algorithm used in the certificate
1046
1047 :return: A byte string giving the name of the signature algorithm used in
1048 the certificate.
1049 :raise ValueError: If the signature algorithm is undefined.
1050 """
1051 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001052 nid = _lib.OBJ_obj2nid(alg)
1053 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001054 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001055 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001056
1057
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001058 def digest(self, digest_name):
1059 """
1060 Return the digest of the X509 object.
1061
1062 :param digest_name: The name of the digest algorithm to use.
1063 :type digest_name: :py:class:`bytes`
1064
1065 :return: The digest of the object
1066 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001067 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001068 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001069 raise ValueError("No such digest method")
1070
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1072 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001073 result_length[0] = len(result_buffer)
1074
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001075 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001076 self._x509, digest, result_buffer, result_length)
1077
1078 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001079 # TODO: This is untested.
1080 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001081
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001082 return b":".join([
1083 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001084 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001085
1086
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001087 def subject_name_hash(self):
1088 """
1089 Return the hash of the X509 subject.
1090
1091 :return: The hash of the subject.
1092 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001093 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001094
1095
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001096 def set_serial_number(self, serial):
1097 """
1098 Set serial number of the certificate
1099
1100 :param serial: The serial number
1101 :type serial: :py:class:`int`
1102
1103 :return: None
1104 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001105 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001106 raise TypeError("serial must be an integer")
1107
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001108 hex_serial = hex(serial)[2:]
1109 if not isinstance(hex_serial, bytes):
1110 hex_serial = hex_serial.encode('ascii')
1111
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001112 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001113
1114 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1115 # it. If bignum is still NULL after this call, then the return value is
1116 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001117 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001118
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001119 if bignum_serial[0] == _ffi.NULL:
1120 set_result = _lib.ASN1_INTEGER_set(
1121 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001122 if set_result:
1123 # TODO Not tested
1124 _raise_current_error()
1125 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001126 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1127 _lib.BN_free(bignum_serial[0])
1128 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001129 # TODO Not tested
1130 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001131 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1132 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001133 if not set_result:
1134 # TODO Not tested
1135 _raise_current_error()
1136
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001137
1138 def get_serial_number(self):
1139 """
1140 Return serial number of the certificate
1141
1142 :return: Serial number as a Python integer
1143 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001144 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1145 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001146 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001147 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001148 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001149 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001150 serial = int(hexstring_serial, 16)
1151 return serial
1152 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001153 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001154 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001155 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001156
1157
1158 def gmtime_adj_notAfter(self, amount):
1159 """
1160 Adjust the time stamp for when the certificate stops being valid
1161
1162 :param amount: The number of seconds by which to adjust the ending
1163 validity time.
1164 :type amount: :py:class:`int`
1165
1166 :return: None
1167 """
1168 if not isinstance(amount, int):
1169 raise TypeError("amount must be an integer")
1170
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001171 notAfter = _lib.X509_get_notAfter(self._x509)
1172 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001173
1174
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001175 def gmtime_adj_notBefore(self, amount):
1176 """
1177 Change the timestamp for when the certificate starts being valid to the current
1178 time plus an offset.
1179
1180 :param amount: The number of seconds by which to adjust the starting validity
1181 time.
1182 :return: None
1183 """
1184 if not isinstance(amount, int):
1185 raise TypeError("amount must be an integer")
1186
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001187 notBefore = _lib.X509_get_notBefore(self._x509)
1188 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001189
1190
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001191 def has_expired(self):
1192 """
1193 Check whether the certificate has expired.
1194
1195 :return: True if the certificate has expired, false otherwise
1196 """
1197 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001198 notAfter = _lib.X509_get_notAfter(self._x509)
1199 return _lib.ASN1_UTCTIME_cmp_time_t(
1200 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001201
1202
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001203 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001204 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001205
1206
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001207 def get_notBefore(self):
1208 """
1209 Retrieve the time stamp for when the certificate starts being valid
1210
1211 :return: A string giving the timestamp, in the format::
1212
1213 YYYYMMDDhhmmssZ
1214 YYYYMMDDhhmmss+hhmm
1215 YYYYMMDDhhmmss-hhmm
1216
1217 or None if there is no value set.
1218 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001219 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001220
1221
1222 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001223 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001224
1225
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001226 def set_notBefore(self, when):
1227 """
1228 Set the time stamp for when the certificate starts being valid
1229
1230 :param when: A string giving the timestamp, in the format:
1231
1232 YYYYMMDDhhmmssZ
1233 YYYYMMDDhhmmss+hhmm
1234 YYYYMMDDhhmmss-hhmm
1235 :type when: :py:class:`bytes`
1236
1237 :return: None
1238 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001239 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001240
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001241
1242 def get_notAfter(self):
1243 """
1244 Retrieve the time stamp for when the certificate stops being valid
1245
1246 :return: A string giving the timestamp, in the format::
1247
1248 YYYYMMDDhhmmssZ
1249 YYYYMMDDhhmmss+hhmm
1250 YYYYMMDDhhmmss-hhmm
1251
1252 or None if there is no value set.
1253 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001254 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001255
1256
1257 def set_notAfter(self, when):
1258 """
1259 Set the time stamp for when the certificate stops being valid
1260
1261 :param when: A string giving the timestamp, in the format:
1262
1263 YYYYMMDDhhmmssZ
1264 YYYYMMDDhhmmss+hhmm
1265 YYYYMMDDhhmmss-hhmm
1266 :type when: :py:class:`bytes`
1267
1268 :return: None
1269 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001270 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001271
1272
1273 def _get_name(self, which):
1274 name = X509Name.__new__(X509Name)
1275 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001276 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001277 # TODO: This is untested.
1278 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001279
1280 # The name is owned by the X509 structure. As long as the X509Name
1281 # Python object is alive, keep the X509 Python object alive.
1282 name._owner = self
1283
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001284 return name
1285
1286
1287 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001288 if not isinstance(name, X509Name):
1289 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001290 set_result = which(self._x509, name._name)
1291 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001292 # TODO: This is untested.
1293 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001294
1295
1296 def get_issuer(self):
1297 """
1298 Create an X509Name object for the issuer of the certificate
1299
1300 :return: An X509Name object
1301 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001302 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001303
1304
1305 def set_issuer(self, issuer):
1306 """
1307 Set the issuer of the certificate
1308
1309 :param issuer: The issuer name
1310 :type issuer: :py:class:`X509Name`
1311
1312 :return: None
1313 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001314 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001315
1316
1317 def get_subject(self):
1318 """
1319 Create an X509Name object for the subject of the certificate
1320
1321 :return: An X509Name object
1322 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001323 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001324
1325
1326 def set_subject(self, subject):
1327 """
1328 Set the subject of the certificate
1329
1330 :param subject: The subject name
1331 :type subject: :py:class:`X509Name`
1332 :return: None
1333 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001334 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001335
1336
1337 def get_extension_count(self):
1338 """
1339 Get the number of extensions on the certificate.
1340
1341 :return: The number of extensions as an integer.
1342 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001343 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001344
1345
1346 def add_extensions(self, extensions):
1347 """
1348 Add extensions to the certificate.
1349
1350 :param extensions: a sequence of X509Extension objects
1351 :return: None
1352 """
1353 for ext in extensions:
1354 if not isinstance(ext, X509Extension):
1355 raise ValueError("One of the elements is not an X509Extension")
1356
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001357 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001358 if not add_result:
1359 _raise_current_error()
1360
1361
1362 def get_extension(self, index):
1363 """
1364 Get a specific extension of the certificate by index.
1365
1366 :param index: The index of the extension to retrieve.
1367 :return: The X509Extension object at the specified index.
1368 """
1369 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001370 ext._extension = _lib.X509_get_ext(self._x509, index)
1371 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001372 raise IndexError("extension index out of bounds")
1373
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001374 extension = _lib.X509_EXTENSION_dup(ext._extension)
1375 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001376 return ext
1377
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001378
1379
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001380X509Type = X509
1381
1382
1383
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001384class X509Store(object):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001385 """
1386 An X509 certificate store.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001387 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001388 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001389 store = _lib.X509_STORE_new()
1390 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001391
1392
1393 def add_cert(self, cert):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001394 """
1395 Adds the certificate :py:data:`cert` to this store.
1396
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +02001397 This is the Python equivalent of OpenSSL's ``X509_STORE_add_cert``.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001398
1399 :param X509 cert: The certificate to add to this store.
1400 :raises TypeError: If the certificate is not an :py:class:`X509`.
1401 :raises Error: If OpenSSL was unhappy with your certificate.
1402 :return: py:data:`None` if the certificate was added successfully.
1403 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001404 if not isinstance(cert, X509):
1405 raise TypeError()
1406
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001407 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001408 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001409 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001410
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001411
1412X509StoreType = X509Store
1413
1414
1415
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001416def load_certificate(type, buffer):
1417 """
1418 Load a certificate from a buffer
1419
1420 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1421
1422 :param buffer: The buffer the certificate is stored in
1423 :type buffer: :py:class:`bytes`
1424
1425 :return: The X509 object
1426 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001427 if isinstance(buffer, _text_type):
1428 buffer = buffer.encode("ascii")
1429
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001430 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001431
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001432 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001433 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001434 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001435 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001436 else:
1437 raise ValueError(
1438 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001439
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001440 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001441 _raise_current_error()
1442
1443 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001444 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001445 return cert
1446
1447
1448def dump_certificate(type, cert):
1449 """
1450 Dump a certificate to a buffer
1451
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001452 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1453 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001454 :param cert: The certificate to dump
1455 :return: The buffer with the dumped certificate in
1456 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001457 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001458
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001459 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001460 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001461 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001462 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001463 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001464 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001465 else:
1466 raise ValueError(
1467 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1468 "FILETYPE_TEXT")
1469
1470 return _bio_to_string(bio)
1471
1472
1473
1474def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1475 """
1476 Dump a private key to a buffer
1477
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001478 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1479 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001480 :param pkey: The PKey to dump
1481 :param cipher: (optional) if encrypted PEM format, the cipher to
1482 use
1483 :param passphrase: (optional) if encrypted PEM format, this can be either
1484 the passphrase to use, or a callback for providing the
1485 passphrase.
1486 :return: The buffer with the dumped key in
1487 :rtype: :py:data:`str`
1488 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001489 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001490
1491 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001492 if passphrase is None:
1493 raise TypeError(
1494 "if a value is given for cipher "
1495 "one must also be given for passphrase")
1496 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001497 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001498 raise ValueError("Invalid cipher name")
1499 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001500 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001501
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001502 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001503 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001504 result_code = _lib.PEM_write_bio_PrivateKey(
1505 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001506 helper.callback, helper.callback_args)
1507 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001508 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001509 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001510 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001511 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1512 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001513 # TODO RSA_free(rsa)?
1514 else:
1515 raise ValueError(
1516 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1517 "FILETYPE_TEXT")
1518
1519 if result_code == 0:
1520 _raise_current_error()
1521
1522 return _bio_to_string(bio)
1523
1524
1525
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001526def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001527 copy = _lib.X509_REVOKED_new()
1528 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001529 # TODO: This is untested.
1530 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001531
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001532 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001533 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001534 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001535
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001536 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001537 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001538 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001539
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001540 if original.extensions != _ffi.NULL:
1541 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1542 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1543 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1544 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1545 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001546 copy.extensions = extension_stack
1547
1548 copy.sequence = original.sequence
1549 return copy
1550
1551
1552
1553class Revoked(object):
1554 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1555 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1556 # OCSP_crl_reason_str. We use the latter, just like the command line
1557 # program.
1558 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001559 b"unspecified",
1560 b"keyCompromise",
1561 b"CACompromise",
1562 b"affiliationChanged",
1563 b"superseded",
1564 b"cessationOfOperation",
1565 b"certificateHold",
1566 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001567 ]
1568
1569 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001570 revoked = _lib.X509_REVOKED_new()
1571 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001572
1573
1574 def set_serial(self, hex_str):
1575 """
1576 Set the serial number of a revoked Revoked structure
1577
1578 :param hex_str: The new serial number.
1579 :type hex_str: :py:data:`str`
1580 :return: None
1581 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001582 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1583 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001584 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001585 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001586 if not bn_result:
1587 raise ValueError("bad hex string")
1588
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001589 asn1_serial = _ffi.gc(
1590 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1591 _lib.ASN1_INTEGER_free)
1592 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001593
1594
1595 def get_serial(self):
1596 """
1597 Return the serial number of a Revoked structure
1598
1599 :return: The serial number as a string
1600 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001601 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001602
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001603 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001604 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001605 # TODO: This is untested.
1606 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001607
1608 return _bio_to_string(bio)
1609
1610
1611 def _delete_reason(self):
1612 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001613 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1614 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1615 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1616 _lib.X509_EXTENSION_free(ext)
1617 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001618 break
1619
1620
1621 def set_reason(self, reason):
1622 """
1623 Set the reason of a Revoked object.
1624
1625 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1626
1627 :param reason: The reason string.
1628 :type reason: :py:class:`str` or :py:class:`NoneType`
1629 :return: None
1630 """
1631 if reason is None:
1632 self._delete_reason()
1633 elif not isinstance(reason, bytes):
1634 raise TypeError("reason must be None or a byte string")
1635 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001636 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001637 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1638
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001639 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1640 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001641 # TODO: This is untested.
1642 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001643 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001644
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001645 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1646 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001647 # TODO: This is untested.
1648 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001649
1650 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001651 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1652 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001653
1654 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001655 # TODO: This is untested.
1656 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001657
1658
1659 def get_reason(self):
1660 """
1661 Return the reason of a Revoked object.
1662
1663 :return: The reason as a string
1664 """
1665 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001666 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1667 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1668 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001669 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001670
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001671 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001672 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001673 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001674 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001675 # TODO: This is untested.
1676 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001677
1678 return _bio_to_string(bio)
1679
1680
1681 def all_reasons(self):
1682 """
1683 Return a list of all the supported reason strings.
1684
1685 :return: A list of reason strings.
1686 """
1687 return self._crl_reasons[:]
1688
1689
1690 def set_rev_date(self, when):
1691 """
1692 Set the revocation timestamp
1693
1694 :param when: A string giving the timestamp, in the format:
1695
1696 YYYYMMDDhhmmssZ
1697 YYYYMMDDhhmmss+hhmm
1698 YYYYMMDDhhmmss-hhmm
1699
1700 :return: None
1701 """
1702 return _set_asn1_time(self._revoked.revocationDate, when)
1703
1704
1705 def get_rev_date(self):
1706 """
1707 Retrieve the revocation date
1708
1709 :return: A string giving the timestamp, in the format:
1710
1711 YYYYMMDDhhmmssZ
1712 YYYYMMDDhhmmss+hhmm
1713 YYYYMMDDhhmmss-hhmm
1714 """
1715 return _get_asn1_time(self._revoked.revocationDate)
1716
1717
1718
1719class CRL(object):
1720 def __init__(self):
1721 """
1722 Create a new empty CRL object.
1723 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001724 crl = _lib.X509_CRL_new()
1725 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001726
1727
1728 def get_revoked(self):
1729 """
1730 Return revoked portion of the CRL structure (by value not reference).
1731
1732 :return: A tuple of Revoked objects.
1733 """
1734 results = []
1735 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001736 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1737 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001738 revoked_copy = _X509_REVOKED_dup(revoked)
1739 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001740 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001741 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001742 if results:
1743 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001744
1745
1746 def add_revoked(self, revoked):
1747 """
1748 Add a revoked (by value not reference) to the CRL structure
1749
1750 :param revoked: The new revoked.
1751 :type revoked: :class:`X509`
1752
1753 :return: None
1754 """
1755 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001756 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001757 # TODO: This is untested.
1758 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001759
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001760 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001761 if add_result == 0:
1762 # TODO: This is untested.
1763 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001764
1765
1766 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1767 """
1768 export a CRL as a string
1769
1770 :param cert: Used to sign CRL.
1771 :type cert: :class:`X509`
1772
1773 :param key: Used to sign CRL.
1774 :type key: :class:`PKey`
1775
1776 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1777
1778 :param days: The number of days until the next update of this CRL.
1779 :type days: :py:data:`int`
1780
1781 :return: :py:data:`str`
1782 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001783 if not isinstance(cert, X509):
1784 raise TypeError("cert must be an X509 instance")
1785 if not isinstance(key, PKey):
1786 raise TypeError("key must be a PKey instance")
1787 if not isinstance(type, int):
1788 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001789
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001790 bio = _lib.BIO_new(_lib.BIO_s_mem())
1791 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001792 # TODO: This is untested.
1793 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001794
1795 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001796 sometime = _lib.ASN1_TIME_new()
1797 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001798 # TODO: This is untested.
1799 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001800
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001801 _lib.X509_gmtime_adj(sometime, 0)
1802 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001803
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001804 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1805 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001806
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001807 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001808
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001809 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001810 if not sign_result:
1811 _raise_current_error()
1812
1813 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001814 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001815 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001816 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001817 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001818 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001819 else:
1820 raise ValueError(
1821 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1822
1823 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001824 # TODO: This is untested.
1825 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001826
1827 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001828CRLType = CRL
1829
1830
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001831
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001832class PKCS7(object):
1833 def type_is_signed(self):
1834 """
1835 Check if this NID_pkcs7_signed object
1836
1837 :return: True if the PKCS7 is of type signed
1838 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001839 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001840 return True
1841 return False
1842
1843
1844 def type_is_enveloped(self):
1845 """
1846 Check if this NID_pkcs7_enveloped object
1847
1848 :returns: True if the PKCS7 is of type enveloped
1849 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001850 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001851 return True
1852 return False
1853
1854
1855 def type_is_signedAndEnveloped(self):
1856 """
1857 Check if this NID_pkcs7_signedAndEnveloped object
1858
1859 :returns: True if the PKCS7 is of type signedAndEnveloped
1860 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001861 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001862 return True
1863 return False
1864
1865
1866 def type_is_data(self):
1867 """
1868 Check if this NID_pkcs7_data object
1869
1870 :return: True if the PKCS7 is of type data
1871 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001872 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001873 return True
1874 return False
1875
1876
1877 def get_type_name(self):
1878 """
1879 Returns the type name of the PKCS7 structure
1880
1881 :return: A string with the typename
1882 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001883 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1884 string_type = _lib.OBJ_nid2sn(nid)
1885 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001886
1887PKCS7Type = PKCS7
1888
1889
1890
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001891class PKCS12(object):
1892 def __init__(self):
1893 self._pkey = None
1894 self._cert = None
1895 self._cacerts = None
1896 self._friendlyname = None
1897
1898
1899 def get_certificate(self):
1900 """
1901 Return certificate portion of the PKCS12 structure
1902
1903 :return: X509 object containing the certificate
1904 """
1905 return self._cert
1906
1907
1908 def set_certificate(self, cert):
1909 """
1910 Replace the certificate portion of the PKCS12 structure
1911
1912 :param cert: The new certificate.
1913 :type cert: :py:class:`X509` or :py:data:`None`
1914 :return: None
1915 """
1916 if not isinstance(cert, X509):
1917 raise TypeError("cert must be an X509 instance")
1918 self._cert = cert
1919
1920
1921 def get_privatekey(self):
1922 """
1923 Return private key portion of the PKCS12 structure
1924
1925 :returns: PKey object containing the private key
1926 """
1927 return self._pkey
1928
1929
1930 def set_privatekey(self, pkey):
1931 """
1932 Replace or set the certificate portion of the PKCS12 structure
1933
1934 :param pkey: The new private key.
1935 :type pkey: :py:class:`PKey`
1936 :return: None
1937 """
1938 if not isinstance(pkey, PKey):
1939 raise TypeError("pkey must be a PKey instance")
1940 self._pkey = pkey
1941
1942
1943 def get_ca_certificates(self):
1944 """
1945 Return CA certificates within of the PKCS12 object
1946
1947 :return: A newly created tuple containing the CA certificates in the chain,
1948 if any are present, or None if no CA certificates are present.
1949 """
1950 if self._cacerts is not None:
1951 return tuple(self._cacerts)
1952
1953
1954 def set_ca_certificates(self, cacerts):
1955 """
1956 Replace or set the CA certificates withing the PKCS12 object.
1957
1958 :param cacerts: The new CA certificates.
1959 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1960 :return: None
1961 """
1962 if cacerts is None:
1963 self._cacerts = None
1964 else:
1965 cacerts = list(cacerts)
1966 for cert in cacerts:
1967 if not isinstance(cert, X509):
1968 raise TypeError("iterable must only contain X509 instances")
1969 self._cacerts = cacerts
1970
1971
1972 def set_friendlyname(self, name):
1973 """
1974 Replace or set the certificate portion of the PKCS12 structure
1975
1976 :param name: The new friendly name.
1977 :type name: :py:class:`bytes`
1978 :return: None
1979 """
1980 if name is None:
1981 self._friendlyname = None
1982 elif not isinstance(name, bytes):
1983 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1984 self._friendlyname = name
1985
1986
1987 def get_friendlyname(self):
1988 """
1989 Return friendly name portion of the PKCS12 structure
1990
1991 :returns: String containing the friendlyname
1992 """
1993 return self._friendlyname
1994
1995
1996 def export(self, passphrase=None, iter=2048, maciter=1):
1997 """
1998 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1999
2000 :param passphrase: used to encrypt the PKCS12
2001 :type passphrase: :py:data:`bytes`
2002
2003 :param iter: How many times to repeat the encryption
2004 :type iter: :py:data:`int`
2005
2006 :param maciter: How many times to repeat the MAC
2007 :type maciter: :py:data:`int`
2008
2009 :return: The string containing the PKCS12
2010 """
2011 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002012 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002013 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002014 cacerts = _lib.sk_X509_new_null()
2015 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002016 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002017 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002018
2019 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002020 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002021
2022 friendlyname = self._friendlyname
2023 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002024 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002025
2026 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002027 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002028 else:
2029 pkey = self._pkey._pkey
2030
2031 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002032 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002033 else:
2034 cert = self._cert._x509
2035
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002036 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002037 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002038 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2039 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002040 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002041 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002042 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002043 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002044
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002045 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002046 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002047 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002048
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002049PKCS12Type = PKCS12
2050
2051
2052
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002053class NetscapeSPKI(object):
2054 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002055 spki = _lib.NETSCAPE_SPKI_new()
2056 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002057
2058
2059 def sign(self, pkey, digest):
2060 """
2061 Sign the certificate request using the supplied key and digest
2062
2063 :param pkey: The key to sign with
2064 :param digest: The message digest to use
2065 :return: None
2066 """
2067 if pkey._only_public:
2068 raise ValueError("Key has only public part")
2069
2070 if not pkey._initialized:
2071 raise ValueError("Key is uninitialized")
2072
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002073 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002074 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002075 raise ValueError("No such digest method")
2076
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002077 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002078 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002079 # TODO: This is untested.
2080 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002081
2082
2083 def verify(self, key):
2084 """
2085 Verifies a certificate request using the supplied public key
2086
2087 :param key: a public key
2088 :return: True if the signature is correct.
2089 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2090 problem verifying the signature.
2091 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002092 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002093 if answer <= 0:
2094 _raise_current_error()
2095 return True
2096
2097
2098 def b64_encode(self):
2099 """
2100 Generate a base64 encoded string from an SPKI
2101
2102 :return: The base64 encoded string
2103 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002104 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2105 result = _ffi.string(encoded)
2106 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002107 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002108
2109
2110 def get_pubkey(self):
2111 """
2112 Get the public key of the certificate
2113
2114 :return: The public key
2115 """
2116 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002117 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2118 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002119 # TODO: This is untested.
2120 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002121 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002122 pkey._only_public = True
2123 return pkey
2124
2125
2126 def set_pubkey(self, pkey):
2127 """
2128 Set the public key of the certificate
2129
2130 :param pkey: The public key
2131 :return: None
2132 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002133 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002134 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002135 # TODO: This is untested.
2136 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002137NetscapeSPKIType = NetscapeSPKI
2138
2139
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002140class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002141 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002142 if type != FILETYPE_PEM and passphrase is not None:
2143 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002144 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002145 self._more_args = more_args
2146 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002147 self._problems = []
2148
2149
2150 @property
2151 def callback(self):
2152 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002153 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002154 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002155 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002156 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002157 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002158 else:
2159 raise TypeError("Last argument must be string or callable")
2160
2161
2162 @property
2163 def callback_args(self):
2164 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002165 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002166 elif isinstance(self._passphrase, bytes):
2167 return self._passphrase
2168 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002169 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002170 else:
2171 raise TypeError("Last argument must be string or callable")
2172
2173
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002174 def raise_if_problem(self, exceptionType=Error):
2175 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002176 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002177 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002178 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002179 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002180 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002181 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002182
2183
2184 def _read_passphrase(self, buf, size, rwflag, userdata):
2185 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002186 if self._more_args:
2187 result = self._passphrase(size, rwflag, userdata)
2188 else:
2189 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002190 if not isinstance(result, bytes):
2191 raise ValueError("String expected")
2192 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002193 if self._truncate:
2194 result = result[:size]
2195 else:
2196 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002197 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002198 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002199 return len(result)
2200 except Exception as e:
2201 self._problems.append(e)
2202 return 0
2203
2204
2205
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002206def load_privatekey(type, buffer, passphrase=None):
2207 """
2208 Load a private key from a buffer
2209
2210 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2211 :param buffer: The buffer the key is stored in
2212 :param passphrase: (optional) if encrypted PEM format, this can be
2213 either the passphrase to use, or a callback for
2214 providing the passphrase.
2215
2216 :return: The PKey object
2217 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002218 if isinstance(buffer, _text_type):
2219 buffer = buffer.encode("ascii")
2220
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002221 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002222
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002223 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002224 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002225 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2226 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002227 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002228 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002229 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002230 else:
2231 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2232
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002233 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002234 _raise_current_error()
2235
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002236 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002237 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002238 return pkey
2239
2240
2241
2242def dump_certificate_request(type, req):
2243 """
2244 Dump a certificate request to a buffer
2245
2246 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2247 :param req: The certificate request to dump
2248 :return: The buffer with the dumped certificate request in
2249 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002250 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002251
2252 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002253 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002254 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002255 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002256 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002257 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002258 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002259 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002260
2261 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002262 # TODO: This is untested.
2263 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002264
2265 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002266
2267
2268
2269def load_certificate_request(type, buffer):
2270 """
2271 Load a certificate request from a buffer
2272
2273 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2274 :param buffer: The buffer the certificate request is stored in
2275 :return: The X509Req object
2276 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002277 if isinstance(buffer, _text_type):
2278 buffer = buffer.encode("ascii")
2279
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002280 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002281
2282 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002283 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002284 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002285 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002286 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002287 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002288
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002289 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002290 # TODO: This is untested.
2291 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002292
2293 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002294 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002295 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002296
2297
2298
2299def sign(pkey, data, digest):
2300 """
2301 Sign data with a digest
2302
2303 :param pkey: Pkey to sign with
2304 :param data: data to be signed
2305 :param digest: message digest to use
2306 :return: signature
2307 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002308 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002309 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002310 raise ValueError("No such digest method")
2311
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002312 md_ctx = _ffi.new("EVP_MD_CTX*")
2313 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002314
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002315 _lib.EVP_SignInit(md_ctx, digest_obj)
2316 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002317
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002318 signature_buffer = _ffi.new("unsigned char[]", 512)
2319 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002320 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002321 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002322 md_ctx, signature_buffer, signature_length, pkey._pkey)
2323
2324 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002325 # TODO: This is untested.
2326 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002327
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002328 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002329
2330
2331
2332def verify(cert, signature, data, digest):
2333 """
2334 Verify a signature
2335
2336 :param cert: signing certificate (X509 object)
2337 :param signature: signature returned by sign function
2338 :param data: data to be verified
2339 :param digest: message digest to use
2340 :return: None if the signature is correct, raise exception otherwise
2341 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002342 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002343 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002344 raise ValueError("No such digest method")
2345
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002346 pkey = _lib.X509_get_pubkey(cert._x509)
2347 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002348 # TODO: This is untested.
2349 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002350 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002351
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002352 md_ctx = _ffi.new("EVP_MD_CTX*")
2353 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002354
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002355 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2356 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2357 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002358
2359 if verify_result != 1:
2360 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002361
2362
2363
2364def load_crl(type, buffer):
2365 """
2366 Load a certificate revocation list from a buffer
2367
2368 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2369 :param buffer: The buffer the CRL is stored in
2370
2371 :return: The PKey object
2372 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002373 if isinstance(buffer, _text_type):
2374 buffer = buffer.encode("ascii")
2375
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002376 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002377
2378 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002379 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002380 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002381 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002382 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002383 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2384
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002385 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002386 _raise_current_error()
2387
2388 result = CRL.__new__(CRL)
2389 result._crl = crl
2390 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002391
2392
2393
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002394def load_pkcs7_data(type, buffer):
2395 """
2396 Load pkcs7 data from a buffer
2397
2398 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2399 :param buffer: The buffer with the pkcs7 data.
2400 :return: The PKCS7 object
2401 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002402 if isinstance(buffer, _text_type):
2403 buffer = buffer.encode("ascii")
2404
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002405 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002406
2407 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002408 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002409 elif type == FILETYPE_ASN1:
2410 pass
2411 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002412 # TODO: This is untested.
2413 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002414 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2415
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002416 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002417 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002418
2419 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002420 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002421 return pypkcs7
2422
2423
2424
Stephen Holsapple38482622014-04-05 20:29:34 -07002425def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002426 """
2427 Load a PKCS12 object from a buffer
2428
2429 :param buffer: The buffer the certificate is stored in
2430 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2431 :returns: The PKCS12 object
2432 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002433 if isinstance(buffer, _text_type):
2434 buffer = buffer.encode("ascii")
2435
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002436 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002437
Stephen Holsapple38482622014-04-05 20:29:34 -07002438 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2439 # password based encryption no password and a zero length password are two
2440 # different things, but OpenSSL implementation will try both to figure out
2441 # which one works.
2442 if not passphrase:
2443 passphrase = _ffi.NULL
2444
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002445 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2446 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002447 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002448 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002449
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002450 pkey = _ffi.new("EVP_PKEY**")
2451 cert = _ffi.new("X509**")
2452 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002453
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002454 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002455 if not parse_result:
2456 _raise_current_error()
2457
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002458 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002459
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002460 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2461 # queue for no particular reason. This error isn't interesting to anyone
2462 # outside this function. It's not even interesting to us. Get rid of it.
2463 try:
2464 _raise_current_error()
2465 except Error:
2466 pass
2467
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002468 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002469 pykey = None
2470 else:
2471 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002472 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002473
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002474 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002475 pycert = None
2476 friendlyname = None
2477 else:
2478 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002479 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002480
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002481 friendlyname_length = _ffi.new("int*")
2482 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2483 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2484 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002485 friendlyname = None
2486
2487 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002488 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002489 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002490 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002491 pycacerts.append(pycacert)
2492 if not pycacerts:
2493 pycacerts = None
2494
2495 pkcs12 = PKCS12.__new__(PKCS12)
2496 pkcs12._pkey = pykey
2497 pkcs12._cert = pycert
2498 pkcs12._cacerts = pycacerts
2499 pkcs12._friendlyname = friendlyname
2500 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002501
2502
2503def _initialize_openssl_threads(get_ident, Lock):
2504 import _ssl
2505 return
2506
2507 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2508
2509 def locking_function(mode, index, filename, line):
2510 if mode & _lib.CRYPTO_LOCK:
2511 locks[index].acquire()
2512 else:
2513 locks[index].release()
2514
2515 _lib.CRYPTO_set_id_callback(
2516 _ffi.callback("unsigned long (*)(void)", get_ident))
2517
2518 _lib.CRYPTO_set_locking_callback(
2519 _ffi.callback(
2520 "void (*)(int, int, const char*, int)", locking_function))
2521
2522
2523try:
2524 from thread import get_ident
2525 from threading import Lock
2526except ImportError:
2527 pass
2528else:
2529 _initialize_openssl_threads(get_ident, Lock)
2530 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002531
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002532# There are no direct unit tests for this initialization. It is tested
2533# indirectly since it is necessary for functions like dump_privatekey when
2534# using encryption.
2535#
2536# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2537# and some other similar tests may fail without this (though they may not if
2538# the Python runtime has already done some initialization of the underlying
2539# OpenSSL library (and is linked against the same one that cryptography is
2540# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002541_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002542
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002543# This is similar but exercised mainly by exception_from_error_queue. It calls
2544# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2545_lib.SSL_load_error_strings()