blob: 6c07962d9b5e3c90c305802513d37bd3a1ac800c [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
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070028
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050029class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050030 """
31 An error occurred in an `OpenSSL.crypto` API.
32 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050033
34
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070035
36def _exception_from_context_error(exception_type, store_ctx):
37 """
38 Convert a :py:func:`OpenSSL.crypto.verify_cert` failure into a Python
39 exception.
40
41 When a call to native OpenSSL X509_verify_cert fails, additonal information
42 about the failure can be contained from the store context.
43 """
44
45 errors = [
46 _lib.X509_STORE_CTX_get_error(store_ctx._store_ctx),
47 _lib.X509_STORE_CTX_get_error_depth(store_ctx._store_ctx),
48 _native(_ffi.string(_lib.X509_verify_cert_error_string(_lib.X509_STORE_CTX_get_error(store_ctx._store_ctx)))),
49 ]
50 _x509 = _lib.X509_STORE_CTX_get_current_cert(store_ctx._store_ctx)
51 if _x509 != _ffi.NULL:
52 _cert = _lib.X509_dup(_x509)
53 pycert = X509.__new__(X509)
54 pycert._x509 = _ffi.gc(_cert, _lib.X509_free)
55 e = exception_type(errors)
56 e.certificate = pycert
57 raise e
58
59
60
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050061_raise_current_error = partial(_exception_from_error_queue, Error)
Stephen Holsapple0d9815f2014-08-27 19:36:53 -070062_raise_context_error = partial(_exception_from_context_error, Error)
63
64
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050065
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050066def _untested_error(where):
67 """
68 An OpenSSL API failed somehow. Additionally, the failure which was
69 encountered isn't one that's exercised by the test suite so future behavior
70 of pyOpenSSL is now somewhat less predictable.
71 """
72 raise RuntimeError("Unknown %s failure" % (where,))
73
74
75
76def _new_mem_buf(buffer=None):
77 """
78 Allocate a new OpenSSL memory BIO.
79
80 Arrange for the garbage collector to clean it up automatically.
81
82 :param buffer: None or some bytes to use to put into the BIO so that they
83 can be read out.
84 """
85 if buffer is None:
86 bio = _lib.BIO_new(_lib.BIO_s_mem())
87 free = _lib.BIO_free
88 else:
89 data = _ffi.new("char[]", buffer)
90 bio = _lib.BIO_new_mem_buf(data, len(buffer))
91 # Keep the memory alive as long as the bio is alive!
92 def free(bio, ref=data):
93 return _lib.BIO_free(bio)
94
95 if bio == _ffi.NULL:
96 # TODO: This is untested.
97 _raise_current_error()
98
99 bio = _ffi.gc(bio, free)
100 return bio
101
102
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500103
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800104def _bio_to_string(bio):
105 """
106 Copy the contents of an OpenSSL BIO object into a Python byte string.
107 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500108 result_buffer = _ffi.new('char**')
109 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
110 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800111
112
113
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800114def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500115 """
116 The the time value of an ASN1 time object.
117
118 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
119 castable to that type) which will have its value set.
120 @param when: A string representation of the desired time value.
121
122 @raise TypeError: If C{when} is not a L{bytes} string.
123 @raise ValueError: If C{when} does not represent a time in the required
124 format.
125 @raise RuntimeError: If the time value cannot be set for some other
126 (unspecified) reason.
127 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800128 if not isinstance(when, bytes):
129 raise TypeError("when must be a byte string")
130
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500131 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
132 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800133 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500134 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
135 _lib.ASN1_STRING_set(dummy, when, len(when))
136 check_result = _lib.ASN1_GENERALIZEDTIME_check(
137 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800138 if not check_result:
139 raise ValueError("Invalid string")
140 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500141 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800142
143
144
145def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500146 """
147 Retrieve the time value of an ASN1 time object.
148
149 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
150 that type) from which the time value will be retrieved.
151
152 @return: The time value from C{timestamp} as a L{bytes} string in a certain
153 format. Or C{None} if the object contains no time value.
154 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500155 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
156 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800157 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500158 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
159 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800160 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500161 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
162 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
163 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500164 # This may happen:
165 # - if timestamp was not an ASN1_TIME
166 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
167 # - if a copy of the time data from timestamp cannot be made for
168 # the newly allocated ASN1_GENERALIZEDTIME
169 #
170 # These are difficult to test. cffi enforces the ASN1_TIME type.
171 # Memory allocation failures are a pain to trigger
172 # deterministically.
173 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800174 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500175 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800176 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500177 string_data = _lib.ASN1_STRING_data(string_timestamp)
178 string_result = _ffi.string(string_data)
179 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800180 return string_result
181
182
183
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800184class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800185 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800186 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800187
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800188 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500189 pkey = _lib.EVP_PKEY_new()
190 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800191 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800192
193
194 def generate_key(self, type, bits):
195 """
196 Generate a key of a given type, with a given number of a bits
197
198 :param type: The key type (TYPE_RSA or TYPE_DSA)
199 :param bits: The number of bits
200
201 :return: None
202 """
203 if not isinstance(type, int):
204 raise TypeError("type must be an integer")
205
206 if not isinstance(bits, int):
207 raise TypeError("bits must be an integer")
208
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800209 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500210 exponent = _lib.BN_new()
211 exponent = _ffi.gc(exponent, _lib.BN_free)
212 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800213
214 if type == TYPE_RSA:
215 if bits <= 0:
216 raise ValueError("Invalid number of bits")
217
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500218 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800219
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500220 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500221 if result == 0:
222 # TODO: The test for this case is commented out. Different
223 # builds of OpenSSL appear to have different failure modes that
224 # make it hard to test. Visual inspection of the OpenSSL
225 # source reveals that a return value of 0 signals an error.
226 # Manual testing on a particular build of OpenSSL suggests that
227 # this is probably the appropriate way to handle those errors.
228 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800229
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500230 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800231 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500232 # TODO: It appears as though this can fail if an engine is in
233 # use which does not support RSA.
234 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800235
236 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500237 dsa = _lib.DSA_generate_parameters(
238 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
239 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500240 # TODO: This is untested.
241 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500242 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500243 # TODO: This is untested.
244 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500245 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500246 # TODO: This is untested.
247 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800248 else:
249 raise Error("No such key type")
250
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800251 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800252
253
254 def check(self):
255 """
256 Check the consistency of an RSA private key.
257
258 :return: True if key is consistent.
259 :raise Error: if the key is inconsistent.
260 :raise TypeError: if the key is of a type which cannot be checked.
261 Only RSA keys can currently be checked.
262 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800263 if self._only_public:
264 raise TypeError("public key only")
265
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500266 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800267 raise TypeError("key type unsupported")
268
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500269 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
270 rsa = _ffi.gc(rsa, _lib.RSA_free)
271 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800272 if result:
273 return True
274 _raise_current_error()
275
276
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800277 def type(self):
278 """
279 Returns the type of the key
280
281 :return: The type of the key.
282 """
283 return self._pkey.type
284
285
286 def bits(self):
287 """
288 Returns the number of bits of the key
289
290 :return: The number of bits of the key.
291 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500292 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800293PKeyType = PKey
294
295
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800296
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400297class _EllipticCurve(object):
298 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400299 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400300
301 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
302 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
303 instances each of which represents one curve supported by the system.
304 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400305 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400306 _curves = None
307
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400308 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400309 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400310 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400311 """
312 Implement cooperation with the right-hand side argument of ``!=``.
313
314 Python 3 seems to have dropped this cooperation in this very narrow
315 circumstance.
316 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400317 if isinstance(other, _EllipticCurve):
318 return super(_EllipticCurve, self).__ne__(other)
319 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400320
321
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400322 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400323 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400324 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400325 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400326
327 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400328
329 :return: A :py:type:`set` of ``cls`` instances giving the names of the
330 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400331 """
332 if lib.Cryptography_HAS_EC:
333 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
334 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
335 # The return value on this call should be num_curves again. We could
336 # check it to make sure but if it *isn't* then.. what could we do?
337 # Abort the whole process, I suppose...? -exarkun
338 lib.EC_get_builtin_curves(builtin_curves, num_curves)
339 return set(
340 cls.from_nid(lib, c.nid)
341 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400342 return set()
343
344
345 @classmethod
346 def _get_elliptic_curves(cls, lib):
347 """
348 Get, cache, and return the curves supported by OpenSSL.
349
350 :param lib: The OpenSSL library binding object.
351
352 :return: A :py:type:`set` of ``cls`` instances giving the names of the
353 elliptic curves the underlying library supports.
354 """
355 if cls._curves is None:
356 cls._curves = cls._load_elliptic_curves(lib)
357 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400358
359
360 @classmethod
361 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400362 """
363 Instantiate a new :py:class:`_EllipticCurve` associated with the given
364 OpenSSL NID.
365
366 :param lib: The OpenSSL library binding object.
367
368 :param nid: The OpenSSL NID the resulting curve object will represent.
369 This must be a curve NID (and not, for example, a hash NID) or
370 subsequent operations will fail in unpredictable ways.
371 :type nid: :py:class:`int`
372
373 :return: The curve object.
374 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400375 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
376
377
378 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400379 """
380 :param _lib: The :py:mod:`cryptography` binding instance used to
381 interface with OpenSSL.
382
383 :param _nid: The OpenSSL NID identifying the curve this object
384 represents.
385 :type _nid: :py:class:`int`
386
387 :param name: The OpenSSL short name identifying the curve this object
388 represents.
389 :type name: :py:class:`unicode`
390 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400391 self._lib = lib
392 self._nid = nid
393 self.name = name
394
395
396 def __repr__(self):
397 return "<Curve %r>" % (self.name,)
398
399
400 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400401 """
402 Create a new OpenSSL EC_KEY structure initialized to use this curve.
403
404 The structure is automatically garbage collected when the Python object
405 is garbage collected.
406 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400407 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
408 return _ffi.gc(key, _lib.EC_KEY_free)
409
410
411
412def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400413 """
414 Return a set of objects representing the elliptic curves supported in the
415 OpenSSL build in use.
416
417 The curve objects have a :py:class:`unicode` ``name`` attribute by which
418 they identify themselves.
419
420 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400421 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
422 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400423 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400424 return _EllipticCurve._get_elliptic_curves(_lib)
425
426
427
428def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400429 """
430 Return a single curve object selected by name.
431
432 See :py:func:`get_elliptic_curves` for information about curve objects.
433
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400434 :param name: The OpenSSL short name identifying the curve object to
435 retrieve.
436 :type name: :py:class:`unicode`
437
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400438 If the named curve is not supported then :py:class:`ValueError` is raised.
439 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400440 for curve in get_elliptic_curves():
441 if curve.name == name:
442 return curve
443 raise ValueError("unknown curve name", name)
444
445
446
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800447class X509Name(object):
448 def __init__(self, name):
449 """
450 Create a new X509Name, copying the given X509Name instance.
451
452 :param name: An X509Name object to copy
453 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500454 name = _lib.X509_NAME_dup(name._name)
455 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800456
457
458 def __setattr__(self, name, value):
459 if name.startswith('_'):
460 return super(X509Name, self).__setattr__(name, value)
461
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800462 # Note: we really do not want str subclasses here, so we do not use
463 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800464 if type(name) is not str:
465 raise TypeError("attribute name must be string, not '%.200s'" % (
466 type(value).__name__,))
467
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500468 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500469 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800470 try:
471 _raise_current_error()
472 except Error:
473 pass
474 raise AttributeError("No such attribute")
475
476 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500477 for i in range(_lib.X509_NAME_entry_count(self._name)):
478 ent = _lib.X509_NAME_get_entry(self._name, i)
479 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
480 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800481 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500482 ent = _lib.X509_NAME_delete_entry(self._name, i)
483 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800484 break
485
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500486 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800487 value = value.encode('utf-8')
488
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500489 add_result = _lib.X509_NAME_add_entry_by_NID(
490 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800491 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500492 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800493
494
495 def __getattr__(self, name):
496 """
497 Find attribute. An X509Name object has the following attributes:
498 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
499 organization (alias O), organizationalUnit (alias OU), commonName (alias
500 CN) and more...
501 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500502 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500503 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800504 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
505 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
506 # push something onto the error queue. If we don't clean that up
507 # now, someone else will bump into it later and be quite confused.
508 # See lp#314814.
509 try:
510 _raise_current_error()
511 except Error:
512 pass
513 return super(X509Name, self).__getattr__(name)
514
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500515 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800516 if entry_index == -1:
517 return None
518
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500519 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
520 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800521
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500522 result_buffer = _ffi.new("unsigned char**")
523 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800524 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500525 # TODO: This is untested.
526 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800527
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700528 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500529 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700530 finally:
531 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500532 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800533 return result
534
535
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500536 def _cmp(op):
537 def f(self, other):
538 if not isinstance(other, X509Name):
539 return NotImplemented
540 result = _lib.X509_NAME_cmp(self._name, other._name)
541 return op(result, 0)
542 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800543
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500544 __eq__ = _cmp(__eq__)
545 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800546
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500547 __lt__ = _cmp(__lt__)
548 __le__ = _cmp(__le__)
549
550 __gt__ = _cmp(__gt__)
551 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800552
553 def __repr__(self):
554 """
555 String representation of an X509Name
556 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500557 result_buffer = _ffi.new("char[]", 512);
558 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800559 self._name, result_buffer, len(result_buffer))
560
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500561 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500562 # TODO: This is untested.
563 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800564
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500565 return "<X509Name object '%s'>" % (
566 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800567
568
569 def hash(self):
570 """
571 Return the hash value of this name
572
573 :return: None
574 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500575 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800576
577
578 def der(self):
579 """
580 Return the DER encoding of this name
581
582 :return: A :py:class:`bytes` instance giving the DER encoded form of
583 this name.
584 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500585 result_buffer = _ffi.new('unsigned char**')
586 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800587 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500588 # TODO: This is untested.
589 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800590
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500591 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
592 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800593 return string_result
594
595
596 def get_components(self):
597 """
598 Returns the split-up components of this name.
599
600 :return: List of tuples (name, value).
601 """
602 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500603 for i in range(_lib.X509_NAME_entry_count(self._name)):
604 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800605
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500606 fname = _lib.X509_NAME_ENTRY_get_object(ent)
607 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800608
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500609 nid = _lib.OBJ_obj2nid(fname)
610 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800611
612 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500613 _ffi.string(name),
614 _ffi.string(
615 _lib.ASN1_STRING_data(fval),
616 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800617
618 return result
619X509NameType = X509Name
620
621
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800622class X509Extension(object):
623 def __init__(self, type_name, critical, value, subject=None, issuer=None):
624 """
625 :param typename: The name of the extension to create.
626 :type typename: :py:data:`str`
627
628 :param critical: A flag indicating whether this is a critical extension.
629
630 :param value: The value of the extension.
631 :type value: :py:data:`str`
632
633 :param subject: Optional X509 cert to use as subject.
634 :type subject: :py:class:`X509`
635
636 :param issuer: Optional X509 cert to use as issuer.
637 :type issuer: :py:class:`X509`
638
639 :return: The X509Extension object
640 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500641 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800642
643 # A context is necessary for any extension which uses the r2i conversion
644 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
645 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500646 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800647
648 # We have no configuration database - but perhaps we should (some
649 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500650 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800651
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800652 # Initialize the subject and issuer, if appropriate. ctx is a local,
653 # and as far as I can tell none of the X509V3_* APIs invoked here steal
654 # any references, so no need to mess with reference counts or duplicates.
655 if issuer is not None:
656 if not isinstance(issuer, X509):
657 raise TypeError("issuer must be an X509 instance")
658 ctx.issuer_cert = issuer._x509
659 if subject is not None:
660 if not isinstance(subject, X509):
661 raise TypeError("subject must be an X509 instance")
662 ctx.subject_cert = subject._x509
663
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800664 if critical:
665 # There are other OpenSSL APIs which would let us pass in critical
666 # separately, but they're harder to use, and since value is already
667 # a pile of crappy junk smuggling a ton of utterly important
668 # structured data, what's the point of trying to avoid nasty stuff
669 # with strings? (However, X509V3_EXT_i2d in particular seems like it
670 # would be a better API to invoke. I do not know where to get the
671 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500672 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800673
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500674 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
675 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800676 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500677 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800678
679
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400680 @property
681 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500682 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400683
684 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500685 _lib.GEN_EMAIL: "email",
686 _lib.GEN_DNS: "DNS",
687 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400688 }
689
690 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500691 method = _lib.X509V3_EXT_get(self._extension)
692 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500693 # TODO: This is untested.
694 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400695 payload = self._extension.value.data
696 length = self._extension.value.length
697
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500698 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400699 payloadptr[0] = payload
700
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500701 if method.it != _ffi.NULL:
702 ptr = _lib.ASN1_ITEM_ptr(method.it)
703 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
704 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400705 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500706 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400707 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500708 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400709
710 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500711 for i in range(_lib.sk_GENERAL_NAME_num(names)):
712 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400713 try:
714 label = self._prefixes[name.type]
715 except KeyError:
716 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500717 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500718 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400719 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500720 value = _native(
721 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
722 parts.append(label + ":" + value)
723 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400724
725
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800726 def __str__(self):
727 """
728 :return: a nice text representation of the extension
729 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500730 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400731 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800732
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400733 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500734 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800735 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500736 # TODO: This is untested.
737 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800738
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500739 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800740
741
742 def get_critical(self):
743 """
744 Returns the critical field of the X509Extension
745
746 :return: The critical field.
747 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500748 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800749
750
751 def get_short_name(self):
752 """
753 Returns the short version of the type name of the X509Extension
754
755 :return: The short type name.
756 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500757 obj = _lib.X509_EXTENSION_get_object(self._extension)
758 nid = _lib.OBJ_obj2nid(obj)
759 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800760
761
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800762 def get_data(self):
763 """
764 Returns the data of the X509Extension
765
766 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
767 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500768 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
769 string_result = _ffi.cast('ASN1_STRING*', octet_result)
770 char_result = _lib.ASN1_STRING_data(string_result)
771 result_length = _lib.ASN1_STRING_length(string_result)
772 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800773
774X509ExtensionType = X509Extension
775
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800776
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800777class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800778 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500779 req = _lib.X509_REQ_new()
780 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800781
782
783 def set_pubkey(self, pkey):
784 """
785 Set the public key of the certificate request
786
787 :param pkey: The public key to use
788 :return: None
789 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500790 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800791 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500792 # TODO: This is untested.
793 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800794
795
796 def get_pubkey(self):
797 """
798 Get the public key from the certificate request
799
800 :return: The public key
801 """
802 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500803 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
804 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500805 # TODO: This is untested.
806 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500807 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800808 pkey._only_public = True
809 return pkey
810
811
812 def set_version(self, version):
813 """
814 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
815 request.
816
817 :param version: The version number
818 :return: None
819 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500820 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800821 if not set_result:
822 _raise_current_error()
823
824
825 def get_version(self):
826 """
827 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
828 request.
829
830 :return: an integer giving the value of the version subfield
831 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500832 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800833
834
835 def get_subject(self):
836 """
837 Create an X509Name object for the subject of the certificate request
838
839 :return: An X509Name object
840 """
841 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500842 name._name = _lib.X509_REQ_get_subject_name(self._req)
843 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500844 # TODO: This is untested.
845 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800846
847 # The name is owned by the X509Req structure. As long as the X509Name
848 # Python object is alive, keep the X509Req Python object alive.
849 name._owner = self
850
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800851 return name
852
853
854 def add_extensions(self, extensions):
855 """
856 Add extensions to the request.
857
858 :param extensions: a sequence of X509Extension objects
859 :return: None
860 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500861 stack = _lib.sk_X509_EXTENSION_new_null()
862 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500863 # TODO: This is untested.
864 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800865
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500866 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800867
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800868 for ext in extensions:
869 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800870 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800871
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800872 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500873 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800874
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500875 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800876 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500877 # TODO: This is untested.
878 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800879
880
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800881 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800882 """
883 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800884
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500885 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800886 """
887 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500888 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500889 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800890 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500891 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800892 exts.append(ext)
893 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800894
895
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800896 def sign(self, pkey, digest):
897 """
898 Sign the certificate request using the supplied key and digest
899
900 :param pkey: The key to sign with
901 :param digest: The message digest to use
902 :return: None
903 """
904 if pkey._only_public:
905 raise ValueError("Key has only public part")
906
907 if not pkey._initialized:
908 raise ValueError("Key is uninitialized")
909
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500910 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500911 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800912 raise ValueError("No such digest method")
913
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500914 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800915 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500916 # TODO: This is untested.
917 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800918
919
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800920 def verify(self, pkey):
921 """
922 Verifies a certificate request using the supplied public key
923
924 :param key: a public key
925 :return: True if the signature is correct.
926
927 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
928 problem verifying the signature.
929 """
930 if not isinstance(pkey, PKey):
931 raise TypeError("pkey must be a PKey instance")
932
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500933 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800934 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500935 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800936
937 return result
938
939
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800940X509ReqType = X509Req
941
942
943
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800944class X509(object):
945 def __init__(self):
946 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500947 x509 = _lib.X509_new()
948 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800949
950
951 def set_version(self, version):
952 """
953 Set version number of the certificate
954
955 :param version: The version number
956 :type version: :py:class:`int`
957
958 :return: None
959 """
960 if not isinstance(version, int):
961 raise TypeError("version must be an integer")
962
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500963 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800964
965
966 def get_version(self):
967 """
968 Return version number of the certificate
969
970 :return: Version number as a Python integer
971 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500972 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800973
974
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800975 def get_pubkey(self):
976 """
977 Get the public key of the certificate
978
979 :return: The public key
980 """
981 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500982 pkey._pkey = _lib.X509_get_pubkey(self._x509)
983 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800984 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500985 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800986 pkey._only_public = True
987 return pkey
988
989
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800990 def set_pubkey(self, pkey):
991 """
992 Set the public key of the certificate
993
994 :param pkey: The public key
995
996 :return: None
997 """
998 if not isinstance(pkey, PKey):
999 raise TypeError("pkey must be a PKey instance")
1000
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001001 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001002 if not set_result:
1003 _raise_current_error()
1004
1005
1006 def sign(self, pkey, digest):
1007 """
1008 Sign the certificate using the supplied key and digest
1009
1010 :param pkey: The key to sign with
1011 :param digest: The message digest to use
1012 :return: None
1013 """
1014 if not isinstance(pkey, PKey):
1015 raise TypeError("pkey must be a PKey instance")
1016
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001017 if pkey._only_public:
1018 raise ValueError("Key only has public part")
1019
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -08001020 if not pkey._initialized:
1021 raise ValueError("Key is uninitialized")
1022
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001023 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001024 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001025 raise ValueError("No such digest method")
1026
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001027 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001028 if not sign_result:
1029 _raise_current_error()
1030
1031
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001032 def get_signature_algorithm(self):
1033 """
1034 Retrieve the signature algorithm used in the certificate
1035
1036 :return: A byte string giving the name of the signature algorithm used in
1037 the certificate.
1038 :raise ValueError: If the signature algorithm is undefined.
1039 """
1040 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001041 nid = _lib.OBJ_obj2nid(alg)
1042 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001043 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001044 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001045
1046
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001047 def digest(self, digest_name):
1048 """
1049 Return the digest of the X509 object.
1050
1051 :param digest_name: The name of the digest algorithm to use.
1052 :type digest_name: :py:class:`bytes`
1053
1054 :return: The digest of the object
1055 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001056 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001057 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001058 raise ValueError("No such digest method")
1059
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001060 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1061 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001062 result_length[0] = len(result_buffer)
1063
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001064 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001065 self._x509, digest, result_buffer, result_length)
1066
1067 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001068 # TODO: This is untested.
1069 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001070
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001071 return b":".join([
1072 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001073 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001074
1075
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001076 def subject_name_hash(self):
1077 """
1078 Return the hash of the X509 subject.
1079
1080 :return: The hash of the subject.
1081 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001082 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001083
1084
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001085 def set_serial_number(self, serial):
1086 """
1087 Set serial number of the certificate
1088
1089 :param serial: The serial number
1090 :type serial: :py:class:`int`
1091
1092 :return: None
1093 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001094 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001095 raise TypeError("serial must be an integer")
1096
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001097 hex_serial = hex(serial)[2:]
1098 if not isinstance(hex_serial, bytes):
1099 hex_serial = hex_serial.encode('ascii')
1100
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001101 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001102
1103 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1104 # it. If bignum is still NULL after this call, then the return value is
1105 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001106 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001107
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001108 if bignum_serial[0] == _ffi.NULL:
1109 set_result = _lib.ASN1_INTEGER_set(
1110 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001111 if set_result:
1112 # TODO Not tested
1113 _raise_current_error()
1114 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001115 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1116 _lib.BN_free(bignum_serial[0])
1117 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001118 # TODO Not tested
1119 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001120 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1121 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001122 if not set_result:
1123 # TODO Not tested
1124 _raise_current_error()
1125
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001126
1127 def get_serial_number(self):
1128 """
1129 Return serial number of the certificate
1130
1131 :return: Serial number as a Python integer
1132 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001133 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1134 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001135 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001136 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001137 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001138 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001139 serial = int(hexstring_serial, 16)
1140 return serial
1141 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001142 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001143 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001144 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001145
1146
1147 def gmtime_adj_notAfter(self, amount):
1148 """
1149 Adjust the time stamp for when the certificate stops being valid
1150
1151 :param amount: The number of seconds by which to adjust the ending
1152 validity time.
1153 :type amount: :py:class:`int`
1154
1155 :return: None
1156 """
1157 if not isinstance(amount, int):
1158 raise TypeError("amount must be an integer")
1159
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001160 notAfter = _lib.X509_get_notAfter(self._x509)
1161 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001162
1163
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001164 def gmtime_adj_notBefore(self, amount):
1165 """
1166 Change the timestamp for when the certificate starts being valid to the current
1167 time plus an offset.
1168
1169 :param amount: The number of seconds by which to adjust the starting validity
1170 time.
1171 :return: None
1172 """
1173 if not isinstance(amount, int):
1174 raise TypeError("amount must be an integer")
1175
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001176 notBefore = _lib.X509_get_notBefore(self._x509)
1177 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001178
1179
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001180 def has_expired(self):
1181 """
1182 Check whether the certificate has expired.
1183
1184 :return: True if the certificate has expired, false otherwise
1185 """
1186 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001187 notAfter = _lib.X509_get_notAfter(self._x509)
1188 return _lib.ASN1_UTCTIME_cmp_time_t(
1189 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001190
1191
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001192 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001193 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001194
1195
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001196 def get_notBefore(self):
1197 """
1198 Retrieve the time stamp for when the certificate starts being valid
1199
1200 :return: A string giving the timestamp, in the format::
1201
1202 YYYYMMDDhhmmssZ
1203 YYYYMMDDhhmmss+hhmm
1204 YYYYMMDDhhmmss-hhmm
1205
1206 or None if there is no value set.
1207 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001208 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001209
1210
1211 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001212 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001213
1214
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001215 def set_notBefore(self, when):
1216 """
1217 Set the time stamp for when the certificate starts being valid
1218
1219 :param when: A string giving the timestamp, in the format:
1220
1221 YYYYMMDDhhmmssZ
1222 YYYYMMDDhhmmss+hhmm
1223 YYYYMMDDhhmmss-hhmm
1224 :type when: :py:class:`bytes`
1225
1226 :return: None
1227 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001228 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001229
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001230
1231 def get_notAfter(self):
1232 """
1233 Retrieve the time stamp for when the certificate stops being valid
1234
1235 :return: A string giving the timestamp, in the format::
1236
1237 YYYYMMDDhhmmssZ
1238 YYYYMMDDhhmmss+hhmm
1239 YYYYMMDDhhmmss-hhmm
1240
1241 or None if there is no value set.
1242 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001243 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001244
1245
1246 def set_notAfter(self, when):
1247 """
1248 Set the time stamp for when the certificate stops being valid
1249
1250 :param when: A string giving the timestamp, in the format:
1251
1252 YYYYMMDDhhmmssZ
1253 YYYYMMDDhhmmss+hhmm
1254 YYYYMMDDhhmmss-hhmm
1255 :type when: :py:class:`bytes`
1256
1257 :return: None
1258 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001259 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001260
1261
1262 def _get_name(self, which):
1263 name = X509Name.__new__(X509Name)
1264 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001265 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001266 # TODO: This is untested.
1267 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001268
1269 # The name is owned by the X509 structure. As long as the X509Name
1270 # Python object is alive, keep the X509 Python object alive.
1271 name._owner = self
1272
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001273 return name
1274
1275
1276 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001277 if not isinstance(name, X509Name):
1278 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001279 set_result = which(self._x509, name._name)
1280 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001281 # TODO: This is untested.
1282 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001283
1284
1285 def get_issuer(self):
1286 """
1287 Create an X509Name object for the issuer of the certificate
1288
1289 :return: An X509Name object
1290 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001291 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001292
1293
1294 def set_issuer(self, issuer):
1295 """
1296 Set the issuer of the certificate
1297
1298 :param issuer: The issuer name
1299 :type issuer: :py:class:`X509Name`
1300
1301 :return: None
1302 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001303 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001304
1305
1306 def get_subject(self):
1307 """
1308 Create an X509Name object for the subject of the certificate
1309
1310 :return: An X509Name object
1311 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001312 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001313
1314
1315 def set_subject(self, subject):
1316 """
1317 Set the subject of the certificate
1318
1319 :param subject: The subject name
1320 :type subject: :py:class:`X509Name`
1321 :return: None
1322 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001323 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001324
1325
1326 def get_extension_count(self):
1327 """
1328 Get the number of extensions on the certificate.
1329
1330 :return: The number of extensions as an integer.
1331 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001332 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001333
1334
1335 def add_extensions(self, extensions):
1336 """
1337 Add extensions to the certificate.
1338
1339 :param extensions: a sequence of X509Extension objects
1340 :return: None
1341 """
1342 for ext in extensions:
1343 if not isinstance(ext, X509Extension):
1344 raise ValueError("One of the elements is not an X509Extension")
1345
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001346 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001347 if not add_result:
1348 _raise_current_error()
1349
1350
1351 def get_extension(self, index):
1352 """
1353 Get a specific extension of the certificate by index.
1354
1355 :param index: The index of the extension to retrieve.
1356 :return: The X509Extension object at the specified index.
1357 """
1358 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001359 ext._extension = _lib.X509_get_ext(self._x509, index)
1360 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001361 raise IndexError("extension index out of bounds")
1362
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001363 extension = _lib.X509_EXTENSION_dup(ext._extension)
1364 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001365 return ext
1366
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001367X509Type = X509
1368
1369
1370
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001371class X509Store(object):
1372 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001373 store = _lib.X509_STORE_new()
1374 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001375
1376
1377 def add_cert(self, cert):
1378 if not isinstance(cert, X509):
1379 raise TypeError()
1380
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001381 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001382 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001383 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001384
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001385
1386X509StoreType = X509Store
1387
1388
1389
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07001390class X509StoreContext(object):
1391 """
1392 An X.509 store context.
1393
1394 A :py:class:`X509StoreContext` is used to verify a certificate in some
1395 context in conjunction with :py:func:`verify_cert`. The information
1396 encapsulated in this object includes, but is not limited to, a set of
1397 trusted certificates, verification parameters and revoked certificates.
1398
1399 :param store: A :py:class:`X509Store` of trusted certificates.
1400 :param cert: An :py:class:`X509` certificate to be validated during a
1401 subsequent call to :py:func:`verify_cert`.
1402 """
1403
1404 def __init__(self, store, cert):
1405 store_ctx = _lib.X509_STORE_CTX_new()
1406 self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
1407 self._store = store
1408 self._cert = cert
1409
1410 def _init(self):
1411 """
1412 Set up the store context for a subsequent verification operation.
1413 """
1414 ret = _lib.X509_STORE_CTX_init(self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL)
1415 if ret <= 0:
1416 _raise_current_error()
1417
1418 def _cleanup(self):
1419 """
1420 Internally cleans up the store context.
1421
1422 The store context can then be reused with a new call to
1423 :py:meth:`init`.
1424 """
1425 _lib.X509_STORE_CTX_cleanup(self._store_ctx)
1426
1427
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001428def load_certificate(type, buffer):
1429 """
1430 Load a certificate from a buffer
1431
1432 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1433
1434 :param buffer: The buffer the certificate is stored in
1435 :type buffer: :py:class:`bytes`
1436
1437 :return: The X509 object
1438 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001439 if isinstance(buffer, _text_type):
1440 buffer = buffer.encode("ascii")
1441
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001442 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001443
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001444 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001445 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001446 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001447 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001448 else:
1449 raise ValueError(
1450 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001451
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001452 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001453 _raise_current_error()
1454
1455 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001456 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001457 return cert
1458
1459
1460def dump_certificate(type, cert):
1461 """
1462 Dump a certificate to a buffer
1463
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001464 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1465 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001466 :param cert: The certificate to dump
1467 :return: The buffer with the dumped certificate in
1468 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001469 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001470
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001471 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001472 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001473 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001474 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001475 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001476 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001477 else:
1478 raise ValueError(
1479 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1480 "FILETYPE_TEXT")
1481
1482 return _bio_to_string(bio)
1483
1484
1485
1486def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1487 """
1488 Dump a private key to a buffer
1489
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001490 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1491 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001492 :param pkey: The PKey to dump
1493 :param cipher: (optional) if encrypted PEM format, the cipher to
1494 use
1495 :param passphrase: (optional) if encrypted PEM format, this can be either
1496 the passphrase to use, or a callback for providing the
1497 passphrase.
1498 :return: The buffer with the dumped key in
1499 :rtype: :py:data:`str`
1500 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001501 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001502
1503 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001504 if passphrase is None:
1505 raise TypeError(
1506 "if a value is given for cipher "
1507 "one must also be given for passphrase")
1508 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001509 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001510 raise ValueError("Invalid cipher name")
1511 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001512 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001513
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001514 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001515 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001516 result_code = _lib.PEM_write_bio_PrivateKey(
1517 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001518 helper.callback, helper.callback_args)
1519 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001520 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001521 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001522 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001523 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1524 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001525 # TODO RSA_free(rsa)?
1526 else:
1527 raise ValueError(
1528 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1529 "FILETYPE_TEXT")
1530
1531 if result_code == 0:
1532 _raise_current_error()
1533
1534 return _bio_to_string(bio)
1535
1536
1537
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001538def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001539 copy = _lib.X509_REVOKED_new()
1540 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001541 # TODO: This is untested.
1542 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001543
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001544 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001545 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001546 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001547
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001548 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001549 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001550 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001551
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001552 if original.extensions != _ffi.NULL:
1553 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1554 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1555 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1556 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1557 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001558 copy.extensions = extension_stack
1559
1560 copy.sequence = original.sequence
1561 return copy
1562
1563
1564
1565class Revoked(object):
1566 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1567 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1568 # OCSP_crl_reason_str. We use the latter, just like the command line
1569 # program.
1570 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001571 b"unspecified",
1572 b"keyCompromise",
1573 b"CACompromise",
1574 b"affiliationChanged",
1575 b"superseded",
1576 b"cessationOfOperation",
1577 b"certificateHold",
1578 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001579 ]
1580
1581 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001582 revoked = _lib.X509_REVOKED_new()
1583 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001584
1585
1586 def set_serial(self, hex_str):
1587 """
1588 Set the serial number of a revoked Revoked structure
1589
1590 :param hex_str: The new serial number.
1591 :type hex_str: :py:data:`str`
1592 :return: None
1593 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001594 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1595 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001596 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001597 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001598 if not bn_result:
1599 raise ValueError("bad hex string")
1600
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001601 asn1_serial = _ffi.gc(
1602 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1603 _lib.ASN1_INTEGER_free)
1604 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001605
1606
1607 def get_serial(self):
1608 """
1609 Return the serial number of a Revoked structure
1610
1611 :return: The serial number as a string
1612 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001613 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001614
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001615 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001616 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001617 # TODO: This is untested.
1618 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001619
1620 return _bio_to_string(bio)
1621
1622
1623 def _delete_reason(self):
1624 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001625 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1626 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1627 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1628 _lib.X509_EXTENSION_free(ext)
1629 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001630 break
1631
1632
1633 def set_reason(self, reason):
1634 """
1635 Set the reason of a Revoked object.
1636
1637 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1638
1639 :param reason: The reason string.
1640 :type reason: :py:class:`str` or :py:class:`NoneType`
1641 :return: None
1642 """
1643 if reason is None:
1644 self._delete_reason()
1645 elif not isinstance(reason, bytes):
1646 raise TypeError("reason must be None or a byte string")
1647 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001648 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001649 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1650
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001651 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1652 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001653 # TODO: This is untested.
1654 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001655 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001656
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001657 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1658 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001659 # TODO: This is untested.
1660 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001661
1662 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001663 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1664 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001665
1666 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001667 # TODO: This is untested.
1668 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001669
1670
1671 def get_reason(self):
1672 """
1673 Return the reason of a Revoked object.
1674
1675 :return: The reason as a string
1676 """
1677 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001678 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1679 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1680 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001681 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001682
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001683 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001684 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001685 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001686 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001687 # TODO: This is untested.
1688 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001689
1690 return _bio_to_string(bio)
1691
1692
1693 def all_reasons(self):
1694 """
1695 Return a list of all the supported reason strings.
1696
1697 :return: A list of reason strings.
1698 """
1699 return self._crl_reasons[:]
1700
1701
1702 def set_rev_date(self, when):
1703 """
1704 Set the revocation timestamp
1705
1706 :param when: A string giving the timestamp, in the format:
1707
1708 YYYYMMDDhhmmssZ
1709 YYYYMMDDhhmmss+hhmm
1710 YYYYMMDDhhmmss-hhmm
1711
1712 :return: None
1713 """
1714 return _set_asn1_time(self._revoked.revocationDate, when)
1715
1716
1717 def get_rev_date(self):
1718 """
1719 Retrieve the revocation date
1720
1721 :return: A string giving the timestamp, in the format:
1722
1723 YYYYMMDDhhmmssZ
1724 YYYYMMDDhhmmss+hhmm
1725 YYYYMMDDhhmmss-hhmm
1726 """
1727 return _get_asn1_time(self._revoked.revocationDate)
1728
1729
1730
1731class CRL(object):
1732 def __init__(self):
1733 """
1734 Create a new empty CRL object.
1735 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001736 crl = _lib.X509_CRL_new()
1737 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001738
1739
1740 def get_revoked(self):
1741 """
1742 Return revoked portion of the CRL structure (by value not reference).
1743
1744 :return: A tuple of Revoked objects.
1745 """
1746 results = []
1747 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001748 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1749 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001750 revoked_copy = _X509_REVOKED_dup(revoked)
1751 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001752 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001753 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001754 if results:
1755 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001756
1757
1758 def add_revoked(self, revoked):
1759 """
1760 Add a revoked (by value not reference) to the CRL structure
1761
1762 :param revoked: The new revoked.
1763 :type revoked: :class:`X509`
1764
1765 :return: None
1766 """
1767 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001768 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001769 # TODO: This is untested.
1770 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001771
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001772 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001773 if add_result == 0:
1774 # TODO: This is untested.
1775 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001776
1777
1778 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1779 """
1780 export a CRL as a string
1781
1782 :param cert: Used to sign CRL.
1783 :type cert: :class:`X509`
1784
1785 :param key: Used to sign CRL.
1786 :type key: :class:`PKey`
1787
1788 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1789
1790 :param days: The number of days until the next update of this CRL.
1791 :type days: :py:data:`int`
1792
1793 :return: :py:data:`str`
1794 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001795 if not isinstance(cert, X509):
1796 raise TypeError("cert must be an X509 instance")
1797 if not isinstance(key, PKey):
1798 raise TypeError("key must be a PKey instance")
1799 if not isinstance(type, int):
1800 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001801
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001802 bio = _lib.BIO_new(_lib.BIO_s_mem())
1803 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001804 # TODO: This is untested.
1805 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001806
1807 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001808 sometime = _lib.ASN1_TIME_new()
1809 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001810 # TODO: This is untested.
1811 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001812
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001813 _lib.X509_gmtime_adj(sometime, 0)
1814 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001815
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001816 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1817 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001818
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001819 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001820
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001821 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001822 if not sign_result:
1823 _raise_current_error()
1824
1825 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001826 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001827 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001828 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001829 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001830 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001831 else:
1832 raise ValueError(
1833 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1834
1835 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001836 # TODO: This is untested.
1837 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001838
1839 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001840CRLType = CRL
1841
1842
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001843
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001844class PKCS7(object):
1845 def type_is_signed(self):
1846 """
1847 Check if this NID_pkcs7_signed object
1848
1849 :return: True if the PKCS7 is of type signed
1850 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001851 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001852 return True
1853 return False
1854
1855
1856 def type_is_enveloped(self):
1857 """
1858 Check if this NID_pkcs7_enveloped object
1859
1860 :returns: True if the PKCS7 is of type enveloped
1861 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001862 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001863 return True
1864 return False
1865
1866
1867 def type_is_signedAndEnveloped(self):
1868 """
1869 Check if this NID_pkcs7_signedAndEnveloped object
1870
1871 :returns: True if the PKCS7 is of type signedAndEnveloped
1872 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001873 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001874 return True
1875 return False
1876
1877
1878 def type_is_data(self):
1879 """
1880 Check if this NID_pkcs7_data object
1881
1882 :return: True if the PKCS7 is of type data
1883 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001884 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001885 return True
1886 return False
1887
1888
1889 def get_type_name(self):
1890 """
1891 Returns the type name of the PKCS7 structure
1892
1893 :return: A string with the typename
1894 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001895 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1896 string_type = _lib.OBJ_nid2sn(nid)
1897 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001898
1899PKCS7Type = PKCS7
1900
1901
1902
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001903class PKCS12(object):
1904 def __init__(self):
1905 self._pkey = None
1906 self._cert = None
1907 self._cacerts = None
1908 self._friendlyname = None
1909
1910
1911 def get_certificate(self):
1912 """
1913 Return certificate portion of the PKCS12 structure
1914
1915 :return: X509 object containing the certificate
1916 """
1917 return self._cert
1918
1919
1920 def set_certificate(self, cert):
1921 """
1922 Replace the certificate portion of the PKCS12 structure
1923
1924 :param cert: The new certificate.
1925 :type cert: :py:class:`X509` or :py:data:`None`
1926 :return: None
1927 """
1928 if not isinstance(cert, X509):
1929 raise TypeError("cert must be an X509 instance")
1930 self._cert = cert
1931
1932
1933 def get_privatekey(self):
1934 """
1935 Return private key portion of the PKCS12 structure
1936
1937 :returns: PKey object containing the private key
1938 """
1939 return self._pkey
1940
1941
1942 def set_privatekey(self, pkey):
1943 """
1944 Replace or set the certificate portion of the PKCS12 structure
1945
1946 :param pkey: The new private key.
1947 :type pkey: :py:class:`PKey`
1948 :return: None
1949 """
1950 if not isinstance(pkey, PKey):
1951 raise TypeError("pkey must be a PKey instance")
1952 self._pkey = pkey
1953
1954
1955 def get_ca_certificates(self):
1956 """
1957 Return CA certificates within of the PKCS12 object
1958
1959 :return: A newly created tuple containing the CA certificates in the chain,
1960 if any are present, or None if no CA certificates are present.
1961 """
1962 if self._cacerts is not None:
1963 return tuple(self._cacerts)
1964
1965
1966 def set_ca_certificates(self, cacerts):
1967 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08001968 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001969
1970 :param cacerts: The new CA certificates.
1971 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1972 :return: None
1973 """
1974 if cacerts is None:
1975 self._cacerts = None
1976 else:
1977 cacerts = list(cacerts)
1978 for cert in cacerts:
1979 if not isinstance(cert, X509):
1980 raise TypeError("iterable must only contain X509 instances")
1981 self._cacerts = cacerts
1982
1983
1984 def set_friendlyname(self, name):
1985 """
1986 Replace or set the certificate portion of the PKCS12 structure
1987
1988 :param name: The new friendly name.
1989 :type name: :py:class:`bytes`
1990 :return: None
1991 """
1992 if name is None:
1993 self._friendlyname = None
1994 elif not isinstance(name, bytes):
1995 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1996 self._friendlyname = name
1997
1998
1999 def get_friendlyname(self):
2000 """
2001 Return friendly name portion of the PKCS12 structure
2002
2003 :returns: String containing the friendlyname
2004 """
2005 return self._friendlyname
2006
2007
2008 def export(self, passphrase=None, iter=2048, maciter=1):
2009 """
2010 Dump a PKCS12 object as a string. See also "man PKCS12_create".
2011
2012 :param passphrase: used to encrypt the PKCS12
2013 :type passphrase: :py:data:`bytes`
2014
2015 :param iter: How many times to repeat the encryption
2016 :type iter: :py:data:`int`
2017
2018 :param maciter: How many times to repeat the MAC
2019 :type maciter: :py:data:`int`
2020
2021 :return: The string containing the PKCS12
2022 """
2023 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002024 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002025 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002026 cacerts = _lib.sk_X509_new_null()
2027 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002028 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002029 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002030
2031 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002032 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002033
2034 friendlyname = self._friendlyname
2035 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002036 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002037
2038 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002039 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002040 else:
2041 pkey = self._pkey._pkey
2042
2043 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002044 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002045 else:
2046 cert = self._cert._x509
2047
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002048 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002049 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002050 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2051 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002052 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002053 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002054 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002055 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002056
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002057 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002058 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002059 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002060
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002061PKCS12Type = PKCS12
2062
2063
2064
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002065class NetscapeSPKI(object):
2066 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002067 spki = _lib.NETSCAPE_SPKI_new()
2068 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002069
2070
2071 def sign(self, pkey, digest):
2072 """
2073 Sign the certificate request using the supplied key and digest
2074
2075 :param pkey: The key to sign with
2076 :param digest: The message digest to use
2077 :return: None
2078 """
2079 if pkey._only_public:
2080 raise ValueError("Key has only public part")
2081
2082 if not pkey._initialized:
2083 raise ValueError("Key is uninitialized")
2084
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002085 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002086 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002087 raise ValueError("No such digest method")
2088
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002089 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002090 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002091 # TODO: This is untested.
2092 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002093
2094
2095 def verify(self, key):
2096 """
2097 Verifies a certificate request using the supplied public key
2098
2099 :param key: a public key
2100 :return: True if the signature is correct.
2101 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2102 problem verifying the signature.
2103 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002104 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002105 if answer <= 0:
2106 _raise_current_error()
2107 return True
2108
2109
2110 def b64_encode(self):
2111 """
2112 Generate a base64 encoded string from an SPKI
2113
2114 :return: The base64 encoded string
2115 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002116 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2117 result = _ffi.string(encoded)
2118 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002119 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002120
2121
2122 def get_pubkey(self):
2123 """
2124 Get the public key of the certificate
2125
2126 :return: The public key
2127 """
2128 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002129 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2130 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002131 # TODO: This is untested.
2132 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002133 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002134 pkey._only_public = True
2135 return pkey
2136
2137
2138 def set_pubkey(self, pkey):
2139 """
2140 Set the public key of the certificate
2141
2142 :param pkey: The public key
2143 :return: None
2144 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002145 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002146 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002147 # TODO: This is untested.
2148 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002149NetscapeSPKIType = NetscapeSPKI
2150
2151
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002152class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002153 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002154 if type != FILETYPE_PEM and passphrase is not None:
2155 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002156 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002157 self._more_args = more_args
2158 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002159 self._problems = []
2160
2161
2162 @property
2163 def callback(self):
2164 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002165 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002166 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002167 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002168 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002169 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002170 else:
2171 raise TypeError("Last argument must be string or callable")
2172
2173
2174 @property
2175 def callback_args(self):
2176 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002177 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002178 elif isinstance(self._passphrase, bytes):
2179 return self._passphrase
2180 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002181 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002182 else:
2183 raise TypeError("Last argument must be string or callable")
2184
2185
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002186 def raise_if_problem(self, exceptionType=Error):
2187 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002188 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002189 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002190 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002191 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002192 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002193 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002194
2195
2196 def _read_passphrase(self, buf, size, rwflag, userdata):
2197 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002198 if self._more_args:
2199 result = self._passphrase(size, rwflag, userdata)
2200 else:
2201 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002202 if not isinstance(result, bytes):
2203 raise ValueError("String expected")
2204 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002205 if self._truncate:
2206 result = result[:size]
2207 else:
2208 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002209 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002210 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002211 return len(result)
2212 except Exception as e:
2213 self._problems.append(e)
2214 return 0
2215
2216
2217
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002218def load_privatekey(type, buffer, passphrase=None):
2219 """
2220 Load a private key from a buffer
2221
2222 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2223 :param buffer: The buffer the key is stored in
2224 :param passphrase: (optional) if encrypted PEM format, this can be
2225 either the passphrase to use, or a callback for
2226 providing the passphrase.
2227
2228 :return: The PKey object
2229 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002230 if isinstance(buffer, _text_type):
2231 buffer = buffer.encode("ascii")
2232
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002233 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002234
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002235 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002236 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002237 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2238 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002239 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002240 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002241 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002242 else:
2243 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2244
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002245 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002246 _raise_current_error()
2247
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002248 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002249 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002250 return pkey
2251
2252
2253
2254def dump_certificate_request(type, req):
2255 """
2256 Dump a certificate request to a buffer
2257
2258 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2259 :param req: The certificate request to dump
2260 :return: The buffer with the dumped certificate request in
2261 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002262 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002263
2264 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002265 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002266 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002267 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002268 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002269 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002270 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002271 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002272
2273 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002274 # TODO: This is untested.
2275 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002276
2277 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002278
2279
2280
2281def load_certificate_request(type, buffer):
2282 """
2283 Load a certificate request from a buffer
2284
2285 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2286 :param buffer: The buffer the certificate request is stored in
2287 :return: The X509Req object
2288 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002289 if isinstance(buffer, _text_type):
2290 buffer = buffer.encode("ascii")
2291
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002292 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002293
2294 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002295 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002296 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002297 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002298 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002299 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002300
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002301 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002302 # TODO: This is untested.
2303 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002304
2305 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002306 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002307 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002308
2309
2310
2311def sign(pkey, data, digest):
2312 """
2313 Sign data with a digest
2314
2315 :param pkey: Pkey to sign with
2316 :param data: data to be signed
2317 :param digest: message digest to use
2318 :return: signature
2319 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002320 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002321 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002322 raise ValueError("No such digest method")
2323
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002324 md_ctx = _ffi.new("EVP_MD_CTX*")
2325 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002326
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002327 _lib.EVP_SignInit(md_ctx, digest_obj)
2328 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002329
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002330 signature_buffer = _ffi.new("unsigned char[]", 512)
2331 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002332 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002333 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002334 md_ctx, signature_buffer, signature_length, pkey._pkey)
2335
2336 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002337 # TODO: This is untested.
2338 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002339
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002340 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002341
2342
2343
2344def verify(cert, signature, data, digest):
2345 """
2346 Verify a signature
2347
2348 :param cert: signing certificate (X509 object)
2349 :param signature: signature returned by sign function
2350 :param data: data to be verified
2351 :param digest: message digest to use
2352 :return: None if the signature is correct, raise exception otherwise
2353 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002354 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002355 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002356 raise ValueError("No such digest method")
2357
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002358 pkey = _lib.X509_get_pubkey(cert._x509)
2359 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002360 # TODO: This is untested.
2361 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002362 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002363
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002364 md_ctx = _ffi.new("EVP_MD_CTX*")
2365 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002366
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002367 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2368 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2369 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002370
2371 if verify_result != 1:
2372 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002373
2374
Stephen Holsapple0d9815f2014-08-27 19:36:53 -07002375def verify_cert(store_ctx):
2376 """
2377 Verify a certificate in a context.
2378
2379 :param store_ctx: The :py:class:`X509StoreContext` to verify.
2380 :raises: Error
2381 """
2382 store_ctx._init()
2383 ret = _lib.X509_verify_cert(store_ctx._store_ctx)
2384 store_ctx._cleanup()
2385 if ret <= 0:
2386 _raise_context_error(store_ctx)
2387
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002388
2389def load_crl(type, buffer):
2390 """
2391 Load a certificate revocation list from a buffer
2392
2393 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2394 :param buffer: The buffer the CRL is stored in
2395
2396 :return: The PKey object
2397 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002398 if isinstance(buffer, _text_type):
2399 buffer = buffer.encode("ascii")
2400
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002401 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002402
2403 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002404 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002405 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002406 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002407 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002408 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2409
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002410 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002411 _raise_current_error()
2412
2413 result = CRL.__new__(CRL)
2414 result._crl = crl
2415 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002416
2417
2418
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002419def load_pkcs7_data(type, buffer):
2420 """
2421 Load pkcs7 data from a buffer
2422
2423 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2424 :param buffer: The buffer with the pkcs7 data.
2425 :return: The PKCS7 object
2426 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002427 if isinstance(buffer, _text_type):
2428 buffer = buffer.encode("ascii")
2429
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002430 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002431
2432 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002433 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002434 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002435 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002436 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002437 # TODO: This is untested.
2438 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002439 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2440
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002441 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002442 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002443
2444 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002445 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002446 return pypkcs7
2447
2448
2449
Stephen Holsapple38482622014-04-05 20:29:34 -07002450def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002451 """
2452 Load a PKCS12 object from a buffer
2453
2454 :param buffer: The buffer the certificate is stored in
2455 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2456 :returns: The PKCS12 object
2457 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002458 if isinstance(buffer, _text_type):
2459 buffer = buffer.encode("ascii")
2460
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002461 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002462
Stephen Holsapple38482622014-04-05 20:29:34 -07002463 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2464 # password based encryption no password and a zero length password are two
2465 # different things, but OpenSSL implementation will try both to figure out
2466 # which one works.
2467 if not passphrase:
2468 passphrase = _ffi.NULL
2469
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002470 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2471 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002472 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002473 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002474
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002475 pkey = _ffi.new("EVP_PKEY**")
2476 cert = _ffi.new("X509**")
2477 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002478
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002479 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002480 if not parse_result:
2481 _raise_current_error()
2482
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002483 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002484
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002485 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2486 # queue for no particular reason. This error isn't interesting to anyone
2487 # outside this function. It's not even interesting to us. Get rid of it.
2488 try:
2489 _raise_current_error()
2490 except Error:
2491 pass
2492
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002493 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002494 pykey = None
2495 else:
2496 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002497 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002498
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002499 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002500 pycert = None
2501 friendlyname = None
2502 else:
2503 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002504 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002505
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002506 friendlyname_length = _ffi.new("int*")
2507 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2508 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2509 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002510 friendlyname = None
2511
2512 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002513 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002514 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002515 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002516 pycacerts.append(pycacert)
2517 if not pycacerts:
2518 pycacerts = None
2519
2520 pkcs12 = PKCS12.__new__(PKCS12)
2521 pkcs12._pkey = pykey
2522 pkcs12._cert = pycert
2523 pkcs12._cacerts = pycacerts
2524 pkcs12._friendlyname = friendlyname
2525 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002526
2527
2528def _initialize_openssl_threads(get_ident, Lock):
2529 import _ssl
2530 return
2531
2532 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2533
2534 def locking_function(mode, index, filename, line):
2535 if mode & _lib.CRYPTO_LOCK:
2536 locks[index].acquire()
2537 else:
2538 locks[index].release()
2539
2540 _lib.CRYPTO_set_id_callback(
2541 _ffi.callback("unsigned long (*)(void)", get_ident))
2542
2543 _lib.CRYPTO_set_locking_callback(
2544 _ffi.callback(
2545 "void (*)(int, int, const char*, int)", locking_function))
2546
2547
2548try:
2549 from thread import get_ident
2550 from threading import Lock
2551except ImportError:
2552 pass
2553else:
2554 _initialize_openssl_threads(get_ident, Lock)
2555 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002556
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002557# There are no direct unit tests for this initialization. It is tested
2558# indirectly since it is necessary for functions like dump_privatekey when
2559# using encryption.
2560#
2561# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2562# and some other similar tests may fail without this (though they may not if
2563# the Python runtime has already done some initialization of the underlying
2564# OpenSSL library (and is linked against the same one that cryptography is
2565# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002566_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002567
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002568# This is similar but exercised mainly by exception_from_error_queue. It calls
2569# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2570_lib.SSL_load_error_strings()