blob: 1312c0a121b869a81e78bfe5ff865e196d011279 [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 Calderone73945e32014-04-30 18:18:01 -0400269
270 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
271 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
272 instances each of which represents one curve supported by the system.
273 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400274 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400275 _curves = None
276
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400277 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400278 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400279 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400280 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400281
282 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400283
284 :return: A :py:type:`set` of ``cls`` instances giving the names of the
285 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400286 """
287 if lib.Cryptography_HAS_EC:
288 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
289 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
290 # The return value on this call should be num_curves again. We could
291 # check it to make sure but if it *isn't* then.. what could we do?
292 # Abort the whole process, I suppose...? -exarkun
293 lib.EC_get_builtin_curves(builtin_curves, num_curves)
294 return set(
295 cls.from_nid(lib, c.nid)
296 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400297 return set()
298
299
300 @classmethod
301 def _get_elliptic_curves(cls, lib):
302 """
303 Get, cache, and return the curves supported by OpenSSL.
304
305 :param lib: The OpenSSL library binding object.
306
307 :return: A :py:type:`set` of ``cls`` instances giving the names of the
308 elliptic curves the underlying library supports.
309 """
310 if cls._curves is None:
311 cls._curves = cls._load_elliptic_curves(lib)
312 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400313
314
315 @classmethod
316 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400317 """
318 Instantiate a new :py:class:`_EllipticCurve` associated with the given
319 OpenSSL NID.
320
321 :param lib: The OpenSSL library binding object.
322
323 :param nid: The OpenSSL NID the resulting curve object will represent.
324 This must be a curve NID (and not, for example, a hash NID) or
325 subsequent operations will fail in unpredictable ways.
326 :type nid: :py:class:`int`
327
328 :return: The curve object.
329 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400330 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
331
332
333 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400334 """
335 :param _lib: The :py:mod:`cryptography` binding instance used to
336 interface with OpenSSL.
337
338 :param _nid: The OpenSSL NID identifying the curve this object
339 represents.
340 :type _nid: :py:class:`int`
341
342 :param name: The OpenSSL short name identifying the curve this object
343 represents.
344 :type name: :py:class:`unicode`
345 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400346 self._lib = lib
347 self._nid = nid
348 self.name = name
349
350
351 def __repr__(self):
352 return "<Curve %r>" % (self.name,)
353
354
355 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400356 """
357 Create a new OpenSSL EC_KEY structure initialized to use this curve.
358
359 The structure is automatically garbage collected when the Python object
360 is garbage collected.
361 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400362 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
363 return _ffi.gc(key, _lib.EC_KEY_free)
364
365
366
367def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400368 """
369 Return a set of objects representing the elliptic curves supported in the
370 OpenSSL build in use.
371
372 The curve objects have a :py:class:`unicode` ``name`` attribute by which
373 they identify themselves.
374
375 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400376 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
377 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400378 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400379 return _EllipticCurve._get_elliptic_curves(_lib)
380
381
382
383def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400384 """
385 Return a single curve object selected by name.
386
387 See :py:func:`get_elliptic_curves` for information about curve objects.
388
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400389 :param name: The OpenSSL short name identifying the curve object to
390 retrieve.
391 :type name: :py:class:`unicode`
392
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400393 If the named curve is not supported then :py:class:`ValueError` is raised.
394 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400395 for curve in get_elliptic_curves():
396 if curve.name == name:
397 return curve
398 raise ValueError("unknown curve name", name)
399
400
401
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800402class X509Name(object):
403 def __init__(self, name):
404 """
405 Create a new X509Name, copying the given X509Name instance.
406
407 :param name: An X509Name object to copy
408 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500409 name = _lib.X509_NAME_dup(name._name)
410 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800411
412
413 def __setattr__(self, name, value):
414 if name.startswith('_'):
415 return super(X509Name, self).__setattr__(name, value)
416
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800417 # Note: we really do not want str subclasses here, so we do not use
418 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800419 if type(name) is not str:
420 raise TypeError("attribute name must be string, not '%.200s'" % (
421 type(value).__name__,))
422
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500423 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500424 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800425 try:
426 _raise_current_error()
427 except Error:
428 pass
429 raise AttributeError("No such attribute")
430
431 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500432 for i in range(_lib.X509_NAME_entry_count(self._name)):
433 ent = _lib.X509_NAME_get_entry(self._name, i)
434 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
435 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800436 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500437 ent = _lib.X509_NAME_delete_entry(self._name, i)
438 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800439 break
440
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500441 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800442 value = value.encode('utf-8')
443
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500444 add_result = _lib.X509_NAME_add_entry_by_NID(
445 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800446 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500447 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800448
449
450 def __getattr__(self, name):
451 """
452 Find attribute. An X509Name object has the following attributes:
453 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
454 organization (alias O), organizationalUnit (alias OU), commonName (alias
455 CN) and more...
456 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500457 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500458 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800459 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
460 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
461 # push something onto the error queue. If we don't clean that up
462 # now, someone else will bump into it later and be quite confused.
463 # See lp#314814.
464 try:
465 _raise_current_error()
466 except Error:
467 pass
468 return super(X509Name, self).__getattr__(name)
469
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500470 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800471 if entry_index == -1:
472 return None
473
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500474 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
475 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800476
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500477 result_buffer = _ffi.new("unsigned char**")
478 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800479 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500480 # TODO: This is untested.
481 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800482
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700483 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500484 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700485 finally:
486 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500487 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800488 return result
489
490
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500491 def _cmp(op):
492 def f(self, other):
493 if not isinstance(other, X509Name):
494 return NotImplemented
495 result = _lib.X509_NAME_cmp(self._name, other._name)
496 return op(result, 0)
497 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800498
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500499 __eq__ = _cmp(__eq__)
500 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800501
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500502 __lt__ = _cmp(__lt__)
503 __le__ = _cmp(__le__)
504
505 __gt__ = _cmp(__gt__)
506 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800507
508 def __repr__(self):
509 """
510 String representation of an X509Name
511 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500512 result_buffer = _ffi.new("char[]", 512);
513 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800514 self._name, result_buffer, len(result_buffer))
515
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500516 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500517 # TODO: This is untested.
518 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800519
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500520 return "<X509Name object '%s'>" % (
521 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522
523
524 def hash(self):
525 """
526 Return the hash value of this name
527
528 :return: None
529 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500530 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800531
532
533 def der(self):
534 """
535 Return the DER encoding of this name
536
537 :return: A :py:class:`bytes` instance giving the DER encoded form of
538 this name.
539 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500540 result_buffer = _ffi.new('unsigned char**')
541 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800542 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500543 # TODO: This is untested.
544 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800545
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500546 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
547 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800548 return string_result
549
550
551 def get_components(self):
552 """
553 Returns the split-up components of this name.
554
555 :return: List of tuples (name, value).
556 """
557 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500558 for i in range(_lib.X509_NAME_entry_count(self._name)):
559 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800560
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500561 fname = _lib.X509_NAME_ENTRY_get_object(ent)
562 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800563
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500564 nid = _lib.OBJ_obj2nid(fname)
565 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800566
567 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500568 _ffi.string(name),
569 _ffi.string(
570 _lib.ASN1_STRING_data(fval),
571 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800572
573 return result
574X509NameType = X509Name
575
576
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800577class X509Extension(object):
578 def __init__(self, type_name, critical, value, subject=None, issuer=None):
579 """
580 :param typename: The name of the extension to create.
581 :type typename: :py:data:`str`
582
583 :param critical: A flag indicating whether this is a critical extension.
584
585 :param value: The value of the extension.
586 :type value: :py:data:`str`
587
588 :param subject: Optional X509 cert to use as subject.
589 :type subject: :py:class:`X509`
590
591 :param issuer: Optional X509 cert to use as issuer.
592 :type issuer: :py:class:`X509`
593
594 :return: The X509Extension object
595 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500596 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800597
598 # A context is necessary for any extension which uses the r2i conversion
599 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
600 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500601 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800602
603 # We have no configuration database - but perhaps we should (some
604 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500605 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800606
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800607 # Initialize the subject and issuer, if appropriate. ctx is a local,
608 # and as far as I can tell none of the X509V3_* APIs invoked here steal
609 # any references, so no need to mess with reference counts or duplicates.
610 if issuer is not None:
611 if not isinstance(issuer, X509):
612 raise TypeError("issuer must be an X509 instance")
613 ctx.issuer_cert = issuer._x509
614 if subject is not None:
615 if not isinstance(subject, X509):
616 raise TypeError("subject must be an X509 instance")
617 ctx.subject_cert = subject._x509
618
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800619 if critical:
620 # There are other OpenSSL APIs which would let us pass in critical
621 # separately, but they're harder to use, and since value is already
622 # a pile of crappy junk smuggling a ton of utterly important
623 # structured data, what's the point of trying to avoid nasty stuff
624 # with strings? (However, X509V3_EXT_i2d in particular seems like it
625 # would be a better API to invoke. I do not know where to get the
626 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500627 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800628
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500629 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
630 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800631 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500632 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800633
634
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400635 @property
636 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500637 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400638
639 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500640 _lib.GEN_EMAIL: "email",
641 _lib.GEN_DNS: "DNS",
642 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400643 }
644
645 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500646 method = _lib.X509V3_EXT_get(self._extension)
647 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500648 # TODO: This is untested.
649 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400650 payload = self._extension.value.data
651 length = self._extension.value.length
652
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500653 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400654 payloadptr[0] = payload
655
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500656 if method.it != _ffi.NULL:
657 ptr = _lib.ASN1_ITEM_ptr(method.it)
658 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
659 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400660 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500661 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400662 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500663 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400664
665 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500666 for i in range(_lib.sk_GENERAL_NAME_num(names)):
667 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400668 try:
669 label = self._prefixes[name.type]
670 except KeyError:
671 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500672 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500673 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400674 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500675 value = _native(
676 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
677 parts.append(label + ":" + value)
678 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400679
680
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800681 def __str__(self):
682 """
683 :return: a nice text representation of the extension
684 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500685 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400686 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800687
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400688 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500689 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800690 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500691 # TODO: This is untested.
692 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800693
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500694 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800695
696
697 def get_critical(self):
698 """
699 Returns the critical field of the X509Extension
700
701 :return: The critical field.
702 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500703 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800704
705
706 def get_short_name(self):
707 """
708 Returns the short version of the type name of the X509Extension
709
710 :return: The short type name.
711 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500712 obj = _lib.X509_EXTENSION_get_object(self._extension)
713 nid = _lib.OBJ_obj2nid(obj)
714 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800715
716
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800717 def get_data(self):
718 """
719 Returns the data of the X509Extension
720
721 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
722 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500723 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
724 string_result = _ffi.cast('ASN1_STRING*', octet_result)
725 char_result = _lib.ASN1_STRING_data(string_result)
726 result_length = _lib.ASN1_STRING_length(string_result)
727 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800728
729X509ExtensionType = X509Extension
730
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800731
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800732class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800733 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500734 req = _lib.X509_REQ_new()
735 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800736
737
738 def set_pubkey(self, pkey):
739 """
740 Set the public key of the certificate request
741
742 :param pkey: The public key to use
743 :return: None
744 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500745 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800746 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500747 # TODO: This is untested.
748 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800749
750
751 def get_pubkey(self):
752 """
753 Get the public key from the certificate request
754
755 :return: The public key
756 """
757 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500758 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
759 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500760 # TODO: This is untested.
761 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500762 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800763 pkey._only_public = True
764 return pkey
765
766
767 def set_version(self, version):
768 """
769 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
770 request.
771
772 :param version: The version number
773 :return: None
774 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500775 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800776 if not set_result:
777 _raise_current_error()
778
779
780 def get_version(self):
781 """
782 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
783 request.
784
785 :return: an integer giving the value of the version subfield
786 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500787 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800788
789
790 def get_subject(self):
791 """
792 Create an X509Name object for the subject of the certificate request
793
794 :return: An X509Name object
795 """
796 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500797 name._name = _lib.X509_REQ_get_subject_name(self._req)
798 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500799 # TODO: This is untested.
800 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800801
802 # The name is owned by the X509Req structure. As long as the X509Name
803 # Python object is alive, keep the X509Req Python object alive.
804 name._owner = self
805
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800806 return name
807
808
809 def add_extensions(self, extensions):
810 """
811 Add extensions to the request.
812
813 :param extensions: a sequence of X509Extension objects
814 :return: None
815 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500816 stack = _lib.sk_X509_EXTENSION_new_null()
817 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500818 # TODO: This is untested.
819 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800820
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500821 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800822
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800823 for ext in extensions:
824 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800825 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800826
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800827 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500828 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800829
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500830 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800831 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500832 # TODO: This is untested.
833 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800834
835
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800836 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800837 """
838 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800839
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500840 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800841 """
842 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500843 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500844 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800845 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500846 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800847 exts.append(ext)
848 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800849
850
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800851 def sign(self, pkey, digest):
852 """
853 Sign the certificate request using the supplied key and digest
854
855 :param pkey: The key to sign with
856 :param digest: The message digest to use
857 :return: None
858 """
859 if pkey._only_public:
860 raise ValueError("Key has only public part")
861
862 if not pkey._initialized:
863 raise ValueError("Key is uninitialized")
864
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500865 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500866 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800867 raise ValueError("No such digest method")
868
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500869 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800870 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500871 # TODO: This is untested.
872 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800873
874
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800875 def verify(self, pkey):
876 """
877 Verifies a certificate request using the supplied public key
878
879 :param key: a public key
880 :return: True if the signature is correct.
881
882 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
883 problem verifying the signature.
884 """
885 if not isinstance(pkey, PKey):
886 raise TypeError("pkey must be a PKey instance")
887
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500888 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800889 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500890 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800891
892 return result
893
894
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800895X509ReqType = X509Req
896
897
898
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800899class X509(object):
900 def __init__(self):
901 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500902 x509 = _lib.X509_new()
903 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800904
905
906 def set_version(self, version):
907 """
908 Set version number of the certificate
909
910 :param version: The version number
911 :type version: :py:class:`int`
912
913 :return: None
914 """
915 if not isinstance(version, int):
916 raise TypeError("version must be an integer")
917
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500918 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800919
920
921 def get_version(self):
922 """
923 Return version number of the certificate
924
925 :return: Version number as a Python integer
926 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500927 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800928
929
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800930 def get_pubkey(self):
931 """
932 Get the public key of the certificate
933
934 :return: The public key
935 """
936 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500937 pkey._pkey = _lib.X509_get_pubkey(self._x509)
938 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800939 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500940 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800941 pkey._only_public = True
942 return pkey
943
944
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800945 def set_pubkey(self, pkey):
946 """
947 Set the public key of the certificate
948
949 :param pkey: The public key
950
951 :return: None
952 """
953 if not isinstance(pkey, PKey):
954 raise TypeError("pkey must be a PKey instance")
955
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500956 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800957 if not set_result:
958 _raise_current_error()
959
960
961 def sign(self, pkey, digest):
962 """
963 Sign the certificate using the supplied key and digest
964
965 :param pkey: The key to sign with
966 :param digest: The message digest to use
967 :return: None
968 """
969 if not isinstance(pkey, PKey):
970 raise TypeError("pkey must be a PKey instance")
971
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800972 if pkey._only_public:
973 raise ValueError("Key only has public part")
974
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800975 if not pkey._initialized:
976 raise ValueError("Key is uninitialized")
977
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500978 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500979 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800980 raise ValueError("No such digest method")
981
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500982 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800983 if not sign_result:
984 _raise_current_error()
985
986
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800987 def get_signature_algorithm(self):
988 """
989 Retrieve the signature algorithm used in the certificate
990
991 :return: A byte string giving the name of the signature algorithm used in
992 the certificate.
993 :raise ValueError: If the signature algorithm is undefined.
994 """
995 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500996 nid = _lib.OBJ_obj2nid(alg)
997 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800998 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500999 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001000
1001
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001002 def digest(self, digest_name):
1003 """
1004 Return the digest of the X509 object.
1005
1006 :param digest_name: The name of the digest algorithm to use.
1007 :type digest_name: :py:class:`bytes`
1008
1009 :return: The digest of the object
1010 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001011 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001012 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001013 raise ValueError("No such digest method")
1014
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001015 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1016 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001017 result_length[0] = len(result_buffer)
1018
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001019 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001020 self._x509, digest, result_buffer, result_length)
1021
1022 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001023 # TODO: This is untested.
1024 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001025
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001026 return b":".join([
1027 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001028 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001029
1030
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001031 def subject_name_hash(self):
1032 """
1033 Return the hash of the X509 subject.
1034
1035 :return: The hash of the subject.
1036 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001037 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001038
1039
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001040 def set_serial_number(self, serial):
1041 """
1042 Set serial number of the certificate
1043
1044 :param serial: The serial number
1045 :type serial: :py:class:`int`
1046
1047 :return: None
1048 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001049 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001050 raise TypeError("serial must be an integer")
1051
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001052 hex_serial = hex(serial)[2:]
1053 if not isinstance(hex_serial, bytes):
1054 hex_serial = hex_serial.encode('ascii')
1055
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001056 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001057
1058 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1059 # it. If bignum is still NULL after this call, then the return value is
1060 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001061 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001062
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001063 if bignum_serial[0] == _ffi.NULL:
1064 set_result = _lib.ASN1_INTEGER_set(
1065 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001066 if set_result:
1067 # TODO Not tested
1068 _raise_current_error()
1069 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001070 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1071 _lib.BN_free(bignum_serial[0])
1072 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001073 # TODO Not tested
1074 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001075 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1076 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001077 if not set_result:
1078 # TODO Not tested
1079 _raise_current_error()
1080
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001081
1082 def get_serial_number(self):
1083 """
1084 Return serial number of the certificate
1085
1086 :return: Serial number as a Python integer
1087 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001088 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1089 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001090 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001091 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001092 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001093 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001094 serial = int(hexstring_serial, 16)
1095 return serial
1096 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001097 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001098 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001099 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001100
1101
1102 def gmtime_adj_notAfter(self, amount):
1103 """
1104 Adjust the time stamp for when the certificate stops being valid
1105
1106 :param amount: The number of seconds by which to adjust the ending
1107 validity time.
1108 :type amount: :py:class:`int`
1109
1110 :return: None
1111 """
1112 if not isinstance(amount, int):
1113 raise TypeError("amount must be an integer")
1114
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001115 notAfter = _lib.X509_get_notAfter(self._x509)
1116 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001117
1118
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001119 def gmtime_adj_notBefore(self, amount):
1120 """
1121 Change the timestamp for when the certificate starts being valid to the current
1122 time plus an offset.
1123
1124 :param amount: The number of seconds by which to adjust the starting validity
1125 time.
1126 :return: None
1127 """
1128 if not isinstance(amount, int):
1129 raise TypeError("amount must be an integer")
1130
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001131 notBefore = _lib.X509_get_notBefore(self._x509)
1132 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001133
1134
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001135 def has_expired(self):
1136 """
1137 Check whether the certificate has expired.
1138
1139 :return: True if the certificate has expired, false otherwise
1140 """
1141 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001142 notAfter = _lib.X509_get_notAfter(self._x509)
1143 return _lib.ASN1_UTCTIME_cmp_time_t(
1144 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001145
1146
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001147 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001148 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001149
1150
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001151 def get_notBefore(self):
1152 """
1153 Retrieve the time stamp for when the certificate starts being valid
1154
1155 :return: A string giving the timestamp, in the format::
1156
1157 YYYYMMDDhhmmssZ
1158 YYYYMMDDhhmmss+hhmm
1159 YYYYMMDDhhmmss-hhmm
1160
1161 or None if there is no value set.
1162 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001163 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001164
1165
1166 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001167 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001168
1169
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001170 def set_notBefore(self, when):
1171 """
1172 Set the time stamp for when the certificate starts being valid
1173
1174 :param when: A string giving the timestamp, in the format:
1175
1176 YYYYMMDDhhmmssZ
1177 YYYYMMDDhhmmss+hhmm
1178 YYYYMMDDhhmmss-hhmm
1179 :type when: :py:class:`bytes`
1180
1181 :return: None
1182 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001183 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001184
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001185
1186 def get_notAfter(self):
1187 """
1188 Retrieve the time stamp for when the certificate stops being valid
1189
1190 :return: A string giving the timestamp, in the format::
1191
1192 YYYYMMDDhhmmssZ
1193 YYYYMMDDhhmmss+hhmm
1194 YYYYMMDDhhmmss-hhmm
1195
1196 or None if there is no value set.
1197 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001198 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001199
1200
1201 def set_notAfter(self, when):
1202 """
1203 Set the time stamp for when the certificate stops being valid
1204
1205 :param when: A string giving the timestamp, in the format:
1206
1207 YYYYMMDDhhmmssZ
1208 YYYYMMDDhhmmss+hhmm
1209 YYYYMMDDhhmmss-hhmm
1210 :type when: :py:class:`bytes`
1211
1212 :return: None
1213 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001214 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001215
1216
1217 def _get_name(self, which):
1218 name = X509Name.__new__(X509Name)
1219 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001220 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001221 # TODO: This is untested.
1222 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001223
1224 # The name is owned by the X509 structure. As long as the X509Name
1225 # Python object is alive, keep the X509 Python object alive.
1226 name._owner = self
1227
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001228 return name
1229
1230
1231 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001232 if not isinstance(name, X509Name):
1233 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001234 set_result = which(self._x509, name._name)
1235 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001236 # TODO: This is untested.
1237 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001238
1239
1240 def get_issuer(self):
1241 """
1242 Create an X509Name object for the issuer of the certificate
1243
1244 :return: An X509Name object
1245 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001246 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001247
1248
1249 def set_issuer(self, issuer):
1250 """
1251 Set the issuer of the certificate
1252
1253 :param issuer: The issuer name
1254 :type issuer: :py:class:`X509Name`
1255
1256 :return: None
1257 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001258 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001259
1260
1261 def get_subject(self):
1262 """
1263 Create an X509Name object for the subject of the certificate
1264
1265 :return: An X509Name object
1266 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001267 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001268
1269
1270 def set_subject(self, subject):
1271 """
1272 Set the subject of the certificate
1273
1274 :param subject: The subject name
1275 :type subject: :py:class:`X509Name`
1276 :return: None
1277 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001278 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001279
1280
1281 def get_extension_count(self):
1282 """
1283 Get the number of extensions on the certificate.
1284
1285 :return: The number of extensions as an integer.
1286 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001287 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001288
1289
1290 def add_extensions(self, extensions):
1291 """
1292 Add extensions to the certificate.
1293
1294 :param extensions: a sequence of X509Extension objects
1295 :return: None
1296 """
1297 for ext in extensions:
1298 if not isinstance(ext, X509Extension):
1299 raise ValueError("One of the elements is not an X509Extension")
1300
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001301 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001302 if not add_result:
1303 _raise_current_error()
1304
1305
1306 def get_extension(self, index):
1307 """
1308 Get a specific extension of the certificate by index.
1309
1310 :param index: The index of the extension to retrieve.
1311 :return: The X509Extension object at the specified index.
1312 """
1313 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001314 ext._extension = _lib.X509_get_ext(self._x509, index)
1315 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001316 raise IndexError("extension index out of bounds")
1317
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001318 extension = _lib.X509_EXTENSION_dup(ext._extension)
1319 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001320 return ext
1321
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001322X509Type = X509
1323
1324
1325
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001326class X509Store(object):
1327 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001328 store = _lib.X509_STORE_new()
1329 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001330
1331
1332 def add_cert(self, cert):
1333 if not isinstance(cert, X509):
1334 raise TypeError()
1335
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001336 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001337 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001338 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001339
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001340
1341X509StoreType = X509Store
1342
1343
1344
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001345def load_certificate(type, buffer):
1346 """
1347 Load a certificate from a buffer
1348
1349 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1350
1351 :param buffer: The buffer the certificate is stored in
1352 :type buffer: :py:class:`bytes`
1353
1354 :return: The X509 object
1355 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001356 if isinstance(buffer, _text_type):
1357 buffer = buffer.encode("ascii")
1358
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001359 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001360
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001361 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001362 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001363 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001364 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001365 else:
1366 raise ValueError(
1367 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001368
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001369 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001370 _raise_current_error()
1371
1372 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001373 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001374 return cert
1375
1376
1377def dump_certificate(type, cert):
1378 """
1379 Dump a certificate to a buffer
1380
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001381 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1382 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001383 :param cert: The certificate to dump
1384 :return: The buffer with the dumped certificate in
1385 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001386 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001387
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001388 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001389 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001390 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001391 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001392 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001393 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001394 else:
1395 raise ValueError(
1396 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1397 "FILETYPE_TEXT")
1398
1399 return _bio_to_string(bio)
1400
1401
1402
1403def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1404 """
1405 Dump a private key to a buffer
1406
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001407 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1408 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001409 :param pkey: The PKey to dump
1410 :param cipher: (optional) if encrypted PEM format, the cipher to
1411 use
1412 :param passphrase: (optional) if encrypted PEM format, this can be either
1413 the passphrase to use, or a callback for providing the
1414 passphrase.
1415 :return: The buffer with the dumped key in
1416 :rtype: :py:data:`str`
1417 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001418 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001419
1420 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001421 if passphrase is None:
1422 raise TypeError(
1423 "if a value is given for cipher "
1424 "one must also be given for passphrase")
1425 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001426 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001427 raise ValueError("Invalid cipher name")
1428 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001429 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001430
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001431 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001432 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001433 result_code = _lib.PEM_write_bio_PrivateKey(
1434 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001435 helper.callback, helper.callback_args)
1436 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001437 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001438 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001439 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001440 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1441 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001442 # TODO RSA_free(rsa)?
1443 else:
1444 raise ValueError(
1445 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1446 "FILETYPE_TEXT")
1447
1448 if result_code == 0:
1449 _raise_current_error()
1450
1451 return _bio_to_string(bio)
1452
1453
1454
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001455def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001456 copy = _lib.X509_REVOKED_new()
1457 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001458 # TODO: This is untested.
1459 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001460
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001461 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001462 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001463 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001464
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001465 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001466 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001467 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001468
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001469 if original.extensions != _ffi.NULL:
1470 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1471 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1472 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1473 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1474 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001475 copy.extensions = extension_stack
1476
1477 copy.sequence = original.sequence
1478 return copy
1479
1480
1481
1482class Revoked(object):
1483 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1484 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1485 # OCSP_crl_reason_str. We use the latter, just like the command line
1486 # program.
1487 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001488 b"unspecified",
1489 b"keyCompromise",
1490 b"CACompromise",
1491 b"affiliationChanged",
1492 b"superseded",
1493 b"cessationOfOperation",
1494 b"certificateHold",
1495 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001496 ]
1497
1498 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001499 revoked = _lib.X509_REVOKED_new()
1500 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001501
1502
1503 def set_serial(self, hex_str):
1504 """
1505 Set the serial number of a revoked Revoked structure
1506
1507 :param hex_str: The new serial number.
1508 :type hex_str: :py:data:`str`
1509 :return: None
1510 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001511 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1512 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001513 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001514 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001515 if not bn_result:
1516 raise ValueError("bad hex string")
1517
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001518 asn1_serial = _ffi.gc(
1519 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1520 _lib.ASN1_INTEGER_free)
1521 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001522
1523
1524 def get_serial(self):
1525 """
1526 Return the serial number of a Revoked structure
1527
1528 :return: The serial number as a string
1529 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001530 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001531
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001532 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001533 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001534 # TODO: This is untested.
1535 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001536
1537 return _bio_to_string(bio)
1538
1539
1540 def _delete_reason(self):
1541 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001542 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1543 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1544 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1545 _lib.X509_EXTENSION_free(ext)
1546 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001547 break
1548
1549
1550 def set_reason(self, reason):
1551 """
1552 Set the reason of a Revoked object.
1553
1554 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1555
1556 :param reason: The reason string.
1557 :type reason: :py:class:`str` or :py:class:`NoneType`
1558 :return: None
1559 """
1560 if reason is None:
1561 self._delete_reason()
1562 elif not isinstance(reason, bytes):
1563 raise TypeError("reason must be None or a byte string")
1564 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001565 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001566 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1567
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001568 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1569 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001570 # TODO: This is untested.
1571 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001572 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001573
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001574 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1575 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001576 # TODO: This is untested.
1577 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001578
1579 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001580 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1581 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001582
1583 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001584 # TODO: This is untested.
1585 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001586
1587
1588 def get_reason(self):
1589 """
1590 Return the reason of a Revoked object.
1591
1592 :return: The reason as a string
1593 """
1594 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001595 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1596 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1597 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001598 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001599
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001600 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001601 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001602 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001603 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001604 # TODO: This is untested.
1605 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001606
1607 return _bio_to_string(bio)
1608
1609
1610 def all_reasons(self):
1611 """
1612 Return a list of all the supported reason strings.
1613
1614 :return: A list of reason strings.
1615 """
1616 return self._crl_reasons[:]
1617
1618
1619 def set_rev_date(self, when):
1620 """
1621 Set the revocation timestamp
1622
1623 :param when: A string giving the timestamp, in the format:
1624
1625 YYYYMMDDhhmmssZ
1626 YYYYMMDDhhmmss+hhmm
1627 YYYYMMDDhhmmss-hhmm
1628
1629 :return: None
1630 """
1631 return _set_asn1_time(self._revoked.revocationDate, when)
1632
1633
1634 def get_rev_date(self):
1635 """
1636 Retrieve the revocation date
1637
1638 :return: A string giving the timestamp, in the format:
1639
1640 YYYYMMDDhhmmssZ
1641 YYYYMMDDhhmmss+hhmm
1642 YYYYMMDDhhmmss-hhmm
1643 """
1644 return _get_asn1_time(self._revoked.revocationDate)
1645
1646
1647
1648class CRL(object):
1649 def __init__(self):
1650 """
1651 Create a new empty CRL object.
1652 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001653 crl = _lib.X509_CRL_new()
1654 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001655
1656
1657 def get_revoked(self):
1658 """
1659 Return revoked portion of the CRL structure (by value not reference).
1660
1661 :return: A tuple of Revoked objects.
1662 """
1663 results = []
1664 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001665 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1666 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001667 revoked_copy = _X509_REVOKED_dup(revoked)
1668 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001669 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001670 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001671 if results:
1672 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001673
1674
1675 def add_revoked(self, revoked):
1676 """
1677 Add a revoked (by value not reference) to the CRL structure
1678
1679 :param revoked: The new revoked.
1680 :type revoked: :class:`X509`
1681
1682 :return: None
1683 """
1684 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001685 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001686 # TODO: This is untested.
1687 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001688
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001689 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001690 if add_result == 0:
1691 # TODO: This is untested.
1692 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001693
1694
1695 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1696 """
1697 export a CRL as a string
1698
1699 :param cert: Used to sign CRL.
1700 :type cert: :class:`X509`
1701
1702 :param key: Used to sign CRL.
1703 :type key: :class:`PKey`
1704
1705 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1706
1707 :param days: The number of days until the next update of this CRL.
1708 :type days: :py:data:`int`
1709
1710 :return: :py:data:`str`
1711 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001712 if not isinstance(cert, X509):
1713 raise TypeError("cert must be an X509 instance")
1714 if not isinstance(key, PKey):
1715 raise TypeError("key must be a PKey instance")
1716 if not isinstance(type, int):
1717 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001718
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001719 bio = _lib.BIO_new(_lib.BIO_s_mem())
1720 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001721 # TODO: This is untested.
1722 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001723
1724 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001725 sometime = _lib.ASN1_TIME_new()
1726 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001727 # TODO: This is untested.
1728 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001729
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001730 _lib.X509_gmtime_adj(sometime, 0)
1731 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001732
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001733 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1734 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001735
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001736 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001737
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001738 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001739 if not sign_result:
1740 _raise_current_error()
1741
1742 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001743 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001744 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001745 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001746 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001747 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001748 else:
1749 raise ValueError(
1750 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1751
1752 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001753 # TODO: This is untested.
1754 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001755
1756 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001757CRLType = CRL
1758
1759
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001760
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001761class PKCS7(object):
1762 def type_is_signed(self):
1763 """
1764 Check if this NID_pkcs7_signed object
1765
1766 :return: True if the PKCS7 is of type signed
1767 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001768 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001769 return True
1770 return False
1771
1772
1773 def type_is_enveloped(self):
1774 """
1775 Check if this NID_pkcs7_enveloped object
1776
1777 :returns: True if the PKCS7 is of type enveloped
1778 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001779 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001780 return True
1781 return False
1782
1783
1784 def type_is_signedAndEnveloped(self):
1785 """
1786 Check if this NID_pkcs7_signedAndEnveloped object
1787
1788 :returns: True if the PKCS7 is of type signedAndEnveloped
1789 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001790 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001791 return True
1792 return False
1793
1794
1795 def type_is_data(self):
1796 """
1797 Check if this NID_pkcs7_data object
1798
1799 :return: True if the PKCS7 is of type data
1800 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001801 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001802 return True
1803 return False
1804
1805
1806 def get_type_name(self):
1807 """
1808 Returns the type name of the PKCS7 structure
1809
1810 :return: A string with the typename
1811 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001812 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1813 string_type = _lib.OBJ_nid2sn(nid)
1814 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001815
1816PKCS7Type = PKCS7
1817
1818
1819
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001820class PKCS12(object):
1821 def __init__(self):
1822 self._pkey = None
1823 self._cert = None
1824 self._cacerts = None
1825 self._friendlyname = None
1826
1827
1828 def get_certificate(self):
1829 """
1830 Return certificate portion of the PKCS12 structure
1831
1832 :return: X509 object containing the certificate
1833 """
1834 return self._cert
1835
1836
1837 def set_certificate(self, cert):
1838 """
1839 Replace the certificate portion of the PKCS12 structure
1840
1841 :param cert: The new certificate.
1842 :type cert: :py:class:`X509` or :py:data:`None`
1843 :return: None
1844 """
1845 if not isinstance(cert, X509):
1846 raise TypeError("cert must be an X509 instance")
1847 self._cert = cert
1848
1849
1850 def get_privatekey(self):
1851 """
1852 Return private key portion of the PKCS12 structure
1853
1854 :returns: PKey object containing the private key
1855 """
1856 return self._pkey
1857
1858
1859 def set_privatekey(self, pkey):
1860 """
1861 Replace or set the certificate portion of the PKCS12 structure
1862
1863 :param pkey: The new private key.
1864 :type pkey: :py:class:`PKey`
1865 :return: None
1866 """
1867 if not isinstance(pkey, PKey):
1868 raise TypeError("pkey must be a PKey instance")
1869 self._pkey = pkey
1870
1871
1872 def get_ca_certificates(self):
1873 """
1874 Return CA certificates within of the PKCS12 object
1875
1876 :return: A newly created tuple containing the CA certificates in the chain,
1877 if any are present, or None if no CA certificates are present.
1878 """
1879 if self._cacerts is not None:
1880 return tuple(self._cacerts)
1881
1882
1883 def set_ca_certificates(self, cacerts):
1884 """
1885 Replace or set the CA certificates withing the PKCS12 object.
1886
1887 :param cacerts: The new CA certificates.
1888 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1889 :return: None
1890 """
1891 if cacerts is None:
1892 self._cacerts = None
1893 else:
1894 cacerts = list(cacerts)
1895 for cert in cacerts:
1896 if not isinstance(cert, X509):
1897 raise TypeError("iterable must only contain X509 instances")
1898 self._cacerts = cacerts
1899
1900
1901 def set_friendlyname(self, name):
1902 """
1903 Replace or set the certificate portion of the PKCS12 structure
1904
1905 :param name: The new friendly name.
1906 :type name: :py:class:`bytes`
1907 :return: None
1908 """
1909 if name is None:
1910 self._friendlyname = None
1911 elif not isinstance(name, bytes):
1912 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1913 self._friendlyname = name
1914
1915
1916 def get_friendlyname(self):
1917 """
1918 Return friendly name portion of the PKCS12 structure
1919
1920 :returns: String containing the friendlyname
1921 """
1922 return self._friendlyname
1923
1924
1925 def export(self, passphrase=None, iter=2048, maciter=1):
1926 """
1927 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1928
1929 :param passphrase: used to encrypt the PKCS12
1930 :type passphrase: :py:data:`bytes`
1931
1932 :param iter: How many times to repeat the encryption
1933 :type iter: :py:data:`int`
1934
1935 :param maciter: How many times to repeat the MAC
1936 :type maciter: :py:data:`int`
1937
1938 :return: The string containing the PKCS12
1939 """
1940 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001941 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001942 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001943 cacerts = _lib.sk_X509_new_null()
1944 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001945 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001946 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001947
1948 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001949 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001950
1951 friendlyname = self._friendlyname
1952 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001953 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001954
1955 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001956 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001957 else:
1958 pkey = self._pkey._pkey
1959
1960 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001961 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001962 else:
1963 cert = self._cert._x509
1964
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001965 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001966 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001967 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1968 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001969 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001970 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001971 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001972 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001973
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001974 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001975 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001976 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001977
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001978PKCS12Type = PKCS12
1979
1980
1981
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001982class NetscapeSPKI(object):
1983 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001984 spki = _lib.NETSCAPE_SPKI_new()
1985 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001986
1987
1988 def sign(self, pkey, digest):
1989 """
1990 Sign the certificate request using the supplied key and digest
1991
1992 :param pkey: The key to sign with
1993 :param digest: The message digest to use
1994 :return: None
1995 """
1996 if pkey._only_public:
1997 raise ValueError("Key has only public part")
1998
1999 if not pkey._initialized:
2000 raise ValueError("Key is uninitialized")
2001
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002002 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002003 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002004 raise ValueError("No such digest method")
2005
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002006 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002007 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002008 # TODO: This is untested.
2009 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002010
2011
2012 def verify(self, key):
2013 """
2014 Verifies a certificate request using the supplied public key
2015
2016 :param key: a public key
2017 :return: True if the signature is correct.
2018 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2019 problem verifying the signature.
2020 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002021 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002022 if answer <= 0:
2023 _raise_current_error()
2024 return True
2025
2026
2027 def b64_encode(self):
2028 """
2029 Generate a base64 encoded string from an SPKI
2030
2031 :return: The base64 encoded string
2032 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002033 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2034 result = _ffi.string(encoded)
2035 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002036 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002037
2038
2039 def get_pubkey(self):
2040 """
2041 Get the public key of the certificate
2042
2043 :return: The public key
2044 """
2045 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002046 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2047 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002048 # TODO: This is untested.
2049 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002050 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002051 pkey._only_public = True
2052 return pkey
2053
2054
2055 def set_pubkey(self, pkey):
2056 """
2057 Set the public key of the certificate
2058
2059 :param pkey: The public key
2060 :return: None
2061 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002062 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002063 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002064 # TODO: This is untested.
2065 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002066NetscapeSPKIType = NetscapeSPKI
2067
2068
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002069class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002070 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002071 if type != FILETYPE_PEM and passphrase is not None:
2072 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002073 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002074 self._more_args = more_args
2075 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002076 self._problems = []
2077
2078
2079 @property
2080 def callback(self):
2081 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002082 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002083 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002084 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002085 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002086 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002087 else:
2088 raise TypeError("Last argument must be string or callable")
2089
2090
2091 @property
2092 def callback_args(self):
2093 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002094 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002095 elif isinstance(self._passphrase, bytes):
2096 return self._passphrase
2097 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002098 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002099 else:
2100 raise TypeError("Last argument must be string or callable")
2101
2102
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002103 def raise_if_problem(self, exceptionType=Error):
2104 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002105 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002106 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002107 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002108 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002109 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002110 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002111
2112
2113 def _read_passphrase(self, buf, size, rwflag, userdata):
2114 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002115 if self._more_args:
2116 result = self._passphrase(size, rwflag, userdata)
2117 else:
2118 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002119 if not isinstance(result, bytes):
2120 raise ValueError("String expected")
2121 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002122 if self._truncate:
2123 result = result[:size]
2124 else:
2125 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002126 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002127 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002128 return len(result)
2129 except Exception as e:
2130 self._problems.append(e)
2131 return 0
2132
2133
2134
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002135def load_privatekey(type, buffer, passphrase=None):
2136 """
2137 Load a private key from a buffer
2138
2139 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2140 :param buffer: The buffer the key is stored in
2141 :param passphrase: (optional) if encrypted PEM format, this can be
2142 either the passphrase to use, or a callback for
2143 providing the passphrase.
2144
2145 :return: The PKey object
2146 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002147 if isinstance(buffer, _text_type):
2148 buffer = buffer.encode("ascii")
2149
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002150 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002151
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002152 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002153 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002154 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2155 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002156 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002157 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002158 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002159 else:
2160 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2161
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002162 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002163 _raise_current_error()
2164
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002165 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002166 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002167 return pkey
2168
2169
2170
2171def dump_certificate_request(type, req):
2172 """
2173 Dump a certificate request to a buffer
2174
2175 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2176 :param req: The certificate request to dump
2177 :return: The buffer with the dumped certificate request in
2178 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002179 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002180
2181 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002182 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002183 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002184 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002185 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002186 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002187 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002188 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002189
2190 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002191 # TODO: This is untested.
2192 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002193
2194 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002195
2196
2197
2198def load_certificate_request(type, buffer):
2199 """
2200 Load a certificate request from a buffer
2201
2202 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2203 :param buffer: The buffer the certificate request is stored in
2204 :return: The X509Req object
2205 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002206 if isinstance(buffer, _text_type):
2207 buffer = buffer.encode("ascii")
2208
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002209 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002210
2211 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002212 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002213 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002214 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002215 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002216 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002217
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002218 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002219 # TODO: This is untested.
2220 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002221
2222 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002223 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002224 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002225
2226
2227
2228def sign(pkey, data, digest):
2229 """
2230 Sign data with a digest
2231
2232 :param pkey: Pkey to sign with
2233 :param data: data to be signed
2234 :param digest: message digest to use
2235 :return: signature
2236 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002237 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002238 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002239 raise ValueError("No such digest method")
2240
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002241 md_ctx = _ffi.new("EVP_MD_CTX*")
2242 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002243
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002244 _lib.EVP_SignInit(md_ctx, digest_obj)
2245 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002246
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002247 signature_buffer = _ffi.new("unsigned char[]", 512)
2248 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002249 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002250 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002251 md_ctx, signature_buffer, signature_length, pkey._pkey)
2252
2253 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002254 # TODO: This is untested.
2255 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002256
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002257 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002258
2259
2260
2261def verify(cert, signature, data, digest):
2262 """
2263 Verify a signature
2264
2265 :param cert: signing certificate (X509 object)
2266 :param signature: signature returned by sign function
2267 :param data: data to be verified
2268 :param digest: message digest to use
2269 :return: None if the signature is correct, raise exception otherwise
2270 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002271 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002272 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002273 raise ValueError("No such digest method")
2274
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002275 pkey = _lib.X509_get_pubkey(cert._x509)
2276 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002277 # TODO: This is untested.
2278 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002279 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002280
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002281 md_ctx = _ffi.new("EVP_MD_CTX*")
2282 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002283
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002284 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2285 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2286 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002287
2288 if verify_result != 1:
2289 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002290
2291
2292
2293def load_crl(type, buffer):
2294 """
2295 Load a certificate revocation list from a buffer
2296
2297 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2298 :param buffer: The buffer the CRL is stored in
2299
2300 :return: The PKey object
2301 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002302 if isinstance(buffer, _text_type):
2303 buffer = buffer.encode("ascii")
2304
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002305 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002306
2307 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002308 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002309 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002310 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002311 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002312 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2313
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002314 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002315 _raise_current_error()
2316
2317 result = CRL.__new__(CRL)
2318 result._crl = crl
2319 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002320
2321
2322
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002323def load_pkcs7_data(type, buffer):
2324 """
2325 Load pkcs7 data from a buffer
2326
2327 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2328 :param buffer: The buffer with the pkcs7 data.
2329 :return: The PKCS7 object
2330 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002331 if isinstance(buffer, _text_type):
2332 buffer = buffer.encode("ascii")
2333
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002334 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002335
2336 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002337 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002338 elif type == FILETYPE_ASN1:
2339 pass
2340 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002341 # TODO: This is untested.
2342 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002343 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2344
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002345 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002346 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002347
2348 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002349 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002350 return pypkcs7
2351
2352
2353
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002354def load_pkcs12(buffer, passphrase):
2355 """
2356 Load a PKCS12 object from a buffer
2357
2358 :param buffer: The buffer the certificate is stored in
2359 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2360 :returns: The PKCS12 object
2361 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002362 if isinstance(buffer, _text_type):
2363 buffer = buffer.encode("ascii")
2364
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002365 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002366
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002367 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2368 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002369 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002370 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002371
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002372 pkey = _ffi.new("EVP_PKEY**")
2373 cert = _ffi.new("X509**")
2374 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002375
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002376 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002377 if not parse_result:
2378 _raise_current_error()
2379
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002380 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002381
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002382 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2383 # queue for no particular reason. This error isn't interesting to anyone
2384 # outside this function. It's not even interesting to us. Get rid of it.
2385 try:
2386 _raise_current_error()
2387 except Error:
2388 pass
2389
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002390 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002391 pykey = None
2392 else:
2393 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002394 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002395
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002396 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002397 pycert = None
2398 friendlyname = None
2399 else:
2400 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002401 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002402
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002403 friendlyname_length = _ffi.new("int*")
2404 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2405 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2406 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002407 friendlyname = None
2408
2409 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002410 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002411 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002412 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002413 pycacerts.append(pycacert)
2414 if not pycacerts:
2415 pycacerts = None
2416
2417 pkcs12 = PKCS12.__new__(PKCS12)
2418 pkcs12._pkey = pykey
2419 pkcs12._cert = pycert
2420 pkcs12._cacerts = pycacerts
2421 pkcs12._friendlyname = friendlyname
2422 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002423
2424
2425def _initialize_openssl_threads(get_ident, Lock):
2426 import _ssl
2427 return
2428
2429 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2430
2431 def locking_function(mode, index, filename, line):
2432 if mode & _lib.CRYPTO_LOCK:
2433 locks[index].acquire()
2434 else:
2435 locks[index].release()
2436
2437 _lib.CRYPTO_set_id_callback(
2438 _ffi.callback("unsigned long (*)(void)", get_ident))
2439
2440 _lib.CRYPTO_set_locking_callback(
2441 _ffi.callback(
2442 "void (*)(int, int, const char*, int)", locking_function))
2443
2444
2445try:
2446 from thread import get_ident
2447 from threading import Lock
2448except ImportError:
2449 pass
2450else:
2451 _initialize_openssl_threads(get_ident, Lock)
2452 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002453
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002454# There are no direct unit tests for this initialization. It is tested
2455# indirectly since it is necessary for functions like dump_privatekey when
2456# using encryption.
2457#
2458# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2459# and some other similar tests may fail without this (though they may not if
2460# the Python runtime has already done some initialization of the underlying
2461# OpenSSL library (and is linked against the same one that cryptography is
2462# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002463_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002464
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002465# This is similar but exercised mainly by exception_from_error_queue. It calls
2466# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2467_lib.SSL_load_error_strings()