blob: 0c4b2f826b26b8cb0a3223733127135cdc44d424 [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,
8 text_type as _text_type)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08009
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050010from OpenSSL._util import (
11 ffi as _ffi,
12 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050013 exception_from_error_queue as _exception_from_error_queue,
14 byte_string as _byte_string,
15 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080016
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050017FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
18FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080019
20# TODO This was an API mistake. OpenSSL has no such constant.
21FILETYPE_TEXT = 2 ** 16 - 1
22
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050023TYPE_RSA = _lib.EVP_PKEY_RSA
24TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080025
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080026
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050027class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050028 """
29 An error occurred in an `OpenSSL.crypto` API.
30 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050031
32
33_raise_current_error = partial(_exception_from_error_queue, Error)
34
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050035def _untested_error(where):
36 """
37 An OpenSSL API failed somehow. Additionally, the failure which was
38 encountered isn't one that's exercised by the test suite so future behavior
39 of pyOpenSSL is now somewhat less predictable.
40 """
41 raise RuntimeError("Unknown %s failure" % (where,))
42
43
44
45def _new_mem_buf(buffer=None):
46 """
47 Allocate a new OpenSSL memory BIO.
48
49 Arrange for the garbage collector to clean it up automatically.
50
51 :param buffer: None or some bytes to use to put into the BIO so that they
52 can be read out.
53 """
54 if buffer is None:
55 bio = _lib.BIO_new(_lib.BIO_s_mem())
56 free = _lib.BIO_free
57 else:
58 data = _ffi.new("char[]", buffer)
59 bio = _lib.BIO_new_mem_buf(data, len(buffer))
60 # Keep the memory alive as long as the bio is alive!
61 def free(bio, ref=data):
62 return _lib.BIO_free(bio)
63
64 if bio == _ffi.NULL:
65 # TODO: This is untested.
66 _raise_current_error()
67
68 bio = _ffi.gc(bio, free)
69 return bio
70
71
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050072
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080073def _bio_to_string(bio):
74 """
75 Copy the contents of an OpenSSL BIO object into a Python byte string.
76 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050077 result_buffer = _ffi.new('char**')
78 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
79 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080080
81
82
Jean-Paul Calderone57122982013-02-21 08:47:05 -080083def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050084 """
85 The the time value of an ASN1 time object.
86
87 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
88 castable to that type) which will have its value set.
89 @param when: A string representation of the desired time value.
90
91 @raise TypeError: If C{when} is not a L{bytes} string.
92 @raise ValueError: If C{when} does not represent a time in the required
93 format.
94 @raise RuntimeError: If the time value cannot be set for some other
95 (unspecified) reason.
96 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080097 if not isinstance(when, bytes):
98 raise TypeError("when must be a byte string")
99
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500100 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
101 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800102 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500103 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
104 _lib.ASN1_STRING_set(dummy, when, len(when))
105 check_result = _lib.ASN1_GENERALIZEDTIME_check(
106 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800107 if not check_result:
108 raise ValueError("Invalid string")
109 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500110 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800111
112
113
114def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500115 """
116 Retrieve the time value of an ASN1 time object.
117
118 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
119 that type) from which the time value will be retrieved.
120
121 @return: The time value from C{timestamp} as a L{bytes} string in a certain
122 format. Or C{None} if the object contains no time value.
123 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500124 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
125 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800126 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500127 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
128 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800129 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500130 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
131 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
132 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500133 # This may happen:
134 # - if timestamp was not an ASN1_TIME
135 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
136 # - if a copy of the time data from timestamp cannot be made for
137 # the newly allocated ASN1_GENERALIZEDTIME
138 #
139 # These are difficult to test. cffi enforces the ASN1_TIME type.
140 # Memory allocation failures are a pain to trigger
141 # deterministically.
142 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800143 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500144 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800145 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500146 string_data = _lib.ASN1_STRING_data(string_timestamp)
147 string_result = _ffi.string(string_data)
148 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800149 return string_result
150
151
152
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800153class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800154 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800155 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800156
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800157 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500158 pkey = _lib.EVP_PKEY_new()
159 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800160 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800161
162
163 def generate_key(self, type, bits):
164 """
165 Generate a key of a given type, with a given number of a bits
166
167 :param type: The key type (TYPE_RSA or TYPE_DSA)
168 :param bits: The number of bits
169
170 :return: None
171 """
172 if not isinstance(type, int):
173 raise TypeError("type must be an integer")
174
175 if not isinstance(bits, int):
176 raise TypeError("bits must be an integer")
177
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800178 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500179 exponent = _lib.BN_new()
180 exponent = _ffi.gc(exponent, _lib.BN_free)
181 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800182
183 if type == TYPE_RSA:
184 if bits <= 0:
185 raise ValueError("Invalid number of bits")
186
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500187 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800188
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500189 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500190 if result == 0:
191 # TODO: The test for this case is commented out. Different
192 # builds of OpenSSL appear to have different failure modes that
193 # make it hard to test. Visual inspection of the OpenSSL
194 # source reveals that a return value of 0 signals an error.
195 # Manual testing on a particular build of OpenSSL suggests that
196 # this is probably the appropriate way to handle those errors.
197 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800198
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500199 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800200 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500201 # TODO: It appears as though this can fail if an engine is in
202 # use which does not support RSA.
203 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800204
205 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500206 dsa = _lib.DSA_generate_parameters(
207 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
208 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500209 # TODO: This is untested.
210 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500211 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500212 # TODO: This is untested.
213 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500214 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500215 # TODO: This is untested.
216 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800217 else:
218 raise Error("No such key type")
219
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800220 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800221
222
223 def check(self):
224 """
225 Check the consistency of an RSA private key.
226
227 :return: True if key is consistent.
228 :raise Error: if the key is inconsistent.
229 :raise TypeError: if the key is of a type which cannot be checked.
230 Only RSA keys can currently be checked.
231 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800232 if self._only_public:
233 raise TypeError("public key only")
234
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500235 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800236 raise TypeError("key type unsupported")
237
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500238 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
239 rsa = _ffi.gc(rsa, _lib.RSA_free)
240 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800241 if result:
242 return True
243 _raise_current_error()
244
245
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800246 def type(self):
247 """
248 Returns the type of the key
249
250 :return: The type of the key.
251 """
252 return self._pkey.type
253
254
255 def bits(self):
256 """
257 Returns the number of bits of the key
258
259 :return: The number of bits of the key.
260 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500261 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800262PKeyType = PKey
263
264
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800265
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400266class _EllipticCurve(object):
267 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400268 A representation of a supported elliptic curve.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400269 """
270 @classmethod
271 def _get_elliptic_curves(cls, lib):
272 """
273 Load the names of the supported elliptic curves from OpenSSL.
274
275 :param lib: The OpenSSL library binding object.
276 :return: A set of :py:obj:`unicode` giving the names of the elliptic curves
277 the underlying library supports.
278 """
279 if lib.Cryptography_HAS_EC:
280 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
281 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
282 # The return value on this call should be num_curves again. We could
283 # check it to make sure but if it *isn't* then.. what could we do?
284 # Abort the whole process, I suppose...? -exarkun
285 lib.EC_get_builtin_curves(builtin_curves, num_curves)
286 return set(
287 cls.from_nid(lib, c.nid)
288 for c in builtin_curves)
289 else:
290 return set()
291
292
293 @classmethod
294 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400295 """
296 Instantiate a new :py:class:`_EllipticCurve` associated with the given
297 OpenSSL NID.
298
299 :param lib: The OpenSSL library binding object.
300
301 :param nid: The OpenSSL NID the resulting curve object will represent.
302 This must be a curve NID (and not, for example, a hash NID) or
303 subsequent operations will fail in unpredictable ways.
304 :type nid: :py:class:`int`
305
306 :return: The curve object.
307 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400308 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
309
310
311 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400312 """
313 :param _lib: The :py:mod:`cryptography` binding instance used to
314 interface with OpenSSL.
315
316 :param _nid: The OpenSSL NID identifying the curve this object
317 represents.
318 :type _nid: :py:class:`int`
319
320 :param name: The OpenSSL short name identifying the curve this object
321 represents.
322 :type name: :py:class:`unicode`
323 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400324 self._lib = lib
325 self._nid = nid
326 self.name = name
327
328
329 def __repr__(self):
330 return "<Curve %r>" % (self.name,)
331
332
333 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400334 """
335 Create a new OpenSSL EC_KEY structure initialized to use this curve.
336
337 The structure is automatically garbage collected when the Python object
338 is garbage collected.
339 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400340 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
341 return _ffi.gc(key, _lib.EC_KEY_free)
342
343
344
345def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400346 """
347 Return a set of objects representing the elliptic curves supported in the
348 OpenSSL build in use.
349
350 The curve objects have a :py:class:`unicode` ``name`` attribute by which
351 they identify themselves.
352
353 The curve objects are useful as values for the argument accepted by
354 :py:meth:`Context.set_tmp_ecdh_curve` to specify which elliptical curve
355 should be used for ECDHE key exchange.
356 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400357 return _EllipticCurve._get_elliptic_curves(_lib)
358
359
360
361def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400362 """
363 Return a single curve object selected by name.
364
365 See :py:func:`get_elliptic_curves` for information about curve objects.
366
367 If the named curve is not supported then :py:class:`ValueError` is raised.
368 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400369 for curve in get_elliptic_curves():
370 if curve.name == name:
371 return curve
372 raise ValueError("unknown curve name", name)
373
374
375
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800376class X509Name(object):
377 def __init__(self, name):
378 """
379 Create a new X509Name, copying the given X509Name instance.
380
381 :param name: An X509Name object to copy
382 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500383 name = _lib.X509_NAME_dup(name._name)
384 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800385
386
387 def __setattr__(self, name, value):
388 if name.startswith('_'):
389 return super(X509Name, self).__setattr__(name, value)
390
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800391 # Note: we really do not want str subclasses here, so we do not use
392 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800393 if type(name) is not str:
394 raise TypeError("attribute name must be string, not '%.200s'" % (
395 type(value).__name__,))
396
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500397 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500398 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800399 try:
400 _raise_current_error()
401 except Error:
402 pass
403 raise AttributeError("No such attribute")
404
405 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500406 for i in range(_lib.X509_NAME_entry_count(self._name)):
407 ent = _lib.X509_NAME_get_entry(self._name, i)
408 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
409 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800410 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500411 ent = _lib.X509_NAME_delete_entry(self._name, i)
412 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800413 break
414
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500415 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800416 value = value.encode('utf-8')
417
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500418 add_result = _lib.X509_NAME_add_entry_by_NID(
419 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800420 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500421 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800422
423
424 def __getattr__(self, name):
425 """
426 Find attribute. An X509Name object has the following attributes:
427 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
428 organization (alias O), organizationalUnit (alias OU), commonName (alias
429 CN) and more...
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 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
434 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
435 # push something onto the error queue. If we don't clean that up
436 # now, someone else will bump into it later and be quite confused.
437 # See lp#314814.
438 try:
439 _raise_current_error()
440 except Error:
441 pass
442 return super(X509Name, self).__getattr__(name)
443
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500444 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800445 if entry_index == -1:
446 return None
447
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500448 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
449 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800450
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500451 result_buffer = _ffi.new("unsigned char**")
452 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800453 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500454 # TODO: This is untested.
455 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800456
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700457 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500458 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700459 finally:
460 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500461 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800462 return result
463
464
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500465 def _cmp(op):
466 def f(self, other):
467 if not isinstance(other, X509Name):
468 return NotImplemented
469 result = _lib.X509_NAME_cmp(self._name, other._name)
470 return op(result, 0)
471 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800472
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500473 __eq__ = _cmp(__eq__)
474 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800475
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500476 __lt__ = _cmp(__lt__)
477 __le__ = _cmp(__le__)
478
479 __gt__ = _cmp(__gt__)
480 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800481
482 def __repr__(self):
483 """
484 String representation of an X509Name
485 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500486 result_buffer = _ffi.new("char[]", 512);
487 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800488 self._name, result_buffer, len(result_buffer))
489
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500490 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500491 # TODO: This is untested.
492 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800493
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500494 return "<X509Name object '%s'>" % (
495 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800496
497
498 def hash(self):
499 """
500 Return the hash value of this name
501
502 :return: None
503 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500504 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800505
506
507 def der(self):
508 """
509 Return the DER encoding of this name
510
511 :return: A :py:class:`bytes` instance giving the DER encoded form of
512 this name.
513 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500514 result_buffer = _ffi.new('unsigned char**')
515 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800516 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500517 # TODO: This is untested.
518 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800519
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500520 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
521 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522 return string_result
523
524
525 def get_components(self):
526 """
527 Returns the split-up components of this name.
528
529 :return: List of tuples (name, value).
530 """
531 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500532 for i in range(_lib.X509_NAME_entry_count(self._name)):
533 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800534
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500535 fname = _lib.X509_NAME_ENTRY_get_object(ent)
536 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800537
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500538 nid = _lib.OBJ_obj2nid(fname)
539 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800540
541 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500542 _ffi.string(name),
543 _ffi.string(
544 _lib.ASN1_STRING_data(fval),
545 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800546
547 return result
548X509NameType = X509Name
549
550
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800551class X509Extension(object):
552 def __init__(self, type_name, critical, value, subject=None, issuer=None):
553 """
554 :param typename: The name of the extension to create.
555 :type typename: :py:data:`str`
556
557 :param critical: A flag indicating whether this is a critical extension.
558
559 :param value: The value of the extension.
560 :type value: :py:data:`str`
561
562 :param subject: Optional X509 cert to use as subject.
563 :type subject: :py:class:`X509`
564
565 :param issuer: Optional X509 cert to use as issuer.
566 :type issuer: :py:class:`X509`
567
568 :return: The X509Extension object
569 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500570 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800571
572 # A context is necessary for any extension which uses the r2i conversion
573 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
574 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500575 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800576
577 # We have no configuration database - but perhaps we should (some
578 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500579 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800580
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800581 # Initialize the subject and issuer, if appropriate. ctx is a local,
582 # and as far as I can tell none of the X509V3_* APIs invoked here steal
583 # any references, so no need to mess with reference counts or duplicates.
584 if issuer is not None:
585 if not isinstance(issuer, X509):
586 raise TypeError("issuer must be an X509 instance")
587 ctx.issuer_cert = issuer._x509
588 if subject is not None:
589 if not isinstance(subject, X509):
590 raise TypeError("subject must be an X509 instance")
591 ctx.subject_cert = subject._x509
592
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800593 if critical:
594 # There are other OpenSSL APIs which would let us pass in critical
595 # separately, but they're harder to use, and since value is already
596 # a pile of crappy junk smuggling a ton of utterly important
597 # structured data, what's the point of trying to avoid nasty stuff
598 # with strings? (However, X509V3_EXT_i2d in particular seems like it
599 # would be a better API to invoke. I do not know where to get the
600 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500601 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800602
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500603 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
604 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800605 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500606 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800607
608
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400609 @property
610 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500611 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400612
613 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500614 _lib.GEN_EMAIL: "email",
615 _lib.GEN_DNS: "DNS",
616 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400617 }
618
619 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500620 method = _lib.X509V3_EXT_get(self._extension)
621 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500622 # TODO: This is untested.
623 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400624 payload = self._extension.value.data
625 length = self._extension.value.length
626
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500627 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400628 payloadptr[0] = payload
629
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500630 if method.it != _ffi.NULL:
631 ptr = _lib.ASN1_ITEM_ptr(method.it)
632 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
633 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400634 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500635 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400636 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500637 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400638
639 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500640 for i in range(_lib.sk_GENERAL_NAME_num(names)):
641 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400642 try:
643 label = self._prefixes[name.type]
644 except KeyError:
645 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500646 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500647 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400648 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500649 value = _native(
650 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
651 parts.append(label + ":" + value)
652 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400653
654
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800655 def __str__(self):
656 """
657 :return: a nice text representation of the extension
658 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500659 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400660 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800661
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400662 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500663 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800664 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500665 # TODO: This is untested.
666 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800667
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500668 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800669
670
671 def get_critical(self):
672 """
673 Returns the critical field of the X509Extension
674
675 :return: The critical field.
676 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500677 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800678
679
680 def get_short_name(self):
681 """
682 Returns the short version of the type name of the X509Extension
683
684 :return: The short type name.
685 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500686 obj = _lib.X509_EXTENSION_get_object(self._extension)
687 nid = _lib.OBJ_obj2nid(obj)
688 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800689
690
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800691 def get_data(self):
692 """
693 Returns the data of the X509Extension
694
695 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
696 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500697 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
698 string_result = _ffi.cast('ASN1_STRING*', octet_result)
699 char_result = _lib.ASN1_STRING_data(string_result)
700 result_length = _lib.ASN1_STRING_length(string_result)
701 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800702
703X509ExtensionType = X509Extension
704
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800705
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800706class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800707 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500708 req = _lib.X509_REQ_new()
709 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800710
711
712 def set_pubkey(self, pkey):
713 """
714 Set the public key of the certificate request
715
716 :param pkey: The public key to use
717 :return: None
718 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500719 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800720 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500721 # TODO: This is untested.
722 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800723
724
725 def get_pubkey(self):
726 """
727 Get the public key from the certificate request
728
729 :return: The public key
730 """
731 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500732 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
733 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500734 # TODO: This is untested.
735 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500736 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800737 pkey._only_public = True
738 return pkey
739
740
741 def set_version(self, version):
742 """
743 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
744 request.
745
746 :param version: The version number
747 :return: None
748 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500749 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800750 if not set_result:
751 _raise_current_error()
752
753
754 def get_version(self):
755 """
756 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
757 request.
758
759 :return: an integer giving the value of the version subfield
760 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500761 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800762
763
764 def get_subject(self):
765 """
766 Create an X509Name object for the subject of the certificate request
767
768 :return: An X509Name object
769 """
770 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500771 name._name = _lib.X509_REQ_get_subject_name(self._req)
772 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500773 # TODO: This is untested.
774 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800775
776 # The name is owned by the X509Req structure. As long as the X509Name
777 # Python object is alive, keep the X509Req Python object alive.
778 name._owner = self
779
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800780 return name
781
782
783 def add_extensions(self, extensions):
784 """
785 Add extensions to the request.
786
787 :param extensions: a sequence of X509Extension objects
788 :return: None
789 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500790 stack = _lib.sk_X509_EXTENSION_new_null()
791 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500792 # TODO: This is untested.
793 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800794
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500795 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800796
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800797 for ext in extensions:
798 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800799 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800800
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800801 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500802 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800803
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500804 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800805 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500806 # TODO: This is untested.
807 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800808
809
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800810 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800811 """
812 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800813
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500814 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800815 """
816 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500817 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500818 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800819 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500820 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800821 exts.append(ext)
822 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800823
824
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800825 def sign(self, pkey, digest):
826 """
827 Sign the certificate request using the supplied key and digest
828
829 :param pkey: The key to sign with
830 :param digest: The message digest to use
831 :return: None
832 """
833 if pkey._only_public:
834 raise ValueError("Key has only public part")
835
836 if not pkey._initialized:
837 raise ValueError("Key is uninitialized")
838
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500839 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500840 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800841 raise ValueError("No such digest method")
842
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500843 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800844 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500845 # TODO: This is untested.
846 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800847
848
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800849 def verify(self, pkey):
850 """
851 Verifies a certificate request using the supplied public key
852
853 :param key: a public key
854 :return: True if the signature is correct.
855
856 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
857 problem verifying the signature.
858 """
859 if not isinstance(pkey, PKey):
860 raise TypeError("pkey must be a PKey instance")
861
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500862 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800863 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500864 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800865
866 return result
867
868
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800869X509ReqType = X509Req
870
871
872
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800873class X509(object):
874 def __init__(self):
875 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500876 x509 = _lib.X509_new()
877 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800878
879
880 def set_version(self, version):
881 """
882 Set version number of the certificate
883
884 :param version: The version number
885 :type version: :py:class:`int`
886
887 :return: None
888 """
889 if not isinstance(version, int):
890 raise TypeError("version must be an integer")
891
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500892 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800893
894
895 def get_version(self):
896 """
897 Return version number of the certificate
898
899 :return: Version number as a Python integer
900 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500901 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800902
903
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800904 def get_pubkey(self):
905 """
906 Get the public key of the certificate
907
908 :return: The public key
909 """
910 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500911 pkey._pkey = _lib.X509_get_pubkey(self._x509)
912 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800913 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500914 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800915 pkey._only_public = True
916 return pkey
917
918
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800919 def set_pubkey(self, pkey):
920 """
921 Set the public key of the certificate
922
923 :param pkey: The public key
924
925 :return: None
926 """
927 if not isinstance(pkey, PKey):
928 raise TypeError("pkey must be a PKey instance")
929
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500930 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800931 if not set_result:
932 _raise_current_error()
933
934
935 def sign(self, pkey, digest):
936 """
937 Sign the certificate using the supplied key and digest
938
939 :param pkey: The key to sign with
940 :param digest: The message digest to use
941 :return: None
942 """
943 if not isinstance(pkey, PKey):
944 raise TypeError("pkey must be a PKey instance")
945
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800946 if pkey._only_public:
947 raise ValueError("Key only has public part")
948
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800949 if not pkey._initialized:
950 raise ValueError("Key is uninitialized")
951
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500952 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500953 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800954 raise ValueError("No such digest method")
955
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500956 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800957 if not sign_result:
958 _raise_current_error()
959
960
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800961 def get_signature_algorithm(self):
962 """
963 Retrieve the signature algorithm used in the certificate
964
965 :return: A byte string giving the name of the signature algorithm used in
966 the certificate.
967 :raise ValueError: If the signature algorithm is undefined.
968 """
969 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500970 nid = _lib.OBJ_obj2nid(alg)
971 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800972 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500973 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800974
975
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800976 def digest(self, digest_name):
977 """
978 Return the digest of the X509 object.
979
980 :param digest_name: The name of the digest algorithm to use.
981 :type digest_name: :py:class:`bytes`
982
983 :return: The digest of the object
984 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500985 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500986 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800987 raise ValueError("No such digest method")
988
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500989 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
990 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800991 result_length[0] = len(result_buffer)
992
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500993 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800994 self._x509, digest, result_buffer, result_length)
995
996 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500997 # TODO: This is untested.
998 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800999
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001000 return b":".join([
1001 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001002 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001003
1004
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001005 def subject_name_hash(self):
1006 """
1007 Return the hash of the X509 subject.
1008
1009 :return: The hash of the subject.
1010 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001011 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001012
1013
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001014 def set_serial_number(self, serial):
1015 """
1016 Set serial number of the certificate
1017
1018 :param serial: The serial number
1019 :type serial: :py:class:`int`
1020
1021 :return: None
1022 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001023 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001024 raise TypeError("serial must be an integer")
1025
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001026 hex_serial = hex(serial)[2:]
1027 if not isinstance(hex_serial, bytes):
1028 hex_serial = hex_serial.encode('ascii')
1029
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001030 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001031
1032 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1033 # it. If bignum is still NULL after this call, then the return value is
1034 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001035 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001036
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001037 if bignum_serial[0] == _ffi.NULL:
1038 set_result = _lib.ASN1_INTEGER_set(
1039 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001040 if set_result:
1041 # TODO Not tested
1042 _raise_current_error()
1043 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001044 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1045 _lib.BN_free(bignum_serial[0])
1046 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001047 # TODO Not tested
1048 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001049 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1050 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001051 if not set_result:
1052 # TODO Not tested
1053 _raise_current_error()
1054
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001055
1056 def get_serial_number(self):
1057 """
1058 Return serial number of the certificate
1059
1060 :return: Serial number as a Python integer
1061 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001062 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1063 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001064 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001065 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001066 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001067 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001068 serial = int(hexstring_serial, 16)
1069 return serial
1070 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001072 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001073 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001074
1075
1076 def gmtime_adj_notAfter(self, amount):
1077 """
1078 Adjust the time stamp for when the certificate stops being valid
1079
1080 :param amount: The number of seconds by which to adjust the ending
1081 validity time.
1082 :type amount: :py:class:`int`
1083
1084 :return: None
1085 """
1086 if not isinstance(amount, int):
1087 raise TypeError("amount must be an integer")
1088
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001089 notAfter = _lib.X509_get_notAfter(self._x509)
1090 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001091
1092
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001093 def gmtime_adj_notBefore(self, amount):
1094 """
1095 Change the timestamp for when the certificate starts being valid to the current
1096 time plus an offset.
1097
1098 :param amount: The number of seconds by which to adjust the starting validity
1099 time.
1100 :return: None
1101 """
1102 if not isinstance(amount, int):
1103 raise TypeError("amount must be an integer")
1104
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001105 notBefore = _lib.X509_get_notBefore(self._x509)
1106 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001107
1108
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001109 def has_expired(self):
1110 """
1111 Check whether the certificate has expired.
1112
1113 :return: True if the certificate has expired, false otherwise
1114 """
1115 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001116 notAfter = _lib.X509_get_notAfter(self._x509)
1117 return _lib.ASN1_UTCTIME_cmp_time_t(
1118 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001119
1120
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001121 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001122 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001123
1124
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001125 def get_notBefore(self):
1126 """
1127 Retrieve the time stamp for when the certificate starts being valid
1128
1129 :return: A string giving the timestamp, in the format::
1130
1131 YYYYMMDDhhmmssZ
1132 YYYYMMDDhhmmss+hhmm
1133 YYYYMMDDhhmmss-hhmm
1134
1135 or None if there is no value set.
1136 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001137 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001138
1139
1140 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001141 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001142
1143
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001144 def set_notBefore(self, when):
1145 """
1146 Set the time stamp for when the certificate starts being valid
1147
1148 :param when: A string giving the timestamp, in the format:
1149
1150 YYYYMMDDhhmmssZ
1151 YYYYMMDDhhmmss+hhmm
1152 YYYYMMDDhhmmss-hhmm
1153 :type when: :py:class:`bytes`
1154
1155 :return: None
1156 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001157 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001158
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001159
1160 def get_notAfter(self):
1161 """
1162 Retrieve the time stamp for when the certificate stops being valid
1163
1164 :return: A string giving the timestamp, in the format::
1165
1166 YYYYMMDDhhmmssZ
1167 YYYYMMDDhhmmss+hhmm
1168 YYYYMMDDhhmmss-hhmm
1169
1170 or None if there is no value set.
1171 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001172 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001173
1174
1175 def set_notAfter(self, when):
1176 """
1177 Set the time stamp for when the certificate stops being valid
1178
1179 :param when: A string giving the timestamp, in the format:
1180
1181 YYYYMMDDhhmmssZ
1182 YYYYMMDDhhmmss+hhmm
1183 YYYYMMDDhhmmss-hhmm
1184 :type when: :py:class:`bytes`
1185
1186 :return: None
1187 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001188 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001189
1190
1191 def _get_name(self, which):
1192 name = X509Name.__new__(X509Name)
1193 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001194 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001195 # TODO: This is untested.
1196 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001197
1198 # The name is owned by the X509 structure. As long as the X509Name
1199 # Python object is alive, keep the X509 Python object alive.
1200 name._owner = self
1201
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001202 return name
1203
1204
1205 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001206 if not isinstance(name, X509Name):
1207 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001208 set_result = which(self._x509, name._name)
1209 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001210 # TODO: This is untested.
1211 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001212
1213
1214 def get_issuer(self):
1215 """
1216 Create an X509Name object for the issuer of the certificate
1217
1218 :return: An X509Name object
1219 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001220 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001221
1222
1223 def set_issuer(self, issuer):
1224 """
1225 Set the issuer of the certificate
1226
1227 :param issuer: The issuer name
1228 :type issuer: :py:class:`X509Name`
1229
1230 :return: None
1231 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001232 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001233
1234
1235 def get_subject(self):
1236 """
1237 Create an X509Name object for the subject of the certificate
1238
1239 :return: An X509Name object
1240 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001241 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001242
1243
1244 def set_subject(self, subject):
1245 """
1246 Set the subject of the certificate
1247
1248 :param subject: The subject name
1249 :type subject: :py:class:`X509Name`
1250 :return: None
1251 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001252 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001253
1254
1255 def get_extension_count(self):
1256 """
1257 Get the number of extensions on the certificate.
1258
1259 :return: The number of extensions as an integer.
1260 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001261 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001262
1263
1264 def add_extensions(self, extensions):
1265 """
1266 Add extensions to the certificate.
1267
1268 :param extensions: a sequence of X509Extension objects
1269 :return: None
1270 """
1271 for ext in extensions:
1272 if not isinstance(ext, X509Extension):
1273 raise ValueError("One of the elements is not an X509Extension")
1274
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001275 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001276 if not add_result:
1277 _raise_current_error()
1278
1279
1280 def get_extension(self, index):
1281 """
1282 Get a specific extension of the certificate by index.
1283
1284 :param index: The index of the extension to retrieve.
1285 :return: The X509Extension object at the specified index.
1286 """
1287 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001288 ext._extension = _lib.X509_get_ext(self._x509, index)
1289 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001290 raise IndexError("extension index out of bounds")
1291
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001292 extension = _lib.X509_EXTENSION_dup(ext._extension)
1293 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001294 return ext
1295
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001296X509Type = X509
1297
1298
1299
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001300class X509Store(object):
1301 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001302 store = _lib.X509_STORE_new()
1303 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001304
1305
1306 def add_cert(self, cert):
1307 if not isinstance(cert, X509):
1308 raise TypeError()
1309
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001310 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001311 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001312 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001313
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001314
1315X509StoreType = X509Store
1316
1317
1318
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001319def load_certificate(type, buffer):
1320 """
1321 Load a certificate from a buffer
1322
1323 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1324
1325 :param buffer: The buffer the certificate is stored in
1326 :type buffer: :py:class:`bytes`
1327
1328 :return: The X509 object
1329 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001330 if isinstance(buffer, _text_type):
1331 buffer = buffer.encode("ascii")
1332
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001333 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001334
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001335 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001336 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001337 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001338 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001339 else:
1340 raise ValueError(
1341 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001342
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001343 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001344 _raise_current_error()
1345
1346 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001347 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001348 return cert
1349
1350
1351def dump_certificate(type, cert):
1352 """
1353 Dump a certificate to a buffer
1354
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001355 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1356 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001357 :param cert: The certificate to dump
1358 :return: The buffer with the dumped certificate in
1359 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001360 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001361
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001362 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001363 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001364 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001365 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001366 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001367 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001368 else:
1369 raise ValueError(
1370 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1371 "FILETYPE_TEXT")
1372
1373 return _bio_to_string(bio)
1374
1375
1376
1377def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1378 """
1379 Dump a private key to a buffer
1380
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001381 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1382 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001383 :param pkey: The PKey to dump
1384 :param cipher: (optional) if encrypted PEM format, the cipher to
1385 use
1386 :param passphrase: (optional) if encrypted PEM format, this can be either
1387 the passphrase to use, or a callback for providing the
1388 passphrase.
1389 :return: The buffer with the dumped key in
1390 :rtype: :py:data:`str`
1391 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001392 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001393
1394 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001395 if passphrase is None:
1396 raise TypeError(
1397 "if a value is given for cipher "
1398 "one must also be given for passphrase")
1399 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001400 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001401 raise ValueError("Invalid cipher name")
1402 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001403 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001404
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001405 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001406 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001407 result_code = _lib.PEM_write_bio_PrivateKey(
1408 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001409 helper.callback, helper.callback_args)
1410 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001411 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001412 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001413 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001414 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1415 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001416 # TODO RSA_free(rsa)?
1417 else:
1418 raise ValueError(
1419 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1420 "FILETYPE_TEXT")
1421
1422 if result_code == 0:
1423 _raise_current_error()
1424
1425 return _bio_to_string(bio)
1426
1427
1428
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001429def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001430 copy = _lib.X509_REVOKED_new()
1431 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001432 # TODO: This is untested.
1433 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001434
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001435 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001436 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001437 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001438
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001439 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001440 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001441 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001442
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001443 if original.extensions != _ffi.NULL:
1444 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1445 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1446 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1447 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1448 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001449 copy.extensions = extension_stack
1450
1451 copy.sequence = original.sequence
1452 return copy
1453
1454
1455
1456class Revoked(object):
1457 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1458 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1459 # OCSP_crl_reason_str. We use the latter, just like the command line
1460 # program.
1461 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001462 b"unspecified",
1463 b"keyCompromise",
1464 b"CACompromise",
1465 b"affiliationChanged",
1466 b"superseded",
1467 b"cessationOfOperation",
1468 b"certificateHold",
1469 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001470 ]
1471
1472 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001473 revoked = _lib.X509_REVOKED_new()
1474 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001475
1476
1477 def set_serial(self, hex_str):
1478 """
1479 Set the serial number of a revoked Revoked structure
1480
1481 :param hex_str: The new serial number.
1482 :type hex_str: :py:data:`str`
1483 :return: None
1484 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001485 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1486 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001487 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001488 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001489 if not bn_result:
1490 raise ValueError("bad hex string")
1491
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001492 asn1_serial = _ffi.gc(
1493 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1494 _lib.ASN1_INTEGER_free)
1495 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001496
1497
1498 def get_serial(self):
1499 """
1500 Return the serial number of a Revoked structure
1501
1502 :return: The serial number as a string
1503 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001504 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001505
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001506 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001507 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001508 # TODO: This is untested.
1509 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001510
1511 return _bio_to_string(bio)
1512
1513
1514 def _delete_reason(self):
1515 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001516 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1517 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1518 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1519 _lib.X509_EXTENSION_free(ext)
1520 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001521 break
1522
1523
1524 def set_reason(self, reason):
1525 """
1526 Set the reason of a Revoked object.
1527
1528 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1529
1530 :param reason: The reason string.
1531 :type reason: :py:class:`str` or :py:class:`NoneType`
1532 :return: None
1533 """
1534 if reason is None:
1535 self._delete_reason()
1536 elif not isinstance(reason, bytes):
1537 raise TypeError("reason must be None or a byte string")
1538 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001539 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001540 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1541
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001542 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1543 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001544 # TODO: This is untested.
1545 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001546 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001547
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001548 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1549 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001550 # TODO: This is untested.
1551 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001552
1553 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001554 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1555 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001556
1557 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001558 # TODO: This is untested.
1559 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001560
1561
1562 def get_reason(self):
1563 """
1564 Return the reason of a Revoked object.
1565
1566 :return: The reason as a string
1567 """
1568 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001569 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1570 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1571 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001572 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001573
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001574 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001575 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001576 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001577 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001578 # TODO: This is untested.
1579 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001580
1581 return _bio_to_string(bio)
1582
1583
1584 def all_reasons(self):
1585 """
1586 Return a list of all the supported reason strings.
1587
1588 :return: A list of reason strings.
1589 """
1590 return self._crl_reasons[:]
1591
1592
1593 def set_rev_date(self, when):
1594 """
1595 Set the revocation timestamp
1596
1597 :param when: A string giving the timestamp, in the format:
1598
1599 YYYYMMDDhhmmssZ
1600 YYYYMMDDhhmmss+hhmm
1601 YYYYMMDDhhmmss-hhmm
1602
1603 :return: None
1604 """
1605 return _set_asn1_time(self._revoked.revocationDate, when)
1606
1607
1608 def get_rev_date(self):
1609 """
1610 Retrieve the revocation date
1611
1612 :return: A string giving the timestamp, in the format:
1613
1614 YYYYMMDDhhmmssZ
1615 YYYYMMDDhhmmss+hhmm
1616 YYYYMMDDhhmmss-hhmm
1617 """
1618 return _get_asn1_time(self._revoked.revocationDate)
1619
1620
1621
1622class CRL(object):
1623 def __init__(self):
1624 """
1625 Create a new empty CRL object.
1626 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001627 crl = _lib.X509_CRL_new()
1628 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001629
1630
1631 def get_revoked(self):
1632 """
1633 Return revoked portion of the CRL structure (by value not reference).
1634
1635 :return: A tuple of Revoked objects.
1636 """
1637 results = []
1638 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001639 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1640 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001641 revoked_copy = _X509_REVOKED_dup(revoked)
1642 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001643 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001644 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001645 if results:
1646 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001647
1648
1649 def add_revoked(self, revoked):
1650 """
1651 Add a revoked (by value not reference) to the CRL structure
1652
1653 :param revoked: The new revoked.
1654 :type revoked: :class:`X509`
1655
1656 :return: None
1657 """
1658 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001659 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001660 # TODO: This is untested.
1661 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001662
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001663 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001664 if add_result == 0:
1665 # TODO: This is untested.
1666 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001667
1668
1669 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1670 """
1671 export a CRL as a string
1672
1673 :param cert: Used to sign CRL.
1674 :type cert: :class:`X509`
1675
1676 :param key: Used to sign CRL.
1677 :type key: :class:`PKey`
1678
1679 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1680
1681 :param days: The number of days until the next update of this CRL.
1682 :type days: :py:data:`int`
1683
1684 :return: :py:data:`str`
1685 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001686 if not isinstance(cert, X509):
1687 raise TypeError("cert must be an X509 instance")
1688 if not isinstance(key, PKey):
1689 raise TypeError("key must be a PKey instance")
1690 if not isinstance(type, int):
1691 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001692
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001693 bio = _lib.BIO_new(_lib.BIO_s_mem())
1694 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001695 # TODO: This is untested.
1696 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001697
1698 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001699 sometime = _lib.ASN1_TIME_new()
1700 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001701 # TODO: This is untested.
1702 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001703
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001704 _lib.X509_gmtime_adj(sometime, 0)
1705 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001706
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001707 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1708 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001709
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001710 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001711
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001712 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001713 if not sign_result:
1714 _raise_current_error()
1715
1716 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001717 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001718 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001719 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001720 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001721 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001722 else:
1723 raise ValueError(
1724 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1725
1726 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001727 # TODO: This is untested.
1728 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001729
1730 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001731CRLType = CRL
1732
1733
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001734
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001735class PKCS7(object):
1736 def type_is_signed(self):
1737 """
1738 Check if this NID_pkcs7_signed object
1739
1740 :return: True if the PKCS7 is of type signed
1741 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001742 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001743 return True
1744 return False
1745
1746
1747 def type_is_enveloped(self):
1748 """
1749 Check if this NID_pkcs7_enveloped object
1750
1751 :returns: True if the PKCS7 is of type enveloped
1752 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001753 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001754 return True
1755 return False
1756
1757
1758 def type_is_signedAndEnveloped(self):
1759 """
1760 Check if this NID_pkcs7_signedAndEnveloped object
1761
1762 :returns: True if the PKCS7 is of type signedAndEnveloped
1763 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001764 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001765 return True
1766 return False
1767
1768
1769 def type_is_data(self):
1770 """
1771 Check if this NID_pkcs7_data object
1772
1773 :return: True if the PKCS7 is of type data
1774 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001775 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001776 return True
1777 return False
1778
1779
1780 def get_type_name(self):
1781 """
1782 Returns the type name of the PKCS7 structure
1783
1784 :return: A string with the typename
1785 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001786 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1787 string_type = _lib.OBJ_nid2sn(nid)
1788 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001789
1790PKCS7Type = PKCS7
1791
1792
1793
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001794class PKCS12(object):
1795 def __init__(self):
1796 self._pkey = None
1797 self._cert = None
1798 self._cacerts = None
1799 self._friendlyname = None
1800
1801
1802 def get_certificate(self):
1803 """
1804 Return certificate portion of the PKCS12 structure
1805
1806 :return: X509 object containing the certificate
1807 """
1808 return self._cert
1809
1810
1811 def set_certificate(self, cert):
1812 """
1813 Replace the certificate portion of the PKCS12 structure
1814
1815 :param cert: The new certificate.
1816 :type cert: :py:class:`X509` or :py:data:`None`
1817 :return: None
1818 """
1819 if not isinstance(cert, X509):
1820 raise TypeError("cert must be an X509 instance")
1821 self._cert = cert
1822
1823
1824 def get_privatekey(self):
1825 """
1826 Return private key portion of the PKCS12 structure
1827
1828 :returns: PKey object containing the private key
1829 """
1830 return self._pkey
1831
1832
1833 def set_privatekey(self, pkey):
1834 """
1835 Replace or set the certificate portion of the PKCS12 structure
1836
1837 :param pkey: The new private key.
1838 :type pkey: :py:class:`PKey`
1839 :return: None
1840 """
1841 if not isinstance(pkey, PKey):
1842 raise TypeError("pkey must be a PKey instance")
1843 self._pkey = pkey
1844
1845
1846 def get_ca_certificates(self):
1847 """
1848 Return CA certificates within of the PKCS12 object
1849
1850 :return: A newly created tuple containing the CA certificates in the chain,
1851 if any are present, or None if no CA certificates are present.
1852 """
1853 if self._cacerts is not None:
1854 return tuple(self._cacerts)
1855
1856
1857 def set_ca_certificates(self, cacerts):
1858 """
1859 Replace or set the CA certificates withing the PKCS12 object.
1860
1861 :param cacerts: The new CA certificates.
1862 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1863 :return: None
1864 """
1865 if cacerts is None:
1866 self._cacerts = None
1867 else:
1868 cacerts = list(cacerts)
1869 for cert in cacerts:
1870 if not isinstance(cert, X509):
1871 raise TypeError("iterable must only contain X509 instances")
1872 self._cacerts = cacerts
1873
1874
1875 def set_friendlyname(self, name):
1876 """
1877 Replace or set the certificate portion of the PKCS12 structure
1878
1879 :param name: The new friendly name.
1880 :type name: :py:class:`bytes`
1881 :return: None
1882 """
1883 if name is None:
1884 self._friendlyname = None
1885 elif not isinstance(name, bytes):
1886 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1887 self._friendlyname = name
1888
1889
1890 def get_friendlyname(self):
1891 """
1892 Return friendly name portion of the PKCS12 structure
1893
1894 :returns: String containing the friendlyname
1895 """
1896 return self._friendlyname
1897
1898
1899 def export(self, passphrase=None, iter=2048, maciter=1):
1900 """
1901 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1902
1903 :param passphrase: used to encrypt the PKCS12
1904 :type passphrase: :py:data:`bytes`
1905
1906 :param iter: How many times to repeat the encryption
1907 :type iter: :py:data:`int`
1908
1909 :param maciter: How many times to repeat the MAC
1910 :type maciter: :py:data:`int`
1911
1912 :return: The string containing the PKCS12
1913 """
1914 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001915 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001916 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001917 cacerts = _lib.sk_X509_new_null()
1918 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001919 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001920 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001921
1922 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001923 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001924
1925 friendlyname = self._friendlyname
1926 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001927 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001928
1929 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001930 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001931 else:
1932 pkey = self._pkey._pkey
1933
1934 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001935 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001936 else:
1937 cert = self._cert._x509
1938
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001939 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001940 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001941 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1942 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001943 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001944 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001945 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001946 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001947
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001948 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001949 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001950 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001951
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001952PKCS12Type = PKCS12
1953
1954
1955
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001956class NetscapeSPKI(object):
1957 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001958 spki = _lib.NETSCAPE_SPKI_new()
1959 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001960
1961
1962 def sign(self, pkey, digest):
1963 """
1964 Sign the certificate request using the supplied key and digest
1965
1966 :param pkey: The key to sign with
1967 :param digest: The message digest to use
1968 :return: None
1969 """
1970 if pkey._only_public:
1971 raise ValueError("Key has only public part")
1972
1973 if not pkey._initialized:
1974 raise ValueError("Key is uninitialized")
1975
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001976 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001977 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001978 raise ValueError("No such digest method")
1979
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001980 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001981 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001982 # TODO: This is untested.
1983 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001984
1985
1986 def verify(self, key):
1987 """
1988 Verifies a certificate request using the supplied public key
1989
1990 :param key: a public key
1991 :return: True if the signature is correct.
1992 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1993 problem verifying the signature.
1994 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001995 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001996 if answer <= 0:
1997 _raise_current_error()
1998 return True
1999
2000
2001 def b64_encode(self):
2002 """
2003 Generate a base64 encoded string from an SPKI
2004
2005 :return: The base64 encoded string
2006 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002007 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2008 result = _ffi.string(encoded)
2009 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002010 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002011
2012
2013 def get_pubkey(self):
2014 """
2015 Get the public key of the certificate
2016
2017 :return: The public key
2018 """
2019 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002020 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2021 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002022 # TODO: This is untested.
2023 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002024 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002025 pkey._only_public = True
2026 return pkey
2027
2028
2029 def set_pubkey(self, pkey):
2030 """
2031 Set the public key of the certificate
2032
2033 :param pkey: The public key
2034 :return: None
2035 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002036 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002037 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002038 # TODO: This is untested.
2039 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002040NetscapeSPKIType = NetscapeSPKI
2041
2042
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002043class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002044 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002045 if type != FILETYPE_PEM and passphrase is not None:
2046 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002047 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002048 self._more_args = more_args
2049 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002050 self._problems = []
2051
2052
2053 @property
2054 def callback(self):
2055 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002056 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002057 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002058 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002059 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002060 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002061 else:
2062 raise TypeError("Last argument must be string or callable")
2063
2064
2065 @property
2066 def callback_args(self):
2067 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002068 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002069 elif isinstance(self._passphrase, bytes):
2070 return self._passphrase
2071 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002072 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002073 else:
2074 raise TypeError("Last argument must be string or callable")
2075
2076
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002077 def raise_if_problem(self, exceptionType=Error):
2078 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002079 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002080 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002081 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002082 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002083 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002084 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002085
2086
2087 def _read_passphrase(self, buf, size, rwflag, userdata):
2088 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002089 if self._more_args:
2090 result = self._passphrase(size, rwflag, userdata)
2091 else:
2092 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002093 if not isinstance(result, bytes):
2094 raise ValueError("String expected")
2095 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002096 if self._truncate:
2097 result = result[:size]
2098 else:
2099 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002100 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002101 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002102 return len(result)
2103 except Exception as e:
2104 self._problems.append(e)
2105 return 0
2106
2107
2108
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002109def load_privatekey(type, buffer, passphrase=None):
2110 """
2111 Load a private key from a buffer
2112
2113 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2114 :param buffer: The buffer the key is stored in
2115 :param passphrase: (optional) if encrypted PEM format, this can be
2116 either the passphrase to use, or a callback for
2117 providing the passphrase.
2118
2119 :return: The PKey object
2120 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002121 if isinstance(buffer, _text_type):
2122 buffer = buffer.encode("ascii")
2123
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002124 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002125
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002126 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002127 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002128 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2129 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002130 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002131 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002132 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002133 else:
2134 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2135
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002136 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002137 _raise_current_error()
2138
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002139 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002140 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002141 return pkey
2142
2143
2144
2145def dump_certificate_request(type, req):
2146 """
2147 Dump a certificate request to a buffer
2148
2149 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2150 :param req: The certificate request to dump
2151 :return: The buffer with the dumped certificate request in
2152 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002153 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002154
2155 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002156 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002157 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002158 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002159 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002160 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002161 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002162 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002163
2164 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002165 # TODO: This is untested.
2166 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002167
2168 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002169
2170
2171
2172def load_certificate_request(type, buffer):
2173 """
2174 Load a certificate request from a buffer
2175
2176 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2177 :param buffer: The buffer the certificate request is stored in
2178 :return: The X509Req object
2179 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002180 if isinstance(buffer, _text_type):
2181 buffer = buffer.encode("ascii")
2182
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002183 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002184
2185 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002186 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002187 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002188 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002189 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002190 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002191
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002192 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002193 # TODO: This is untested.
2194 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002195
2196 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002197 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002198 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002199
2200
2201
2202def sign(pkey, data, digest):
2203 """
2204 Sign data with a digest
2205
2206 :param pkey: Pkey to sign with
2207 :param data: data to be signed
2208 :param digest: message digest to use
2209 :return: signature
2210 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002211 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002212 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002213 raise ValueError("No such digest method")
2214
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002215 md_ctx = _ffi.new("EVP_MD_CTX*")
2216 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002217
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002218 _lib.EVP_SignInit(md_ctx, digest_obj)
2219 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002220
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002221 signature_buffer = _ffi.new("unsigned char[]", 512)
2222 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002223 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002224 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002225 md_ctx, signature_buffer, signature_length, pkey._pkey)
2226
2227 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002228 # TODO: This is untested.
2229 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002230
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002231 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002232
2233
2234
2235def verify(cert, signature, data, digest):
2236 """
2237 Verify a signature
2238
2239 :param cert: signing certificate (X509 object)
2240 :param signature: signature returned by sign function
2241 :param data: data to be verified
2242 :param digest: message digest to use
2243 :return: None if the signature is correct, raise exception otherwise
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 pkey = _lib.X509_get_pubkey(cert._x509)
2250 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002251 # TODO: This is untested.
2252 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002253 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002254
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002255 md_ctx = _ffi.new("EVP_MD_CTX*")
2256 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002257
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002258 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2259 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2260 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002261
2262 if verify_result != 1:
2263 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002264
2265
2266
2267def load_crl(type, buffer):
2268 """
2269 Load a certificate revocation list from a buffer
2270
2271 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2272 :param buffer: The buffer the CRL is stored in
2273
2274 :return: The PKey object
2275 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002276 if isinstance(buffer, _text_type):
2277 buffer = buffer.encode("ascii")
2278
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002279 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002280
2281 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002282 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002283 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002284 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002285 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002286 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2287
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002288 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002289 _raise_current_error()
2290
2291 result = CRL.__new__(CRL)
2292 result._crl = crl
2293 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002294
2295
2296
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002297def load_pkcs7_data(type, buffer):
2298 """
2299 Load pkcs7 data from a buffer
2300
2301 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2302 :param buffer: The buffer with the pkcs7 data.
2303 :return: The PKCS7 object
2304 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002305 if isinstance(buffer, _text_type):
2306 buffer = buffer.encode("ascii")
2307
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002308 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002309
2310 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002311 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002312 elif type == FILETYPE_ASN1:
2313 pass
2314 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002315 # TODO: This is untested.
2316 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002317 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2318
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002319 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002320 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002321
2322 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002323 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002324 return pypkcs7
2325
2326
2327
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002328def load_pkcs12(buffer, passphrase):
2329 """
2330 Load a PKCS12 object from a buffer
2331
2332 :param buffer: The buffer the certificate is stored in
2333 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2334 :returns: The PKCS12 object
2335 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002336 if isinstance(buffer, _text_type):
2337 buffer = buffer.encode("ascii")
2338
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002339 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002340
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002341 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2342 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002343 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002344 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002345
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002346 pkey = _ffi.new("EVP_PKEY**")
2347 cert = _ffi.new("X509**")
2348 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002349
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002350 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002351 if not parse_result:
2352 _raise_current_error()
2353
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002354 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002355
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002356 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2357 # queue for no particular reason. This error isn't interesting to anyone
2358 # outside this function. It's not even interesting to us. Get rid of it.
2359 try:
2360 _raise_current_error()
2361 except Error:
2362 pass
2363
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002364 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002365 pykey = None
2366 else:
2367 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002368 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002369
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002370 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002371 pycert = None
2372 friendlyname = None
2373 else:
2374 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002375 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002376
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002377 friendlyname_length = _ffi.new("int*")
2378 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2379 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2380 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002381 friendlyname = None
2382
2383 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002384 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002385 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002386 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002387 pycacerts.append(pycacert)
2388 if not pycacerts:
2389 pycacerts = None
2390
2391 pkcs12 = PKCS12.__new__(PKCS12)
2392 pkcs12._pkey = pykey
2393 pkcs12._cert = pycert
2394 pkcs12._cacerts = pycacerts
2395 pkcs12._friendlyname = friendlyname
2396 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002397
2398
2399def _initialize_openssl_threads(get_ident, Lock):
2400 import _ssl
2401 return
2402
2403 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2404
2405 def locking_function(mode, index, filename, line):
2406 if mode & _lib.CRYPTO_LOCK:
2407 locks[index].acquire()
2408 else:
2409 locks[index].release()
2410
2411 _lib.CRYPTO_set_id_callback(
2412 _ffi.callback("unsigned long (*)(void)", get_ident))
2413
2414 _lib.CRYPTO_set_locking_callback(
2415 _ffi.callback(
2416 "void (*)(int, int, const char*, int)", locking_function))
2417
2418
2419try:
2420 from thread import get_ident
2421 from threading import Lock
2422except ImportError:
2423 pass
2424else:
2425 _initialize_openssl_threads(get_ident, Lock)
2426 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002427
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002428# There are no direct unit tests for this initialization. It is tested
2429# indirectly since it is necessary for functions like dump_privatekey when
2430# using encryption.
2431#
2432# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2433# and some other similar tests may fail without this (though they may not if
2434# the Python runtime has already done some initialization of the underlying
2435# OpenSSL library (and is linked against the same one that cryptography is
2436# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002437_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002438
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002439# This is similar but exercised mainly by exception_from_error_queue. It calls
2440# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2441_lib.SSL_load_error_strings()