blob: 6e32ad1165cfefdd3f599bd2c7704980041b0173 [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
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400367 :param name: The OpenSSL short name identifying the curve object to
368 retrieve.
369 :type name: :py:class:`unicode`
370
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400371 If the named curve is not supported then :py:class:`ValueError` is raised.
372 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400373 for curve in get_elliptic_curves():
374 if curve.name == name:
375 return curve
376 raise ValueError("unknown curve name", name)
377
378
379
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800380class X509Name(object):
381 def __init__(self, name):
382 """
383 Create a new X509Name, copying the given X509Name instance.
384
385 :param name: An X509Name object to copy
386 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500387 name = _lib.X509_NAME_dup(name._name)
388 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800389
390
391 def __setattr__(self, name, value):
392 if name.startswith('_'):
393 return super(X509Name, self).__setattr__(name, value)
394
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800395 # Note: we really do not want str subclasses here, so we do not use
396 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800397 if type(name) is not str:
398 raise TypeError("attribute name must be string, not '%.200s'" % (
399 type(value).__name__,))
400
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500401 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500402 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800403 try:
404 _raise_current_error()
405 except Error:
406 pass
407 raise AttributeError("No such attribute")
408
409 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500410 for i in range(_lib.X509_NAME_entry_count(self._name)):
411 ent = _lib.X509_NAME_get_entry(self._name, i)
412 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
413 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800414 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500415 ent = _lib.X509_NAME_delete_entry(self._name, i)
416 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800417 break
418
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500419 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800420 value = value.encode('utf-8')
421
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500422 add_result = _lib.X509_NAME_add_entry_by_NID(
423 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800424 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500425 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800426
427
428 def __getattr__(self, name):
429 """
430 Find attribute. An X509Name object has the following attributes:
431 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
432 organization (alias O), organizationalUnit (alias OU), commonName (alias
433 CN) and more...
434 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500435 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500436 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800437 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
438 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
439 # push something onto the error queue. If we don't clean that up
440 # now, someone else will bump into it later and be quite confused.
441 # See lp#314814.
442 try:
443 _raise_current_error()
444 except Error:
445 pass
446 return super(X509Name, self).__getattr__(name)
447
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500448 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800449 if entry_index == -1:
450 return None
451
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500452 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
453 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800454
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500455 result_buffer = _ffi.new("unsigned char**")
456 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800457 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500458 # TODO: This is untested.
459 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800460
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700461 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500462 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700463 finally:
464 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500465 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800466 return result
467
468
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500469 def _cmp(op):
470 def f(self, other):
471 if not isinstance(other, X509Name):
472 return NotImplemented
473 result = _lib.X509_NAME_cmp(self._name, other._name)
474 return op(result, 0)
475 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800476
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500477 __eq__ = _cmp(__eq__)
478 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800479
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500480 __lt__ = _cmp(__lt__)
481 __le__ = _cmp(__le__)
482
483 __gt__ = _cmp(__gt__)
484 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800485
486 def __repr__(self):
487 """
488 String representation of an X509Name
489 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500490 result_buffer = _ffi.new("char[]", 512);
491 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800492 self._name, result_buffer, len(result_buffer))
493
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500494 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500495 # TODO: This is untested.
496 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800497
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500498 return "<X509Name object '%s'>" % (
499 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800500
501
502 def hash(self):
503 """
504 Return the hash value of this name
505
506 :return: None
507 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500508 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800509
510
511 def der(self):
512 """
513 Return the DER encoding of this name
514
515 :return: A :py:class:`bytes` instance giving the DER encoded form of
516 this name.
517 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500518 result_buffer = _ffi.new('unsigned char**')
519 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800520 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500521 # TODO: This is untested.
522 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800523
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500524 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
525 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800526 return string_result
527
528
529 def get_components(self):
530 """
531 Returns the split-up components of this name.
532
533 :return: List of tuples (name, value).
534 """
535 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500536 for i in range(_lib.X509_NAME_entry_count(self._name)):
537 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800538
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500539 fname = _lib.X509_NAME_ENTRY_get_object(ent)
540 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800541
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500542 nid = _lib.OBJ_obj2nid(fname)
543 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800544
545 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500546 _ffi.string(name),
547 _ffi.string(
548 _lib.ASN1_STRING_data(fval),
549 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800550
551 return result
552X509NameType = X509Name
553
554
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800555class X509Extension(object):
556 def __init__(self, type_name, critical, value, subject=None, issuer=None):
557 """
558 :param typename: The name of the extension to create.
559 :type typename: :py:data:`str`
560
561 :param critical: A flag indicating whether this is a critical extension.
562
563 :param value: The value of the extension.
564 :type value: :py:data:`str`
565
566 :param subject: Optional X509 cert to use as subject.
567 :type subject: :py:class:`X509`
568
569 :param issuer: Optional X509 cert to use as issuer.
570 :type issuer: :py:class:`X509`
571
572 :return: The X509Extension object
573 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500574 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800575
576 # A context is necessary for any extension which uses the r2i conversion
577 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
578 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500579 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800580
581 # We have no configuration database - but perhaps we should (some
582 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500583 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800584
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800585 # Initialize the subject and issuer, if appropriate. ctx is a local,
586 # and as far as I can tell none of the X509V3_* APIs invoked here steal
587 # any references, so no need to mess with reference counts or duplicates.
588 if issuer is not None:
589 if not isinstance(issuer, X509):
590 raise TypeError("issuer must be an X509 instance")
591 ctx.issuer_cert = issuer._x509
592 if subject is not None:
593 if not isinstance(subject, X509):
594 raise TypeError("subject must be an X509 instance")
595 ctx.subject_cert = subject._x509
596
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800597 if critical:
598 # There are other OpenSSL APIs which would let us pass in critical
599 # separately, but they're harder to use, and since value is already
600 # a pile of crappy junk smuggling a ton of utterly important
601 # structured data, what's the point of trying to avoid nasty stuff
602 # with strings? (However, X509V3_EXT_i2d in particular seems like it
603 # would be a better API to invoke. I do not know where to get the
604 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500605 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800606
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500607 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
608 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800609 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500610 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800611
612
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400613 @property
614 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500615 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400616
617 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500618 _lib.GEN_EMAIL: "email",
619 _lib.GEN_DNS: "DNS",
620 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400621 }
622
623 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500624 method = _lib.X509V3_EXT_get(self._extension)
625 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500626 # TODO: This is untested.
627 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400628 payload = self._extension.value.data
629 length = self._extension.value.length
630
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500631 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400632 payloadptr[0] = payload
633
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500634 if method.it != _ffi.NULL:
635 ptr = _lib.ASN1_ITEM_ptr(method.it)
636 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
637 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400638 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500639 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400640 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500641 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400642
643 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500644 for i in range(_lib.sk_GENERAL_NAME_num(names)):
645 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400646 try:
647 label = self._prefixes[name.type]
648 except KeyError:
649 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500650 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500651 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400652 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500653 value = _native(
654 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
655 parts.append(label + ":" + value)
656 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400657
658
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800659 def __str__(self):
660 """
661 :return: a nice text representation of the extension
662 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500663 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400664 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800665
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400666 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500667 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800668 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500669 # TODO: This is untested.
670 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800671
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500672 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800673
674
675 def get_critical(self):
676 """
677 Returns the critical field of the X509Extension
678
679 :return: The critical field.
680 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500681 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800682
683
684 def get_short_name(self):
685 """
686 Returns the short version of the type name of the X509Extension
687
688 :return: The short type name.
689 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500690 obj = _lib.X509_EXTENSION_get_object(self._extension)
691 nid = _lib.OBJ_obj2nid(obj)
692 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800693
694
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800695 def get_data(self):
696 """
697 Returns the data of the X509Extension
698
699 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
700 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500701 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
702 string_result = _ffi.cast('ASN1_STRING*', octet_result)
703 char_result = _lib.ASN1_STRING_data(string_result)
704 result_length = _lib.ASN1_STRING_length(string_result)
705 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800706
707X509ExtensionType = X509Extension
708
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800709
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800710class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800711 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500712 req = _lib.X509_REQ_new()
713 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800714
715
716 def set_pubkey(self, pkey):
717 """
718 Set the public key of the certificate request
719
720 :param pkey: The public key to use
721 :return: None
722 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500723 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800724 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500725 # TODO: This is untested.
726 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800727
728
729 def get_pubkey(self):
730 """
731 Get the public key from the certificate request
732
733 :return: The public key
734 """
735 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500736 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
737 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500738 # TODO: This is untested.
739 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500740 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800741 pkey._only_public = True
742 return pkey
743
744
745 def set_version(self, version):
746 """
747 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
748 request.
749
750 :param version: The version number
751 :return: None
752 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500753 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800754 if not set_result:
755 _raise_current_error()
756
757
758 def get_version(self):
759 """
760 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
761 request.
762
763 :return: an integer giving the value of the version subfield
764 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500765 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800766
767
768 def get_subject(self):
769 """
770 Create an X509Name object for the subject of the certificate request
771
772 :return: An X509Name object
773 """
774 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500775 name._name = _lib.X509_REQ_get_subject_name(self._req)
776 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500777 # TODO: This is untested.
778 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800779
780 # The name is owned by the X509Req structure. As long as the X509Name
781 # Python object is alive, keep the X509Req Python object alive.
782 name._owner = self
783
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800784 return name
785
786
787 def add_extensions(self, extensions):
788 """
789 Add extensions to the request.
790
791 :param extensions: a sequence of X509Extension objects
792 :return: None
793 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500794 stack = _lib.sk_X509_EXTENSION_new_null()
795 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500796 # TODO: This is untested.
797 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800798
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500799 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800800
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800801 for ext in extensions:
802 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800803 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800804
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800805 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500806 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800807
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500808 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800809 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500810 # TODO: This is untested.
811 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800812
813
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800814 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800815 """
816 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800817
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500818 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800819 """
820 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500821 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500822 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800823 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500824 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800825 exts.append(ext)
826 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800827
828
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800829 def sign(self, pkey, digest):
830 """
831 Sign the certificate request using the supplied key and digest
832
833 :param pkey: The key to sign with
834 :param digest: The message digest to use
835 :return: None
836 """
837 if pkey._only_public:
838 raise ValueError("Key has only public part")
839
840 if not pkey._initialized:
841 raise ValueError("Key is uninitialized")
842
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500843 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500844 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800845 raise ValueError("No such digest method")
846
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500847 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800848 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500849 # TODO: This is untested.
850 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800851
852
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800853 def verify(self, pkey):
854 """
855 Verifies a certificate request using the supplied public key
856
857 :param key: a public key
858 :return: True if the signature is correct.
859
860 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
861 problem verifying the signature.
862 """
863 if not isinstance(pkey, PKey):
864 raise TypeError("pkey must be a PKey instance")
865
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500866 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800867 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500868 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800869
870 return result
871
872
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800873X509ReqType = X509Req
874
875
876
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800877class X509(object):
878 def __init__(self):
879 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500880 x509 = _lib.X509_new()
881 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800882
883
884 def set_version(self, version):
885 """
886 Set version number of the certificate
887
888 :param version: The version number
889 :type version: :py:class:`int`
890
891 :return: None
892 """
893 if not isinstance(version, int):
894 raise TypeError("version must be an integer")
895
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500896 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800897
898
899 def get_version(self):
900 """
901 Return version number of the certificate
902
903 :return: Version number as a Python integer
904 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500905 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800906
907
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800908 def get_pubkey(self):
909 """
910 Get the public key of the certificate
911
912 :return: The public key
913 """
914 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500915 pkey._pkey = _lib.X509_get_pubkey(self._x509)
916 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800917 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500918 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800919 pkey._only_public = True
920 return pkey
921
922
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800923 def set_pubkey(self, pkey):
924 """
925 Set the public key of the certificate
926
927 :param pkey: The public key
928
929 :return: None
930 """
931 if not isinstance(pkey, PKey):
932 raise TypeError("pkey must be a PKey instance")
933
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500934 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800935 if not set_result:
936 _raise_current_error()
937
938
939 def sign(self, pkey, digest):
940 """
941 Sign the certificate using the supplied key and digest
942
943 :param pkey: The key to sign with
944 :param digest: The message digest to use
945 :return: None
946 """
947 if not isinstance(pkey, PKey):
948 raise TypeError("pkey must be a PKey instance")
949
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800950 if pkey._only_public:
951 raise ValueError("Key only has public part")
952
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800953 if not pkey._initialized:
954 raise ValueError("Key is uninitialized")
955
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500956 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500957 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800958 raise ValueError("No such digest method")
959
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500960 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800961 if not sign_result:
962 _raise_current_error()
963
964
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800965 def get_signature_algorithm(self):
966 """
967 Retrieve the signature algorithm used in the certificate
968
969 :return: A byte string giving the name of the signature algorithm used in
970 the certificate.
971 :raise ValueError: If the signature algorithm is undefined.
972 """
973 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500974 nid = _lib.OBJ_obj2nid(alg)
975 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800976 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500977 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800978
979
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800980 def digest(self, digest_name):
981 """
982 Return the digest of the X509 object.
983
984 :param digest_name: The name of the digest algorithm to use.
985 :type digest_name: :py:class:`bytes`
986
987 :return: The digest of the object
988 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500989 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500990 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800991 raise ValueError("No such digest method")
992
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500993 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
994 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800995 result_length[0] = len(result_buffer)
996
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500997 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800998 self._x509, digest, result_buffer, result_length)
999
1000 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001001 # TODO: This is untested.
1002 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001003
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001004 return b":".join([
1005 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001006 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001007
1008
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001009 def subject_name_hash(self):
1010 """
1011 Return the hash of the X509 subject.
1012
1013 :return: The hash of the subject.
1014 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001015 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001016
1017
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001018 def set_serial_number(self, serial):
1019 """
1020 Set serial number of the certificate
1021
1022 :param serial: The serial number
1023 :type serial: :py:class:`int`
1024
1025 :return: None
1026 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001027 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001028 raise TypeError("serial must be an integer")
1029
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001030 hex_serial = hex(serial)[2:]
1031 if not isinstance(hex_serial, bytes):
1032 hex_serial = hex_serial.encode('ascii')
1033
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001034 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001035
1036 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1037 # it. If bignum is still NULL after this call, then the return value is
1038 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001039 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001040
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001041 if bignum_serial[0] == _ffi.NULL:
1042 set_result = _lib.ASN1_INTEGER_set(
1043 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001044 if set_result:
1045 # TODO Not tested
1046 _raise_current_error()
1047 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001048 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1049 _lib.BN_free(bignum_serial[0])
1050 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001051 # TODO Not tested
1052 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001053 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1054 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001055 if not set_result:
1056 # TODO Not tested
1057 _raise_current_error()
1058
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001059
1060 def get_serial_number(self):
1061 """
1062 Return serial number of the certificate
1063
1064 :return: Serial number as a Python integer
1065 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001066 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1067 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001068 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001069 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001070 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001072 serial = int(hexstring_serial, 16)
1073 return serial
1074 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001075 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001076 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001077 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001078
1079
1080 def gmtime_adj_notAfter(self, amount):
1081 """
1082 Adjust the time stamp for when the certificate stops being valid
1083
1084 :param amount: The number of seconds by which to adjust the ending
1085 validity time.
1086 :type amount: :py:class:`int`
1087
1088 :return: None
1089 """
1090 if not isinstance(amount, int):
1091 raise TypeError("amount must be an integer")
1092
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001093 notAfter = _lib.X509_get_notAfter(self._x509)
1094 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001095
1096
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001097 def gmtime_adj_notBefore(self, amount):
1098 """
1099 Change the timestamp for when the certificate starts being valid to the current
1100 time plus an offset.
1101
1102 :param amount: The number of seconds by which to adjust the starting validity
1103 time.
1104 :return: None
1105 """
1106 if not isinstance(amount, int):
1107 raise TypeError("amount must be an integer")
1108
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001109 notBefore = _lib.X509_get_notBefore(self._x509)
1110 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001111
1112
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001113 def has_expired(self):
1114 """
1115 Check whether the certificate has expired.
1116
1117 :return: True if the certificate has expired, false otherwise
1118 """
1119 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001120 notAfter = _lib.X509_get_notAfter(self._x509)
1121 return _lib.ASN1_UTCTIME_cmp_time_t(
1122 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001123
1124
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001125 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001126 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001127
1128
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001129 def get_notBefore(self):
1130 """
1131 Retrieve the time stamp for when the certificate starts being valid
1132
1133 :return: A string giving the timestamp, in the format::
1134
1135 YYYYMMDDhhmmssZ
1136 YYYYMMDDhhmmss+hhmm
1137 YYYYMMDDhhmmss-hhmm
1138
1139 or None if there is no value set.
1140 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001141 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001142
1143
1144 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001145 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001146
1147
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001148 def set_notBefore(self, when):
1149 """
1150 Set the time stamp for when the certificate starts being valid
1151
1152 :param when: A string giving the timestamp, in the format:
1153
1154 YYYYMMDDhhmmssZ
1155 YYYYMMDDhhmmss+hhmm
1156 YYYYMMDDhhmmss-hhmm
1157 :type when: :py:class:`bytes`
1158
1159 :return: None
1160 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001161 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001162
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001163
1164 def get_notAfter(self):
1165 """
1166 Retrieve the time stamp for when the certificate stops being valid
1167
1168 :return: A string giving the timestamp, in the format::
1169
1170 YYYYMMDDhhmmssZ
1171 YYYYMMDDhhmmss+hhmm
1172 YYYYMMDDhhmmss-hhmm
1173
1174 or None if there is no value set.
1175 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001176 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001177
1178
1179 def set_notAfter(self, when):
1180 """
1181 Set the time stamp for when the certificate stops being valid
1182
1183 :param when: A string giving the timestamp, in the format:
1184
1185 YYYYMMDDhhmmssZ
1186 YYYYMMDDhhmmss+hhmm
1187 YYYYMMDDhhmmss-hhmm
1188 :type when: :py:class:`bytes`
1189
1190 :return: None
1191 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001192 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001193
1194
1195 def _get_name(self, which):
1196 name = X509Name.__new__(X509Name)
1197 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001198 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001199 # TODO: This is untested.
1200 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001201
1202 # The name is owned by the X509 structure. As long as the X509Name
1203 # Python object is alive, keep the X509 Python object alive.
1204 name._owner = self
1205
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001206 return name
1207
1208
1209 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001210 if not isinstance(name, X509Name):
1211 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001212 set_result = which(self._x509, name._name)
1213 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001214 # TODO: This is untested.
1215 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001216
1217
1218 def get_issuer(self):
1219 """
1220 Create an X509Name object for the issuer of the certificate
1221
1222 :return: An X509Name object
1223 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001224 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001225
1226
1227 def set_issuer(self, issuer):
1228 """
1229 Set the issuer of the certificate
1230
1231 :param issuer: The issuer name
1232 :type issuer: :py:class:`X509Name`
1233
1234 :return: None
1235 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001236 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001237
1238
1239 def get_subject(self):
1240 """
1241 Create an X509Name object for the subject of the certificate
1242
1243 :return: An X509Name object
1244 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001245 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001246
1247
1248 def set_subject(self, subject):
1249 """
1250 Set the subject of the certificate
1251
1252 :param subject: The subject name
1253 :type subject: :py:class:`X509Name`
1254 :return: None
1255 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001256 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001257
1258
1259 def get_extension_count(self):
1260 """
1261 Get the number of extensions on the certificate.
1262
1263 :return: The number of extensions as an integer.
1264 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001265 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001266
1267
1268 def add_extensions(self, extensions):
1269 """
1270 Add extensions to the certificate.
1271
1272 :param extensions: a sequence of X509Extension objects
1273 :return: None
1274 """
1275 for ext in extensions:
1276 if not isinstance(ext, X509Extension):
1277 raise ValueError("One of the elements is not an X509Extension")
1278
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001279 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001280 if not add_result:
1281 _raise_current_error()
1282
1283
1284 def get_extension(self, index):
1285 """
1286 Get a specific extension of the certificate by index.
1287
1288 :param index: The index of the extension to retrieve.
1289 :return: The X509Extension object at the specified index.
1290 """
1291 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001292 ext._extension = _lib.X509_get_ext(self._x509, index)
1293 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001294 raise IndexError("extension index out of bounds")
1295
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001296 extension = _lib.X509_EXTENSION_dup(ext._extension)
1297 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001298 return ext
1299
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001300X509Type = X509
1301
1302
1303
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001304class X509Store(object):
1305 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001306 store = _lib.X509_STORE_new()
1307 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001308
1309
1310 def add_cert(self, cert):
1311 if not isinstance(cert, X509):
1312 raise TypeError()
1313
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001314 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001315 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001316 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001317
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001318
1319X509StoreType = X509Store
1320
1321
1322
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001323def load_certificate(type, buffer):
1324 """
1325 Load a certificate from a buffer
1326
1327 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1328
1329 :param buffer: The buffer the certificate is stored in
1330 :type buffer: :py:class:`bytes`
1331
1332 :return: The X509 object
1333 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001334 if isinstance(buffer, _text_type):
1335 buffer = buffer.encode("ascii")
1336
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001337 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001338
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001339 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001340 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001341 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001342 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001343 else:
1344 raise ValueError(
1345 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001346
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001347 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001348 _raise_current_error()
1349
1350 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001351 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001352 return cert
1353
1354
1355def dump_certificate(type, cert):
1356 """
1357 Dump a certificate to a buffer
1358
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001359 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1360 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001361 :param cert: The certificate to dump
1362 :return: The buffer with the dumped certificate in
1363 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001364 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001365
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001366 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001367 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001368 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001369 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001370 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001371 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001372 else:
1373 raise ValueError(
1374 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1375 "FILETYPE_TEXT")
1376
1377 return _bio_to_string(bio)
1378
1379
1380
1381def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1382 """
1383 Dump a private key to a buffer
1384
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001385 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1386 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001387 :param pkey: The PKey to dump
1388 :param cipher: (optional) if encrypted PEM format, the cipher to
1389 use
1390 :param passphrase: (optional) if encrypted PEM format, this can be either
1391 the passphrase to use, or a callback for providing the
1392 passphrase.
1393 :return: The buffer with the dumped key in
1394 :rtype: :py:data:`str`
1395 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001396 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001397
1398 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001399 if passphrase is None:
1400 raise TypeError(
1401 "if a value is given for cipher "
1402 "one must also be given for passphrase")
1403 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001404 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001405 raise ValueError("Invalid cipher name")
1406 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001407 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001408
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001409 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001410 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001411 result_code = _lib.PEM_write_bio_PrivateKey(
1412 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001413 helper.callback, helper.callback_args)
1414 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001415 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001416 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001417 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001418 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1419 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001420 # TODO RSA_free(rsa)?
1421 else:
1422 raise ValueError(
1423 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1424 "FILETYPE_TEXT")
1425
1426 if result_code == 0:
1427 _raise_current_error()
1428
1429 return _bio_to_string(bio)
1430
1431
1432
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001433def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001434 copy = _lib.X509_REVOKED_new()
1435 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001436 # TODO: This is untested.
1437 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001438
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001439 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001440 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001441 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001442
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001443 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001444 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001445 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001446
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001447 if original.extensions != _ffi.NULL:
1448 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1449 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1450 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1451 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1452 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001453 copy.extensions = extension_stack
1454
1455 copy.sequence = original.sequence
1456 return copy
1457
1458
1459
1460class Revoked(object):
1461 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1462 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1463 # OCSP_crl_reason_str. We use the latter, just like the command line
1464 # program.
1465 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001466 b"unspecified",
1467 b"keyCompromise",
1468 b"CACompromise",
1469 b"affiliationChanged",
1470 b"superseded",
1471 b"cessationOfOperation",
1472 b"certificateHold",
1473 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001474 ]
1475
1476 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001477 revoked = _lib.X509_REVOKED_new()
1478 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001479
1480
1481 def set_serial(self, hex_str):
1482 """
1483 Set the serial number of a revoked Revoked structure
1484
1485 :param hex_str: The new serial number.
1486 :type hex_str: :py:data:`str`
1487 :return: None
1488 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001489 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1490 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001491 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001492 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001493 if not bn_result:
1494 raise ValueError("bad hex string")
1495
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001496 asn1_serial = _ffi.gc(
1497 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1498 _lib.ASN1_INTEGER_free)
1499 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001500
1501
1502 def get_serial(self):
1503 """
1504 Return the serial number of a Revoked structure
1505
1506 :return: The serial number as a string
1507 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001508 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001509
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001510 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001511 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001512 # TODO: This is untested.
1513 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001514
1515 return _bio_to_string(bio)
1516
1517
1518 def _delete_reason(self):
1519 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001520 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1521 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1522 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1523 _lib.X509_EXTENSION_free(ext)
1524 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001525 break
1526
1527
1528 def set_reason(self, reason):
1529 """
1530 Set the reason of a Revoked object.
1531
1532 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1533
1534 :param reason: The reason string.
1535 :type reason: :py:class:`str` or :py:class:`NoneType`
1536 :return: None
1537 """
1538 if reason is None:
1539 self._delete_reason()
1540 elif not isinstance(reason, bytes):
1541 raise TypeError("reason must be None or a byte string")
1542 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001543 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001544 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1545
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001546 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1547 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001548 # TODO: This is untested.
1549 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001550 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001551
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001552 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1553 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001554 # TODO: This is untested.
1555 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001556
1557 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001558 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1559 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001560
1561 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001562 # TODO: This is untested.
1563 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001564
1565
1566 def get_reason(self):
1567 """
1568 Return the reason of a Revoked object.
1569
1570 :return: The reason as a string
1571 """
1572 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001573 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1574 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1575 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001576 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001577
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001578 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001579 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001580 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001581 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001582 # TODO: This is untested.
1583 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001584
1585 return _bio_to_string(bio)
1586
1587
1588 def all_reasons(self):
1589 """
1590 Return a list of all the supported reason strings.
1591
1592 :return: A list of reason strings.
1593 """
1594 return self._crl_reasons[:]
1595
1596
1597 def set_rev_date(self, when):
1598 """
1599 Set the revocation timestamp
1600
1601 :param when: A string giving the timestamp, in the format:
1602
1603 YYYYMMDDhhmmssZ
1604 YYYYMMDDhhmmss+hhmm
1605 YYYYMMDDhhmmss-hhmm
1606
1607 :return: None
1608 """
1609 return _set_asn1_time(self._revoked.revocationDate, when)
1610
1611
1612 def get_rev_date(self):
1613 """
1614 Retrieve the revocation date
1615
1616 :return: A string giving the timestamp, in the format:
1617
1618 YYYYMMDDhhmmssZ
1619 YYYYMMDDhhmmss+hhmm
1620 YYYYMMDDhhmmss-hhmm
1621 """
1622 return _get_asn1_time(self._revoked.revocationDate)
1623
1624
1625
1626class CRL(object):
1627 def __init__(self):
1628 """
1629 Create a new empty CRL object.
1630 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001631 crl = _lib.X509_CRL_new()
1632 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001633
1634
1635 def get_revoked(self):
1636 """
1637 Return revoked portion of the CRL structure (by value not reference).
1638
1639 :return: A tuple of Revoked objects.
1640 """
1641 results = []
1642 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001643 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1644 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001645 revoked_copy = _X509_REVOKED_dup(revoked)
1646 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001647 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001648 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001649 if results:
1650 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001651
1652
1653 def add_revoked(self, revoked):
1654 """
1655 Add a revoked (by value not reference) to the CRL structure
1656
1657 :param revoked: The new revoked.
1658 :type revoked: :class:`X509`
1659
1660 :return: None
1661 """
1662 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001663 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001664 # TODO: This is untested.
1665 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001666
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001667 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001668 if add_result == 0:
1669 # TODO: This is untested.
1670 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001671
1672
1673 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1674 """
1675 export a CRL as a string
1676
1677 :param cert: Used to sign CRL.
1678 :type cert: :class:`X509`
1679
1680 :param key: Used to sign CRL.
1681 :type key: :class:`PKey`
1682
1683 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1684
1685 :param days: The number of days until the next update of this CRL.
1686 :type days: :py:data:`int`
1687
1688 :return: :py:data:`str`
1689 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001690 if not isinstance(cert, X509):
1691 raise TypeError("cert must be an X509 instance")
1692 if not isinstance(key, PKey):
1693 raise TypeError("key must be a PKey instance")
1694 if not isinstance(type, int):
1695 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001696
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001697 bio = _lib.BIO_new(_lib.BIO_s_mem())
1698 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001699 # TODO: This is untested.
1700 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001701
1702 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001703 sometime = _lib.ASN1_TIME_new()
1704 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001705 # TODO: This is untested.
1706 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001707
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001708 _lib.X509_gmtime_adj(sometime, 0)
1709 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001710
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001711 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1712 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001713
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001714 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001715
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001716 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001717 if not sign_result:
1718 _raise_current_error()
1719
1720 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001721 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001722 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001723 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001724 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001725 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001726 else:
1727 raise ValueError(
1728 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1729
1730 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001731 # TODO: This is untested.
1732 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001733
1734 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001735CRLType = CRL
1736
1737
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001738
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001739class PKCS7(object):
1740 def type_is_signed(self):
1741 """
1742 Check if this NID_pkcs7_signed object
1743
1744 :return: True if the PKCS7 is of type signed
1745 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001746 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001747 return True
1748 return False
1749
1750
1751 def type_is_enveloped(self):
1752 """
1753 Check if this NID_pkcs7_enveloped object
1754
1755 :returns: True if the PKCS7 is of type enveloped
1756 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001757 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001758 return True
1759 return False
1760
1761
1762 def type_is_signedAndEnveloped(self):
1763 """
1764 Check if this NID_pkcs7_signedAndEnveloped object
1765
1766 :returns: True if the PKCS7 is of type signedAndEnveloped
1767 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001768 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001769 return True
1770 return False
1771
1772
1773 def type_is_data(self):
1774 """
1775 Check if this NID_pkcs7_data object
1776
1777 :return: True if the PKCS7 is of type data
1778 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001779 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001780 return True
1781 return False
1782
1783
1784 def get_type_name(self):
1785 """
1786 Returns the type name of the PKCS7 structure
1787
1788 :return: A string with the typename
1789 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001790 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1791 string_type = _lib.OBJ_nid2sn(nid)
1792 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001793
1794PKCS7Type = PKCS7
1795
1796
1797
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001798class PKCS12(object):
1799 def __init__(self):
1800 self._pkey = None
1801 self._cert = None
1802 self._cacerts = None
1803 self._friendlyname = None
1804
1805
1806 def get_certificate(self):
1807 """
1808 Return certificate portion of the PKCS12 structure
1809
1810 :return: X509 object containing the certificate
1811 """
1812 return self._cert
1813
1814
1815 def set_certificate(self, cert):
1816 """
1817 Replace the certificate portion of the PKCS12 structure
1818
1819 :param cert: The new certificate.
1820 :type cert: :py:class:`X509` or :py:data:`None`
1821 :return: None
1822 """
1823 if not isinstance(cert, X509):
1824 raise TypeError("cert must be an X509 instance")
1825 self._cert = cert
1826
1827
1828 def get_privatekey(self):
1829 """
1830 Return private key portion of the PKCS12 structure
1831
1832 :returns: PKey object containing the private key
1833 """
1834 return self._pkey
1835
1836
1837 def set_privatekey(self, pkey):
1838 """
1839 Replace or set the certificate portion of the PKCS12 structure
1840
1841 :param pkey: The new private key.
1842 :type pkey: :py:class:`PKey`
1843 :return: None
1844 """
1845 if not isinstance(pkey, PKey):
1846 raise TypeError("pkey must be a PKey instance")
1847 self._pkey = pkey
1848
1849
1850 def get_ca_certificates(self):
1851 """
1852 Return CA certificates within of the PKCS12 object
1853
1854 :return: A newly created tuple containing the CA certificates in the chain,
1855 if any are present, or None if no CA certificates are present.
1856 """
1857 if self._cacerts is not None:
1858 return tuple(self._cacerts)
1859
1860
1861 def set_ca_certificates(self, cacerts):
1862 """
1863 Replace or set the CA certificates withing the PKCS12 object.
1864
1865 :param cacerts: The new CA certificates.
1866 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1867 :return: None
1868 """
1869 if cacerts is None:
1870 self._cacerts = None
1871 else:
1872 cacerts = list(cacerts)
1873 for cert in cacerts:
1874 if not isinstance(cert, X509):
1875 raise TypeError("iterable must only contain X509 instances")
1876 self._cacerts = cacerts
1877
1878
1879 def set_friendlyname(self, name):
1880 """
1881 Replace or set the certificate portion of the PKCS12 structure
1882
1883 :param name: The new friendly name.
1884 :type name: :py:class:`bytes`
1885 :return: None
1886 """
1887 if name is None:
1888 self._friendlyname = None
1889 elif not isinstance(name, bytes):
1890 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1891 self._friendlyname = name
1892
1893
1894 def get_friendlyname(self):
1895 """
1896 Return friendly name portion of the PKCS12 structure
1897
1898 :returns: String containing the friendlyname
1899 """
1900 return self._friendlyname
1901
1902
1903 def export(self, passphrase=None, iter=2048, maciter=1):
1904 """
1905 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1906
1907 :param passphrase: used to encrypt the PKCS12
1908 :type passphrase: :py:data:`bytes`
1909
1910 :param iter: How many times to repeat the encryption
1911 :type iter: :py:data:`int`
1912
1913 :param maciter: How many times to repeat the MAC
1914 :type maciter: :py:data:`int`
1915
1916 :return: The string containing the PKCS12
1917 """
1918 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001919 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001920 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001921 cacerts = _lib.sk_X509_new_null()
1922 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001923 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001924 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001925
1926 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001927 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001928
1929 friendlyname = self._friendlyname
1930 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001931 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001932
1933 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001934 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001935 else:
1936 pkey = self._pkey._pkey
1937
1938 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001939 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001940 else:
1941 cert = self._cert._x509
1942
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001943 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001944 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001945 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1946 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001947 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001948 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001949 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001950 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001951
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001952 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001953 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001954 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001955
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001956PKCS12Type = PKCS12
1957
1958
1959
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001960class NetscapeSPKI(object):
1961 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001962 spki = _lib.NETSCAPE_SPKI_new()
1963 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001964
1965
1966 def sign(self, pkey, digest):
1967 """
1968 Sign the certificate request using the supplied key and digest
1969
1970 :param pkey: The key to sign with
1971 :param digest: The message digest to use
1972 :return: None
1973 """
1974 if pkey._only_public:
1975 raise ValueError("Key has only public part")
1976
1977 if not pkey._initialized:
1978 raise ValueError("Key is uninitialized")
1979
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001980 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001981 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001982 raise ValueError("No such digest method")
1983
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001984 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001985 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001986 # TODO: This is untested.
1987 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001988
1989
1990 def verify(self, key):
1991 """
1992 Verifies a certificate request using the supplied public key
1993
1994 :param key: a public key
1995 :return: True if the signature is correct.
1996 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1997 problem verifying the signature.
1998 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001999 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002000 if answer <= 0:
2001 _raise_current_error()
2002 return True
2003
2004
2005 def b64_encode(self):
2006 """
2007 Generate a base64 encoded string from an SPKI
2008
2009 :return: The base64 encoded string
2010 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002011 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2012 result = _ffi.string(encoded)
2013 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002014 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002015
2016
2017 def get_pubkey(self):
2018 """
2019 Get the public key of the certificate
2020
2021 :return: The public key
2022 """
2023 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002024 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2025 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002026 # TODO: This is untested.
2027 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002028 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002029 pkey._only_public = True
2030 return pkey
2031
2032
2033 def set_pubkey(self, pkey):
2034 """
2035 Set the public key of the certificate
2036
2037 :param pkey: The public key
2038 :return: None
2039 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002040 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002041 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002042 # TODO: This is untested.
2043 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002044NetscapeSPKIType = NetscapeSPKI
2045
2046
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002047class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002048 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002049 if type != FILETYPE_PEM and passphrase is not None:
2050 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002051 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002052 self._more_args = more_args
2053 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002054 self._problems = []
2055
2056
2057 @property
2058 def callback(self):
2059 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002060 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002061 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002062 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002063 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002064 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002065 else:
2066 raise TypeError("Last argument must be string or callable")
2067
2068
2069 @property
2070 def callback_args(self):
2071 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002072 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002073 elif isinstance(self._passphrase, bytes):
2074 return self._passphrase
2075 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002076 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002077 else:
2078 raise TypeError("Last argument must be string or callable")
2079
2080
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002081 def raise_if_problem(self, exceptionType=Error):
2082 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002083 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002084 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002085 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002086 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002087 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002088 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002089
2090
2091 def _read_passphrase(self, buf, size, rwflag, userdata):
2092 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002093 if self._more_args:
2094 result = self._passphrase(size, rwflag, userdata)
2095 else:
2096 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002097 if not isinstance(result, bytes):
2098 raise ValueError("String expected")
2099 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002100 if self._truncate:
2101 result = result[:size]
2102 else:
2103 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002104 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002105 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002106 return len(result)
2107 except Exception as e:
2108 self._problems.append(e)
2109 return 0
2110
2111
2112
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002113def load_privatekey(type, buffer, passphrase=None):
2114 """
2115 Load a private key from a buffer
2116
2117 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2118 :param buffer: The buffer the key is stored in
2119 :param passphrase: (optional) if encrypted PEM format, this can be
2120 either the passphrase to use, or a callback for
2121 providing the passphrase.
2122
2123 :return: The PKey object
2124 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002125 if isinstance(buffer, _text_type):
2126 buffer = buffer.encode("ascii")
2127
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002128 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002129
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002130 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002131 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002132 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2133 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002134 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002135 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002136 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002137 else:
2138 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2139
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002140 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002141 _raise_current_error()
2142
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002143 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002144 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002145 return pkey
2146
2147
2148
2149def dump_certificate_request(type, req):
2150 """
2151 Dump a certificate request to a buffer
2152
2153 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2154 :param req: The certificate request to dump
2155 :return: The buffer with the dumped certificate request in
2156 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002157 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002158
2159 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002160 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002161 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002162 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002163 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002164 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002165 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002166 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002167
2168 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002169 # TODO: This is untested.
2170 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002171
2172 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002173
2174
2175
2176def load_certificate_request(type, buffer):
2177 """
2178 Load a certificate request from a buffer
2179
2180 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2181 :param buffer: The buffer the certificate request is stored in
2182 :return: The X509Req object
2183 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002184 if isinstance(buffer, _text_type):
2185 buffer = buffer.encode("ascii")
2186
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002187 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002188
2189 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002190 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002191 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002192 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002193 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002194 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002195
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002196 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002197 # TODO: This is untested.
2198 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002199
2200 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002201 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002202 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002203
2204
2205
2206def sign(pkey, data, digest):
2207 """
2208 Sign data with a digest
2209
2210 :param pkey: Pkey to sign with
2211 :param data: data to be signed
2212 :param digest: message digest to use
2213 :return: signature
2214 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002215 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002216 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002217 raise ValueError("No such digest method")
2218
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002219 md_ctx = _ffi.new("EVP_MD_CTX*")
2220 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002221
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002222 _lib.EVP_SignInit(md_ctx, digest_obj)
2223 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002224
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002225 signature_buffer = _ffi.new("unsigned char[]", 512)
2226 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002227 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002228 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002229 md_ctx, signature_buffer, signature_length, pkey._pkey)
2230
2231 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002232 # TODO: This is untested.
2233 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002234
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002235 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002236
2237
2238
2239def verify(cert, signature, data, digest):
2240 """
2241 Verify a signature
2242
2243 :param cert: signing certificate (X509 object)
2244 :param signature: signature returned by sign function
2245 :param data: data to be verified
2246 :param digest: message digest to use
2247 :return: None if the signature is correct, raise exception otherwise
2248 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002249 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002250 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002251 raise ValueError("No such digest method")
2252
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002253 pkey = _lib.X509_get_pubkey(cert._x509)
2254 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002255 # TODO: This is untested.
2256 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002257 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002258
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002259 md_ctx = _ffi.new("EVP_MD_CTX*")
2260 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002261
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002262 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2263 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2264 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002265
2266 if verify_result != 1:
2267 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002268
2269
2270
2271def load_crl(type, buffer):
2272 """
2273 Load a certificate revocation list from a buffer
2274
2275 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2276 :param buffer: The buffer the CRL is stored in
2277
2278 :return: The PKey object
2279 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002280 if isinstance(buffer, _text_type):
2281 buffer = buffer.encode("ascii")
2282
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002283 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002284
2285 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002286 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002287 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002288 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002289 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002290 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2291
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002292 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002293 _raise_current_error()
2294
2295 result = CRL.__new__(CRL)
2296 result._crl = crl
2297 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002298
2299
2300
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002301def load_pkcs7_data(type, buffer):
2302 """
2303 Load pkcs7 data from a buffer
2304
2305 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2306 :param buffer: The buffer with the pkcs7 data.
2307 :return: The PKCS7 object
2308 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002309 if isinstance(buffer, _text_type):
2310 buffer = buffer.encode("ascii")
2311
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002312 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002313
2314 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002315 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002316 elif type == FILETYPE_ASN1:
2317 pass
2318 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002319 # TODO: This is untested.
2320 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002321 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2322
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002323 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002324 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002325
2326 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002327 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002328 return pypkcs7
2329
2330
2331
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002332def load_pkcs12(buffer, passphrase):
2333 """
2334 Load a PKCS12 object from a buffer
2335
2336 :param buffer: The buffer the certificate is stored in
2337 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2338 :returns: The PKCS12 object
2339 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002340 if isinstance(buffer, _text_type):
2341 buffer = buffer.encode("ascii")
2342
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002343 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002344
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002345 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2346 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002347 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002348 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002349
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002350 pkey = _ffi.new("EVP_PKEY**")
2351 cert = _ffi.new("X509**")
2352 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002353
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002354 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002355 if not parse_result:
2356 _raise_current_error()
2357
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002358 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002359
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002360 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2361 # queue for no particular reason. This error isn't interesting to anyone
2362 # outside this function. It's not even interesting to us. Get rid of it.
2363 try:
2364 _raise_current_error()
2365 except Error:
2366 pass
2367
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002368 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002369 pykey = None
2370 else:
2371 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002372 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002373
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002374 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002375 pycert = None
2376 friendlyname = None
2377 else:
2378 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002379 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002380
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002381 friendlyname_length = _ffi.new("int*")
2382 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2383 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2384 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002385 friendlyname = None
2386
2387 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002388 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002389 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002390 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002391 pycacerts.append(pycacert)
2392 if not pycacerts:
2393 pycacerts = None
2394
2395 pkcs12 = PKCS12.__new__(PKCS12)
2396 pkcs12._pkey = pykey
2397 pkcs12._cert = pycert
2398 pkcs12._cacerts = pycacerts
2399 pkcs12._friendlyname = friendlyname
2400 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002401
2402
2403def _initialize_openssl_threads(get_ident, Lock):
2404 import _ssl
2405 return
2406
2407 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2408
2409 def locking_function(mode, index, filename, line):
2410 if mode & _lib.CRYPTO_LOCK:
2411 locks[index].acquire()
2412 else:
2413 locks[index].release()
2414
2415 _lib.CRYPTO_set_id_callback(
2416 _ffi.callback("unsigned long (*)(void)", get_ident))
2417
2418 _lib.CRYPTO_set_locking_callback(
2419 _ffi.callback(
2420 "void (*)(int, int, const char*, int)", locking_function))
2421
2422
2423try:
2424 from thread import get_ident
2425 from threading import Lock
2426except ImportError:
2427 pass
2428else:
2429 _initialize_openssl_threads(get_ident, Lock)
2430 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002431
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002432# There are no direct unit tests for this initialization. It is tested
2433# indirectly since it is necessary for functions like dump_privatekey when
2434# using encryption.
2435#
2436# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2437# and some other similar tests may fail without this (though they may not if
2438# the Python runtime has already done some initialization of the underlying
2439# OpenSSL library (and is linked against the same one that cryptography is
2440# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002441_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002442
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002443# This is similar but exercised mainly by exception_from_error_queue. It calls
2444# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2445_lib.SSL_load_error_strings()