blob: c48c84f7acb36db377c7c78f2d38b2a77d42bec0 [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:
279 def __ne__(self, other):
280 if isinstance(other, _EllipticCurve):
281 return super(_EllipticCurve, self).__ne__(other)
282 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400283
284
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400285 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400286 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400287 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400288 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400289
290 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400291
292 :return: A :py:type:`set` of ``cls`` instances giving the names of the
293 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400294 """
295 if lib.Cryptography_HAS_EC:
296 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
297 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
298 # The return value on this call should be num_curves again. We could
299 # check it to make sure but if it *isn't* then.. what could we do?
300 # Abort the whole process, I suppose...? -exarkun
301 lib.EC_get_builtin_curves(builtin_curves, num_curves)
302 return set(
303 cls.from_nid(lib, c.nid)
304 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400305 return set()
306
307
308 @classmethod
309 def _get_elliptic_curves(cls, lib):
310 """
311 Get, cache, and return the curves supported by OpenSSL.
312
313 :param lib: The OpenSSL library binding object.
314
315 :return: A :py:type:`set` of ``cls`` instances giving the names of the
316 elliptic curves the underlying library supports.
317 """
318 if cls._curves is None:
319 cls._curves = cls._load_elliptic_curves(lib)
320 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400321
322
323 @classmethod
324 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400325 """
326 Instantiate a new :py:class:`_EllipticCurve` associated with the given
327 OpenSSL NID.
328
329 :param lib: The OpenSSL library binding object.
330
331 :param nid: The OpenSSL NID the resulting curve object will represent.
332 This must be a curve NID (and not, for example, a hash NID) or
333 subsequent operations will fail in unpredictable ways.
334 :type nid: :py:class:`int`
335
336 :return: The curve object.
337 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400338 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
339
340
341 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400342 """
343 :param _lib: The :py:mod:`cryptography` binding instance used to
344 interface with OpenSSL.
345
346 :param _nid: The OpenSSL NID identifying the curve this object
347 represents.
348 :type _nid: :py:class:`int`
349
350 :param name: The OpenSSL short name identifying the curve this object
351 represents.
352 :type name: :py:class:`unicode`
353 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400354 self._lib = lib
355 self._nid = nid
356 self.name = name
357
358
359 def __repr__(self):
360 return "<Curve %r>" % (self.name,)
361
362
363 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400364 """
365 Create a new OpenSSL EC_KEY structure initialized to use this curve.
366
367 The structure is automatically garbage collected when the Python object
368 is garbage collected.
369 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400370 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
371 return _ffi.gc(key, _lib.EC_KEY_free)
372
373
374
375def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400376 """
377 Return a set of objects representing the elliptic curves supported in the
378 OpenSSL build in use.
379
380 The curve objects have a :py:class:`unicode` ``name`` attribute by which
381 they identify themselves.
382
383 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400384 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
385 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400386 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400387 return _EllipticCurve._get_elliptic_curves(_lib)
388
389
390
391def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400392 """
393 Return a single curve object selected by name.
394
395 See :py:func:`get_elliptic_curves` for information about curve objects.
396
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400397 :param name: The OpenSSL short name identifying the curve object to
398 retrieve.
399 :type name: :py:class:`unicode`
400
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400401 If the named curve is not supported then :py:class:`ValueError` is raised.
402 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400403 for curve in get_elliptic_curves():
404 if curve.name == name:
405 return curve
406 raise ValueError("unknown curve name", name)
407
408
409
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800410class X509Name(object):
411 def __init__(self, name):
412 """
413 Create a new X509Name, copying the given X509Name instance.
414
415 :param name: An X509Name object to copy
416 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500417 name = _lib.X509_NAME_dup(name._name)
418 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800419
420
421 def __setattr__(self, name, value):
422 if name.startswith('_'):
423 return super(X509Name, self).__setattr__(name, value)
424
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800425 # Note: we really do not want str subclasses here, so we do not use
426 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800427 if type(name) is not str:
428 raise TypeError("attribute name must be string, not '%.200s'" % (
429 type(value).__name__,))
430
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500431 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500432 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800433 try:
434 _raise_current_error()
435 except Error:
436 pass
437 raise AttributeError("No such attribute")
438
439 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500440 for i in range(_lib.X509_NAME_entry_count(self._name)):
441 ent = _lib.X509_NAME_get_entry(self._name, i)
442 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
443 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800444 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500445 ent = _lib.X509_NAME_delete_entry(self._name, i)
446 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800447 break
448
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500449 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800450 value = value.encode('utf-8')
451
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500452 add_result = _lib.X509_NAME_add_entry_by_NID(
453 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800454 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500455 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800456
457
458 def __getattr__(self, name):
459 """
460 Find attribute. An X509Name object has the following attributes:
461 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
462 organization (alias O), organizationalUnit (alias OU), commonName (alias
463 CN) and more...
464 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500465 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500466 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800467 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
468 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
469 # push something onto the error queue. If we don't clean that up
470 # now, someone else will bump into it later and be quite confused.
471 # See lp#314814.
472 try:
473 _raise_current_error()
474 except Error:
475 pass
476 return super(X509Name, self).__getattr__(name)
477
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500478 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800479 if entry_index == -1:
480 return None
481
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500482 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
483 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800484
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500485 result_buffer = _ffi.new("unsigned char**")
486 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800487 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500488 # TODO: This is untested.
489 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800490
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700491 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500492 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700493 finally:
494 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500495 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800496 return result
497
498
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500499 def _cmp(op):
500 def f(self, other):
501 if not isinstance(other, X509Name):
502 return NotImplemented
503 result = _lib.X509_NAME_cmp(self._name, other._name)
504 return op(result, 0)
505 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800506
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500507 __eq__ = _cmp(__eq__)
508 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800509
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500510 __lt__ = _cmp(__lt__)
511 __le__ = _cmp(__le__)
512
513 __gt__ = _cmp(__gt__)
514 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800515
516 def __repr__(self):
517 """
518 String representation of an X509Name
519 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500520 result_buffer = _ffi.new("char[]", 512);
521 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522 self._name, result_buffer, len(result_buffer))
523
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500524 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500525 # TODO: This is untested.
526 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800527
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500528 return "<X509Name object '%s'>" % (
529 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800530
531
532 def hash(self):
533 """
534 Return the hash value of this name
535
536 :return: None
537 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500538 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800539
540
541 def der(self):
542 """
543 Return the DER encoding of this name
544
545 :return: A :py:class:`bytes` instance giving the DER encoded form of
546 this name.
547 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500548 result_buffer = _ffi.new('unsigned char**')
549 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800550 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500551 # TODO: This is untested.
552 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800553
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500554 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
555 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800556 return string_result
557
558
559 def get_components(self):
560 """
561 Returns the split-up components of this name.
562
563 :return: List of tuples (name, value).
564 """
565 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500566 for i in range(_lib.X509_NAME_entry_count(self._name)):
567 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800568
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500569 fname = _lib.X509_NAME_ENTRY_get_object(ent)
570 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800571
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500572 nid = _lib.OBJ_obj2nid(fname)
573 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800574
575 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500576 _ffi.string(name),
577 _ffi.string(
578 _lib.ASN1_STRING_data(fval),
579 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800580
581 return result
582X509NameType = X509Name
583
584
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800585class X509Extension(object):
586 def __init__(self, type_name, critical, value, subject=None, issuer=None):
587 """
588 :param typename: The name of the extension to create.
589 :type typename: :py:data:`str`
590
591 :param critical: A flag indicating whether this is a critical extension.
592
593 :param value: The value of the extension.
594 :type value: :py:data:`str`
595
596 :param subject: Optional X509 cert to use as subject.
597 :type subject: :py:class:`X509`
598
599 :param issuer: Optional X509 cert to use as issuer.
600 :type issuer: :py:class:`X509`
601
602 :return: The X509Extension object
603 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500604 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800605
606 # A context is necessary for any extension which uses the r2i conversion
607 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
608 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500609 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800610
611 # We have no configuration database - but perhaps we should (some
612 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500613 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800614
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800615 # Initialize the subject and issuer, if appropriate. ctx is a local,
616 # and as far as I can tell none of the X509V3_* APIs invoked here steal
617 # any references, so no need to mess with reference counts or duplicates.
618 if issuer is not None:
619 if not isinstance(issuer, X509):
620 raise TypeError("issuer must be an X509 instance")
621 ctx.issuer_cert = issuer._x509
622 if subject is not None:
623 if not isinstance(subject, X509):
624 raise TypeError("subject must be an X509 instance")
625 ctx.subject_cert = subject._x509
626
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800627 if critical:
628 # There are other OpenSSL APIs which would let us pass in critical
629 # separately, but they're harder to use, and since value is already
630 # a pile of crappy junk smuggling a ton of utterly important
631 # structured data, what's the point of trying to avoid nasty stuff
632 # with strings? (However, X509V3_EXT_i2d in particular seems like it
633 # would be a better API to invoke. I do not know where to get the
634 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500635 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800636
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500637 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
638 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800639 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500640 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800641
642
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400643 @property
644 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500645 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400646
647 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500648 _lib.GEN_EMAIL: "email",
649 _lib.GEN_DNS: "DNS",
650 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400651 }
652
653 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500654 method = _lib.X509V3_EXT_get(self._extension)
655 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500656 # TODO: This is untested.
657 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400658 payload = self._extension.value.data
659 length = self._extension.value.length
660
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500661 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400662 payloadptr[0] = payload
663
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500664 if method.it != _ffi.NULL:
665 ptr = _lib.ASN1_ITEM_ptr(method.it)
666 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
667 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400668 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500669 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400670 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500671 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400672
673 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500674 for i in range(_lib.sk_GENERAL_NAME_num(names)):
675 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400676 try:
677 label = self._prefixes[name.type]
678 except KeyError:
679 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500680 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500681 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400682 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500683 value = _native(
684 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
685 parts.append(label + ":" + value)
686 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400687
688
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800689 def __str__(self):
690 """
691 :return: a nice text representation of the extension
692 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500693 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400694 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800695
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400696 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500697 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800698 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500699 # TODO: This is untested.
700 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800701
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500702 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800703
704
705 def get_critical(self):
706 """
707 Returns the critical field of the X509Extension
708
709 :return: The critical field.
710 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500711 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800712
713
714 def get_short_name(self):
715 """
716 Returns the short version of the type name of the X509Extension
717
718 :return: The short type name.
719 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500720 obj = _lib.X509_EXTENSION_get_object(self._extension)
721 nid = _lib.OBJ_obj2nid(obj)
722 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800723
724
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800725 def get_data(self):
726 """
727 Returns the data of the X509Extension
728
729 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
730 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500731 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
732 string_result = _ffi.cast('ASN1_STRING*', octet_result)
733 char_result = _lib.ASN1_STRING_data(string_result)
734 result_length = _lib.ASN1_STRING_length(string_result)
735 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800736
737X509ExtensionType = X509Extension
738
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800739
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800740class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800741 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500742 req = _lib.X509_REQ_new()
743 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800744
745
746 def set_pubkey(self, pkey):
747 """
748 Set the public key of the certificate request
749
750 :param pkey: The public key to use
751 :return: None
752 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500753 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800754 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500755 # TODO: This is untested.
756 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800757
758
759 def get_pubkey(self):
760 """
761 Get the public key from the certificate request
762
763 :return: The public key
764 """
765 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500766 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
767 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500768 # TODO: This is untested.
769 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500770 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800771 pkey._only_public = True
772 return pkey
773
774
775 def set_version(self, version):
776 """
777 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
778 request.
779
780 :param version: The version number
781 :return: None
782 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500783 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800784 if not set_result:
785 _raise_current_error()
786
787
788 def get_version(self):
789 """
790 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
791 request.
792
793 :return: an integer giving the value of the version subfield
794 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500795 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800796
797
798 def get_subject(self):
799 """
800 Create an X509Name object for the subject of the certificate request
801
802 :return: An X509Name object
803 """
804 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500805 name._name = _lib.X509_REQ_get_subject_name(self._req)
806 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500807 # TODO: This is untested.
808 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800809
810 # The name is owned by the X509Req structure. As long as the X509Name
811 # Python object is alive, keep the X509Req Python object alive.
812 name._owner = self
813
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800814 return name
815
816
817 def add_extensions(self, extensions):
818 """
819 Add extensions to the request.
820
821 :param extensions: a sequence of X509Extension objects
822 :return: None
823 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500824 stack = _lib.sk_X509_EXTENSION_new_null()
825 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500826 # TODO: This is untested.
827 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800828
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500829 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800830
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800831 for ext in extensions:
832 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800833 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800834
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800835 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500836 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800837
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500838 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800839 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500840 # TODO: This is untested.
841 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800842
843
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800844 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800845 """
846 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800847
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500848 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800849 """
850 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500851 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500852 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800853 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500854 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800855 exts.append(ext)
856 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800857
858
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800859 def sign(self, pkey, digest):
860 """
861 Sign the certificate request using the supplied key and digest
862
863 :param pkey: The key to sign with
864 :param digest: The message digest to use
865 :return: None
866 """
867 if pkey._only_public:
868 raise ValueError("Key has only public part")
869
870 if not pkey._initialized:
871 raise ValueError("Key is uninitialized")
872
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500873 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500874 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800875 raise ValueError("No such digest method")
876
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500877 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800878 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500879 # TODO: This is untested.
880 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800881
882
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800883 def verify(self, pkey):
884 """
885 Verifies a certificate request using the supplied public key
886
887 :param key: a public key
888 :return: True if the signature is correct.
889
890 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
891 problem verifying the signature.
892 """
893 if not isinstance(pkey, PKey):
894 raise TypeError("pkey must be a PKey instance")
895
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500896 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800897 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500898 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800899
900 return result
901
902
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800903X509ReqType = X509Req
904
905
906
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800907class X509(object):
908 def __init__(self):
909 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500910 x509 = _lib.X509_new()
911 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800912
913
914 def set_version(self, version):
915 """
916 Set version number of the certificate
917
918 :param version: The version number
919 :type version: :py:class:`int`
920
921 :return: None
922 """
923 if not isinstance(version, int):
924 raise TypeError("version must be an integer")
925
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500926 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800927
928
929 def get_version(self):
930 """
931 Return version number of the certificate
932
933 :return: Version number as a Python integer
934 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500935 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800936
937
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800938 def get_pubkey(self):
939 """
940 Get the public key of the certificate
941
942 :return: The public key
943 """
944 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500945 pkey._pkey = _lib.X509_get_pubkey(self._x509)
946 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800947 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500948 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800949 pkey._only_public = True
950 return pkey
951
952
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800953 def set_pubkey(self, pkey):
954 """
955 Set the public key of the certificate
956
957 :param pkey: The public key
958
959 :return: None
960 """
961 if not isinstance(pkey, PKey):
962 raise TypeError("pkey must be a PKey instance")
963
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500964 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800965 if not set_result:
966 _raise_current_error()
967
968
969 def sign(self, pkey, digest):
970 """
971 Sign the certificate using the supplied key and digest
972
973 :param pkey: The key to sign with
974 :param digest: The message digest to use
975 :return: None
976 """
977 if not isinstance(pkey, PKey):
978 raise TypeError("pkey must be a PKey instance")
979
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800980 if pkey._only_public:
981 raise ValueError("Key only has public part")
982
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800983 if not pkey._initialized:
984 raise ValueError("Key is uninitialized")
985
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500986 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500987 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800988 raise ValueError("No such digest method")
989
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500990 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800991 if not sign_result:
992 _raise_current_error()
993
994
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800995 def get_signature_algorithm(self):
996 """
997 Retrieve the signature algorithm used in the certificate
998
999 :return: A byte string giving the name of the signature algorithm used in
1000 the certificate.
1001 :raise ValueError: If the signature algorithm is undefined.
1002 """
1003 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001004 nid = _lib.OBJ_obj2nid(alg)
1005 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001006 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001007 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001008
1009
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001010 def digest(self, digest_name):
1011 """
1012 Return the digest of the X509 object.
1013
1014 :param digest_name: The name of the digest algorithm to use.
1015 :type digest_name: :py:class:`bytes`
1016
1017 :return: The digest of the object
1018 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001019 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001020 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001021 raise ValueError("No such digest method")
1022
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001023 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1024 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001025 result_length[0] = len(result_buffer)
1026
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001027 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001028 self._x509, digest, result_buffer, result_length)
1029
1030 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001031 # TODO: This is untested.
1032 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001033
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001034 return b":".join([
1035 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001036 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001037
1038
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001039 def subject_name_hash(self):
1040 """
1041 Return the hash of the X509 subject.
1042
1043 :return: The hash of the subject.
1044 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001045 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001046
1047
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001048 def set_serial_number(self, serial):
1049 """
1050 Set serial number of the certificate
1051
1052 :param serial: The serial number
1053 :type serial: :py:class:`int`
1054
1055 :return: None
1056 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001057 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001058 raise TypeError("serial must be an integer")
1059
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001060 hex_serial = hex(serial)[2:]
1061 if not isinstance(hex_serial, bytes):
1062 hex_serial = hex_serial.encode('ascii')
1063
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001064 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001065
1066 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1067 # it. If bignum is still NULL after this call, then the return value is
1068 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001069 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001070
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 if bignum_serial[0] == _ffi.NULL:
1072 set_result = _lib.ASN1_INTEGER_set(
1073 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001074 if set_result:
1075 # TODO Not tested
1076 _raise_current_error()
1077 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001078 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1079 _lib.BN_free(bignum_serial[0])
1080 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001081 # TODO Not tested
1082 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001083 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1084 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001085 if not set_result:
1086 # TODO Not tested
1087 _raise_current_error()
1088
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001089
1090 def get_serial_number(self):
1091 """
1092 Return serial number of the certificate
1093
1094 :return: Serial number as a Python integer
1095 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001096 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1097 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001098 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001099 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001100 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001101 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001102 serial = int(hexstring_serial, 16)
1103 return serial
1104 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001105 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001106 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001107 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001108
1109
1110 def gmtime_adj_notAfter(self, amount):
1111 """
1112 Adjust the time stamp for when the certificate stops being valid
1113
1114 :param amount: The number of seconds by which to adjust the ending
1115 validity time.
1116 :type amount: :py:class:`int`
1117
1118 :return: None
1119 """
1120 if not isinstance(amount, int):
1121 raise TypeError("amount must be an integer")
1122
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001123 notAfter = _lib.X509_get_notAfter(self._x509)
1124 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001125
1126
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001127 def gmtime_adj_notBefore(self, amount):
1128 """
1129 Change the timestamp for when the certificate starts being valid to the current
1130 time plus an offset.
1131
1132 :param amount: The number of seconds by which to adjust the starting validity
1133 time.
1134 :return: None
1135 """
1136 if not isinstance(amount, int):
1137 raise TypeError("amount must be an integer")
1138
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001139 notBefore = _lib.X509_get_notBefore(self._x509)
1140 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001141
1142
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001143 def has_expired(self):
1144 """
1145 Check whether the certificate has expired.
1146
1147 :return: True if the certificate has expired, false otherwise
1148 """
1149 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001150 notAfter = _lib.X509_get_notAfter(self._x509)
1151 return _lib.ASN1_UTCTIME_cmp_time_t(
1152 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001153
1154
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001155 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001156 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001157
1158
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001159 def get_notBefore(self):
1160 """
1161 Retrieve the time stamp for when the certificate starts being valid
1162
1163 :return: A string giving the timestamp, in the format::
1164
1165 YYYYMMDDhhmmssZ
1166 YYYYMMDDhhmmss+hhmm
1167 YYYYMMDDhhmmss-hhmm
1168
1169 or None if there is no value set.
1170 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001171 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001172
1173
1174 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001175 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001176
1177
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001178 def set_notBefore(self, when):
1179 """
1180 Set the time stamp for when the certificate starts being valid
1181
1182 :param when: A string giving the timestamp, in the format:
1183
1184 YYYYMMDDhhmmssZ
1185 YYYYMMDDhhmmss+hhmm
1186 YYYYMMDDhhmmss-hhmm
1187 :type when: :py:class:`bytes`
1188
1189 :return: None
1190 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001191 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001192
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001193
1194 def get_notAfter(self):
1195 """
1196 Retrieve the time stamp for when the certificate stops being valid
1197
1198 :return: A string giving the timestamp, in the format::
1199
1200 YYYYMMDDhhmmssZ
1201 YYYYMMDDhhmmss+hhmm
1202 YYYYMMDDhhmmss-hhmm
1203
1204 or None if there is no value set.
1205 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001206 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001207
1208
1209 def set_notAfter(self, when):
1210 """
1211 Set the time stamp for when the certificate stops being valid
1212
1213 :param when: A string giving the timestamp, in the format:
1214
1215 YYYYMMDDhhmmssZ
1216 YYYYMMDDhhmmss+hhmm
1217 YYYYMMDDhhmmss-hhmm
1218 :type when: :py:class:`bytes`
1219
1220 :return: None
1221 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001222 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001223
1224
1225 def _get_name(self, which):
1226 name = X509Name.__new__(X509Name)
1227 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001228 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001229 # TODO: This is untested.
1230 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001231
1232 # The name is owned by the X509 structure. As long as the X509Name
1233 # Python object is alive, keep the X509 Python object alive.
1234 name._owner = self
1235
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001236 return name
1237
1238
1239 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001240 if not isinstance(name, X509Name):
1241 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001242 set_result = which(self._x509, name._name)
1243 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001244 # TODO: This is untested.
1245 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001246
1247
1248 def get_issuer(self):
1249 """
1250 Create an X509Name object for the issuer of the certificate
1251
1252 :return: An X509Name object
1253 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001254 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001255
1256
1257 def set_issuer(self, issuer):
1258 """
1259 Set the issuer of the certificate
1260
1261 :param issuer: The issuer name
1262 :type issuer: :py:class:`X509Name`
1263
1264 :return: None
1265 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001266 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001267
1268
1269 def get_subject(self):
1270 """
1271 Create an X509Name object for the subject of the certificate
1272
1273 :return: An X509Name object
1274 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001275 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001276
1277
1278 def set_subject(self, subject):
1279 """
1280 Set the subject of the certificate
1281
1282 :param subject: The subject name
1283 :type subject: :py:class:`X509Name`
1284 :return: None
1285 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001286 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001287
1288
1289 def get_extension_count(self):
1290 """
1291 Get the number of extensions on the certificate.
1292
1293 :return: The number of extensions as an integer.
1294 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001295 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001296
1297
1298 def add_extensions(self, extensions):
1299 """
1300 Add extensions to the certificate.
1301
1302 :param extensions: a sequence of X509Extension objects
1303 :return: None
1304 """
1305 for ext in extensions:
1306 if not isinstance(ext, X509Extension):
1307 raise ValueError("One of the elements is not an X509Extension")
1308
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001309 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001310 if not add_result:
1311 _raise_current_error()
1312
1313
1314 def get_extension(self, index):
1315 """
1316 Get a specific extension of the certificate by index.
1317
1318 :param index: The index of the extension to retrieve.
1319 :return: The X509Extension object at the specified index.
1320 """
1321 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001322 ext._extension = _lib.X509_get_ext(self._x509, index)
1323 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001324 raise IndexError("extension index out of bounds")
1325
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001326 extension = _lib.X509_EXTENSION_dup(ext._extension)
1327 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001328 return ext
1329
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001330X509Type = X509
1331
1332
1333
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001334class X509Store(object):
1335 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001336 store = _lib.X509_STORE_new()
1337 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001338
1339
1340 def add_cert(self, cert):
1341 if not isinstance(cert, X509):
1342 raise TypeError()
1343
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001344 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001345 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001346 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001347
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001348
1349X509StoreType = X509Store
1350
1351
1352
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001353def load_certificate(type, buffer):
1354 """
1355 Load a certificate from a buffer
1356
1357 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1358
1359 :param buffer: The buffer the certificate is stored in
1360 :type buffer: :py:class:`bytes`
1361
1362 :return: The X509 object
1363 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001364 if isinstance(buffer, _text_type):
1365 buffer = buffer.encode("ascii")
1366
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001367 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001368
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001369 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001370 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001371 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001372 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001373 else:
1374 raise ValueError(
1375 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001376
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001377 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001378 _raise_current_error()
1379
1380 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001381 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001382 return cert
1383
1384
1385def dump_certificate(type, cert):
1386 """
1387 Dump a certificate to a buffer
1388
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001389 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1390 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001391 :param cert: The certificate to dump
1392 :return: The buffer with the dumped certificate in
1393 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001394 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001395
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001396 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001397 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001398 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001399 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001400 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001401 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001402 else:
1403 raise ValueError(
1404 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1405 "FILETYPE_TEXT")
1406
1407 return _bio_to_string(bio)
1408
1409
1410
1411def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1412 """
1413 Dump a private key to a buffer
1414
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001415 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1416 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001417 :param pkey: The PKey to dump
1418 :param cipher: (optional) if encrypted PEM format, the cipher to
1419 use
1420 :param passphrase: (optional) if encrypted PEM format, this can be either
1421 the passphrase to use, or a callback for providing the
1422 passphrase.
1423 :return: The buffer with the dumped key in
1424 :rtype: :py:data:`str`
1425 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001426 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001427
1428 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001429 if passphrase is None:
1430 raise TypeError(
1431 "if a value is given for cipher "
1432 "one must also be given for passphrase")
1433 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001434 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001435 raise ValueError("Invalid cipher name")
1436 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001437 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001438
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001439 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001440 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001441 result_code = _lib.PEM_write_bio_PrivateKey(
1442 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001443 helper.callback, helper.callback_args)
1444 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001445 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001446 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001447 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001448 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1449 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001450 # TODO RSA_free(rsa)?
1451 else:
1452 raise ValueError(
1453 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1454 "FILETYPE_TEXT")
1455
1456 if result_code == 0:
1457 _raise_current_error()
1458
1459 return _bio_to_string(bio)
1460
1461
1462
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001463def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001464 copy = _lib.X509_REVOKED_new()
1465 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001466 # TODO: This is untested.
1467 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001468
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001469 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001470 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001471 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001472
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001473 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001474 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001475 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001476
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001477 if original.extensions != _ffi.NULL:
1478 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1479 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1480 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1481 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1482 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001483 copy.extensions = extension_stack
1484
1485 copy.sequence = original.sequence
1486 return copy
1487
1488
1489
1490class Revoked(object):
1491 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1492 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1493 # OCSP_crl_reason_str. We use the latter, just like the command line
1494 # program.
1495 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001496 b"unspecified",
1497 b"keyCompromise",
1498 b"CACompromise",
1499 b"affiliationChanged",
1500 b"superseded",
1501 b"cessationOfOperation",
1502 b"certificateHold",
1503 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001504 ]
1505
1506 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001507 revoked = _lib.X509_REVOKED_new()
1508 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001509
1510
1511 def set_serial(self, hex_str):
1512 """
1513 Set the serial number of a revoked Revoked structure
1514
1515 :param hex_str: The new serial number.
1516 :type hex_str: :py:data:`str`
1517 :return: None
1518 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001519 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1520 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001521 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001522 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001523 if not bn_result:
1524 raise ValueError("bad hex string")
1525
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001526 asn1_serial = _ffi.gc(
1527 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1528 _lib.ASN1_INTEGER_free)
1529 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001530
1531
1532 def get_serial(self):
1533 """
1534 Return the serial number of a Revoked structure
1535
1536 :return: The serial number as a string
1537 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001538 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001539
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001540 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001541 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001542 # TODO: This is untested.
1543 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001544
1545 return _bio_to_string(bio)
1546
1547
1548 def _delete_reason(self):
1549 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001550 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1551 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1552 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1553 _lib.X509_EXTENSION_free(ext)
1554 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001555 break
1556
1557
1558 def set_reason(self, reason):
1559 """
1560 Set the reason of a Revoked object.
1561
1562 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1563
1564 :param reason: The reason string.
1565 :type reason: :py:class:`str` or :py:class:`NoneType`
1566 :return: None
1567 """
1568 if reason is None:
1569 self._delete_reason()
1570 elif not isinstance(reason, bytes):
1571 raise TypeError("reason must be None or a byte string")
1572 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001573 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001574 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1575
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001576 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1577 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001578 # TODO: This is untested.
1579 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001580 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001581
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001582 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1583 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001584 # TODO: This is untested.
1585 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001586
1587 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001588 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1589 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001590
1591 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001592 # TODO: This is untested.
1593 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001594
1595
1596 def get_reason(self):
1597 """
1598 Return the reason of a Revoked object.
1599
1600 :return: The reason as a string
1601 """
1602 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001603 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1604 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1605 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001606 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001607
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001608 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001609 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001610 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001611 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001612 # TODO: This is untested.
1613 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001614
1615 return _bio_to_string(bio)
1616
1617
1618 def all_reasons(self):
1619 """
1620 Return a list of all the supported reason strings.
1621
1622 :return: A list of reason strings.
1623 """
1624 return self._crl_reasons[:]
1625
1626
1627 def set_rev_date(self, when):
1628 """
1629 Set the revocation timestamp
1630
1631 :param when: A string giving the timestamp, in the format:
1632
1633 YYYYMMDDhhmmssZ
1634 YYYYMMDDhhmmss+hhmm
1635 YYYYMMDDhhmmss-hhmm
1636
1637 :return: None
1638 """
1639 return _set_asn1_time(self._revoked.revocationDate, when)
1640
1641
1642 def get_rev_date(self):
1643 """
1644 Retrieve the revocation date
1645
1646 :return: A string giving the timestamp, in the format:
1647
1648 YYYYMMDDhhmmssZ
1649 YYYYMMDDhhmmss+hhmm
1650 YYYYMMDDhhmmss-hhmm
1651 """
1652 return _get_asn1_time(self._revoked.revocationDate)
1653
1654
1655
1656class CRL(object):
1657 def __init__(self):
1658 """
1659 Create a new empty CRL object.
1660 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001661 crl = _lib.X509_CRL_new()
1662 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001663
1664
1665 def get_revoked(self):
1666 """
1667 Return revoked portion of the CRL structure (by value not reference).
1668
1669 :return: A tuple of Revoked objects.
1670 """
1671 results = []
1672 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001673 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1674 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001675 revoked_copy = _X509_REVOKED_dup(revoked)
1676 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001677 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001678 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001679 if results:
1680 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001681
1682
1683 def add_revoked(self, revoked):
1684 """
1685 Add a revoked (by value not reference) to the CRL structure
1686
1687 :param revoked: The new revoked.
1688 :type revoked: :class:`X509`
1689
1690 :return: None
1691 """
1692 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001693 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001694 # TODO: This is untested.
1695 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001696
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001697 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001698 if add_result == 0:
1699 # TODO: This is untested.
1700 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001701
1702
1703 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1704 """
1705 export a CRL as a string
1706
1707 :param cert: Used to sign CRL.
1708 :type cert: :class:`X509`
1709
1710 :param key: Used to sign CRL.
1711 :type key: :class:`PKey`
1712
1713 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1714
1715 :param days: The number of days until the next update of this CRL.
1716 :type days: :py:data:`int`
1717
1718 :return: :py:data:`str`
1719 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001720 if not isinstance(cert, X509):
1721 raise TypeError("cert must be an X509 instance")
1722 if not isinstance(key, PKey):
1723 raise TypeError("key must be a PKey instance")
1724 if not isinstance(type, int):
1725 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001726
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001727 bio = _lib.BIO_new(_lib.BIO_s_mem())
1728 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001729 # TODO: This is untested.
1730 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001731
1732 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001733 sometime = _lib.ASN1_TIME_new()
1734 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001735 # TODO: This is untested.
1736 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001737
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001738 _lib.X509_gmtime_adj(sometime, 0)
1739 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001740
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001741 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1742 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001743
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001744 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001745
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001746 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001747 if not sign_result:
1748 _raise_current_error()
1749
1750 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001751 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001752 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001753 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001754 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001755 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001756 else:
1757 raise ValueError(
1758 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1759
1760 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001761 # TODO: This is untested.
1762 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001763
1764 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001765CRLType = CRL
1766
1767
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001768
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001769class PKCS7(object):
1770 def type_is_signed(self):
1771 """
1772 Check if this NID_pkcs7_signed object
1773
1774 :return: True if the PKCS7 is of type signed
1775 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001776 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001777 return True
1778 return False
1779
1780
1781 def type_is_enveloped(self):
1782 """
1783 Check if this NID_pkcs7_enveloped object
1784
1785 :returns: True if the PKCS7 is of type enveloped
1786 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001787 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001788 return True
1789 return False
1790
1791
1792 def type_is_signedAndEnveloped(self):
1793 """
1794 Check if this NID_pkcs7_signedAndEnveloped object
1795
1796 :returns: True if the PKCS7 is of type signedAndEnveloped
1797 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001798 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001799 return True
1800 return False
1801
1802
1803 def type_is_data(self):
1804 """
1805 Check if this NID_pkcs7_data object
1806
1807 :return: True if the PKCS7 is of type data
1808 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001809 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001810 return True
1811 return False
1812
1813
1814 def get_type_name(self):
1815 """
1816 Returns the type name of the PKCS7 structure
1817
1818 :return: A string with the typename
1819 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001820 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1821 string_type = _lib.OBJ_nid2sn(nid)
1822 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001823
1824PKCS7Type = PKCS7
1825
1826
1827
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001828class PKCS12(object):
1829 def __init__(self):
1830 self._pkey = None
1831 self._cert = None
1832 self._cacerts = None
1833 self._friendlyname = None
1834
1835
1836 def get_certificate(self):
1837 """
1838 Return certificate portion of the PKCS12 structure
1839
1840 :return: X509 object containing the certificate
1841 """
1842 return self._cert
1843
1844
1845 def set_certificate(self, cert):
1846 """
1847 Replace the certificate portion of the PKCS12 structure
1848
1849 :param cert: The new certificate.
1850 :type cert: :py:class:`X509` or :py:data:`None`
1851 :return: None
1852 """
1853 if not isinstance(cert, X509):
1854 raise TypeError("cert must be an X509 instance")
1855 self._cert = cert
1856
1857
1858 def get_privatekey(self):
1859 """
1860 Return private key portion of the PKCS12 structure
1861
1862 :returns: PKey object containing the private key
1863 """
1864 return self._pkey
1865
1866
1867 def set_privatekey(self, pkey):
1868 """
1869 Replace or set the certificate portion of the PKCS12 structure
1870
1871 :param pkey: The new private key.
1872 :type pkey: :py:class:`PKey`
1873 :return: None
1874 """
1875 if not isinstance(pkey, PKey):
1876 raise TypeError("pkey must be a PKey instance")
1877 self._pkey = pkey
1878
1879
1880 def get_ca_certificates(self):
1881 """
1882 Return CA certificates within of the PKCS12 object
1883
1884 :return: A newly created tuple containing the CA certificates in the chain,
1885 if any are present, or None if no CA certificates are present.
1886 """
1887 if self._cacerts is not None:
1888 return tuple(self._cacerts)
1889
1890
1891 def set_ca_certificates(self, cacerts):
1892 """
1893 Replace or set the CA certificates withing the PKCS12 object.
1894
1895 :param cacerts: The new CA certificates.
1896 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1897 :return: None
1898 """
1899 if cacerts is None:
1900 self._cacerts = None
1901 else:
1902 cacerts = list(cacerts)
1903 for cert in cacerts:
1904 if not isinstance(cert, X509):
1905 raise TypeError("iterable must only contain X509 instances")
1906 self._cacerts = cacerts
1907
1908
1909 def set_friendlyname(self, name):
1910 """
1911 Replace or set the certificate portion of the PKCS12 structure
1912
1913 :param name: The new friendly name.
1914 :type name: :py:class:`bytes`
1915 :return: None
1916 """
1917 if name is None:
1918 self._friendlyname = None
1919 elif not isinstance(name, bytes):
1920 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1921 self._friendlyname = name
1922
1923
1924 def get_friendlyname(self):
1925 """
1926 Return friendly name portion of the PKCS12 structure
1927
1928 :returns: String containing the friendlyname
1929 """
1930 return self._friendlyname
1931
1932
1933 def export(self, passphrase=None, iter=2048, maciter=1):
1934 """
1935 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1936
1937 :param passphrase: used to encrypt the PKCS12
1938 :type passphrase: :py:data:`bytes`
1939
1940 :param iter: How many times to repeat the encryption
1941 :type iter: :py:data:`int`
1942
1943 :param maciter: How many times to repeat the MAC
1944 :type maciter: :py:data:`int`
1945
1946 :return: The string containing the PKCS12
1947 """
1948 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001949 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001950 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001951 cacerts = _lib.sk_X509_new_null()
1952 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001953 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001954 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001955
1956 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001957 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001958
1959 friendlyname = self._friendlyname
1960 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001961 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001962
1963 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001964 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001965 else:
1966 pkey = self._pkey._pkey
1967
1968 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001969 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001970 else:
1971 cert = self._cert._x509
1972
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001973 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001974 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001975 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1976 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001977 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001978 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001979 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001980 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001981
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001982 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001983 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001984 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001985
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001986PKCS12Type = PKCS12
1987
1988
1989
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001990class NetscapeSPKI(object):
1991 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001992 spki = _lib.NETSCAPE_SPKI_new()
1993 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001994
1995
1996 def sign(self, pkey, digest):
1997 """
1998 Sign the certificate request using the supplied key and digest
1999
2000 :param pkey: The key to sign with
2001 :param digest: The message digest to use
2002 :return: None
2003 """
2004 if pkey._only_public:
2005 raise ValueError("Key has only public part")
2006
2007 if not pkey._initialized:
2008 raise ValueError("Key is uninitialized")
2009
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002010 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002011 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002012 raise ValueError("No such digest method")
2013
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002014 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002015 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002016 # TODO: This is untested.
2017 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002018
2019
2020 def verify(self, key):
2021 """
2022 Verifies a certificate request using the supplied public key
2023
2024 :param key: a public key
2025 :return: True if the signature is correct.
2026 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2027 problem verifying the signature.
2028 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002029 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002030 if answer <= 0:
2031 _raise_current_error()
2032 return True
2033
2034
2035 def b64_encode(self):
2036 """
2037 Generate a base64 encoded string from an SPKI
2038
2039 :return: The base64 encoded string
2040 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002041 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2042 result = _ffi.string(encoded)
2043 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002044 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002045
2046
2047 def get_pubkey(self):
2048 """
2049 Get the public key of the certificate
2050
2051 :return: The public key
2052 """
2053 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002054 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2055 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002056 # TODO: This is untested.
2057 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002058 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002059 pkey._only_public = True
2060 return pkey
2061
2062
2063 def set_pubkey(self, pkey):
2064 """
2065 Set the public key of the certificate
2066
2067 :param pkey: The public key
2068 :return: None
2069 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002070 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002071 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002072 # TODO: This is untested.
2073 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002074NetscapeSPKIType = NetscapeSPKI
2075
2076
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002077class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002078 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002079 if type != FILETYPE_PEM and passphrase is not None:
2080 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002081 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002082 self._more_args = more_args
2083 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002084 self._problems = []
2085
2086
2087 @property
2088 def callback(self):
2089 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002090 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002091 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002092 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002093 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002094 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002095 else:
2096 raise TypeError("Last argument must be string or callable")
2097
2098
2099 @property
2100 def callback_args(self):
2101 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002102 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002103 elif isinstance(self._passphrase, bytes):
2104 return self._passphrase
2105 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002106 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002107 else:
2108 raise TypeError("Last argument must be string or callable")
2109
2110
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002111 def raise_if_problem(self, exceptionType=Error):
2112 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002113 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002114 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002115 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002116 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002117 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002118 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002119
2120
2121 def _read_passphrase(self, buf, size, rwflag, userdata):
2122 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002123 if self._more_args:
2124 result = self._passphrase(size, rwflag, userdata)
2125 else:
2126 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002127 if not isinstance(result, bytes):
2128 raise ValueError("String expected")
2129 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002130 if self._truncate:
2131 result = result[:size]
2132 else:
2133 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002134 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002135 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002136 return len(result)
2137 except Exception as e:
2138 self._problems.append(e)
2139 return 0
2140
2141
2142
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002143def load_privatekey(type, buffer, passphrase=None):
2144 """
2145 Load a private key from a buffer
2146
2147 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2148 :param buffer: The buffer the key is stored in
2149 :param passphrase: (optional) if encrypted PEM format, this can be
2150 either the passphrase to use, or a callback for
2151 providing the passphrase.
2152
2153 :return: The PKey object
2154 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002155 if isinstance(buffer, _text_type):
2156 buffer = buffer.encode("ascii")
2157
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002158 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002159
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002160 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002161 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002162 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2163 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002164 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002165 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002166 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002167 else:
2168 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2169
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002170 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002171 _raise_current_error()
2172
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002173 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002174 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002175 return pkey
2176
2177
2178
2179def dump_certificate_request(type, req):
2180 """
2181 Dump a certificate request to a buffer
2182
2183 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2184 :param req: The certificate request to dump
2185 :return: The buffer with the dumped certificate request in
2186 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002187 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002188
2189 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002190 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002191 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002192 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002193 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002194 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002195 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002196 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002197
2198 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002199 # TODO: This is untested.
2200 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002201
2202 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002203
2204
2205
2206def load_certificate_request(type, buffer):
2207 """
2208 Load a certificate request from a buffer
2209
2210 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2211 :param buffer: The buffer the certificate request is stored in
2212 :return: The X509Req object
2213 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002214 if isinstance(buffer, _text_type):
2215 buffer = buffer.encode("ascii")
2216
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002217 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002218
2219 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002220 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002221 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002222 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002223 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002224 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002225
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002226 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002227 # TODO: This is untested.
2228 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002229
2230 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002231 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002232 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002233
2234
2235
2236def sign(pkey, data, digest):
2237 """
2238 Sign data with a digest
2239
2240 :param pkey: Pkey to sign with
2241 :param data: data to be signed
2242 :param digest: message digest to use
2243 :return: signature
2244 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002245 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002246 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002247 raise ValueError("No such digest method")
2248
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002249 md_ctx = _ffi.new("EVP_MD_CTX*")
2250 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002251
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002252 _lib.EVP_SignInit(md_ctx, digest_obj)
2253 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002254
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002255 signature_buffer = _ffi.new("unsigned char[]", 512)
2256 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002257 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002258 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002259 md_ctx, signature_buffer, signature_length, pkey._pkey)
2260
2261 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002262 # TODO: This is untested.
2263 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002264
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002265 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002266
2267
2268
2269def verify(cert, signature, data, digest):
2270 """
2271 Verify a signature
2272
2273 :param cert: signing certificate (X509 object)
2274 :param signature: signature returned by sign function
2275 :param data: data to be verified
2276 :param digest: message digest to use
2277 :return: None if the signature is correct, raise exception otherwise
2278 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002279 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002280 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002281 raise ValueError("No such digest method")
2282
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002283 pkey = _lib.X509_get_pubkey(cert._x509)
2284 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002285 # TODO: This is untested.
2286 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002287 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002288
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002289 md_ctx = _ffi.new("EVP_MD_CTX*")
2290 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002291
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002292 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2293 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2294 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002295
2296 if verify_result != 1:
2297 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002298
2299
2300
2301def load_crl(type, buffer):
2302 """
2303 Load a certificate revocation list from a buffer
2304
2305 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2306 :param buffer: The buffer the CRL is stored in
2307
2308 :return: The PKey object
2309 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002310 if isinstance(buffer, _text_type):
2311 buffer = buffer.encode("ascii")
2312
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002313 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002314
2315 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002316 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002317 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002318 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002319 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002320 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2321
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002322 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002323 _raise_current_error()
2324
2325 result = CRL.__new__(CRL)
2326 result._crl = crl
2327 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002328
2329
2330
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002331def load_pkcs7_data(type, buffer):
2332 """
2333 Load pkcs7 data from a buffer
2334
2335 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2336 :param buffer: The buffer with the pkcs7 data.
2337 :return: The PKCS7 object
2338 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002339 if isinstance(buffer, _text_type):
2340 buffer = buffer.encode("ascii")
2341
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002342 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002343
2344 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002345 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002346 elif type == FILETYPE_ASN1:
2347 pass
2348 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002349 # TODO: This is untested.
2350 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002351 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2352
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002353 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002354 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002355
2356 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002357 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002358 return pypkcs7
2359
2360
2361
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002362def load_pkcs12(buffer, passphrase):
2363 """
2364 Load a PKCS12 object from a buffer
2365
2366 :param buffer: The buffer the certificate is stored in
2367 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2368 :returns: The PKCS12 object
2369 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002370 if isinstance(buffer, _text_type):
2371 buffer = buffer.encode("ascii")
2372
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002373 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002374
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002375 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2376 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002377 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002378 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002379
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002380 pkey = _ffi.new("EVP_PKEY**")
2381 cert = _ffi.new("X509**")
2382 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002383
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002384 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002385 if not parse_result:
2386 _raise_current_error()
2387
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002388 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002389
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002390 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2391 # queue for no particular reason. This error isn't interesting to anyone
2392 # outside this function. It's not even interesting to us. Get rid of it.
2393 try:
2394 _raise_current_error()
2395 except Error:
2396 pass
2397
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002398 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002399 pykey = None
2400 else:
2401 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002402 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002403
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002404 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002405 pycert = None
2406 friendlyname = None
2407 else:
2408 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002409 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002410
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002411 friendlyname_length = _ffi.new("int*")
2412 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2413 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2414 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002415 friendlyname = None
2416
2417 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002418 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002419 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002420 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002421 pycacerts.append(pycacert)
2422 if not pycacerts:
2423 pycacerts = None
2424
2425 pkcs12 = PKCS12.__new__(PKCS12)
2426 pkcs12._pkey = pykey
2427 pkcs12._cert = pycert
2428 pkcs12._cacerts = pycacerts
2429 pkcs12._friendlyname = friendlyname
2430 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002431
2432
2433def _initialize_openssl_threads(get_ident, Lock):
2434 import _ssl
2435 return
2436
2437 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2438
2439 def locking_function(mode, index, filename, line):
2440 if mode & _lib.CRYPTO_LOCK:
2441 locks[index].acquire()
2442 else:
2443 locks[index].release()
2444
2445 _lib.CRYPTO_set_id_callback(
2446 _ffi.callback("unsigned long (*)(void)", get_ident))
2447
2448 _lib.CRYPTO_set_locking_callback(
2449 _ffi.callback(
2450 "void (*)(int, int, const char*, int)", locking_function))
2451
2452
2453try:
2454 from thread import get_ident
2455 from threading import Lock
2456except ImportError:
2457 pass
2458else:
2459 _initialize_openssl_threads(get_ident, Lock)
2460 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002461
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002462# There are no direct unit tests for this initialization. It is tested
2463# indirectly since it is necessary for functions like dump_privatekey when
2464# using encryption.
2465#
2466# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2467# and some other similar tests may fail without this (though they may not if
2468# the Python runtime has already done some initialization of the underlying
2469# OpenSSL library (and is linked against the same one that cryptography is
2470# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002471_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002472
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002473# This is similar but exercised mainly by exception_from_error_queue. It calls
2474# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2475_lib.SSL_load_error_strings()