blob: d62ce84d1f3cc20de74a77f739d708134e7445b7 [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 Calderone40da72d2014-05-01 09:25:17 -0400277 def __ne__(self, other):
278 if isinstance(other, _EllipticCurve):
279 return super(_EllipticCurve, self).__ne__(other)
280 return NotImplemented
281
282
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400283 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400284 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400285 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400286 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400287
288 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400289
290 :return: A :py:type:`set` of ``cls`` instances giving the names of the
291 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400292 """
293 if lib.Cryptography_HAS_EC:
294 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
295 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
296 # The return value on this call should be num_curves again. We could
297 # check it to make sure but if it *isn't* then.. what could we do?
298 # Abort the whole process, I suppose...? -exarkun
299 lib.EC_get_builtin_curves(builtin_curves, num_curves)
300 return set(
301 cls.from_nid(lib, c.nid)
302 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400303 return set()
304
305
306 @classmethod
307 def _get_elliptic_curves(cls, lib):
308 """
309 Get, cache, and return the curves supported by OpenSSL.
310
311 :param lib: The OpenSSL library binding object.
312
313 :return: A :py:type:`set` of ``cls`` instances giving the names of the
314 elliptic curves the underlying library supports.
315 """
316 if cls._curves is None:
317 cls._curves = cls._load_elliptic_curves(lib)
318 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400319
320
321 @classmethod
322 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400323 """
324 Instantiate a new :py:class:`_EllipticCurve` associated with the given
325 OpenSSL NID.
326
327 :param lib: The OpenSSL library binding object.
328
329 :param nid: The OpenSSL NID the resulting curve object will represent.
330 This must be a curve NID (and not, for example, a hash NID) or
331 subsequent operations will fail in unpredictable ways.
332 :type nid: :py:class:`int`
333
334 :return: The curve object.
335 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400336 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
337
338
339 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400340 """
341 :param _lib: The :py:mod:`cryptography` binding instance used to
342 interface with OpenSSL.
343
344 :param _nid: The OpenSSL NID identifying the curve this object
345 represents.
346 :type _nid: :py:class:`int`
347
348 :param name: The OpenSSL short name identifying the curve this object
349 represents.
350 :type name: :py:class:`unicode`
351 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400352 self._lib = lib
353 self._nid = nid
354 self.name = name
355
356
357 def __repr__(self):
358 return "<Curve %r>" % (self.name,)
359
360
361 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400362 """
363 Create a new OpenSSL EC_KEY structure initialized to use this curve.
364
365 The structure is automatically garbage collected when the Python object
366 is garbage collected.
367 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400368 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
369 return _ffi.gc(key, _lib.EC_KEY_free)
370
371
372
373def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400374 """
375 Return a set of objects representing the elliptic curves supported in the
376 OpenSSL build in use.
377
378 The curve objects have a :py:class:`unicode` ``name`` attribute by which
379 they identify themselves.
380
381 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400382 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
383 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400384 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400385 return _EllipticCurve._get_elliptic_curves(_lib)
386
387
388
389def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400390 """
391 Return a single curve object selected by name.
392
393 See :py:func:`get_elliptic_curves` for information about curve objects.
394
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400395 :param name: The OpenSSL short name identifying the curve object to
396 retrieve.
397 :type name: :py:class:`unicode`
398
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400399 If the named curve is not supported then :py:class:`ValueError` is raised.
400 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400401 for curve in get_elliptic_curves():
402 if curve.name == name:
403 return curve
404 raise ValueError("unknown curve name", name)
405
406
407
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800408class X509Name(object):
409 def __init__(self, name):
410 """
411 Create a new X509Name, copying the given X509Name instance.
412
413 :param name: An X509Name object to copy
414 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500415 name = _lib.X509_NAME_dup(name._name)
416 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800417
418
419 def __setattr__(self, name, value):
420 if name.startswith('_'):
421 return super(X509Name, self).__setattr__(name, value)
422
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800423 # Note: we really do not want str subclasses here, so we do not use
424 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800425 if type(name) is not str:
426 raise TypeError("attribute name must be string, not '%.200s'" % (
427 type(value).__name__,))
428
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500429 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500430 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800431 try:
432 _raise_current_error()
433 except Error:
434 pass
435 raise AttributeError("No such attribute")
436
437 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500438 for i in range(_lib.X509_NAME_entry_count(self._name)):
439 ent = _lib.X509_NAME_get_entry(self._name, i)
440 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
441 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800442 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500443 ent = _lib.X509_NAME_delete_entry(self._name, i)
444 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800445 break
446
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500447 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800448 value = value.encode('utf-8')
449
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500450 add_result = _lib.X509_NAME_add_entry_by_NID(
451 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800452 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500453 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800454
455
456 def __getattr__(self, name):
457 """
458 Find attribute. An X509Name object has the following attributes:
459 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
460 organization (alias O), organizationalUnit (alias OU), commonName (alias
461 CN) and more...
462 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500463 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500464 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800465 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
466 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
467 # push something onto the error queue. If we don't clean that up
468 # now, someone else will bump into it later and be quite confused.
469 # See lp#314814.
470 try:
471 _raise_current_error()
472 except Error:
473 pass
474 return super(X509Name, self).__getattr__(name)
475
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500476 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800477 if entry_index == -1:
478 return None
479
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500480 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
481 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800482
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500483 result_buffer = _ffi.new("unsigned char**")
484 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800485 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500486 # TODO: This is untested.
487 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800488
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700489 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500490 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700491 finally:
492 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500493 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800494 return result
495
496
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500497 def _cmp(op):
498 def f(self, other):
499 if not isinstance(other, X509Name):
500 return NotImplemented
501 result = _lib.X509_NAME_cmp(self._name, other._name)
502 return op(result, 0)
503 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800504
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500505 __eq__ = _cmp(__eq__)
506 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800507
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500508 __lt__ = _cmp(__lt__)
509 __le__ = _cmp(__le__)
510
511 __gt__ = _cmp(__gt__)
512 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800513
514 def __repr__(self):
515 """
516 String representation of an X509Name
517 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500518 result_buffer = _ffi.new("char[]", 512);
519 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800520 self._name, result_buffer, len(result_buffer))
521
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500522 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500523 # TODO: This is untested.
524 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800525
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500526 return "<X509Name object '%s'>" % (
527 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800528
529
530 def hash(self):
531 """
532 Return the hash value of this name
533
534 :return: None
535 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500536 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800537
538
539 def der(self):
540 """
541 Return the DER encoding of this name
542
543 :return: A :py:class:`bytes` instance giving the DER encoded form of
544 this name.
545 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500546 result_buffer = _ffi.new('unsigned char**')
547 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800548 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500549 # TODO: This is untested.
550 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800551
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500552 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
553 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800554 return string_result
555
556
557 def get_components(self):
558 """
559 Returns the split-up components of this name.
560
561 :return: List of tuples (name, value).
562 """
563 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500564 for i in range(_lib.X509_NAME_entry_count(self._name)):
565 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800566
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500567 fname = _lib.X509_NAME_ENTRY_get_object(ent)
568 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800569
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500570 nid = _lib.OBJ_obj2nid(fname)
571 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800572
573 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500574 _ffi.string(name),
575 _ffi.string(
576 _lib.ASN1_STRING_data(fval),
577 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800578
579 return result
580X509NameType = X509Name
581
582
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800583class X509Extension(object):
584 def __init__(self, type_name, critical, value, subject=None, issuer=None):
585 """
586 :param typename: The name of the extension to create.
587 :type typename: :py:data:`str`
588
589 :param critical: A flag indicating whether this is a critical extension.
590
591 :param value: The value of the extension.
592 :type value: :py:data:`str`
593
594 :param subject: Optional X509 cert to use as subject.
595 :type subject: :py:class:`X509`
596
597 :param issuer: Optional X509 cert to use as issuer.
598 :type issuer: :py:class:`X509`
599
600 :return: The X509Extension object
601 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500602 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800603
604 # A context is necessary for any extension which uses the r2i conversion
605 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
606 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500607 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800608
609 # We have no configuration database - but perhaps we should (some
610 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500611 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800612
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800613 # Initialize the subject and issuer, if appropriate. ctx is a local,
614 # and as far as I can tell none of the X509V3_* APIs invoked here steal
615 # any references, so no need to mess with reference counts or duplicates.
616 if issuer is not None:
617 if not isinstance(issuer, X509):
618 raise TypeError("issuer must be an X509 instance")
619 ctx.issuer_cert = issuer._x509
620 if subject is not None:
621 if not isinstance(subject, X509):
622 raise TypeError("subject must be an X509 instance")
623 ctx.subject_cert = subject._x509
624
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800625 if critical:
626 # There are other OpenSSL APIs which would let us pass in critical
627 # separately, but they're harder to use, and since value is already
628 # a pile of crappy junk smuggling a ton of utterly important
629 # structured data, what's the point of trying to avoid nasty stuff
630 # with strings? (However, X509V3_EXT_i2d in particular seems like it
631 # would be a better API to invoke. I do not know where to get the
632 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500633 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800634
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500635 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
636 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800637 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500638 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800639
640
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400641 @property
642 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500643 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400644
645 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500646 _lib.GEN_EMAIL: "email",
647 _lib.GEN_DNS: "DNS",
648 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400649 }
650
651 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500652 method = _lib.X509V3_EXT_get(self._extension)
653 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500654 # TODO: This is untested.
655 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400656 payload = self._extension.value.data
657 length = self._extension.value.length
658
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500659 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400660 payloadptr[0] = payload
661
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500662 if method.it != _ffi.NULL:
663 ptr = _lib.ASN1_ITEM_ptr(method.it)
664 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
665 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400666 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500667 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400668 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500669 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400670
671 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500672 for i in range(_lib.sk_GENERAL_NAME_num(names)):
673 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400674 try:
675 label = self._prefixes[name.type]
676 except KeyError:
677 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500678 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500679 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400680 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500681 value = _native(
682 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
683 parts.append(label + ":" + value)
684 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400685
686
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800687 def __str__(self):
688 """
689 :return: a nice text representation of the extension
690 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500691 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400692 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800693
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400694 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500695 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800696 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500697 # TODO: This is untested.
698 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800699
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500700 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800701
702
703 def get_critical(self):
704 """
705 Returns the critical field of the X509Extension
706
707 :return: The critical field.
708 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500709 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800710
711
712 def get_short_name(self):
713 """
714 Returns the short version of the type name of the X509Extension
715
716 :return: The short type name.
717 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500718 obj = _lib.X509_EXTENSION_get_object(self._extension)
719 nid = _lib.OBJ_obj2nid(obj)
720 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800721
722
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800723 def get_data(self):
724 """
725 Returns the data of the X509Extension
726
727 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
728 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500729 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
730 string_result = _ffi.cast('ASN1_STRING*', octet_result)
731 char_result = _lib.ASN1_STRING_data(string_result)
732 result_length = _lib.ASN1_STRING_length(string_result)
733 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800734
735X509ExtensionType = X509Extension
736
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800737
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800738class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800739 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500740 req = _lib.X509_REQ_new()
741 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800742
743
744 def set_pubkey(self, pkey):
745 """
746 Set the public key of the certificate request
747
748 :param pkey: The public key to use
749 :return: None
750 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500751 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800752 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500753 # TODO: This is untested.
754 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800755
756
757 def get_pubkey(self):
758 """
759 Get the public key from the certificate request
760
761 :return: The public key
762 """
763 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500764 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
765 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500766 # TODO: This is untested.
767 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500768 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800769 pkey._only_public = True
770 return pkey
771
772
773 def set_version(self, version):
774 """
775 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
776 request.
777
778 :param version: The version number
779 :return: None
780 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500781 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800782 if not set_result:
783 _raise_current_error()
784
785
786 def get_version(self):
787 """
788 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
789 request.
790
791 :return: an integer giving the value of the version subfield
792 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500793 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800794
795
796 def get_subject(self):
797 """
798 Create an X509Name object for the subject of the certificate request
799
800 :return: An X509Name object
801 """
802 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500803 name._name = _lib.X509_REQ_get_subject_name(self._req)
804 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500805 # TODO: This is untested.
806 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800807
808 # The name is owned by the X509Req structure. As long as the X509Name
809 # Python object is alive, keep the X509Req Python object alive.
810 name._owner = self
811
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800812 return name
813
814
815 def add_extensions(self, extensions):
816 """
817 Add extensions to the request.
818
819 :param extensions: a sequence of X509Extension objects
820 :return: None
821 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500822 stack = _lib.sk_X509_EXTENSION_new_null()
823 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500824 # TODO: This is untested.
825 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800826
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500827 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800828
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800829 for ext in extensions:
830 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800831 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800832
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800833 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500834 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800835
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500836 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800837 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500838 # TODO: This is untested.
839 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800840
841
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800842 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800843 """
844 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800845
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500846 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800847 """
848 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500849 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500850 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800851 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500852 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800853 exts.append(ext)
854 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800855
856
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800857 def sign(self, pkey, digest):
858 """
859 Sign the certificate request using the supplied key and digest
860
861 :param pkey: The key to sign with
862 :param digest: The message digest to use
863 :return: None
864 """
865 if pkey._only_public:
866 raise ValueError("Key has only public part")
867
868 if not pkey._initialized:
869 raise ValueError("Key is uninitialized")
870
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500871 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500872 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800873 raise ValueError("No such digest method")
874
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500875 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800876 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500877 # TODO: This is untested.
878 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800879
880
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800881 def verify(self, pkey):
882 """
883 Verifies a certificate request using the supplied public key
884
885 :param key: a public key
886 :return: True if the signature is correct.
887
888 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
889 problem verifying the signature.
890 """
891 if not isinstance(pkey, PKey):
892 raise TypeError("pkey must be a PKey instance")
893
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500894 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800895 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500896 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800897
898 return result
899
900
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800901X509ReqType = X509Req
902
903
904
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800905class X509(object):
906 def __init__(self):
907 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500908 x509 = _lib.X509_new()
909 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800910
911
912 def set_version(self, version):
913 """
914 Set version number of the certificate
915
916 :param version: The version number
917 :type version: :py:class:`int`
918
919 :return: None
920 """
921 if not isinstance(version, int):
922 raise TypeError("version must be an integer")
923
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500924 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800925
926
927 def get_version(self):
928 """
929 Return version number of the certificate
930
931 :return: Version number as a Python integer
932 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500933 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800934
935
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800936 def get_pubkey(self):
937 """
938 Get the public key of the certificate
939
940 :return: The public key
941 """
942 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500943 pkey._pkey = _lib.X509_get_pubkey(self._x509)
944 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800945 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500946 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800947 pkey._only_public = True
948 return pkey
949
950
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800951 def set_pubkey(self, pkey):
952 """
953 Set the public key of the certificate
954
955 :param pkey: The public key
956
957 :return: None
958 """
959 if not isinstance(pkey, PKey):
960 raise TypeError("pkey must be a PKey instance")
961
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500962 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800963 if not set_result:
964 _raise_current_error()
965
966
967 def sign(self, pkey, digest):
968 """
969 Sign the certificate using the supplied key and digest
970
971 :param pkey: The key to sign with
972 :param digest: The message digest to use
973 :return: None
974 """
975 if not isinstance(pkey, PKey):
976 raise TypeError("pkey must be a PKey instance")
977
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800978 if pkey._only_public:
979 raise ValueError("Key only has public part")
980
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800981 if not pkey._initialized:
982 raise ValueError("Key is uninitialized")
983
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500984 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500985 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800986 raise ValueError("No such digest method")
987
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500988 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800989 if not sign_result:
990 _raise_current_error()
991
992
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800993 def get_signature_algorithm(self):
994 """
995 Retrieve the signature algorithm used in the certificate
996
997 :return: A byte string giving the name of the signature algorithm used in
998 the certificate.
999 :raise ValueError: If the signature algorithm is undefined.
1000 """
1001 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001002 nid = _lib.OBJ_obj2nid(alg)
1003 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001004 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001005 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001006
1007
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001008 def digest(self, digest_name):
1009 """
1010 Return the digest of the X509 object.
1011
1012 :param digest_name: The name of the digest algorithm to use.
1013 :type digest_name: :py:class:`bytes`
1014
1015 :return: The digest of the object
1016 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001017 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001018 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001019 raise ValueError("No such digest method")
1020
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001021 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1022 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001023 result_length[0] = len(result_buffer)
1024
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001025 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001026 self._x509, digest, result_buffer, result_length)
1027
1028 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001029 # TODO: This is untested.
1030 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001031
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001032 return b":".join([
1033 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001034 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001035
1036
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001037 def subject_name_hash(self):
1038 """
1039 Return the hash of the X509 subject.
1040
1041 :return: The hash of the subject.
1042 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001043 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001044
1045
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001046 def set_serial_number(self, serial):
1047 """
1048 Set serial number of the certificate
1049
1050 :param serial: The serial number
1051 :type serial: :py:class:`int`
1052
1053 :return: None
1054 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001055 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001056 raise TypeError("serial must be an integer")
1057
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001058 hex_serial = hex(serial)[2:]
1059 if not isinstance(hex_serial, bytes):
1060 hex_serial = hex_serial.encode('ascii')
1061
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001062 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001063
1064 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1065 # it. If bignum is still NULL after this call, then the return value is
1066 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001067 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001068
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001069 if bignum_serial[0] == _ffi.NULL:
1070 set_result = _lib.ASN1_INTEGER_set(
1071 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001072 if set_result:
1073 # TODO Not tested
1074 _raise_current_error()
1075 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001076 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1077 _lib.BN_free(bignum_serial[0])
1078 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001079 # TODO Not tested
1080 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001081 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1082 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001083 if not set_result:
1084 # TODO Not tested
1085 _raise_current_error()
1086
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001087
1088 def get_serial_number(self):
1089 """
1090 Return serial number of the certificate
1091
1092 :return: Serial number as a Python integer
1093 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001094 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1095 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001096 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001097 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001098 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001099 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001100 serial = int(hexstring_serial, 16)
1101 return serial
1102 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001103 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001104 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001105 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001106
1107
1108 def gmtime_adj_notAfter(self, amount):
1109 """
1110 Adjust the time stamp for when the certificate stops being valid
1111
1112 :param amount: The number of seconds by which to adjust the ending
1113 validity time.
1114 :type amount: :py:class:`int`
1115
1116 :return: None
1117 """
1118 if not isinstance(amount, int):
1119 raise TypeError("amount must be an integer")
1120
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001121 notAfter = _lib.X509_get_notAfter(self._x509)
1122 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001123
1124
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001125 def gmtime_adj_notBefore(self, amount):
1126 """
1127 Change the timestamp for when the certificate starts being valid to the current
1128 time plus an offset.
1129
1130 :param amount: The number of seconds by which to adjust the starting validity
1131 time.
1132 :return: None
1133 """
1134 if not isinstance(amount, int):
1135 raise TypeError("amount must be an integer")
1136
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001137 notBefore = _lib.X509_get_notBefore(self._x509)
1138 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001139
1140
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001141 def has_expired(self):
1142 """
1143 Check whether the certificate has expired.
1144
1145 :return: True if the certificate has expired, false otherwise
1146 """
1147 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001148 notAfter = _lib.X509_get_notAfter(self._x509)
1149 return _lib.ASN1_UTCTIME_cmp_time_t(
1150 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001151
1152
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001153 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001154 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001155
1156
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001157 def get_notBefore(self):
1158 """
1159 Retrieve the time stamp for when the certificate starts being valid
1160
1161 :return: A string giving the timestamp, in the format::
1162
1163 YYYYMMDDhhmmssZ
1164 YYYYMMDDhhmmss+hhmm
1165 YYYYMMDDhhmmss-hhmm
1166
1167 or None if there is no value set.
1168 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001169 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001170
1171
1172 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001173 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001174
1175
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001176 def set_notBefore(self, when):
1177 """
1178 Set the time stamp for when the certificate starts being valid
1179
1180 :param when: A string giving the timestamp, in the format:
1181
1182 YYYYMMDDhhmmssZ
1183 YYYYMMDDhhmmss+hhmm
1184 YYYYMMDDhhmmss-hhmm
1185 :type when: :py:class:`bytes`
1186
1187 :return: None
1188 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001189 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001190
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001191
1192 def get_notAfter(self):
1193 """
1194 Retrieve the time stamp for when the certificate stops being valid
1195
1196 :return: A string giving the timestamp, in the format::
1197
1198 YYYYMMDDhhmmssZ
1199 YYYYMMDDhhmmss+hhmm
1200 YYYYMMDDhhmmss-hhmm
1201
1202 or None if there is no value set.
1203 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001204 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001205
1206
1207 def set_notAfter(self, when):
1208 """
1209 Set the time stamp for when the certificate stops being valid
1210
1211 :param when: A string giving the timestamp, in the format:
1212
1213 YYYYMMDDhhmmssZ
1214 YYYYMMDDhhmmss+hhmm
1215 YYYYMMDDhhmmss-hhmm
1216 :type when: :py:class:`bytes`
1217
1218 :return: None
1219 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001220 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001221
1222
1223 def _get_name(self, which):
1224 name = X509Name.__new__(X509Name)
1225 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001226 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001227 # TODO: This is untested.
1228 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001229
1230 # The name is owned by the X509 structure. As long as the X509Name
1231 # Python object is alive, keep the X509 Python object alive.
1232 name._owner = self
1233
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001234 return name
1235
1236
1237 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001238 if not isinstance(name, X509Name):
1239 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001240 set_result = which(self._x509, name._name)
1241 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001242 # TODO: This is untested.
1243 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001244
1245
1246 def get_issuer(self):
1247 """
1248 Create an X509Name object for the issuer of the certificate
1249
1250 :return: An X509Name object
1251 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001252 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001253
1254
1255 def set_issuer(self, issuer):
1256 """
1257 Set the issuer of the certificate
1258
1259 :param issuer: The issuer name
1260 :type issuer: :py:class:`X509Name`
1261
1262 :return: None
1263 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001264 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001265
1266
1267 def get_subject(self):
1268 """
1269 Create an X509Name object for the subject of the certificate
1270
1271 :return: An X509Name object
1272 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001273 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001274
1275
1276 def set_subject(self, subject):
1277 """
1278 Set the subject of the certificate
1279
1280 :param subject: The subject name
1281 :type subject: :py:class:`X509Name`
1282 :return: None
1283 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001284 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001285
1286
1287 def get_extension_count(self):
1288 """
1289 Get the number of extensions on the certificate.
1290
1291 :return: The number of extensions as an integer.
1292 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001293 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001294
1295
1296 def add_extensions(self, extensions):
1297 """
1298 Add extensions to the certificate.
1299
1300 :param extensions: a sequence of X509Extension objects
1301 :return: None
1302 """
1303 for ext in extensions:
1304 if not isinstance(ext, X509Extension):
1305 raise ValueError("One of the elements is not an X509Extension")
1306
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001307 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001308 if not add_result:
1309 _raise_current_error()
1310
1311
1312 def get_extension(self, index):
1313 """
1314 Get a specific extension of the certificate by index.
1315
1316 :param index: The index of the extension to retrieve.
1317 :return: The X509Extension object at the specified index.
1318 """
1319 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001320 ext._extension = _lib.X509_get_ext(self._x509, index)
1321 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001322 raise IndexError("extension index out of bounds")
1323
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001324 extension = _lib.X509_EXTENSION_dup(ext._extension)
1325 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001326 return ext
1327
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001328X509Type = X509
1329
1330
1331
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001332class X509Store(object):
1333 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001334 store = _lib.X509_STORE_new()
1335 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001336
1337
1338 def add_cert(self, cert):
1339 if not isinstance(cert, X509):
1340 raise TypeError()
1341
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001342 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001343 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001344 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001345
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001346
1347X509StoreType = X509Store
1348
1349
1350
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001351def load_certificate(type, buffer):
1352 """
1353 Load a certificate from a buffer
1354
1355 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1356
1357 :param buffer: The buffer the certificate is stored in
1358 :type buffer: :py:class:`bytes`
1359
1360 :return: The X509 object
1361 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001362 if isinstance(buffer, _text_type):
1363 buffer = buffer.encode("ascii")
1364
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001365 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001366
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001367 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001368 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001369 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001370 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001371 else:
1372 raise ValueError(
1373 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001374
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001375 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001376 _raise_current_error()
1377
1378 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001379 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001380 return cert
1381
1382
1383def dump_certificate(type, cert):
1384 """
1385 Dump a certificate to a buffer
1386
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001387 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1388 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001389 :param cert: The certificate to dump
1390 :return: The buffer with the dumped certificate in
1391 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001392 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001393
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001394 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001395 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001396 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001397 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001398 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001399 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001400 else:
1401 raise ValueError(
1402 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1403 "FILETYPE_TEXT")
1404
1405 return _bio_to_string(bio)
1406
1407
1408
1409def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1410 """
1411 Dump a private key to a buffer
1412
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001413 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1414 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001415 :param pkey: The PKey to dump
1416 :param cipher: (optional) if encrypted PEM format, the cipher to
1417 use
1418 :param passphrase: (optional) if encrypted PEM format, this can be either
1419 the passphrase to use, or a callback for providing the
1420 passphrase.
1421 :return: The buffer with the dumped key in
1422 :rtype: :py:data:`str`
1423 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001424 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001425
1426 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001427 if passphrase is None:
1428 raise TypeError(
1429 "if a value is given for cipher "
1430 "one must also be given for passphrase")
1431 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001432 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001433 raise ValueError("Invalid cipher name")
1434 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001435 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001436
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001437 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001438 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001439 result_code = _lib.PEM_write_bio_PrivateKey(
1440 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001441 helper.callback, helper.callback_args)
1442 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001443 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001444 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001445 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001446 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1447 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001448 # TODO RSA_free(rsa)?
1449 else:
1450 raise ValueError(
1451 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1452 "FILETYPE_TEXT")
1453
1454 if result_code == 0:
1455 _raise_current_error()
1456
1457 return _bio_to_string(bio)
1458
1459
1460
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001461def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001462 copy = _lib.X509_REVOKED_new()
1463 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001464 # TODO: This is untested.
1465 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001466
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001467 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001468 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001469 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001470
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001471 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001472 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001473 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001474
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001475 if original.extensions != _ffi.NULL:
1476 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1477 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1478 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1479 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1480 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001481 copy.extensions = extension_stack
1482
1483 copy.sequence = original.sequence
1484 return copy
1485
1486
1487
1488class Revoked(object):
1489 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1490 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1491 # OCSP_crl_reason_str. We use the latter, just like the command line
1492 # program.
1493 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001494 b"unspecified",
1495 b"keyCompromise",
1496 b"CACompromise",
1497 b"affiliationChanged",
1498 b"superseded",
1499 b"cessationOfOperation",
1500 b"certificateHold",
1501 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001502 ]
1503
1504 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001505 revoked = _lib.X509_REVOKED_new()
1506 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001507
1508
1509 def set_serial(self, hex_str):
1510 """
1511 Set the serial number of a revoked Revoked structure
1512
1513 :param hex_str: The new serial number.
1514 :type hex_str: :py:data:`str`
1515 :return: None
1516 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001517 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1518 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001519 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001520 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001521 if not bn_result:
1522 raise ValueError("bad hex string")
1523
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001524 asn1_serial = _ffi.gc(
1525 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1526 _lib.ASN1_INTEGER_free)
1527 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001528
1529
1530 def get_serial(self):
1531 """
1532 Return the serial number of a Revoked structure
1533
1534 :return: The serial number as a string
1535 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001536 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001537
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001538 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001539 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001540 # TODO: This is untested.
1541 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001542
1543 return _bio_to_string(bio)
1544
1545
1546 def _delete_reason(self):
1547 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001548 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1549 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1550 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1551 _lib.X509_EXTENSION_free(ext)
1552 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001553 break
1554
1555
1556 def set_reason(self, reason):
1557 """
1558 Set the reason of a Revoked object.
1559
1560 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1561
1562 :param reason: The reason string.
1563 :type reason: :py:class:`str` or :py:class:`NoneType`
1564 :return: None
1565 """
1566 if reason is None:
1567 self._delete_reason()
1568 elif not isinstance(reason, bytes):
1569 raise TypeError("reason must be None or a byte string")
1570 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001571 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001572 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1573
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001574 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1575 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001576 # TODO: This is untested.
1577 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001578 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001579
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001580 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1581 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001582 # TODO: This is untested.
1583 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001584
1585 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001586 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1587 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001588
1589 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001590 # TODO: This is untested.
1591 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001592
1593
1594 def get_reason(self):
1595 """
1596 Return the reason of a Revoked object.
1597
1598 :return: The reason as a string
1599 """
1600 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001601 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1602 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1603 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001604 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001605
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001606 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001607 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001608 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001609 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001610 # TODO: This is untested.
1611 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001612
1613 return _bio_to_string(bio)
1614
1615
1616 def all_reasons(self):
1617 """
1618 Return a list of all the supported reason strings.
1619
1620 :return: A list of reason strings.
1621 """
1622 return self._crl_reasons[:]
1623
1624
1625 def set_rev_date(self, when):
1626 """
1627 Set the revocation timestamp
1628
1629 :param when: A string giving the timestamp, in the format:
1630
1631 YYYYMMDDhhmmssZ
1632 YYYYMMDDhhmmss+hhmm
1633 YYYYMMDDhhmmss-hhmm
1634
1635 :return: None
1636 """
1637 return _set_asn1_time(self._revoked.revocationDate, when)
1638
1639
1640 def get_rev_date(self):
1641 """
1642 Retrieve the revocation date
1643
1644 :return: A string giving the timestamp, in the format:
1645
1646 YYYYMMDDhhmmssZ
1647 YYYYMMDDhhmmss+hhmm
1648 YYYYMMDDhhmmss-hhmm
1649 """
1650 return _get_asn1_time(self._revoked.revocationDate)
1651
1652
1653
1654class CRL(object):
1655 def __init__(self):
1656 """
1657 Create a new empty CRL object.
1658 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001659 crl = _lib.X509_CRL_new()
1660 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001661
1662
1663 def get_revoked(self):
1664 """
1665 Return revoked portion of the CRL structure (by value not reference).
1666
1667 :return: A tuple of Revoked objects.
1668 """
1669 results = []
1670 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001671 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1672 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001673 revoked_copy = _X509_REVOKED_dup(revoked)
1674 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001675 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001676 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001677 if results:
1678 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001679
1680
1681 def add_revoked(self, revoked):
1682 """
1683 Add a revoked (by value not reference) to the CRL structure
1684
1685 :param revoked: The new revoked.
1686 :type revoked: :class:`X509`
1687
1688 :return: None
1689 """
1690 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001691 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001692 # TODO: This is untested.
1693 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001694
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001695 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001696 if add_result == 0:
1697 # TODO: This is untested.
1698 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001699
1700
1701 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1702 """
1703 export a CRL as a string
1704
1705 :param cert: Used to sign CRL.
1706 :type cert: :class:`X509`
1707
1708 :param key: Used to sign CRL.
1709 :type key: :class:`PKey`
1710
1711 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1712
1713 :param days: The number of days until the next update of this CRL.
1714 :type days: :py:data:`int`
1715
1716 :return: :py:data:`str`
1717 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001718 if not isinstance(cert, X509):
1719 raise TypeError("cert must be an X509 instance")
1720 if not isinstance(key, PKey):
1721 raise TypeError("key must be a PKey instance")
1722 if not isinstance(type, int):
1723 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001724
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001725 bio = _lib.BIO_new(_lib.BIO_s_mem())
1726 if bio == _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
1730 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001731 sometime = _lib.ASN1_TIME_new()
1732 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001733 # TODO: This is untested.
1734 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001735
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001736 _lib.X509_gmtime_adj(sometime, 0)
1737 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001738
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001739 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1740 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001741
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001742 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001743
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001744 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001745 if not sign_result:
1746 _raise_current_error()
1747
1748 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001749 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001750 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001751 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001752 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001753 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001754 else:
1755 raise ValueError(
1756 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1757
1758 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001759 # TODO: This is untested.
1760 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001761
1762 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001763CRLType = CRL
1764
1765
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001766
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001767class PKCS7(object):
1768 def type_is_signed(self):
1769 """
1770 Check if this NID_pkcs7_signed object
1771
1772 :return: True if the PKCS7 is of type signed
1773 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001774 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001775 return True
1776 return False
1777
1778
1779 def type_is_enveloped(self):
1780 """
1781 Check if this NID_pkcs7_enveloped object
1782
1783 :returns: True if the PKCS7 is of type enveloped
1784 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001785 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001786 return True
1787 return False
1788
1789
1790 def type_is_signedAndEnveloped(self):
1791 """
1792 Check if this NID_pkcs7_signedAndEnveloped object
1793
1794 :returns: True if the PKCS7 is of type signedAndEnveloped
1795 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001796 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001797 return True
1798 return False
1799
1800
1801 def type_is_data(self):
1802 """
1803 Check if this NID_pkcs7_data object
1804
1805 :return: True if the PKCS7 is of type data
1806 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001807 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001808 return True
1809 return False
1810
1811
1812 def get_type_name(self):
1813 """
1814 Returns the type name of the PKCS7 structure
1815
1816 :return: A string with the typename
1817 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001818 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1819 string_type = _lib.OBJ_nid2sn(nid)
1820 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001821
1822PKCS7Type = PKCS7
1823
1824
1825
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001826class PKCS12(object):
1827 def __init__(self):
1828 self._pkey = None
1829 self._cert = None
1830 self._cacerts = None
1831 self._friendlyname = None
1832
1833
1834 def get_certificate(self):
1835 """
1836 Return certificate portion of the PKCS12 structure
1837
1838 :return: X509 object containing the certificate
1839 """
1840 return self._cert
1841
1842
1843 def set_certificate(self, cert):
1844 """
1845 Replace the certificate portion of the PKCS12 structure
1846
1847 :param cert: The new certificate.
1848 :type cert: :py:class:`X509` or :py:data:`None`
1849 :return: None
1850 """
1851 if not isinstance(cert, X509):
1852 raise TypeError("cert must be an X509 instance")
1853 self._cert = cert
1854
1855
1856 def get_privatekey(self):
1857 """
1858 Return private key portion of the PKCS12 structure
1859
1860 :returns: PKey object containing the private key
1861 """
1862 return self._pkey
1863
1864
1865 def set_privatekey(self, pkey):
1866 """
1867 Replace or set the certificate portion of the PKCS12 structure
1868
1869 :param pkey: The new private key.
1870 :type pkey: :py:class:`PKey`
1871 :return: None
1872 """
1873 if not isinstance(pkey, PKey):
1874 raise TypeError("pkey must be a PKey instance")
1875 self._pkey = pkey
1876
1877
1878 def get_ca_certificates(self):
1879 """
1880 Return CA certificates within of the PKCS12 object
1881
1882 :return: A newly created tuple containing the CA certificates in the chain,
1883 if any are present, or None if no CA certificates are present.
1884 """
1885 if self._cacerts is not None:
1886 return tuple(self._cacerts)
1887
1888
1889 def set_ca_certificates(self, cacerts):
1890 """
1891 Replace or set the CA certificates withing the PKCS12 object.
1892
1893 :param cacerts: The new CA certificates.
1894 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1895 :return: None
1896 """
1897 if cacerts is None:
1898 self._cacerts = None
1899 else:
1900 cacerts = list(cacerts)
1901 for cert in cacerts:
1902 if not isinstance(cert, X509):
1903 raise TypeError("iterable must only contain X509 instances")
1904 self._cacerts = cacerts
1905
1906
1907 def set_friendlyname(self, name):
1908 """
1909 Replace or set the certificate portion of the PKCS12 structure
1910
1911 :param name: The new friendly name.
1912 :type name: :py:class:`bytes`
1913 :return: None
1914 """
1915 if name is None:
1916 self._friendlyname = None
1917 elif not isinstance(name, bytes):
1918 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1919 self._friendlyname = name
1920
1921
1922 def get_friendlyname(self):
1923 """
1924 Return friendly name portion of the PKCS12 structure
1925
1926 :returns: String containing the friendlyname
1927 """
1928 return self._friendlyname
1929
1930
1931 def export(self, passphrase=None, iter=2048, maciter=1):
1932 """
1933 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1934
1935 :param passphrase: used to encrypt the PKCS12
1936 :type passphrase: :py:data:`bytes`
1937
1938 :param iter: How many times to repeat the encryption
1939 :type iter: :py:data:`int`
1940
1941 :param maciter: How many times to repeat the MAC
1942 :type maciter: :py:data:`int`
1943
1944 :return: The string containing the PKCS12
1945 """
1946 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001947 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001948 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001949 cacerts = _lib.sk_X509_new_null()
1950 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001951 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001952 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001953
1954 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001955 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001956
1957 friendlyname = self._friendlyname
1958 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001959 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001960
1961 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001962 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001963 else:
1964 pkey = self._pkey._pkey
1965
1966 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001967 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001968 else:
1969 cert = self._cert._x509
1970
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001971 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001972 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001973 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1974 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001975 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001976 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001977 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001978 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001979
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001980 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001981 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001982 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001983
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001984PKCS12Type = PKCS12
1985
1986
1987
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001988class NetscapeSPKI(object):
1989 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001990 spki = _lib.NETSCAPE_SPKI_new()
1991 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001992
1993
1994 def sign(self, pkey, digest):
1995 """
1996 Sign the certificate request using the supplied key and digest
1997
1998 :param pkey: The key to sign with
1999 :param digest: The message digest to use
2000 :return: None
2001 """
2002 if pkey._only_public:
2003 raise ValueError("Key has only public part")
2004
2005 if not pkey._initialized:
2006 raise ValueError("Key is uninitialized")
2007
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002008 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002009 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002010 raise ValueError("No such digest method")
2011
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002012 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002013 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002014 # TODO: This is untested.
2015 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002016
2017
2018 def verify(self, key):
2019 """
2020 Verifies a certificate request using the supplied public key
2021
2022 :param key: a public key
2023 :return: True if the signature is correct.
2024 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2025 problem verifying the signature.
2026 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002027 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002028 if answer <= 0:
2029 _raise_current_error()
2030 return True
2031
2032
2033 def b64_encode(self):
2034 """
2035 Generate a base64 encoded string from an SPKI
2036
2037 :return: The base64 encoded string
2038 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002039 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2040 result = _ffi.string(encoded)
2041 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002042 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002043
2044
2045 def get_pubkey(self):
2046 """
2047 Get the public key of the certificate
2048
2049 :return: The public key
2050 """
2051 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002052 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2053 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002054 # TODO: This is untested.
2055 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002056 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002057 pkey._only_public = True
2058 return pkey
2059
2060
2061 def set_pubkey(self, pkey):
2062 """
2063 Set the public key of the certificate
2064
2065 :param pkey: The public key
2066 :return: None
2067 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002068 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002069 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002070 # TODO: This is untested.
2071 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002072NetscapeSPKIType = NetscapeSPKI
2073
2074
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002075class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002076 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002077 if type != FILETYPE_PEM and passphrase is not None:
2078 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002079 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002080 self._more_args = more_args
2081 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002082 self._problems = []
2083
2084
2085 @property
2086 def callback(self):
2087 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002088 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002089 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002090 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002091 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002092 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002093 else:
2094 raise TypeError("Last argument must be string or callable")
2095
2096
2097 @property
2098 def callback_args(self):
2099 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002100 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002101 elif isinstance(self._passphrase, bytes):
2102 return self._passphrase
2103 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002104 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002105 else:
2106 raise TypeError("Last argument must be string or callable")
2107
2108
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002109 def raise_if_problem(self, exceptionType=Error):
2110 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002111 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002112 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002113 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002114 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002115 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002116 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002117
2118
2119 def _read_passphrase(self, buf, size, rwflag, userdata):
2120 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002121 if self._more_args:
2122 result = self._passphrase(size, rwflag, userdata)
2123 else:
2124 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002125 if not isinstance(result, bytes):
2126 raise ValueError("String expected")
2127 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002128 if self._truncate:
2129 result = result[:size]
2130 else:
2131 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002132 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002133 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002134 return len(result)
2135 except Exception as e:
2136 self._problems.append(e)
2137 return 0
2138
2139
2140
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002141def load_privatekey(type, buffer, passphrase=None):
2142 """
2143 Load a private key from a buffer
2144
2145 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2146 :param buffer: The buffer the key is stored in
2147 :param passphrase: (optional) if encrypted PEM format, this can be
2148 either the passphrase to use, or a callback for
2149 providing the passphrase.
2150
2151 :return: The PKey object
2152 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002153 if isinstance(buffer, _text_type):
2154 buffer = buffer.encode("ascii")
2155
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002156 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002157
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002158 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002159 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002160 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2161 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002162 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002163 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002164 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002165 else:
2166 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2167
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002168 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002169 _raise_current_error()
2170
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002171 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002172 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002173 return pkey
2174
2175
2176
2177def dump_certificate_request(type, req):
2178 """
2179 Dump a certificate request to a buffer
2180
2181 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2182 :param req: The certificate request to dump
2183 :return: The buffer with the dumped certificate request in
2184 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002185 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002186
2187 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002188 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002189 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002190 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002191 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002192 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002193 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002194 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002195
2196 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002197 # TODO: This is untested.
2198 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002199
2200 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002201
2202
2203
2204def load_certificate_request(type, buffer):
2205 """
2206 Load a certificate request from a buffer
2207
2208 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2209 :param buffer: The buffer the certificate request is stored in
2210 :return: The X509Req object
2211 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002212 if isinstance(buffer, _text_type):
2213 buffer = buffer.encode("ascii")
2214
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002215 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002216
2217 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002218 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002219 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002220 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002221 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002222 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002223
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002224 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002225 # TODO: This is untested.
2226 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002227
2228 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002229 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002230 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002231
2232
2233
2234def sign(pkey, data, digest):
2235 """
2236 Sign data with a digest
2237
2238 :param pkey: Pkey to sign with
2239 :param data: data to be signed
2240 :param digest: message digest to use
2241 :return: signature
2242 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002243 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002244 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002245 raise ValueError("No such digest method")
2246
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002247 md_ctx = _ffi.new("EVP_MD_CTX*")
2248 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002249
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002250 _lib.EVP_SignInit(md_ctx, digest_obj)
2251 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002252
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002253 signature_buffer = _ffi.new("unsigned char[]", 512)
2254 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002255 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002256 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002257 md_ctx, signature_buffer, signature_length, pkey._pkey)
2258
2259 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002260 # TODO: This is untested.
2261 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002262
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002263 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002264
2265
2266
2267def verify(cert, signature, data, digest):
2268 """
2269 Verify a signature
2270
2271 :param cert: signing certificate (X509 object)
2272 :param signature: signature returned by sign function
2273 :param data: data to be verified
2274 :param digest: message digest to use
2275 :return: None if the signature is correct, raise exception otherwise
2276 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002277 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002278 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002279 raise ValueError("No such digest method")
2280
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002281 pkey = _lib.X509_get_pubkey(cert._x509)
2282 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002283 # TODO: This is untested.
2284 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002285 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002286
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002287 md_ctx = _ffi.new("EVP_MD_CTX*")
2288 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002289
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002290 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2291 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2292 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002293
2294 if verify_result != 1:
2295 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002296
2297
2298
2299def load_crl(type, buffer):
2300 """
2301 Load a certificate revocation list from a buffer
2302
2303 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2304 :param buffer: The buffer the CRL is stored in
2305
2306 :return: The PKey object
2307 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002308 if isinstance(buffer, _text_type):
2309 buffer = buffer.encode("ascii")
2310
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002311 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002312
2313 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002314 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002315 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002316 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002317 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002318 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2319
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002320 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002321 _raise_current_error()
2322
2323 result = CRL.__new__(CRL)
2324 result._crl = crl
2325 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002326
2327
2328
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002329def load_pkcs7_data(type, buffer):
2330 """
2331 Load pkcs7 data from a buffer
2332
2333 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2334 :param buffer: The buffer with the pkcs7 data.
2335 :return: The PKCS7 object
2336 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002337 if isinstance(buffer, _text_type):
2338 buffer = buffer.encode("ascii")
2339
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002340 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002341
2342 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002343 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002344 elif type == FILETYPE_ASN1:
2345 pass
2346 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002347 # TODO: This is untested.
2348 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002349 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2350
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002351 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002352 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002353
2354 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002355 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002356 return pypkcs7
2357
2358
2359
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002360def load_pkcs12(buffer, passphrase):
2361 """
2362 Load a PKCS12 object from a buffer
2363
2364 :param buffer: The buffer the certificate is stored in
2365 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2366 :returns: The PKCS12 object
2367 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002368 if isinstance(buffer, _text_type):
2369 buffer = buffer.encode("ascii")
2370
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002371 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002372
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002373 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2374 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002375 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002376 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002377
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002378 pkey = _ffi.new("EVP_PKEY**")
2379 cert = _ffi.new("X509**")
2380 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002381
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002382 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002383 if not parse_result:
2384 _raise_current_error()
2385
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002386 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002387
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002388 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2389 # queue for no particular reason. This error isn't interesting to anyone
2390 # outside this function. It's not even interesting to us. Get rid of it.
2391 try:
2392 _raise_current_error()
2393 except Error:
2394 pass
2395
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002396 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002397 pykey = None
2398 else:
2399 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002400 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002401
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002402 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002403 pycert = None
2404 friendlyname = None
2405 else:
2406 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002407 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002408
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002409 friendlyname_length = _ffi.new("int*")
2410 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2411 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2412 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002413 friendlyname = None
2414
2415 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002416 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002417 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002418 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002419 pycacerts.append(pycacert)
2420 if not pycacerts:
2421 pycacerts = None
2422
2423 pkcs12 = PKCS12.__new__(PKCS12)
2424 pkcs12._pkey = pykey
2425 pkcs12._cert = pycert
2426 pkcs12._cacerts = pycacerts
2427 pkcs12._friendlyname = friendlyname
2428 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002429
2430
2431def _initialize_openssl_threads(get_ident, Lock):
2432 import _ssl
2433 return
2434
2435 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2436
2437 def locking_function(mode, index, filename, line):
2438 if mode & _lib.CRYPTO_LOCK:
2439 locks[index].acquire()
2440 else:
2441 locks[index].release()
2442
2443 _lib.CRYPTO_set_id_callback(
2444 _ffi.callback("unsigned long (*)(void)", get_ident))
2445
2446 _lib.CRYPTO_set_locking_callback(
2447 _ffi.callback(
2448 "void (*)(int, int, const char*, int)", locking_function))
2449
2450
2451try:
2452 from thread import get_ident
2453 from threading import Lock
2454except ImportError:
2455 pass
2456else:
2457 _initialize_openssl_threads(get_ident, Lock)
2458 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002459
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002460# There are no direct unit tests for this initialization. It is tested
2461# indirectly since it is necessary for functions like dump_privatekey when
2462# using encryption.
2463#
2464# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2465# and some other similar tests may fail without this (though they may not if
2466# the Python runtime has already done some initialization of the underlying
2467# OpenSSL library (and is linked against the same one that cryptography is
2468# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002469_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002470
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002471# This is similar but exercised mainly by exception_from_error_queue. It calls
2472# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2473_lib.SSL_load_error_strings()