blob: 6cc5ff2cd39da5ed3fb20dba64c77db15a0218d3 [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):
430 def __init__(self, name):
431 """
432 Create a new X509Name, copying the given X509Name instance.
433
434 :param name: An X509Name object to copy
435 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500436 name = _lib.X509_NAME_dup(name._name)
437 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800438
439
440 def __setattr__(self, name, value):
441 if name.startswith('_'):
442 return super(X509Name, self).__setattr__(name, value)
443
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800444 # Note: we really do not want str subclasses here, so we do not use
445 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800446 if type(name) is not str:
447 raise TypeError("attribute name must be string, not '%.200s'" % (
448 type(value).__name__,))
449
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500450 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500451 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800452 try:
453 _raise_current_error()
454 except Error:
455 pass
456 raise AttributeError("No such attribute")
457
458 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500459 for i in range(_lib.X509_NAME_entry_count(self._name)):
460 ent = _lib.X509_NAME_get_entry(self._name, i)
461 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
462 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800463 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500464 ent = _lib.X509_NAME_delete_entry(self._name, i)
465 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800466 break
467
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500468 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800469 value = value.encode('utf-8')
470
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500471 add_result = _lib.X509_NAME_add_entry_by_NID(
472 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800473 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500474 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800475
476
477 def __getattr__(self, name):
478 """
479 Find attribute. An X509Name object has the following attributes:
480 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
481 organization (alias O), organizationalUnit (alias OU), commonName (alias
482 CN) and more...
483 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500484 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500485 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800486 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
487 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
488 # push something onto the error queue. If we don't clean that up
489 # now, someone else will bump into it later and be quite confused.
490 # See lp#314814.
491 try:
492 _raise_current_error()
493 except Error:
494 pass
495 return super(X509Name, self).__getattr__(name)
496
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500497 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800498 if entry_index == -1:
499 return None
500
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500501 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
502 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800503
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500504 result_buffer = _ffi.new("unsigned char**")
505 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800506 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500507 # TODO: This is untested.
508 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800509
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700510 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500511 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700512 finally:
513 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500514 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800515 return result
516
517
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500518 def _cmp(op):
519 def f(self, other):
520 if not isinstance(other, X509Name):
521 return NotImplemented
522 result = _lib.X509_NAME_cmp(self._name, other._name)
523 return op(result, 0)
524 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800525
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500526 __eq__ = _cmp(__eq__)
527 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800528
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500529 __lt__ = _cmp(__lt__)
530 __le__ = _cmp(__le__)
531
532 __gt__ = _cmp(__gt__)
533 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800534
535 def __repr__(self):
536 """
537 String representation of an X509Name
538 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500539 result_buffer = _ffi.new("char[]", 512);
540 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800541 self._name, result_buffer, len(result_buffer))
542
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500543 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500544 # TODO: This is untested.
545 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800546
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500547 return "<X509Name object '%s'>" % (
548 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800549
550
551 def hash(self):
552 """
553 Return the hash value of this name
554
555 :return: None
556 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500557 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800558
559
560 def der(self):
561 """
562 Return the DER encoding of this name
563
564 :return: A :py:class:`bytes` instance giving the DER encoded form of
565 this name.
566 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500567 result_buffer = _ffi.new('unsigned char**')
568 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800569 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500570 # TODO: This is untested.
571 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800572
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500573 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
574 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800575 return string_result
576
577
578 def get_components(self):
579 """
580 Returns the split-up components of this name.
581
582 :return: List of tuples (name, value).
583 """
584 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500585 for i in range(_lib.X509_NAME_entry_count(self._name)):
586 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800587
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500588 fname = _lib.X509_NAME_ENTRY_get_object(ent)
589 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800590
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500591 nid = _lib.OBJ_obj2nid(fname)
592 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800593
594 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500595 _ffi.string(name),
596 _ffi.string(
597 _lib.ASN1_STRING_data(fval),
598 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800599
600 return result
601X509NameType = X509Name
602
603
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800604class X509Extension(object):
605 def __init__(self, type_name, critical, value, subject=None, issuer=None):
606 """
607 :param typename: The name of the extension to create.
608 :type typename: :py:data:`str`
609
610 :param critical: A flag indicating whether this is a critical extension.
611
612 :param value: The value of the extension.
613 :type value: :py:data:`str`
614
615 :param subject: Optional X509 cert to use as subject.
616 :type subject: :py:class:`X509`
617
618 :param issuer: Optional X509 cert to use as issuer.
619 :type issuer: :py:class:`X509`
620
621 :return: The X509Extension object
622 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500623 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800624
625 # A context is necessary for any extension which uses the r2i conversion
626 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
627 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500628 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800629
630 # We have no configuration database - but perhaps we should (some
631 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500632 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800633
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800634 # Initialize the subject and issuer, if appropriate. ctx is a local,
635 # and as far as I can tell none of the X509V3_* APIs invoked here steal
636 # any references, so no need to mess with reference counts or duplicates.
637 if issuer is not None:
638 if not isinstance(issuer, X509):
639 raise TypeError("issuer must be an X509 instance")
640 ctx.issuer_cert = issuer._x509
641 if subject is not None:
642 if not isinstance(subject, X509):
643 raise TypeError("subject must be an X509 instance")
644 ctx.subject_cert = subject._x509
645
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800646 if critical:
647 # There are other OpenSSL APIs which would let us pass in critical
648 # separately, but they're harder to use, and since value is already
649 # a pile of crappy junk smuggling a ton of utterly important
650 # structured data, what's the point of trying to avoid nasty stuff
651 # with strings? (However, X509V3_EXT_i2d in particular seems like it
652 # would be a better API to invoke. I do not know where to get the
653 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500654 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800655
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500656 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
657 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800658 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500659 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800660
661
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400662 @property
663 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500664 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400665
666 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500667 _lib.GEN_EMAIL: "email",
668 _lib.GEN_DNS: "DNS",
669 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400670 }
671
672 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500673 method = _lib.X509V3_EXT_get(self._extension)
674 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500675 # TODO: This is untested.
676 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400677 payload = self._extension.value.data
678 length = self._extension.value.length
679
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500680 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400681 payloadptr[0] = payload
682
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500683 if method.it != _ffi.NULL:
684 ptr = _lib.ASN1_ITEM_ptr(method.it)
685 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
686 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400687 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500688 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400689 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500690 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400691
692 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500693 for i in range(_lib.sk_GENERAL_NAME_num(names)):
694 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400695 try:
696 label = self._prefixes[name.type]
697 except KeyError:
698 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500699 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500700 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400701 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500702 value = _native(
703 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
704 parts.append(label + ":" + value)
705 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400706
707
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800708 def __str__(self):
709 """
710 :return: a nice text representation of the extension
711 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500712 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400713 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800714
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400715 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500716 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800717 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500718 # TODO: This is untested.
719 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800720
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500721 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800722
723
724 def get_critical(self):
725 """
726 Returns the critical field of the X509Extension
727
728 :return: The critical field.
729 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500730 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800731
732
733 def get_short_name(self):
734 """
735 Returns the short version of the type name of the X509Extension
736
737 :return: The short type name.
738 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500739 obj = _lib.X509_EXTENSION_get_object(self._extension)
740 nid = _lib.OBJ_obj2nid(obj)
741 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800742
743
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800744 def get_data(self):
745 """
746 Returns the data of the X509Extension
747
748 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
749 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500750 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
751 string_result = _ffi.cast('ASN1_STRING*', octet_result)
752 char_result = _lib.ASN1_STRING_data(string_result)
753 result_length = _lib.ASN1_STRING_length(string_result)
754 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800755
756X509ExtensionType = X509Extension
757
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800758
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800759class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800760 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500761 req = _lib.X509_REQ_new()
762 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800763
764
765 def set_pubkey(self, pkey):
766 """
767 Set the public key of the certificate request
768
769 :param pkey: The public key to use
770 :return: None
771 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500772 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800773 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500774 # TODO: This is untested.
775 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800776
777
778 def get_pubkey(self):
779 """
780 Get the public key from the certificate request
781
782 :return: The public key
783 """
784 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500785 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
786 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500787 # TODO: This is untested.
788 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500789 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800790 pkey._only_public = True
791 return pkey
792
793
794 def set_version(self, version):
795 """
796 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
797 request.
798
799 :param version: The version number
800 :return: None
801 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500802 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800803 if not set_result:
804 _raise_current_error()
805
806
807 def get_version(self):
808 """
809 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
810 request.
811
812 :return: an integer giving the value of the version subfield
813 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500814 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800815
816
817 def get_subject(self):
818 """
819 Create an X509Name object for the subject of the certificate request
820
821 :return: An X509Name object
822 """
823 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500824 name._name = _lib.X509_REQ_get_subject_name(self._req)
825 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500826 # TODO: This is untested.
827 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800828
829 # The name is owned by the X509Req structure. As long as the X509Name
830 # Python object is alive, keep the X509Req Python object alive.
831 name._owner = self
832
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800833 return name
834
835
836 def add_extensions(self, extensions):
837 """
838 Add extensions to the request.
839
840 :param extensions: a sequence of X509Extension objects
841 :return: None
842 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500843 stack = _lib.sk_X509_EXTENSION_new_null()
844 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500845 # TODO: This is untested.
846 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800847
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500848 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800849
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800850 for ext in extensions:
851 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800852 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800853
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800854 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500855 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800856
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500857 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800858 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500859 # TODO: This is untested.
860 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800861
862
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800863 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800864 """
865 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800866
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500867 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800868 """
869 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500870 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500871 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800872 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500873 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800874 exts.append(ext)
875 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800876
877
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800878 def sign(self, pkey, digest):
879 """
880 Sign the certificate request using the supplied key and digest
881
882 :param pkey: The key to sign with
883 :param digest: The message digest to use
884 :return: None
885 """
886 if pkey._only_public:
887 raise ValueError("Key has only public part")
888
889 if not pkey._initialized:
890 raise ValueError("Key is uninitialized")
891
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500892 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500893 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800894 raise ValueError("No such digest method")
895
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500896 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800897 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500898 # TODO: This is untested.
899 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800900
901
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800902 def verify(self, pkey):
903 """
904 Verifies a certificate request using the supplied public key
905
906 :param key: a public key
907 :return: True if the signature is correct.
908
909 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
910 problem verifying the signature.
911 """
912 if not isinstance(pkey, PKey):
913 raise TypeError("pkey must be a PKey instance")
914
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500915 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800916 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500917 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800918
919 return result
920
921
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800922X509ReqType = X509Req
923
924
925
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800926class X509(object):
927 def __init__(self):
928 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500929 x509 = _lib.X509_new()
930 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800931
932
933 def set_version(self, version):
934 """
935 Set version number of the certificate
936
937 :param version: The version number
938 :type version: :py:class:`int`
939
940 :return: None
941 """
942 if not isinstance(version, int):
943 raise TypeError("version must be an integer")
944
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500945 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800946
947
948 def get_version(self):
949 """
950 Return version number of the certificate
951
952 :return: Version number as a Python integer
953 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500954 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800955
956
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800957 def get_pubkey(self):
958 """
959 Get the public key of the certificate
960
961 :return: The public key
962 """
963 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500964 pkey._pkey = _lib.X509_get_pubkey(self._x509)
965 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800966 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500967 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800968 pkey._only_public = True
969 return pkey
970
971
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800972 def set_pubkey(self, pkey):
973 """
974 Set the public key of the certificate
975
976 :param pkey: The public key
977
978 :return: None
979 """
980 if not isinstance(pkey, PKey):
981 raise TypeError("pkey must be a PKey instance")
982
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500983 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800984 if not set_result:
985 _raise_current_error()
986
987
988 def sign(self, pkey, digest):
989 """
990 Sign the certificate using the supplied key and digest
991
992 :param pkey: The key to sign with
993 :param digest: The message digest to use
994 :return: None
995 """
996 if not isinstance(pkey, PKey):
997 raise TypeError("pkey must be a PKey instance")
998
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800999 if pkey._only_public:
1000 raise ValueError("Key only has public part")
1001
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -08001002 if not pkey._initialized:
1003 raise ValueError("Key is uninitialized")
1004
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001005 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001006 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001007 raise ValueError("No such digest method")
1008
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001009 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001010 if not sign_result:
1011 _raise_current_error()
1012
1013
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001014 def get_signature_algorithm(self):
1015 """
1016 Retrieve the signature algorithm used in the certificate
1017
1018 :return: A byte string giving the name of the signature algorithm used in
1019 the certificate.
1020 :raise ValueError: If the signature algorithm is undefined.
1021 """
1022 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001023 nid = _lib.OBJ_obj2nid(alg)
1024 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001025 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001026 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001027
1028
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001029 def digest(self, digest_name):
1030 """
1031 Return the digest of the X509 object.
1032
1033 :param digest_name: The name of the digest algorithm to use.
1034 :type digest_name: :py:class:`bytes`
1035
1036 :return: The digest of the object
1037 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001038 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001039 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001040 raise ValueError("No such digest method")
1041
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001042 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1043 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001044 result_length[0] = len(result_buffer)
1045
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001046 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001047 self._x509, digest, result_buffer, result_length)
1048
1049 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001050 # TODO: This is untested.
1051 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001052
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001053 return b":".join([
1054 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001055 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001056
1057
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001058 def subject_name_hash(self):
1059 """
1060 Return the hash of the X509 subject.
1061
1062 :return: The hash of the subject.
1063 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001064 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001065
1066
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001067 def set_serial_number(self, serial):
1068 """
1069 Set serial number of the certificate
1070
1071 :param serial: The serial number
1072 :type serial: :py:class:`int`
1073
1074 :return: None
1075 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001076 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001077 raise TypeError("serial must be an integer")
1078
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001079 hex_serial = hex(serial)[2:]
1080 if not isinstance(hex_serial, bytes):
1081 hex_serial = hex_serial.encode('ascii')
1082
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001083 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001084
1085 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1086 # it. If bignum is still NULL after this call, then the return value is
1087 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001088 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001089
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001090 if bignum_serial[0] == _ffi.NULL:
1091 set_result = _lib.ASN1_INTEGER_set(
1092 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001093 if set_result:
1094 # TODO Not tested
1095 _raise_current_error()
1096 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001097 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1098 _lib.BN_free(bignum_serial[0])
1099 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001100 # TODO Not tested
1101 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001102 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1103 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001104 if not set_result:
1105 # TODO Not tested
1106 _raise_current_error()
1107
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001108
1109 def get_serial_number(self):
1110 """
1111 Return serial number of the certificate
1112
1113 :return: Serial number as a Python integer
1114 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001115 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1116 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001117 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001118 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001119 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001120 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001121 serial = int(hexstring_serial, 16)
1122 return serial
1123 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001124 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001125 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001126 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001127
1128
1129 def gmtime_adj_notAfter(self, amount):
1130 """
1131 Adjust the time stamp for when the certificate stops being valid
1132
1133 :param amount: The number of seconds by which to adjust the ending
1134 validity time.
1135 :type amount: :py:class:`int`
1136
1137 :return: None
1138 """
1139 if not isinstance(amount, int):
1140 raise TypeError("amount must be an integer")
1141
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001142 notAfter = _lib.X509_get_notAfter(self._x509)
1143 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001144
1145
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001146 def gmtime_adj_notBefore(self, amount):
1147 """
1148 Change the timestamp for when the certificate starts being valid to the current
1149 time plus an offset.
1150
1151 :param amount: The number of seconds by which to adjust the starting validity
1152 time.
1153 :return: None
1154 """
1155 if not isinstance(amount, int):
1156 raise TypeError("amount must be an integer")
1157
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001158 notBefore = _lib.X509_get_notBefore(self._x509)
1159 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001160
1161
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001162 def has_expired(self):
1163 """
1164 Check whether the certificate has expired.
1165
1166 :return: True if the certificate has expired, false otherwise
1167 """
1168 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001169 notAfter = _lib.X509_get_notAfter(self._x509)
1170 return _lib.ASN1_UTCTIME_cmp_time_t(
1171 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001172
1173
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001174 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001175 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001176
1177
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001178 def get_notBefore(self):
1179 """
1180 Retrieve the time stamp for when the certificate starts being valid
1181
1182 :return: A string giving the timestamp, in the format::
1183
1184 YYYYMMDDhhmmssZ
1185 YYYYMMDDhhmmss+hhmm
1186 YYYYMMDDhhmmss-hhmm
1187
1188 or None if there is no value set.
1189 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001190 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001191
1192
1193 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001194 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001195
1196
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001197 def set_notBefore(self, when):
1198 """
1199 Set the time stamp for when the certificate starts being valid
1200
1201 :param when: A string giving the timestamp, in the format:
1202
1203 YYYYMMDDhhmmssZ
1204 YYYYMMDDhhmmss+hhmm
1205 YYYYMMDDhhmmss-hhmm
1206 :type when: :py:class:`bytes`
1207
1208 :return: None
1209 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001210 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001211
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001212
1213 def get_notAfter(self):
1214 """
1215 Retrieve the time stamp for when the certificate stops being valid
1216
1217 :return: A string giving the timestamp, in the format::
1218
1219 YYYYMMDDhhmmssZ
1220 YYYYMMDDhhmmss+hhmm
1221 YYYYMMDDhhmmss-hhmm
1222
1223 or None if there is no value set.
1224 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001225 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001226
1227
1228 def set_notAfter(self, when):
1229 """
1230 Set the time stamp for when the certificate stops being valid
1231
1232 :param when: A string giving the timestamp, in the format:
1233
1234 YYYYMMDDhhmmssZ
1235 YYYYMMDDhhmmss+hhmm
1236 YYYYMMDDhhmmss-hhmm
1237 :type when: :py:class:`bytes`
1238
1239 :return: None
1240 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001241 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001242
1243
1244 def _get_name(self, which):
1245 name = X509Name.__new__(X509Name)
1246 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001247 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001248 # TODO: This is untested.
1249 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001250
1251 # The name is owned by the X509 structure. As long as the X509Name
1252 # Python object is alive, keep the X509 Python object alive.
1253 name._owner = self
1254
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001255 return name
1256
1257
1258 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001259 if not isinstance(name, X509Name):
1260 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001261 set_result = which(self._x509, name._name)
1262 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001263 # TODO: This is untested.
1264 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001265
1266
1267 def get_issuer(self):
1268 """
1269 Create an X509Name object for the issuer of the certificate
1270
1271 :return: An X509Name object
1272 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001273 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001274
1275
1276 def set_issuer(self, issuer):
1277 """
1278 Set the issuer of the certificate
1279
1280 :param issuer: The issuer name
1281 :type issuer: :py:class:`X509Name`
1282
1283 :return: None
1284 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001285 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001286
1287
1288 def get_subject(self):
1289 """
1290 Create an X509Name object for the subject of the certificate
1291
1292 :return: An X509Name object
1293 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001294 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001295
1296
1297 def set_subject(self, subject):
1298 """
1299 Set the subject of the certificate
1300
1301 :param subject: The subject name
1302 :type subject: :py:class:`X509Name`
1303 :return: None
1304 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001305 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001306
1307
1308 def get_extension_count(self):
1309 """
1310 Get the number of extensions on the certificate.
1311
1312 :return: The number of extensions as an integer.
1313 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001314 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001315
1316
1317 def add_extensions(self, extensions):
1318 """
1319 Add extensions to the certificate.
1320
1321 :param extensions: a sequence of X509Extension objects
1322 :return: None
1323 """
1324 for ext in extensions:
1325 if not isinstance(ext, X509Extension):
1326 raise ValueError("One of the elements is not an X509Extension")
1327
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001328 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001329 if not add_result:
1330 _raise_current_error()
1331
1332
1333 def get_extension(self, index):
1334 """
1335 Get a specific extension of the certificate by index.
1336
1337 :param index: The index of the extension to retrieve.
1338 :return: The X509Extension object at the specified index.
1339 """
1340 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001341 ext._extension = _lib.X509_get_ext(self._x509, index)
1342 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001343 raise IndexError("extension index out of bounds")
1344
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001345 extension = _lib.X509_EXTENSION_dup(ext._extension)
1346 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001347 return ext
1348
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001349
1350
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001351X509Type = X509
1352
1353
1354
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001355class X509Store(object):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001356 """
1357 An X509 certificate store.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001358 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001359 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001360 store = _lib.X509_STORE_new()
1361 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001362
1363
1364 def add_cert(self, cert):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001365 """
1366 Adds the certificate :py:data:`cert` to this store.
1367
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +02001368 This is the Python equivalent of OpenSSL's ``X509_STORE_add_cert``.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001369
1370 :param X509 cert: The certificate to add to this store.
1371 :raises TypeError: If the certificate is not an :py:class:`X509`.
1372 :raises Error: If OpenSSL was unhappy with your certificate.
1373 :return: py:data:`None` if the certificate was added successfully.
1374 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001375 if not isinstance(cert, X509):
1376 raise TypeError()
1377
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001378 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001379 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001380 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001381
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001382
1383X509StoreType = X509Store
1384
1385
1386
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001387def load_certificate(type, buffer):
1388 """
1389 Load a certificate from a buffer
1390
1391 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1392
1393 :param buffer: The buffer the certificate is stored in
1394 :type buffer: :py:class:`bytes`
1395
1396 :return: The X509 object
1397 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001398 if isinstance(buffer, _text_type):
1399 buffer = buffer.encode("ascii")
1400
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001401 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001402
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001403 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001404 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001405 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001406 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001407 else:
1408 raise ValueError(
1409 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001410
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001411 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001412 _raise_current_error()
1413
1414 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001415 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001416 return cert
1417
1418
1419def dump_certificate(type, cert):
1420 """
1421 Dump a certificate to a buffer
1422
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001423 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1424 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001425 :param cert: The certificate to dump
1426 :return: The buffer with the dumped certificate in
1427 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001428 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001429
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001430 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001431 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001432 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001433 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001434 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001435 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001436 else:
1437 raise ValueError(
1438 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1439 "FILETYPE_TEXT")
1440
1441 return _bio_to_string(bio)
1442
1443
1444
1445def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1446 """
1447 Dump a private key to a buffer
1448
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001449 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1450 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001451 :param pkey: The PKey to dump
1452 :param cipher: (optional) if encrypted PEM format, the cipher to
1453 use
1454 :param passphrase: (optional) if encrypted PEM format, this can be either
1455 the passphrase to use, or a callback for providing the
1456 passphrase.
1457 :return: The buffer with the dumped key in
1458 :rtype: :py:data:`str`
1459 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001460 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001461
1462 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001463 if passphrase is None:
1464 raise TypeError(
1465 "if a value is given for cipher "
1466 "one must also be given for passphrase")
1467 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001468 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001469 raise ValueError("Invalid cipher name")
1470 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001471 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001472
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001473 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001474 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001475 result_code = _lib.PEM_write_bio_PrivateKey(
1476 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001477 helper.callback, helper.callback_args)
1478 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001479 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001480 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001481 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001482 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1483 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001484 # TODO RSA_free(rsa)?
1485 else:
1486 raise ValueError(
1487 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1488 "FILETYPE_TEXT")
1489
1490 if result_code == 0:
1491 _raise_current_error()
1492
1493 return _bio_to_string(bio)
1494
1495
1496
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001497def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001498 copy = _lib.X509_REVOKED_new()
1499 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001500 # TODO: This is untested.
1501 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001502
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001503 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001504 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001505 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001506
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001507 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001508 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001509 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001510
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001511 if original.extensions != _ffi.NULL:
1512 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1513 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1514 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1515 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1516 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001517 copy.extensions = extension_stack
1518
1519 copy.sequence = original.sequence
1520 return copy
1521
1522
1523
1524class Revoked(object):
1525 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1526 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1527 # OCSP_crl_reason_str. We use the latter, just like the command line
1528 # program.
1529 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001530 b"unspecified",
1531 b"keyCompromise",
1532 b"CACompromise",
1533 b"affiliationChanged",
1534 b"superseded",
1535 b"cessationOfOperation",
1536 b"certificateHold",
1537 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001538 ]
1539
1540 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001541 revoked = _lib.X509_REVOKED_new()
1542 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001543
1544
1545 def set_serial(self, hex_str):
1546 """
1547 Set the serial number of a revoked Revoked structure
1548
1549 :param hex_str: The new serial number.
1550 :type hex_str: :py:data:`str`
1551 :return: None
1552 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001553 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1554 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001555 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001556 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001557 if not bn_result:
1558 raise ValueError("bad hex string")
1559
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001560 asn1_serial = _ffi.gc(
1561 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1562 _lib.ASN1_INTEGER_free)
1563 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001564
1565
1566 def get_serial(self):
1567 """
1568 Return the serial number of a Revoked structure
1569
1570 :return: The serial number as a string
1571 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001572 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001573
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001574 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001575 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001576 # TODO: This is untested.
1577 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001578
1579 return _bio_to_string(bio)
1580
1581
1582 def _delete_reason(self):
1583 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001584 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1585 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1586 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1587 _lib.X509_EXTENSION_free(ext)
1588 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001589 break
1590
1591
1592 def set_reason(self, reason):
1593 """
1594 Set the reason of a Revoked object.
1595
1596 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1597
1598 :param reason: The reason string.
1599 :type reason: :py:class:`str` or :py:class:`NoneType`
1600 :return: None
1601 """
1602 if reason is None:
1603 self._delete_reason()
1604 elif not isinstance(reason, bytes):
1605 raise TypeError("reason must be None or a byte string")
1606 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001607 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001608 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1609
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001610 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1611 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001612 # TODO: This is untested.
1613 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001614 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001615
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001616 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1617 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001618 # TODO: This is untested.
1619 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001620
1621 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001622 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1623 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001624
1625 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001626 # TODO: This is untested.
1627 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001628
1629
1630 def get_reason(self):
1631 """
1632 Return the reason of a Revoked object.
1633
1634 :return: The reason as a string
1635 """
1636 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001637 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1638 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1639 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001640 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001641
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001642 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001643 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001644 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001645 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001646 # TODO: This is untested.
1647 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001648
1649 return _bio_to_string(bio)
1650
1651
1652 def all_reasons(self):
1653 """
1654 Return a list of all the supported reason strings.
1655
1656 :return: A list of reason strings.
1657 """
1658 return self._crl_reasons[:]
1659
1660
1661 def set_rev_date(self, when):
1662 """
1663 Set the revocation timestamp
1664
1665 :param when: A string giving the timestamp, in the format:
1666
1667 YYYYMMDDhhmmssZ
1668 YYYYMMDDhhmmss+hhmm
1669 YYYYMMDDhhmmss-hhmm
1670
1671 :return: None
1672 """
1673 return _set_asn1_time(self._revoked.revocationDate, when)
1674
1675
1676 def get_rev_date(self):
1677 """
1678 Retrieve the revocation date
1679
1680 :return: A string giving the timestamp, in the format:
1681
1682 YYYYMMDDhhmmssZ
1683 YYYYMMDDhhmmss+hhmm
1684 YYYYMMDDhhmmss-hhmm
1685 """
1686 return _get_asn1_time(self._revoked.revocationDate)
1687
1688
1689
1690class CRL(object):
1691 def __init__(self):
1692 """
1693 Create a new empty CRL object.
1694 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001695 crl = _lib.X509_CRL_new()
1696 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001697
1698
1699 def get_revoked(self):
1700 """
1701 Return revoked portion of the CRL structure (by value not reference).
1702
1703 :return: A tuple of Revoked objects.
1704 """
1705 results = []
1706 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001707 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1708 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001709 revoked_copy = _X509_REVOKED_dup(revoked)
1710 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001711 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001712 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001713 if results:
1714 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001715
1716
1717 def add_revoked(self, revoked):
1718 """
1719 Add a revoked (by value not reference) to the CRL structure
1720
1721 :param revoked: The new revoked.
1722 :type revoked: :class:`X509`
1723
1724 :return: None
1725 """
1726 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001727 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001728 # TODO: This is untested.
1729 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001730
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001731 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001732 if add_result == 0:
1733 # TODO: This is untested.
1734 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001735
1736
1737 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1738 """
1739 export a CRL as a string
1740
1741 :param cert: Used to sign CRL.
1742 :type cert: :class:`X509`
1743
1744 :param key: Used to sign CRL.
1745 :type key: :class:`PKey`
1746
1747 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1748
1749 :param days: The number of days until the next update of this CRL.
1750 :type days: :py:data:`int`
1751
1752 :return: :py:data:`str`
1753 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001754 if not isinstance(cert, X509):
1755 raise TypeError("cert must be an X509 instance")
1756 if not isinstance(key, PKey):
1757 raise TypeError("key must be a PKey instance")
1758 if not isinstance(type, int):
1759 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001760
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001761 bio = _lib.BIO_new(_lib.BIO_s_mem())
1762 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001763 # TODO: This is untested.
1764 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001765
1766 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001767 sometime = _lib.ASN1_TIME_new()
1768 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001769 # TODO: This is untested.
1770 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001771
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001772 _lib.X509_gmtime_adj(sometime, 0)
1773 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001774
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001775 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1776 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001777
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001778 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001779
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001780 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001781 if not sign_result:
1782 _raise_current_error()
1783
1784 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001785 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001786 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001787 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001788 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001789 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001790 else:
1791 raise ValueError(
1792 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1793
1794 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001795 # TODO: This is untested.
1796 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001797
1798 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001799CRLType = CRL
1800
1801
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001802
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001803class PKCS7(object):
1804 def type_is_signed(self):
1805 """
1806 Check if this NID_pkcs7_signed object
1807
1808 :return: True if the PKCS7 is of type signed
1809 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001810 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001811 return True
1812 return False
1813
1814
1815 def type_is_enveloped(self):
1816 """
1817 Check if this NID_pkcs7_enveloped object
1818
1819 :returns: True if the PKCS7 is of type enveloped
1820 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001821 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001822 return True
1823 return False
1824
1825
1826 def type_is_signedAndEnveloped(self):
1827 """
1828 Check if this NID_pkcs7_signedAndEnveloped object
1829
1830 :returns: True if the PKCS7 is of type signedAndEnveloped
1831 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001832 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001833 return True
1834 return False
1835
1836
1837 def type_is_data(self):
1838 """
1839 Check if this NID_pkcs7_data object
1840
1841 :return: True if the PKCS7 is of type data
1842 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001843 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001844 return True
1845 return False
1846
1847
1848 def get_type_name(self):
1849 """
1850 Returns the type name of the PKCS7 structure
1851
1852 :return: A string with the typename
1853 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001854 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1855 string_type = _lib.OBJ_nid2sn(nid)
1856 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001857
1858PKCS7Type = PKCS7
1859
1860
1861
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001862class PKCS12(object):
1863 def __init__(self):
1864 self._pkey = None
1865 self._cert = None
1866 self._cacerts = None
1867 self._friendlyname = None
1868
1869
1870 def get_certificate(self):
1871 """
1872 Return certificate portion of the PKCS12 structure
1873
1874 :return: X509 object containing the certificate
1875 """
1876 return self._cert
1877
1878
1879 def set_certificate(self, cert):
1880 """
1881 Replace the certificate portion of the PKCS12 structure
1882
1883 :param cert: The new certificate.
1884 :type cert: :py:class:`X509` or :py:data:`None`
1885 :return: None
1886 """
1887 if not isinstance(cert, X509):
1888 raise TypeError("cert must be an X509 instance")
1889 self._cert = cert
1890
1891
1892 def get_privatekey(self):
1893 """
1894 Return private key portion of the PKCS12 structure
1895
1896 :returns: PKey object containing the private key
1897 """
1898 return self._pkey
1899
1900
1901 def set_privatekey(self, pkey):
1902 """
1903 Replace or set the certificate portion of the PKCS12 structure
1904
1905 :param pkey: The new private key.
1906 :type pkey: :py:class:`PKey`
1907 :return: None
1908 """
1909 if not isinstance(pkey, PKey):
1910 raise TypeError("pkey must be a PKey instance")
1911 self._pkey = pkey
1912
1913
1914 def get_ca_certificates(self):
1915 """
1916 Return CA certificates within of the PKCS12 object
1917
1918 :return: A newly created tuple containing the CA certificates in the chain,
1919 if any are present, or None if no CA certificates are present.
1920 """
1921 if self._cacerts is not None:
1922 return tuple(self._cacerts)
1923
1924
1925 def set_ca_certificates(self, cacerts):
1926 """
1927 Replace or set the CA certificates withing the PKCS12 object.
1928
1929 :param cacerts: The new CA certificates.
1930 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1931 :return: None
1932 """
1933 if cacerts is None:
1934 self._cacerts = None
1935 else:
1936 cacerts = list(cacerts)
1937 for cert in cacerts:
1938 if not isinstance(cert, X509):
1939 raise TypeError("iterable must only contain X509 instances")
1940 self._cacerts = cacerts
1941
1942
1943 def set_friendlyname(self, name):
1944 """
1945 Replace or set the certificate portion of the PKCS12 structure
1946
1947 :param name: The new friendly name.
1948 :type name: :py:class:`bytes`
1949 :return: None
1950 """
1951 if name is None:
1952 self._friendlyname = None
1953 elif not isinstance(name, bytes):
1954 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1955 self._friendlyname = name
1956
1957
1958 def get_friendlyname(self):
1959 """
1960 Return friendly name portion of the PKCS12 structure
1961
1962 :returns: String containing the friendlyname
1963 """
1964 return self._friendlyname
1965
1966
1967 def export(self, passphrase=None, iter=2048, maciter=1):
1968 """
1969 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1970
1971 :param passphrase: used to encrypt the PKCS12
1972 :type passphrase: :py:data:`bytes`
1973
1974 :param iter: How many times to repeat the encryption
1975 :type iter: :py:data:`int`
1976
1977 :param maciter: How many times to repeat the MAC
1978 :type maciter: :py:data:`int`
1979
1980 :return: The string containing the PKCS12
1981 """
1982 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001983 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001984 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001985 cacerts = _lib.sk_X509_new_null()
1986 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001987 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001988 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001989
1990 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001991 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001992
1993 friendlyname = self._friendlyname
1994 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001995 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001996
1997 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001998 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001999 else:
2000 pkey = self._pkey._pkey
2001
2002 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002003 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002004 else:
2005 cert = self._cert._x509
2006
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002007 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002008 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002009 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2010 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002011 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002012 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002013 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002014 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002015
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002016 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002017 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002018 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002019
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002020PKCS12Type = PKCS12
2021
2022
2023
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002024class NetscapeSPKI(object):
2025 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002026 spki = _lib.NETSCAPE_SPKI_new()
2027 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002028
2029
2030 def sign(self, pkey, digest):
2031 """
2032 Sign the certificate request using the supplied key and digest
2033
2034 :param pkey: The key to sign with
2035 :param digest: The message digest to use
2036 :return: None
2037 """
2038 if pkey._only_public:
2039 raise ValueError("Key has only public part")
2040
2041 if not pkey._initialized:
2042 raise ValueError("Key is uninitialized")
2043
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002044 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002045 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002046 raise ValueError("No such digest method")
2047
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002048 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002049 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002050 # TODO: This is untested.
2051 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002052
2053
2054 def verify(self, key):
2055 """
2056 Verifies a certificate request using the supplied public key
2057
2058 :param key: a public key
2059 :return: True if the signature is correct.
2060 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2061 problem verifying the signature.
2062 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002063 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002064 if answer <= 0:
2065 _raise_current_error()
2066 return True
2067
2068
2069 def b64_encode(self):
2070 """
2071 Generate a base64 encoded string from an SPKI
2072
2073 :return: The base64 encoded string
2074 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002075 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2076 result = _ffi.string(encoded)
2077 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002078 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002079
2080
2081 def get_pubkey(self):
2082 """
2083 Get the public key of the certificate
2084
2085 :return: The public key
2086 """
2087 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002088 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2089 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002090 # TODO: This is untested.
2091 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002092 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002093 pkey._only_public = True
2094 return pkey
2095
2096
2097 def set_pubkey(self, pkey):
2098 """
2099 Set the public key of the certificate
2100
2101 :param pkey: The public key
2102 :return: None
2103 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002104 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002105 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002106 # TODO: This is untested.
2107 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002108NetscapeSPKIType = NetscapeSPKI
2109
2110
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002111class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002112 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002113 if type != FILETYPE_PEM and passphrase is not None:
2114 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002115 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002116 self._more_args = more_args
2117 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002118 self._problems = []
2119
2120
2121 @property
2122 def callback(self):
2123 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002124 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002125 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002126 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002127 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002128 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002129 else:
2130 raise TypeError("Last argument must be string or callable")
2131
2132
2133 @property
2134 def callback_args(self):
2135 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002136 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002137 elif isinstance(self._passphrase, bytes):
2138 return self._passphrase
2139 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002140 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002141 else:
2142 raise TypeError("Last argument must be string or callable")
2143
2144
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002145 def raise_if_problem(self, exceptionType=Error):
2146 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002147 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002148 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002149 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002150 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002151 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002152 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002153
2154
2155 def _read_passphrase(self, buf, size, rwflag, userdata):
2156 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002157 if self._more_args:
2158 result = self._passphrase(size, rwflag, userdata)
2159 else:
2160 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002161 if not isinstance(result, bytes):
2162 raise ValueError("String expected")
2163 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002164 if self._truncate:
2165 result = result[:size]
2166 else:
2167 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002168 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002169 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002170 return len(result)
2171 except Exception as e:
2172 self._problems.append(e)
2173 return 0
2174
2175
2176
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002177def load_privatekey(type, buffer, passphrase=None):
2178 """
2179 Load a private key from a buffer
2180
2181 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2182 :param buffer: The buffer the key is stored in
2183 :param passphrase: (optional) if encrypted PEM format, this can be
2184 either the passphrase to use, or a callback for
2185 providing the passphrase.
2186
2187 :return: The PKey object
2188 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002189 if isinstance(buffer, _text_type):
2190 buffer = buffer.encode("ascii")
2191
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002192 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002193
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002194 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002195 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002196 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2197 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002198 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002199 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002200 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002201 else:
2202 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2203
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002204 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002205 _raise_current_error()
2206
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002207 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002208 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002209 return pkey
2210
2211
2212
2213def dump_certificate_request(type, req):
2214 """
2215 Dump a certificate request to a buffer
2216
2217 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2218 :param req: The certificate request to dump
2219 :return: The buffer with the dumped certificate request in
2220 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002221 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002222
2223 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002224 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002225 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002226 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002227 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002228 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002229 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002230 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002231
2232 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002233 # TODO: This is untested.
2234 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002235
2236 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002237
2238
2239
2240def load_certificate_request(type, buffer):
2241 """
2242 Load a certificate request from a buffer
2243
2244 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2245 :param buffer: The buffer the certificate request is stored in
2246 :return: The X509Req object
2247 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002248 if isinstance(buffer, _text_type):
2249 buffer = buffer.encode("ascii")
2250
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002251 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002252
2253 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002254 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002255 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002256 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002257 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002258 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002259
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002260 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002261 # TODO: This is untested.
2262 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002263
2264 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002265 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002266 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002267
2268
2269
2270def sign(pkey, data, digest):
2271 """
2272 Sign data with a digest
2273
2274 :param pkey: Pkey to sign with
2275 :param data: data to be signed
2276 :param digest: message digest to use
2277 :return: signature
2278 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002279 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002280 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002281 raise ValueError("No such digest method")
2282
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002283 md_ctx = _ffi.new("EVP_MD_CTX*")
2284 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002285
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002286 _lib.EVP_SignInit(md_ctx, digest_obj)
2287 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002288
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002289 signature_buffer = _ffi.new("unsigned char[]", 512)
2290 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002291 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002292 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002293 md_ctx, signature_buffer, signature_length, pkey._pkey)
2294
2295 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002296 # TODO: This is untested.
2297 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002298
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002299 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002300
2301
2302
2303def verify(cert, signature, data, digest):
2304 """
2305 Verify a signature
2306
2307 :param cert: signing certificate (X509 object)
2308 :param signature: signature returned by sign function
2309 :param data: data to be verified
2310 :param digest: message digest to use
2311 :return: None if the signature is correct, raise exception otherwise
2312 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002313 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002314 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002315 raise ValueError("No such digest method")
2316
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002317 pkey = _lib.X509_get_pubkey(cert._x509)
2318 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002319 # TODO: This is untested.
2320 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002321 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002322
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002323 md_ctx = _ffi.new("EVP_MD_CTX*")
2324 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002325
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002326 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2327 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2328 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002329
2330 if verify_result != 1:
2331 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002332
2333
2334
2335def load_crl(type, buffer):
2336 """
2337 Load a certificate revocation list from a buffer
2338
2339 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2340 :param buffer: The buffer the CRL is stored in
2341
2342 :return: The PKey object
2343 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002344 if isinstance(buffer, _text_type):
2345 buffer = buffer.encode("ascii")
2346
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002347 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002348
2349 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002350 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002351 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002352 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002353 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002354 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2355
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002356 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002357 _raise_current_error()
2358
2359 result = CRL.__new__(CRL)
2360 result._crl = crl
2361 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002362
2363
2364
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002365def load_pkcs7_data(type, buffer):
2366 """
2367 Load pkcs7 data from a buffer
2368
2369 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2370 :param buffer: The buffer with the pkcs7 data.
2371 :return: The PKCS7 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 Calderone4e8be1c2013-02-21 18:31:12 -08002377
2378 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002379 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002380 elif type == FILETYPE_ASN1:
2381 pass
2382 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002383 # TODO: This is untested.
2384 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002385 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2386
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002387 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002388 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002389
2390 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002391 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002392 return pypkcs7
2393
2394
2395
Stephen Holsapple38482622014-04-05 20:29:34 -07002396def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002397 """
2398 Load a PKCS12 object from a buffer
2399
2400 :param buffer: The buffer the certificate is stored in
2401 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2402 :returns: The PKCS12 object
2403 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002404 if isinstance(buffer, _text_type):
2405 buffer = buffer.encode("ascii")
2406
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002407 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002408
Stephen Holsapple38482622014-04-05 20:29:34 -07002409 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2410 # password based encryption no password and a zero length password are two
2411 # different things, but OpenSSL implementation will try both to figure out
2412 # which one works.
2413 if not passphrase:
2414 passphrase = _ffi.NULL
2415
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002416 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2417 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002418 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002419 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002420
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002421 pkey = _ffi.new("EVP_PKEY**")
2422 cert = _ffi.new("X509**")
2423 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002424
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002425 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002426 if not parse_result:
2427 _raise_current_error()
2428
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002429 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002430
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002431 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2432 # queue for no particular reason. This error isn't interesting to anyone
2433 # outside this function. It's not even interesting to us. Get rid of it.
2434 try:
2435 _raise_current_error()
2436 except Error:
2437 pass
2438
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002439 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002440 pykey = None
2441 else:
2442 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002443 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002444
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002445 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002446 pycert = None
2447 friendlyname = None
2448 else:
2449 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002450 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002451
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002452 friendlyname_length = _ffi.new("int*")
2453 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2454 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2455 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002456 friendlyname = None
2457
2458 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002459 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002460 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002461 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002462 pycacerts.append(pycacert)
2463 if not pycacerts:
2464 pycacerts = None
2465
2466 pkcs12 = PKCS12.__new__(PKCS12)
2467 pkcs12._pkey = pykey
2468 pkcs12._cert = pycert
2469 pkcs12._cacerts = pycacerts
2470 pkcs12._friendlyname = friendlyname
2471 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002472
2473
2474def _initialize_openssl_threads(get_ident, Lock):
2475 import _ssl
2476 return
2477
2478 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2479
2480 def locking_function(mode, index, filename, line):
2481 if mode & _lib.CRYPTO_LOCK:
2482 locks[index].acquire()
2483 else:
2484 locks[index].release()
2485
2486 _lib.CRYPTO_set_id_callback(
2487 _ffi.callback("unsigned long (*)(void)", get_ident))
2488
2489 _lib.CRYPTO_set_locking_callback(
2490 _ffi.callback(
2491 "void (*)(int, int, const char*, int)", locking_function))
2492
2493
2494try:
2495 from thread import get_ident
2496 from threading import Lock
2497except ImportError:
2498 pass
2499else:
2500 _initialize_openssl_threads(get_ident, Lock)
2501 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002502
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002503# There are no direct unit tests for this initialization. It is tested
2504# indirectly since it is necessary for functions like dump_privatekey when
2505# using encryption.
2506#
2507# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2508# and some other similar tests may fail without this (though they may not if
2509# the Python runtime has already done some initialization of the underlying
2510# OpenSSL library (and is linked against the same one that cryptography is
2511# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002512_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002513
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002514# This is similar but exercised mainly by exception_from_error_queue. It calls
2515# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2516_lib.SSL_load_error_strings()