blob: 8d971f211a6c0525909588f4dc493f8f7811e12a [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05003from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
5
6from six import (
7 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -04008 text_type as _text_type,
9 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080010
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050011from OpenSSL._util import (
12 ffi as _ffi,
13 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050014 exception_from_error_queue as _exception_from_error_queue,
15 byte_string as _byte_string,
16 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080017
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050018FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
19FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080020
21# TODO This was an API mistake. OpenSSL has no such constant.
22FILETYPE_TEXT = 2 ** 16 - 1
23
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050024TYPE_RSA = _lib.EVP_PKEY_RSA
25TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080026
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080027
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050028class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050029 """
30 An error occurred in an `OpenSSL.crypto` API.
31 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050032
33
34_raise_current_error = partial(_exception_from_error_queue, Error)
35
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050036def _untested_error(where):
37 """
38 An OpenSSL API failed somehow. Additionally, the failure which was
39 encountered isn't one that's exercised by the test suite so future behavior
40 of pyOpenSSL is now somewhat less predictable.
41 """
42 raise RuntimeError("Unknown %s failure" % (where,))
43
44
45
46def _new_mem_buf(buffer=None):
47 """
48 Allocate a new OpenSSL memory BIO.
49
50 Arrange for the garbage collector to clean it up automatically.
51
52 :param buffer: None or some bytes to use to put into the BIO so that they
53 can be read out.
54 """
55 if buffer is None:
56 bio = _lib.BIO_new(_lib.BIO_s_mem())
57 free = _lib.BIO_free
58 else:
59 data = _ffi.new("char[]", buffer)
60 bio = _lib.BIO_new_mem_buf(data, len(buffer))
61 # Keep the memory alive as long as the bio is alive!
62 def free(bio, ref=data):
63 return _lib.BIO_free(bio)
64
65 if bio == _ffi.NULL:
66 # TODO: This is untested.
67 _raise_current_error()
68
69 bio = _ffi.gc(bio, free)
70 return bio
71
72
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050073
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080074def _bio_to_string(bio):
75 """
76 Copy the contents of an OpenSSL BIO object into a Python byte string.
77 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050078 result_buffer = _ffi.new('char**')
79 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
80 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080081
82
83
Jean-Paul Calderone57122982013-02-21 08:47:05 -080084def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050085 """
86 The the time value of an ASN1 time object.
87
88 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
89 castable to that type) which will have its value set.
90 @param when: A string representation of the desired time value.
91
92 @raise TypeError: If C{when} is not a L{bytes} string.
93 @raise ValueError: If C{when} does not represent a time in the required
94 format.
95 @raise RuntimeError: If the time value cannot be set for some other
96 (unspecified) reason.
97 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080098 if not isinstance(when, bytes):
99 raise TypeError("when must be a byte string")
100
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500101 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
102 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800103 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500104 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
105 _lib.ASN1_STRING_set(dummy, when, len(when))
106 check_result = _lib.ASN1_GENERALIZEDTIME_check(
107 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800108 if not check_result:
109 raise ValueError("Invalid string")
110 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500111 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800112
113
114
115def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500116 """
117 Retrieve the time value of an ASN1 time object.
118
119 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
120 that type) from which the time value will be retrieved.
121
122 @return: The time value from C{timestamp} as a L{bytes} string in a certain
123 format. Or C{None} if the object contains no time value.
124 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500125 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
126 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800127 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500128 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
129 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800130 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500131 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
132 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
133 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500134 # This may happen:
135 # - if timestamp was not an ASN1_TIME
136 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
137 # - if a copy of the time data from timestamp cannot be made for
138 # the newly allocated ASN1_GENERALIZEDTIME
139 #
140 # These are difficult to test. cffi enforces the ASN1_TIME type.
141 # Memory allocation failures are a pain to trigger
142 # deterministically.
143 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800144 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500145 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800146 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500147 string_data = _lib.ASN1_STRING_data(string_timestamp)
148 string_result = _ffi.string(string_data)
149 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800150 return string_result
151
152
153
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800154class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800155 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800156 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800157
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800158 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500159 pkey = _lib.EVP_PKEY_new()
160 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800161 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800162
163
164 def generate_key(self, type, bits):
165 """
166 Generate a key of a given type, with a given number of a bits
167
168 :param type: The key type (TYPE_RSA or TYPE_DSA)
169 :param bits: The number of bits
170
171 :return: None
172 """
173 if not isinstance(type, int):
174 raise TypeError("type must be an integer")
175
176 if not isinstance(bits, int):
177 raise TypeError("bits must be an integer")
178
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800179 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500180 exponent = _lib.BN_new()
181 exponent = _ffi.gc(exponent, _lib.BN_free)
182 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800183
184 if type == TYPE_RSA:
185 if bits <= 0:
186 raise ValueError("Invalid number of bits")
187
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500188 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800189
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500190 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500191 if result == 0:
192 # TODO: The test for this case is commented out. Different
193 # builds of OpenSSL appear to have different failure modes that
194 # make it hard to test. Visual inspection of the OpenSSL
195 # source reveals that a return value of 0 signals an error.
196 # Manual testing on a particular build of OpenSSL suggests that
197 # this is probably the appropriate way to handle those errors.
198 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800199
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500200 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800201 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500202 # TODO: It appears as though this can fail if an engine is in
203 # use which does not support RSA.
204 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800205
206 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500207 dsa = _lib.DSA_generate_parameters(
208 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
209 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500210 # TODO: This is untested.
211 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500212 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500213 # TODO: This is untested.
214 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500215 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500216 # TODO: This is untested.
217 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800218 else:
219 raise Error("No such key type")
220
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800221 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800222
223
224 def check(self):
225 """
226 Check the consistency of an RSA private key.
227
228 :return: True if key is consistent.
229 :raise Error: if the key is inconsistent.
230 :raise TypeError: if the key is of a type which cannot be checked.
231 Only RSA keys can currently be checked.
232 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800233 if self._only_public:
234 raise TypeError("public key only")
235
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500236 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800237 raise TypeError("key type unsupported")
238
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500239 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
240 rsa = _ffi.gc(rsa, _lib.RSA_free)
241 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800242 if result:
243 return True
244 _raise_current_error()
245
246
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800247 def type(self):
248 """
249 Returns the type of the key
250
251 :return: The type of the key.
252 """
253 return self._pkey.type
254
255
256 def bits(self):
257 """
258 Returns the number of bits of the key
259
260 :return: The number of bits of the key.
261 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500262 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800263PKeyType = PKey
264
265
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800266
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400267class _EllipticCurve(object):
268 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400269 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400270
271 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
272 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
273 instances each of which represents one curve supported by the system.
274 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400275 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400276 _curves = None
277
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400278 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400279 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400280 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400281 """
282 Implement cooperation with the right-hand side argument of ``!=``.
283
284 Python 3 seems to have dropped this cooperation in this very narrow
285 circumstance.
286 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400287 if isinstance(other, _EllipticCurve):
288 return super(_EllipticCurve, self).__ne__(other)
289 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400290
291
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400292 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400293 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400294 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400295 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400296
297 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400298
299 :return: A :py:type:`set` of ``cls`` instances giving the names of the
300 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400301 """
302 if lib.Cryptography_HAS_EC:
303 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
304 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
305 # The return value on this call should be num_curves again. We could
306 # check it to make sure but if it *isn't* then.. what could we do?
307 # Abort the whole process, I suppose...? -exarkun
308 lib.EC_get_builtin_curves(builtin_curves, num_curves)
309 return set(
310 cls.from_nid(lib, c.nid)
311 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400312 return set()
313
314
315 @classmethod
316 def _get_elliptic_curves(cls, lib):
317 """
318 Get, cache, and return the curves supported by OpenSSL.
319
320 :param lib: The OpenSSL library binding object.
321
322 :return: A :py:type:`set` of ``cls`` instances giving the names of the
323 elliptic curves the underlying library supports.
324 """
325 if cls._curves is None:
326 cls._curves = cls._load_elliptic_curves(lib)
327 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400328
329
330 @classmethod
331 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400332 """
333 Instantiate a new :py:class:`_EllipticCurve` associated with the given
334 OpenSSL NID.
335
336 :param lib: The OpenSSL library binding object.
337
338 :param nid: The OpenSSL NID the resulting curve object will represent.
339 This must be a curve NID (and not, for example, a hash NID) or
340 subsequent operations will fail in unpredictable ways.
341 :type nid: :py:class:`int`
342
343 :return: The curve object.
344 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400345 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
346
347
348 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400349 """
350 :param _lib: The :py:mod:`cryptography` binding instance used to
351 interface with OpenSSL.
352
353 :param _nid: The OpenSSL NID identifying the curve this object
354 represents.
355 :type _nid: :py:class:`int`
356
357 :param name: The OpenSSL short name identifying the curve this object
358 represents.
359 :type name: :py:class:`unicode`
360 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400361 self._lib = lib
362 self._nid = nid
363 self.name = name
364
365
366 def __repr__(self):
367 return "<Curve %r>" % (self.name,)
368
369
370 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400371 """
372 Create a new OpenSSL EC_KEY structure initialized to use this curve.
373
374 The structure is automatically garbage collected when the Python object
375 is garbage collected.
376 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400377 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
378 return _ffi.gc(key, _lib.EC_KEY_free)
379
380
381
382def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400383 """
384 Return a set of objects representing the elliptic curves supported in the
385 OpenSSL build in use.
386
387 The curve objects have a :py:class:`unicode` ``name`` attribute by which
388 they identify themselves.
389
390 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400391 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
392 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400393 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400394 return _EllipticCurve._get_elliptic_curves(_lib)
395
396
397
398def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400399 """
400 Return a single curve object selected by name.
401
402 See :py:func:`get_elliptic_curves` for information about curve objects.
403
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400404 :param name: The OpenSSL short name identifying the curve object to
405 retrieve.
406 :type name: :py:class:`unicode`
407
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400408 If the named curve is not supported then :py:class:`ValueError` is raised.
409 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400410 for curve in get_elliptic_curves():
411 if curve.name == name:
412 return curve
413 raise ValueError("unknown curve name", name)
414
415
416
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800417class X509Name(object):
418 def __init__(self, name):
419 """
420 Create a new X509Name, copying the given X509Name instance.
421
422 :param name: An X509Name object to copy
423 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500424 name = _lib.X509_NAME_dup(name._name)
425 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800426
427
428 def __setattr__(self, name, value):
429 if name.startswith('_'):
430 return super(X509Name, self).__setattr__(name, value)
431
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800432 # Note: we really do not want str subclasses here, so we do not use
433 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800434 if type(name) is not str:
435 raise TypeError("attribute name must be string, not '%.200s'" % (
436 type(value).__name__,))
437
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500438 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500439 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800440 try:
441 _raise_current_error()
442 except Error:
443 pass
444 raise AttributeError("No such attribute")
445
446 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500447 for i in range(_lib.X509_NAME_entry_count(self._name)):
448 ent = _lib.X509_NAME_get_entry(self._name, i)
449 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
450 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800451 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500452 ent = _lib.X509_NAME_delete_entry(self._name, i)
453 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800454 break
455
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500456 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800457 value = value.encode('utf-8')
458
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500459 add_result = _lib.X509_NAME_add_entry_by_NID(
460 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800461 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500462 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800463
464
465 def __getattr__(self, name):
466 """
467 Find attribute. An X509Name object has the following attributes:
468 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
469 organization (alias O), organizationalUnit (alias OU), commonName (alias
470 CN) and more...
471 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500472 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500473 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800474 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
475 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
476 # push something onto the error queue. If we don't clean that up
477 # now, someone else will bump into it later and be quite confused.
478 # See lp#314814.
479 try:
480 _raise_current_error()
481 except Error:
482 pass
483 return super(X509Name, self).__getattr__(name)
484
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500485 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800486 if entry_index == -1:
487 return None
488
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500489 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
490 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800491
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500492 result_buffer = _ffi.new("unsigned char**")
493 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800494 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500495 # TODO: This is untested.
496 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800497
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700498 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500499 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700500 finally:
501 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500502 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800503 return result
504
505
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500506 def _cmp(op):
507 def f(self, other):
508 if not isinstance(other, X509Name):
509 return NotImplemented
510 result = _lib.X509_NAME_cmp(self._name, other._name)
511 return op(result, 0)
512 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800513
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500514 __eq__ = _cmp(__eq__)
515 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800516
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500517 __lt__ = _cmp(__lt__)
518 __le__ = _cmp(__le__)
519
520 __gt__ = _cmp(__gt__)
521 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522
523 def __repr__(self):
524 """
525 String representation of an X509Name
526 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500527 result_buffer = _ffi.new("char[]", 512);
528 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800529 self._name, result_buffer, len(result_buffer))
530
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500531 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500532 # TODO: This is untested.
533 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800534
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500535 return "<X509Name object '%s'>" % (
536 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800537
538
539 def hash(self):
540 """
541 Return the hash value of this name
542
543 :return: None
544 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500545 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800546
547
548 def der(self):
549 """
550 Return the DER encoding of this name
551
552 :return: A :py:class:`bytes` instance giving the DER encoded form of
553 this name.
554 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500555 result_buffer = _ffi.new('unsigned char**')
556 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800557 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500558 # TODO: This is untested.
559 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800560
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500561 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
562 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800563 return string_result
564
565
566 def get_components(self):
567 """
568 Returns the split-up components of this name.
569
570 :return: List of tuples (name, value).
571 """
572 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500573 for i in range(_lib.X509_NAME_entry_count(self._name)):
574 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800575
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500576 fname = _lib.X509_NAME_ENTRY_get_object(ent)
577 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800578
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500579 nid = _lib.OBJ_obj2nid(fname)
580 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800581
582 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500583 _ffi.string(name),
584 _ffi.string(
585 _lib.ASN1_STRING_data(fval),
586 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800587
588 return result
589X509NameType = X509Name
590
591
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800592class X509Extension(object):
593 def __init__(self, type_name, critical, value, subject=None, issuer=None):
594 """
595 :param typename: The name of the extension to create.
596 :type typename: :py:data:`str`
597
598 :param critical: A flag indicating whether this is a critical extension.
599
600 :param value: The value of the extension.
601 :type value: :py:data:`str`
602
603 :param subject: Optional X509 cert to use as subject.
604 :type subject: :py:class:`X509`
605
606 :param issuer: Optional X509 cert to use as issuer.
607 :type issuer: :py:class:`X509`
608
609 :return: The X509Extension object
610 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500611 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800612
613 # A context is necessary for any extension which uses the r2i conversion
614 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
615 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500616 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800617
618 # We have no configuration database - but perhaps we should (some
619 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500620 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800621
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800622 # Initialize the subject and issuer, if appropriate. ctx is a local,
623 # and as far as I can tell none of the X509V3_* APIs invoked here steal
624 # any references, so no need to mess with reference counts or duplicates.
625 if issuer is not None:
626 if not isinstance(issuer, X509):
627 raise TypeError("issuer must be an X509 instance")
628 ctx.issuer_cert = issuer._x509
629 if subject is not None:
630 if not isinstance(subject, X509):
631 raise TypeError("subject must be an X509 instance")
632 ctx.subject_cert = subject._x509
633
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800634 if critical:
635 # There are other OpenSSL APIs which would let us pass in critical
636 # separately, but they're harder to use, and since value is already
637 # a pile of crappy junk smuggling a ton of utterly important
638 # structured data, what's the point of trying to avoid nasty stuff
639 # with strings? (However, X509V3_EXT_i2d in particular seems like it
640 # would be a better API to invoke. I do not know where to get the
641 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500642 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800643
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500644 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
645 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800646 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500647 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800648
649
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400650 @property
651 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500652 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400653
654 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500655 _lib.GEN_EMAIL: "email",
656 _lib.GEN_DNS: "DNS",
657 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400658 }
659
660 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500661 method = _lib.X509V3_EXT_get(self._extension)
662 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500663 # TODO: This is untested.
664 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400665 payload = self._extension.value.data
666 length = self._extension.value.length
667
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500668 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400669 payloadptr[0] = payload
670
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500671 if method.it != _ffi.NULL:
672 ptr = _lib.ASN1_ITEM_ptr(method.it)
673 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
674 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400675 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500676 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400677 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500678 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400679
680 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500681 for i in range(_lib.sk_GENERAL_NAME_num(names)):
682 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400683 try:
684 label = self._prefixes[name.type]
685 except KeyError:
686 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500687 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500688 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400689 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500690 value = _native(
691 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
692 parts.append(label + ":" + value)
693 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400694
695
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800696 def __str__(self):
697 """
698 :return: a nice text representation of the extension
699 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500700 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400701 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800702
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400703 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500704 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800705 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500706 # TODO: This is untested.
707 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800708
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500709 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800710
711
712 def get_critical(self):
713 """
714 Returns the critical field of the X509Extension
715
716 :return: The critical field.
717 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500718 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800719
720
721 def get_short_name(self):
722 """
723 Returns the short version of the type name of the X509Extension
724
725 :return: The short type name.
726 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500727 obj = _lib.X509_EXTENSION_get_object(self._extension)
728 nid = _lib.OBJ_obj2nid(obj)
729 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800730
731
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800732 def get_data(self):
733 """
734 Returns the data of the X509Extension
735
736 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
737 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500738 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
739 string_result = _ffi.cast('ASN1_STRING*', octet_result)
740 char_result = _lib.ASN1_STRING_data(string_result)
741 result_length = _lib.ASN1_STRING_length(string_result)
742 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800743
744X509ExtensionType = X509Extension
745
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800746
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800747class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800748 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500749 req = _lib.X509_REQ_new()
750 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800751
752
753 def set_pubkey(self, pkey):
754 """
755 Set the public key of the certificate request
756
757 :param pkey: The public key to use
758 :return: None
759 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500760 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800761 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500762 # TODO: This is untested.
763 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800764
765
766 def get_pubkey(self):
767 """
768 Get the public key from the certificate request
769
770 :return: The public key
771 """
772 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500773 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
774 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500775 # TODO: This is untested.
776 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500777 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800778 pkey._only_public = True
779 return pkey
780
781
782 def set_version(self, version):
783 """
784 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
785 request.
786
787 :param version: The version number
788 :return: None
789 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500790 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800791 if not set_result:
792 _raise_current_error()
793
794
795 def get_version(self):
796 """
797 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
798 request.
799
800 :return: an integer giving the value of the version subfield
801 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500802 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800803
804
805 def get_subject(self):
806 """
807 Create an X509Name object for the subject of the certificate request
808
809 :return: An X509Name object
810 """
811 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500812 name._name = _lib.X509_REQ_get_subject_name(self._req)
813 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500814 # TODO: This is untested.
815 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800816
817 # The name is owned by the X509Req structure. As long as the X509Name
818 # Python object is alive, keep the X509Req Python object alive.
819 name._owner = self
820
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800821 return name
822
823
824 def add_extensions(self, extensions):
825 """
826 Add extensions to the request.
827
828 :param extensions: a sequence of X509Extension objects
829 :return: None
830 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500831 stack = _lib.sk_X509_EXTENSION_new_null()
832 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500833 # TODO: This is untested.
834 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800835
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500836 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800837
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800838 for ext in extensions:
839 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800840 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800841
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800842 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500843 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800844
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500845 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800846 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500847 # TODO: This is untested.
848 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800849
850
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800851 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800852 """
853 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800854
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500855 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800856 """
857 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500858 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500859 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800860 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500861 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800862 exts.append(ext)
863 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800864
865
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800866 def sign(self, pkey, digest):
867 """
868 Sign the certificate request using the supplied key and digest
869
870 :param pkey: The key to sign with
871 :param digest: The message digest to use
872 :return: None
873 """
874 if pkey._only_public:
875 raise ValueError("Key has only public part")
876
877 if not pkey._initialized:
878 raise ValueError("Key is uninitialized")
879
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500880 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500881 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800882 raise ValueError("No such digest method")
883
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500884 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800885 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500886 # TODO: This is untested.
887 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800888
889
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800890 def verify(self, pkey):
891 """
892 Verifies a certificate request using the supplied public key
893
894 :param key: a public key
895 :return: True if the signature is correct.
896
897 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
898 problem verifying the signature.
899 """
900 if not isinstance(pkey, PKey):
901 raise TypeError("pkey must be a PKey instance")
902
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500903 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800904 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500905 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800906
907 return result
908
909
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800910X509ReqType = X509Req
911
912
913
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800914class X509(object):
915 def __init__(self):
916 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500917 x509 = _lib.X509_new()
918 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800919
920
921 def set_version(self, version):
922 """
923 Set version number of the certificate
924
925 :param version: The version number
926 :type version: :py:class:`int`
927
928 :return: None
929 """
930 if not isinstance(version, int):
931 raise TypeError("version must be an integer")
932
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500933 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800934
935
936 def get_version(self):
937 """
938 Return version number of the certificate
939
940 :return: Version number as a Python integer
941 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500942 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800943
944
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800945 def get_pubkey(self):
946 """
947 Get the public key of the certificate
948
949 :return: The public key
950 """
951 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500952 pkey._pkey = _lib.X509_get_pubkey(self._x509)
953 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800954 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500955 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800956 pkey._only_public = True
957 return pkey
958
959
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800960 def set_pubkey(self, pkey):
961 """
962 Set the public key of the certificate
963
964 :param pkey: The public key
965
966 :return: None
967 """
968 if not isinstance(pkey, PKey):
969 raise TypeError("pkey must be a PKey instance")
970
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500971 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800972 if not set_result:
973 _raise_current_error()
974
975
976 def sign(self, pkey, digest):
977 """
978 Sign the certificate using the supplied key and digest
979
980 :param pkey: The key to sign with
981 :param digest: The message digest to use
982 :return: None
983 """
984 if not isinstance(pkey, PKey):
985 raise TypeError("pkey must be a PKey instance")
986
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800987 if pkey._only_public:
988 raise ValueError("Key only has public part")
989
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800990 if not pkey._initialized:
991 raise ValueError("Key is uninitialized")
992
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500993 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500994 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800995 raise ValueError("No such digest method")
996
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500997 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800998 if not sign_result:
999 _raise_current_error()
1000
1001
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001002 def get_signature_algorithm(self):
1003 """
1004 Retrieve the signature algorithm used in the certificate
1005
1006 :return: A byte string giving the name of the signature algorithm used in
1007 the certificate.
1008 :raise ValueError: If the signature algorithm is undefined.
1009 """
1010 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001011 nid = _lib.OBJ_obj2nid(alg)
1012 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001013 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001014 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001015
1016
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001017 def digest(self, digest_name):
1018 """
1019 Return the digest of the X509 object.
1020
1021 :param digest_name: The name of the digest algorithm to use.
1022 :type digest_name: :py:class:`bytes`
1023
1024 :return: The digest of the object
1025 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001026 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001027 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001028 raise ValueError("No such digest method")
1029
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001030 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1031 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001032 result_length[0] = len(result_buffer)
1033
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001034 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001035 self._x509, digest, result_buffer, result_length)
1036
1037 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001038 # TODO: This is untested.
1039 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001040
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001041 return b":".join([
1042 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001043 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001044
1045
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001046 def subject_name_hash(self):
1047 """
1048 Return the hash of the X509 subject.
1049
1050 :return: The hash of the subject.
1051 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001052 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001053
1054
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001055 def set_serial_number(self, serial):
1056 """
1057 Set serial number of the certificate
1058
1059 :param serial: The serial number
1060 :type serial: :py:class:`int`
1061
1062 :return: None
1063 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001064 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001065 raise TypeError("serial must be an integer")
1066
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001067 hex_serial = hex(serial)[2:]
1068 if not isinstance(hex_serial, bytes):
1069 hex_serial = hex_serial.encode('ascii')
1070
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001072
1073 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1074 # it. If bignum is still NULL after this call, then the return value is
1075 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001076 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001077
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001078 if bignum_serial[0] == _ffi.NULL:
1079 set_result = _lib.ASN1_INTEGER_set(
1080 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001081 if set_result:
1082 # TODO Not tested
1083 _raise_current_error()
1084 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001085 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1086 _lib.BN_free(bignum_serial[0])
1087 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001088 # TODO Not tested
1089 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001090 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1091 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001092 if not set_result:
1093 # TODO Not tested
1094 _raise_current_error()
1095
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001096
1097 def get_serial_number(self):
1098 """
1099 Return serial number of the certificate
1100
1101 :return: Serial number as a Python integer
1102 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001103 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1104 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001105 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001106 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001107 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001108 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001109 serial = int(hexstring_serial, 16)
1110 return serial
1111 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001112 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001113 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001114 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001115
1116
1117 def gmtime_adj_notAfter(self, amount):
1118 """
1119 Adjust the time stamp for when the certificate stops being valid
1120
1121 :param amount: The number of seconds by which to adjust the ending
1122 validity time.
1123 :type amount: :py:class:`int`
1124
1125 :return: None
1126 """
1127 if not isinstance(amount, int):
1128 raise TypeError("amount must be an integer")
1129
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001130 notAfter = _lib.X509_get_notAfter(self._x509)
1131 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001132
1133
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001134 def gmtime_adj_notBefore(self, amount):
1135 """
1136 Change the timestamp for when the certificate starts being valid to the current
1137 time plus an offset.
1138
1139 :param amount: The number of seconds by which to adjust the starting validity
1140 time.
1141 :return: None
1142 """
1143 if not isinstance(amount, int):
1144 raise TypeError("amount must be an integer")
1145
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001146 notBefore = _lib.X509_get_notBefore(self._x509)
1147 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001148
1149
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001150 def has_expired(self):
1151 """
1152 Check whether the certificate has expired.
1153
1154 :return: True if the certificate has expired, false otherwise
1155 """
1156 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001157 notAfter = _lib.X509_get_notAfter(self._x509)
1158 return _lib.ASN1_UTCTIME_cmp_time_t(
1159 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001160
1161
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001162 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001163 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001164
1165
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001166 def get_notBefore(self):
1167 """
1168 Retrieve the time stamp for when the certificate starts being valid
1169
1170 :return: A string giving the timestamp, in the format::
1171
1172 YYYYMMDDhhmmssZ
1173 YYYYMMDDhhmmss+hhmm
1174 YYYYMMDDhhmmss-hhmm
1175
1176 or None if there is no value set.
1177 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001178 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001179
1180
1181 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001182 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001183
1184
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001185 def set_notBefore(self, when):
1186 """
1187 Set the time stamp for when the certificate starts being valid
1188
1189 :param when: A string giving the timestamp, in the format:
1190
1191 YYYYMMDDhhmmssZ
1192 YYYYMMDDhhmmss+hhmm
1193 YYYYMMDDhhmmss-hhmm
1194 :type when: :py:class:`bytes`
1195
1196 :return: None
1197 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001198 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001199
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001200
1201 def get_notAfter(self):
1202 """
1203 Retrieve the time stamp for when the certificate stops being valid
1204
1205 :return: A string giving the timestamp, in the format::
1206
1207 YYYYMMDDhhmmssZ
1208 YYYYMMDDhhmmss+hhmm
1209 YYYYMMDDhhmmss-hhmm
1210
1211 or None if there is no value set.
1212 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001213 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001214
1215
1216 def set_notAfter(self, when):
1217 """
1218 Set the time stamp for when the certificate stops being valid
1219
1220 :param when: A string giving the timestamp, in the format:
1221
1222 YYYYMMDDhhmmssZ
1223 YYYYMMDDhhmmss+hhmm
1224 YYYYMMDDhhmmss-hhmm
1225 :type when: :py:class:`bytes`
1226
1227 :return: None
1228 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001229 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001230
1231
1232 def _get_name(self, which):
1233 name = X509Name.__new__(X509Name)
1234 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001235 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001236 # TODO: This is untested.
1237 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001238
1239 # The name is owned by the X509 structure. As long as the X509Name
1240 # Python object is alive, keep the X509 Python object alive.
1241 name._owner = self
1242
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001243 return name
1244
1245
1246 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001247 if not isinstance(name, X509Name):
1248 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001249 set_result = which(self._x509, name._name)
1250 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001251 # TODO: This is untested.
1252 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001253
1254
1255 def get_issuer(self):
1256 """
1257 Create an X509Name object for the issuer of the certificate
1258
1259 :return: An X509Name object
1260 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001261 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001262
1263
1264 def set_issuer(self, issuer):
1265 """
1266 Set the issuer of the certificate
1267
1268 :param issuer: The issuer name
1269 :type issuer: :py:class:`X509Name`
1270
1271 :return: None
1272 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001273 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001274
1275
1276 def get_subject(self):
1277 """
1278 Create an X509Name object for the subject of the certificate
1279
1280 :return: An X509Name object
1281 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001282 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001283
1284
1285 def set_subject(self, subject):
1286 """
1287 Set the subject of the certificate
1288
1289 :param subject: The subject name
1290 :type subject: :py:class:`X509Name`
1291 :return: None
1292 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001293 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001294
1295
1296 def get_extension_count(self):
1297 """
1298 Get the number of extensions on the certificate.
1299
1300 :return: The number of extensions as an integer.
1301 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001302 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001303
1304
1305 def add_extensions(self, extensions):
1306 """
1307 Add extensions to the certificate.
1308
1309 :param extensions: a sequence of X509Extension objects
1310 :return: None
1311 """
1312 for ext in extensions:
1313 if not isinstance(ext, X509Extension):
1314 raise ValueError("One of the elements is not an X509Extension")
1315
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001316 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001317 if not add_result:
1318 _raise_current_error()
1319
1320
1321 def get_extension(self, index):
1322 """
1323 Get a specific extension of the certificate by index.
1324
1325 :param index: The index of the extension to retrieve.
1326 :return: The X509Extension object at the specified index.
1327 """
1328 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001329 ext._extension = _lib.X509_get_ext(self._x509, index)
1330 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001331 raise IndexError("extension index out of bounds")
1332
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001333 extension = _lib.X509_EXTENSION_dup(ext._extension)
1334 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001335 return ext
1336
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001337X509Type = X509
1338
1339
1340
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001341class X509Store(object):
1342 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001343 store = _lib.X509_STORE_new()
1344 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001345
1346
1347 def add_cert(self, cert):
1348 if not isinstance(cert, X509):
1349 raise TypeError()
1350
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001351 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001352 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001353 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001354
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001355
1356X509StoreType = X509Store
1357
1358
1359
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001360def load_certificate(type, buffer):
1361 """
1362 Load a certificate from a buffer
1363
1364 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1365
1366 :param buffer: The buffer the certificate is stored in
1367 :type buffer: :py:class:`bytes`
1368
1369 :return: The X509 object
1370 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001371 if isinstance(buffer, _text_type):
1372 buffer = buffer.encode("ascii")
1373
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001374 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001375
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001376 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001377 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001378 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001379 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001380 else:
1381 raise ValueError(
1382 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001383
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001384 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001385 _raise_current_error()
1386
1387 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001388 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001389 return cert
1390
1391
1392def dump_certificate(type, cert):
1393 """
1394 Dump a certificate to a buffer
1395
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001396 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1397 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001398 :param cert: The certificate to dump
1399 :return: The buffer with the dumped certificate in
1400 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001401 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001402
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001403 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001404 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001405 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001406 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001407 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001408 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001409 else:
1410 raise ValueError(
1411 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1412 "FILETYPE_TEXT")
1413
1414 return _bio_to_string(bio)
1415
1416
1417
1418def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1419 """
1420 Dump a private key to a buffer
1421
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001422 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1423 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001424 :param pkey: The PKey to dump
1425 :param cipher: (optional) if encrypted PEM format, the cipher to
1426 use
1427 :param passphrase: (optional) if encrypted PEM format, this can be either
1428 the passphrase to use, or a callback for providing the
1429 passphrase.
1430 :return: The buffer with the dumped key in
1431 :rtype: :py:data:`str`
1432 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001433 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001434
1435 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001436 if passphrase is None:
1437 raise TypeError(
1438 "if a value is given for cipher "
1439 "one must also be given for passphrase")
1440 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001441 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001442 raise ValueError("Invalid cipher name")
1443 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001444 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001445
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001446 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001447 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001448 result_code = _lib.PEM_write_bio_PrivateKey(
1449 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001450 helper.callback, helper.callback_args)
1451 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001452 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001453 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001454 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001455 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1456 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001457 # TODO RSA_free(rsa)?
1458 else:
1459 raise ValueError(
1460 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1461 "FILETYPE_TEXT")
1462
1463 if result_code == 0:
1464 _raise_current_error()
1465
1466 return _bio_to_string(bio)
1467
1468
1469
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001470def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001471 copy = _lib.X509_REVOKED_new()
1472 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001473 # TODO: This is untested.
1474 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001475
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001476 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001477 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001478 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001479
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001480 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001481 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001482 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001483
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001484 if original.extensions != _ffi.NULL:
1485 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1486 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1487 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1488 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1489 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001490 copy.extensions = extension_stack
1491
1492 copy.sequence = original.sequence
1493 return copy
1494
1495
1496
1497class Revoked(object):
1498 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1499 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1500 # OCSP_crl_reason_str. We use the latter, just like the command line
1501 # program.
1502 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001503 b"unspecified",
1504 b"keyCompromise",
1505 b"CACompromise",
1506 b"affiliationChanged",
1507 b"superseded",
1508 b"cessationOfOperation",
1509 b"certificateHold",
1510 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001511 ]
1512
1513 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001514 revoked = _lib.X509_REVOKED_new()
1515 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001516
1517
1518 def set_serial(self, hex_str):
1519 """
1520 Set the serial number of a revoked Revoked structure
1521
1522 :param hex_str: The new serial number.
1523 :type hex_str: :py:data:`str`
1524 :return: None
1525 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001526 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1527 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001528 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001529 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001530 if not bn_result:
1531 raise ValueError("bad hex string")
1532
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001533 asn1_serial = _ffi.gc(
1534 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1535 _lib.ASN1_INTEGER_free)
1536 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001537
1538
1539 def get_serial(self):
1540 """
1541 Return the serial number of a Revoked structure
1542
1543 :return: The serial number as a string
1544 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001545 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001546
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001547 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001548 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001549 # TODO: This is untested.
1550 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001551
1552 return _bio_to_string(bio)
1553
1554
1555 def _delete_reason(self):
1556 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001557 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1558 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1559 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1560 _lib.X509_EXTENSION_free(ext)
1561 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001562 break
1563
1564
1565 def set_reason(self, reason):
1566 """
1567 Set the reason of a Revoked object.
1568
1569 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1570
1571 :param reason: The reason string.
1572 :type reason: :py:class:`str` or :py:class:`NoneType`
1573 :return: None
1574 """
1575 if reason is None:
1576 self._delete_reason()
1577 elif not isinstance(reason, bytes):
1578 raise TypeError("reason must be None or a byte string")
1579 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001580 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001581 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1582
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001583 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1584 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001585 # TODO: This is untested.
1586 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001587 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001588
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001589 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1590 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001591 # TODO: This is untested.
1592 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001593
1594 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001595 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1596 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001597
1598 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001599 # TODO: This is untested.
1600 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001601
1602
1603 def get_reason(self):
1604 """
1605 Return the reason of a Revoked object.
1606
1607 :return: The reason as a string
1608 """
1609 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001610 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1611 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1612 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
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 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001616 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001617 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001618 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001619 # TODO: This is untested.
1620 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001621
1622 return _bio_to_string(bio)
1623
1624
1625 def all_reasons(self):
1626 """
1627 Return a list of all the supported reason strings.
1628
1629 :return: A list of reason strings.
1630 """
1631 return self._crl_reasons[:]
1632
1633
1634 def set_rev_date(self, when):
1635 """
1636 Set the revocation timestamp
1637
1638 :param when: A string giving the timestamp, in the format:
1639
1640 YYYYMMDDhhmmssZ
1641 YYYYMMDDhhmmss+hhmm
1642 YYYYMMDDhhmmss-hhmm
1643
1644 :return: None
1645 """
1646 return _set_asn1_time(self._revoked.revocationDate, when)
1647
1648
1649 def get_rev_date(self):
1650 """
1651 Retrieve the revocation date
1652
1653 :return: A string giving the timestamp, in the format:
1654
1655 YYYYMMDDhhmmssZ
1656 YYYYMMDDhhmmss+hhmm
1657 YYYYMMDDhhmmss-hhmm
1658 """
1659 return _get_asn1_time(self._revoked.revocationDate)
1660
1661
1662
1663class CRL(object):
1664 def __init__(self):
1665 """
1666 Create a new empty CRL object.
1667 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001668 crl = _lib.X509_CRL_new()
1669 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001670
1671
1672 def get_revoked(self):
1673 """
1674 Return revoked portion of the CRL structure (by value not reference).
1675
1676 :return: A tuple of Revoked objects.
1677 """
1678 results = []
1679 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001680 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1681 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001682 revoked_copy = _X509_REVOKED_dup(revoked)
1683 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001684 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001685 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001686 if results:
1687 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001688
1689
1690 def add_revoked(self, revoked):
1691 """
1692 Add a revoked (by value not reference) to the CRL structure
1693
1694 :param revoked: The new revoked.
1695 :type revoked: :class:`X509`
1696
1697 :return: None
1698 """
1699 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001700 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001701 # TODO: This is untested.
1702 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001703
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001704 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001705 if add_result == 0:
1706 # TODO: This is untested.
1707 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001708
1709
1710 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1711 """
1712 export a CRL as a string
1713
1714 :param cert: Used to sign CRL.
1715 :type cert: :class:`X509`
1716
1717 :param key: Used to sign CRL.
1718 :type key: :class:`PKey`
1719
1720 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1721
1722 :param days: The number of days until the next update of this CRL.
1723 :type days: :py:data:`int`
1724
1725 :return: :py:data:`str`
1726 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001727 if not isinstance(cert, X509):
1728 raise TypeError("cert must be an X509 instance")
1729 if not isinstance(key, PKey):
1730 raise TypeError("key must be a PKey instance")
1731 if not isinstance(type, int):
1732 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001733
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001734 bio = _lib.BIO_new(_lib.BIO_s_mem())
1735 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001736 # TODO: This is untested.
1737 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001738
1739 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001740 sometime = _lib.ASN1_TIME_new()
1741 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001742 # TODO: This is untested.
1743 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001744
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001745 _lib.X509_gmtime_adj(sometime, 0)
1746 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001747
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001748 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1749 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001750
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001751 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001752
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001753 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001754 if not sign_result:
1755 _raise_current_error()
1756
1757 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001758 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001759 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001760 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001761 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001762 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001763 else:
1764 raise ValueError(
1765 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1766
1767 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001768 # TODO: This is untested.
1769 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001770
1771 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001772CRLType = CRL
1773
1774
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001775
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001776class PKCS7(object):
1777 def type_is_signed(self):
1778 """
1779 Check if this NID_pkcs7_signed object
1780
1781 :return: True if the PKCS7 is of type signed
1782 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001783 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001784 return True
1785 return False
1786
1787
1788 def type_is_enveloped(self):
1789 """
1790 Check if this NID_pkcs7_enveloped object
1791
1792 :returns: True if the PKCS7 is of type enveloped
1793 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001794 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001795 return True
1796 return False
1797
1798
1799 def type_is_signedAndEnveloped(self):
1800 """
1801 Check if this NID_pkcs7_signedAndEnveloped object
1802
1803 :returns: True if the PKCS7 is of type signedAndEnveloped
1804 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001805 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001806 return True
1807 return False
1808
1809
1810 def type_is_data(self):
1811 """
1812 Check if this NID_pkcs7_data object
1813
1814 :return: True if the PKCS7 is of type data
1815 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001816 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001817 return True
1818 return False
1819
1820
1821 def get_type_name(self):
1822 """
1823 Returns the type name of the PKCS7 structure
1824
1825 :return: A string with the typename
1826 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001827 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1828 string_type = _lib.OBJ_nid2sn(nid)
1829 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001830
1831PKCS7Type = PKCS7
1832
1833
1834
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001835class PKCS12(object):
1836 def __init__(self):
1837 self._pkey = None
1838 self._cert = None
1839 self._cacerts = None
1840 self._friendlyname = None
1841
1842
1843 def get_certificate(self):
1844 """
1845 Return certificate portion of the PKCS12 structure
1846
1847 :return: X509 object containing the certificate
1848 """
1849 return self._cert
1850
1851
1852 def set_certificate(self, cert):
1853 """
1854 Replace the certificate portion of the PKCS12 structure
1855
1856 :param cert: The new certificate.
1857 :type cert: :py:class:`X509` or :py:data:`None`
1858 :return: None
1859 """
1860 if not isinstance(cert, X509):
1861 raise TypeError("cert must be an X509 instance")
1862 self._cert = cert
1863
1864
1865 def get_privatekey(self):
1866 """
1867 Return private key portion of the PKCS12 structure
1868
1869 :returns: PKey object containing the private key
1870 """
1871 return self._pkey
1872
1873
1874 def set_privatekey(self, pkey):
1875 """
1876 Replace or set the certificate portion of the PKCS12 structure
1877
1878 :param pkey: The new private key.
1879 :type pkey: :py:class:`PKey`
1880 :return: None
1881 """
1882 if not isinstance(pkey, PKey):
1883 raise TypeError("pkey must be a PKey instance")
1884 self._pkey = pkey
1885
1886
1887 def get_ca_certificates(self):
1888 """
1889 Return CA certificates within of the PKCS12 object
1890
1891 :return: A newly created tuple containing the CA certificates in the chain,
1892 if any are present, or None if no CA certificates are present.
1893 """
1894 if self._cacerts is not None:
1895 return tuple(self._cacerts)
1896
1897
1898 def set_ca_certificates(self, cacerts):
1899 """
Alex Gaynor3b0ee972014-11-15 09:17:33 -08001900 Replace or set the CA certificates within the PKCS12 object.
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001901
1902 :param cacerts: The new CA certificates.
1903 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1904 :return: None
1905 """
1906 if cacerts is None:
1907 self._cacerts = None
1908 else:
1909 cacerts = list(cacerts)
1910 for cert in cacerts:
1911 if not isinstance(cert, X509):
1912 raise TypeError("iterable must only contain X509 instances")
1913 self._cacerts = cacerts
1914
1915
1916 def set_friendlyname(self, name):
1917 """
1918 Replace or set the certificate portion of the PKCS12 structure
1919
1920 :param name: The new friendly name.
1921 :type name: :py:class:`bytes`
1922 :return: None
1923 """
1924 if name is None:
1925 self._friendlyname = None
1926 elif not isinstance(name, bytes):
1927 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1928 self._friendlyname = name
1929
1930
1931 def get_friendlyname(self):
1932 """
1933 Return friendly name portion of the PKCS12 structure
1934
1935 :returns: String containing the friendlyname
1936 """
1937 return self._friendlyname
1938
1939
1940 def export(self, passphrase=None, iter=2048, maciter=1):
1941 """
1942 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1943
1944 :param passphrase: used to encrypt the PKCS12
1945 :type passphrase: :py:data:`bytes`
1946
1947 :param iter: How many times to repeat the encryption
1948 :type iter: :py:data:`int`
1949
1950 :param maciter: How many times to repeat the MAC
1951 :type maciter: :py:data:`int`
1952
1953 :return: The string containing the PKCS12
1954 """
1955 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001956 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001957 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001958 cacerts = _lib.sk_X509_new_null()
1959 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001960 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001961 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001962
1963 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001964 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001965
1966 friendlyname = self._friendlyname
1967 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001968 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001969
1970 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001971 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001972 else:
1973 pkey = self._pkey._pkey
1974
1975 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001976 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001977 else:
1978 cert = self._cert._x509
1979
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001980 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001981 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001982 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1983 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001984 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001985 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001986 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001987 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001988
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001989 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001990 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001991 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001992
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001993PKCS12Type = PKCS12
1994
1995
1996
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001997class NetscapeSPKI(object):
1998 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001999 spki = _lib.NETSCAPE_SPKI_new()
2000 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002001
2002
2003 def sign(self, pkey, digest):
2004 """
2005 Sign the certificate request using the supplied key and digest
2006
2007 :param pkey: The key to sign with
2008 :param digest: The message digest to use
2009 :return: None
2010 """
2011 if pkey._only_public:
2012 raise ValueError("Key has only public part")
2013
2014 if not pkey._initialized:
2015 raise ValueError("Key is uninitialized")
2016
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002017 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002018 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002019 raise ValueError("No such digest method")
2020
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002021 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002022 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002023 # TODO: This is untested.
2024 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002025
2026
2027 def verify(self, key):
2028 """
2029 Verifies a certificate request using the supplied public key
2030
2031 :param key: a public key
2032 :return: True if the signature is correct.
2033 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2034 problem verifying the signature.
2035 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002036 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002037 if answer <= 0:
2038 _raise_current_error()
2039 return True
2040
2041
2042 def b64_encode(self):
2043 """
2044 Generate a base64 encoded string from an SPKI
2045
2046 :return: The base64 encoded string
2047 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002048 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2049 result = _ffi.string(encoded)
2050 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002051 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002052
2053
2054 def get_pubkey(self):
2055 """
2056 Get the public key of the certificate
2057
2058 :return: The public key
2059 """
2060 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002061 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2062 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002063 # TODO: This is untested.
2064 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002065 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002066 pkey._only_public = True
2067 return pkey
2068
2069
2070 def set_pubkey(self, pkey):
2071 """
2072 Set the public key of the certificate
2073
2074 :param pkey: The public key
2075 :return: None
2076 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002077 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002078 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002079 # TODO: This is untested.
2080 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002081NetscapeSPKIType = NetscapeSPKI
2082
2083
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002084class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002085 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002086 if type != FILETYPE_PEM and passphrase is not None:
2087 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002088 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002089 self._more_args = more_args
2090 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002091 self._problems = []
2092
2093
2094 @property
2095 def callback(self):
2096 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002097 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002098 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002099 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002100 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002101 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002102 else:
2103 raise TypeError("Last argument must be string or callable")
2104
2105
2106 @property
2107 def callback_args(self):
2108 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002109 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002110 elif isinstance(self._passphrase, bytes):
2111 return self._passphrase
2112 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002113 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002114 else:
2115 raise TypeError("Last argument must be string or callable")
2116
2117
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002118 def raise_if_problem(self, exceptionType=Error):
2119 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002120 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002121 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002122 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002123 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002124 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002125 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002126
2127
2128 def _read_passphrase(self, buf, size, rwflag, userdata):
2129 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002130 if self._more_args:
2131 result = self._passphrase(size, rwflag, userdata)
2132 else:
2133 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002134 if not isinstance(result, bytes):
2135 raise ValueError("String expected")
2136 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002137 if self._truncate:
2138 result = result[:size]
2139 else:
2140 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002141 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002142 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002143 return len(result)
2144 except Exception as e:
2145 self._problems.append(e)
2146 return 0
2147
2148
2149
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002150def load_privatekey(type, buffer, passphrase=None):
2151 """
2152 Load a private key from a buffer
2153
2154 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2155 :param buffer: The buffer the key is stored in
2156 :param passphrase: (optional) if encrypted PEM format, this can be
2157 either the passphrase to use, or a callback for
2158 providing the passphrase.
2159
2160 :return: The PKey object
2161 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002162 if isinstance(buffer, _text_type):
2163 buffer = buffer.encode("ascii")
2164
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002165 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002166
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002167 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002168 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002169 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2170 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002171 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002172 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002173 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002174 else:
2175 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2176
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002177 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002178 _raise_current_error()
2179
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002180 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002181 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002182 return pkey
2183
2184
2185
2186def dump_certificate_request(type, req):
2187 """
2188 Dump a certificate request to a buffer
2189
2190 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2191 :param req: The certificate request to dump
2192 :return: The buffer with the dumped certificate request in
2193 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002194 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002195
2196 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002197 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002198 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002199 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002200 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002201 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002202 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002203 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002204
2205 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002206 # TODO: This is untested.
2207 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002208
2209 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002210
2211
2212
2213def load_certificate_request(type, buffer):
2214 """
2215 Load a certificate request from a buffer
2216
2217 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2218 :param buffer: The buffer the certificate request is stored in
2219 :return: The X509Req object
2220 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002221 if isinstance(buffer, _text_type):
2222 buffer = buffer.encode("ascii")
2223
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002224 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002225
2226 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002227 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002228 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002229 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002230 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002231 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002232
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002233 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002234 # TODO: This is untested.
2235 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002236
2237 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002238 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002239 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002240
2241
2242
2243def sign(pkey, data, digest):
2244 """
2245 Sign data with a digest
2246
2247 :param pkey: Pkey to sign with
2248 :param data: data to be signed
2249 :param digest: message digest to use
2250 :return: signature
2251 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002252 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002253 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002254 raise ValueError("No such digest method")
2255
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002256 md_ctx = _ffi.new("EVP_MD_CTX*")
2257 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002258
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002259 _lib.EVP_SignInit(md_ctx, digest_obj)
2260 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002261
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002262 signature_buffer = _ffi.new("unsigned char[]", 512)
2263 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002264 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002265 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002266 md_ctx, signature_buffer, signature_length, pkey._pkey)
2267
2268 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002269 # TODO: This is untested.
2270 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002271
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002272 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002273
2274
2275
2276def verify(cert, signature, data, digest):
2277 """
2278 Verify a signature
2279
2280 :param cert: signing certificate (X509 object)
2281 :param signature: signature returned by sign function
2282 :param data: data to be verified
2283 :param digest: message digest to use
2284 :return: None if the signature is correct, raise exception otherwise
2285 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002286 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002287 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002288 raise ValueError("No such digest method")
2289
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002290 pkey = _lib.X509_get_pubkey(cert._x509)
2291 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002292 # TODO: This is untested.
2293 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002294 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002295
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002296 md_ctx = _ffi.new("EVP_MD_CTX*")
2297 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002298
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002299 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2300 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2301 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002302
2303 if verify_result != 1:
2304 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002305
2306
2307
2308def load_crl(type, buffer):
2309 """
2310 Load a certificate revocation list from a buffer
2311
2312 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2313 :param buffer: The buffer the CRL is stored in
2314
2315 :return: The PKey object
2316 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002317 if isinstance(buffer, _text_type):
2318 buffer = buffer.encode("ascii")
2319
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002320 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002321
2322 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002323 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002324 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002325 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002326 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002327 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2328
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002329 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002330 _raise_current_error()
2331
2332 result = CRL.__new__(CRL)
2333 result._crl = crl
2334 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002335
2336
2337
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002338def load_pkcs7_data(type, buffer):
2339 """
2340 Load pkcs7 data from a buffer
2341
2342 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2343 :param buffer: The buffer with the pkcs7 data.
2344 :return: The PKCS7 object
2345 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002346 if isinstance(buffer, _text_type):
2347 buffer = buffer.encode("ascii")
2348
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002349 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002350
2351 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002352 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002353 elif type == FILETYPE_ASN1:
Alex Gaynor77acc362014-08-13 14:46:15 -07002354 pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002355 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002356 # TODO: This is untested.
2357 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002358 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2359
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002360 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002361 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002362
2363 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002364 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002365 return pypkcs7
2366
2367
2368
Stephen Holsapple38482622014-04-05 20:29:34 -07002369def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002370 """
2371 Load a PKCS12 object from a buffer
2372
2373 :param buffer: The buffer the certificate is stored in
2374 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2375 :returns: The PKCS12 object
2376 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002377 if isinstance(buffer, _text_type):
2378 buffer = buffer.encode("ascii")
2379
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002380 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002381
Stephen Holsapple38482622014-04-05 20:29:34 -07002382 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2383 # password based encryption no password and a zero length password are two
2384 # different things, but OpenSSL implementation will try both to figure out
2385 # which one works.
2386 if not passphrase:
2387 passphrase = _ffi.NULL
2388
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002389 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2390 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002391 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002392 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002393
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002394 pkey = _ffi.new("EVP_PKEY**")
2395 cert = _ffi.new("X509**")
2396 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002397
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002398 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002399 if not parse_result:
2400 _raise_current_error()
2401
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002402 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002403
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002404 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2405 # queue for no particular reason. This error isn't interesting to anyone
2406 # outside this function. It's not even interesting to us. Get rid of it.
2407 try:
2408 _raise_current_error()
2409 except Error:
2410 pass
2411
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002412 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002413 pykey = None
2414 else:
2415 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002416 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002417
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002418 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002419 pycert = None
2420 friendlyname = None
2421 else:
2422 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002423 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002424
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002425 friendlyname_length = _ffi.new("int*")
2426 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2427 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2428 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002429 friendlyname = None
2430
2431 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002432 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002433 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002434 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002435 pycacerts.append(pycacert)
2436 if not pycacerts:
2437 pycacerts = None
2438
2439 pkcs12 = PKCS12.__new__(PKCS12)
2440 pkcs12._pkey = pykey
2441 pkcs12._cert = pycert
2442 pkcs12._cacerts = pycacerts
2443 pkcs12._friendlyname = friendlyname
2444 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002445
2446
2447def _initialize_openssl_threads(get_ident, Lock):
2448 import _ssl
2449 return
2450
2451 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2452
2453 def locking_function(mode, index, filename, line):
2454 if mode & _lib.CRYPTO_LOCK:
2455 locks[index].acquire()
2456 else:
2457 locks[index].release()
2458
2459 _lib.CRYPTO_set_id_callback(
2460 _ffi.callback("unsigned long (*)(void)", get_ident))
2461
2462 _lib.CRYPTO_set_locking_callback(
2463 _ffi.callback(
2464 "void (*)(int, int, const char*, int)", locking_function))
2465
2466
2467try:
2468 from thread import get_ident
2469 from threading import Lock
2470except ImportError:
2471 pass
2472else:
2473 _initialize_openssl_threads(get_ident, Lock)
2474 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002475
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002476# There are no direct unit tests for this initialization. It is tested
2477# indirectly since it is necessary for functions like dump_privatekey when
2478# using encryption.
2479#
2480# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2481# and some other similar tests may fail without this (though they may not if
2482# the Python runtime has already done some initialization of the underlying
2483# OpenSSL library (and is linked against the same one that cryptography is
2484# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002485_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002486
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002487# This is similar but exercised mainly by exception_from_error_queue. It calls
2488# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2489_lib.SSL_load_error_strings()