blob: 8722a9f4d4358e8637a8186515517d85d8c82467 [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05003from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
5
6from six import (
7 integer_types as _integer_types,
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -04008 text_type as _text_type,
9 PY3 as _PY3)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080010
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050011from OpenSSL._util import (
12 ffi as _ffi,
13 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050014 exception_from_error_queue as _exception_from_error_queue,
15 byte_string as _byte_string,
16 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080017
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050018FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
19FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080020
21# TODO This was an API mistake. OpenSSL has no such constant.
22FILETYPE_TEXT = 2 ** 16 - 1
23
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050024TYPE_RSA = _lib.EVP_PKEY_RSA
25TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080026
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080027
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050028class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050029 """
30 An error occurred in an `OpenSSL.crypto` API.
31 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050032
33
34_raise_current_error = partial(_exception_from_error_queue, Error)
35
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050036def _untested_error(where):
37 """
38 An OpenSSL API failed somehow. Additionally, the failure which was
39 encountered isn't one that's exercised by the test suite so future behavior
40 of pyOpenSSL is now somewhat less predictable.
41 """
42 raise RuntimeError("Unknown %s failure" % (where,))
43
44
45
46def _new_mem_buf(buffer=None):
47 """
48 Allocate a new OpenSSL memory BIO.
49
50 Arrange for the garbage collector to clean it up automatically.
51
52 :param buffer: None or some bytes to use to put into the BIO so that they
53 can be read out.
54 """
55 if buffer is None:
56 bio = _lib.BIO_new(_lib.BIO_s_mem())
57 free = _lib.BIO_free
58 else:
59 data = _ffi.new("char[]", buffer)
60 bio = _lib.BIO_new_mem_buf(data, len(buffer))
61 # Keep the memory alive as long as the bio is alive!
62 def free(bio, ref=data):
63 return _lib.BIO_free(bio)
64
65 if bio == _ffi.NULL:
66 # TODO: This is untested.
67 _raise_current_error()
68
69 bio = _ffi.gc(bio, free)
70 return bio
71
72
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050073
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080074def _bio_to_string(bio):
75 """
76 Copy the contents of an OpenSSL BIO object into a Python byte string.
77 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050078 result_buffer = _ffi.new('char**')
79 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
80 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080081
82
83
Jean-Paul Calderone57122982013-02-21 08:47:05 -080084def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050085 """
86 The the time value of an ASN1 time object.
87
88 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
89 castable to that type) which will have its value set.
90 @param when: A string representation of the desired time value.
91
92 @raise TypeError: If C{when} is not a L{bytes} string.
93 @raise ValueError: If C{when} does not represent a time in the required
94 format.
95 @raise RuntimeError: If the time value cannot be set for some other
96 (unspecified) reason.
97 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080098 if not isinstance(when, bytes):
99 raise TypeError("when must be a byte string")
100
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500101 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
102 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800103 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500104 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
105 _lib.ASN1_STRING_set(dummy, when, len(when))
106 check_result = _lib.ASN1_GENERALIZEDTIME_check(
107 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800108 if not check_result:
109 raise ValueError("Invalid string")
110 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500111 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800112
113
114
115def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500116 """
117 Retrieve the time value of an ASN1 time object.
118
119 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
120 that type) from which the time value will be retrieved.
121
122 @return: The time value from C{timestamp} as a L{bytes} string in a certain
123 format. Or C{None} if the object contains no time value.
124 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500125 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
126 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800127 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500128 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
129 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800130 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500131 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
132 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
133 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500134 # This may happen:
135 # - if timestamp was not an ASN1_TIME
136 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
137 # - if a copy of the time data from timestamp cannot be made for
138 # the newly allocated ASN1_GENERALIZEDTIME
139 #
140 # These are difficult to test. cffi enforces the ASN1_TIME type.
141 # Memory allocation failures are a pain to trigger
142 # deterministically.
143 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800144 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500145 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800146 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500147 string_data = _lib.ASN1_STRING_data(string_timestamp)
148 string_result = _ffi.string(string_data)
149 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800150 return string_result
151
152
153
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800154class PKey(object):
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200155 """
156 A class representing an DSA or RSA public key or key pair.
157 """
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800158 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800159 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800160
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800161 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500162 pkey = _lib.EVP_PKEY_new()
163 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800164 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800165
166
167 def generate_key(self, type, bits):
168 """
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200169 Generate a key pair of the given type, with the given number of a bits.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800170
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200171 This generates a key "into" the this object.
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800172
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200173 :param type: The key type.
174 :type type: :py:data:`TYPE_RSA` or :py:data:`TYPE_DSA`
175 :param bits: The number of bits.
176 :type bits: :py:data:`int` ``>= 0``
177 :raises TypeError: If :py:data:`type` or :py:data:`bits` isn't
178 of the appropriate type.
179 :raises ValueError: If the number of bits isn't an integer of
180 the appropriate size.
181 :return: :py:data:`None`
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800182 """
183 if not isinstance(type, int):
184 raise TypeError("type must be an integer")
185
186 if not isinstance(bits, int):
187 raise TypeError("bits must be an integer")
188
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800189 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500190 exponent = _lib.BN_new()
191 exponent = _ffi.gc(exponent, _lib.BN_free)
192 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800193
194 if type == TYPE_RSA:
195 if bits <= 0:
196 raise ValueError("Invalid number of bits")
197
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500198 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800199
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500200 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500201 if result == 0:
202 # TODO: The test for this case is commented out. Different
203 # builds of OpenSSL appear to have different failure modes that
204 # make it hard to test. Visual inspection of the OpenSSL
205 # source reveals that a return value of 0 signals an error.
206 # Manual testing on a particular build of OpenSSL suggests that
207 # this is probably the appropriate way to handle those errors.
208 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800209
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500210 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800211 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500212 # TODO: It appears as though this can fail if an engine is in
213 # use which does not support RSA.
214 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800215
216 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500217 dsa = _lib.DSA_generate_parameters(
218 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
219 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500220 # TODO: This is untested.
221 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500222 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500223 # TODO: This is untested.
224 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500225 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500226 # TODO: This is untested.
227 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800228 else:
229 raise Error("No such key type")
230
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800231 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800232
233
234 def check(self):
235 """
236 Check the consistency of an RSA private key.
237
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +0200238 This is the Python equivalent of OpenSSL's ``RSA_check_key``.
239
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800240 :return: True if key is consistent.
241 :raise Error: if the key is inconsistent.
242 :raise TypeError: if the key is of a type which cannot be checked.
243 Only RSA keys can currently be checked.
244 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800245 if self._only_public:
246 raise TypeError("public key only")
247
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500248 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800249 raise TypeError("key type unsupported")
250
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500251 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
252 rsa = _ffi.gc(rsa, _lib.RSA_free)
253 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800254 if result:
255 return True
256 _raise_current_error()
257
258
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800259 def type(self):
260 """
261 Returns the type of the key
262
263 :return: The type of the key.
264 """
265 return self._pkey.type
266
267
268 def bits(self):
269 """
270 Returns the number of bits of the key
271
272 :return: The number of bits of the key.
273 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500274 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800275PKeyType = PKey
276
277
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800278
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400279class _EllipticCurve(object):
280 """
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400281 A representation of a supported elliptic curve.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400282
283 @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
284 Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
285 instances each of which represents one curve supported by the system.
286 @type _curves: :py:type:`NoneType` or :py:type:`set`
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400287 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400288 _curves = None
289
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400290 if _PY3:
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400291 # This only necessary on Python 3. Morever, it is broken on Python 2.
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400292 def __ne__(self, other):
Jean-Paul Calderonea5381052014-05-01 09:32:46 -0400293 """
294 Implement cooperation with the right-hand side argument of ``!=``.
295
296 Python 3 seems to have dropped this cooperation in this very narrow
297 circumstance.
298 """
Jean-Paul Calderonef22abcd2014-05-01 09:31:19 -0400299 if isinstance(other, _EllipticCurve):
300 return super(_EllipticCurve, self).__ne__(other)
301 return NotImplemented
Jean-Paul Calderone40da72d2014-05-01 09:25:17 -0400302
303
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400304 @classmethod
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400305 def _load_elliptic_curves(cls, lib):
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400306 """
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400307 Get the curves supported by OpenSSL.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400308
309 :param lib: The OpenSSL library binding object.
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400310
311 :return: A :py:type:`set` of ``cls`` instances giving the names of the
312 elliptic curves the underlying library supports.
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400313 """
314 if lib.Cryptography_HAS_EC:
315 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
316 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
317 # The return value on this call should be num_curves again. We could
318 # check it to make sure but if it *isn't* then.. what could we do?
319 # Abort the whole process, I suppose...? -exarkun
320 lib.EC_get_builtin_curves(builtin_curves, num_curves)
321 return set(
322 cls.from_nid(lib, c.nid)
323 for c in builtin_curves)
Jean-Paul Calderone73945e32014-04-30 18:18:01 -0400324 return set()
325
326
327 @classmethod
328 def _get_elliptic_curves(cls, lib):
329 """
330 Get, cache, and return the curves supported by OpenSSL.
331
332 :param lib: The OpenSSL library binding object.
333
334 :return: A :py:type:`set` of ``cls`` instances giving the names of the
335 elliptic curves the underlying library supports.
336 """
337 if cls._curves is None:
338 cls._curves = cls._load_elliptic_curves(lib)
339 return cls._curves
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400340
341
342 @classmethod
343 def from_nid(cls, lib, nid):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400344 """
345 Instantiate a new :py:class:`_EllipticCurve` associated with the given
346 OpenSSL NID.
347
348 :param lib: The OpenSSL library binding object.
349
350 :param nid: The OpenSSL NID the resulting curve object will represent.
351 This must be a curve NID (and not, for example, a hash NID) or
352 subsequent operations will fail in unpredictable ways.
353 :type nid: :py:class:`int`
354
355 :return: The curve object.
356 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400357 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
358
359
360 def __init__(self, lib, nid, name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400361 """
362 :param _lib: The :py:mod:`cryptography` binding instance used to
363 interface with OpenSSL.
364
365 :param _nid: The OpenSSL NID identifying the curve this object
366 represents.
367 :type _nid: :py:class:`int`
368
369 :param name: The OpenSSL short name identifying the curve this object
370 represents.
371 :type name: :py:class:`unicode`
372 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400373 self._lib = lib
374 self._nid = nid
375 self.name = name
376
377
378 def __repr__(self):
379 return "<Curve %r>" % (self.name,)
380
381
382 def _to_EC_KEY(self):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400383 """
384 Create a new OpenSSL EC_KEY structure initialized to use this curve.
385
386 The structure is automatically garbage collected when the Python object
387 is garbage collected.
388 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400389 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
390 return _ffi.gc(key, _lib.EC_KEY_free)
391
392
393
394def get_elliptic_curves():
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400395 """
396 Return a set of objects representing the elliptic curves supported in the
397 OpenSSL build in use.
398
399 The curve objects have a :py:class:`unicode` ``name`` attribute by which
400 they identify themselves.
401
402 The curve objects are useful as values for the argument accepted by
Jean-Paul Calderone3b04e352014-04-19 09:29:10 -0400403 :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
404 used for ECDHE key exchange.
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400405 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400406 return _EllipticCurve._get_elliptic_curves(_lib)
407
408
409
410def get_elliptic_curve(name):
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400411 """
412 Return a single curve object selected by name.
413
414 See :py:func:`get_elliptic_curves` for information about curve objects.
415
Jean-Paul Calderoned5839e22014-04-19 09:26:44 -0400416 :param name: The OpenSSL short name identifying the curve object to
417 retrieve.
418 :type name: :py:class:`unicode`
419
Jean-Paul Calderoneaaf516d2014-04-19 09:10:45 -0400420 If the named curve is not supported then :py:class:`ValueError` is raised.
421 """
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400422 for curve in get_elliptic_curves():
423 if curve.name == name:
424 return curve
425 raise ValueError("unknown curve name", name)
426
427
428
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800429class X509Name(object):
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200430 """
431 An X.509 Distinguished Name.
432
433 :ivar countryName: The country of the entity.
434 :ivar C: Alias for :py:attr:`countryName`.
435
436 :ivar stateOrProvinceName: The state or province of the entity.
437 :ivar ST: Alias for :py:attr:`stateOrProvinceName`.
438
439 :ivar localityName: The locality of the entity.
440 :ivar L: Alias for :py:attr:`localityName`.
441
442 :ivar organizationName: The organization name of the entity.
443 :ivar O: Alias for :py:attr:`organizationName`.
444
445 :ivar organizationalUnitName: The organizational unit of the entity.
446 :ivar OU: Alias for :py:attr:`organizationalUnitName`
447
448 :ivar commonName: The common name of the entity.
449 :ivar CN: Alias for :py:attr:`commonName`.
450
451 :ivar emailAddress: The e-mail address of the entity.
452 """
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800453 def __init__(self, name):
454 """
455 Create a new X509Name, copying the given X509Name instance.
456
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200457 :param name: The name to copy.
458 :type name: :py:class:`X509Name`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800459 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500460 name = _lib.X509_NAME_dup(name._name)
461 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800462
463
464 def __setattr__(self, name, value):
465 if name.startswith('_'):
466 return super(X509Name, self).__setattr__(name, value)
467
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800468 # Note: we really do not want str subclasses here, so we do not use
469 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800470 if type(name) is not str:
471 raise TypeError("attribute name must be string, not '%.200s'" % (
472 type(value).__name__,))
473
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500474 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500475 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800476 try:
477 _raise_current_error()
478 except Error:
479 pass
480 raise AttributeError("No such attribute")
481
482 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500483 for i in range(_lib.X509_NAME_entry_count(self._name)):
484 ent = _lib.X509_NAME_get_entry(self._name, i)
485 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
486 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800487 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500488 ent = _lib.X509_NAME_delete_entry(self._name, i)
489 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800490 break
491
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500492 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800493 value = value.encode('utf-8')
494
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500495 add_result = _lib.X509_NAME_add_entry_by_NID(
496 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800497 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500498 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800499
500
501 def __getattr__(self, name):
502 """
503 Find attribute. An X509Name object has the following attributes:
504 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
505 organization (alias O), organizationalUnit (alias OU), commonName (alias
506 CN) and more...
507 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500508 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500509 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800510 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
511 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
512 # push something onto the error queue. If we don't clean that up
513 # now, someone else will bump into it later and be quite confused.
514 # See lp#314814.
515 try:
516 _raise_current_error()
517 except Error:
518 pass
519 return super(X509Name, self).__getattr__(name)
520
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500521 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800522 if entry_index == -1:
523 return None
524
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500525 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
526 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800527
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500528 result_buffer = _ffi.new("unsigned char**")
529 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800530 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500531 # TODO: This is untested.
532 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800533
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700534 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500535 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700536 finally:
537 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500538 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800539 return result
540
541
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500542 def _cmp(op):
543 def f(self, other):
544 if not isinstance(other, X509Name):
545 return NotImplemented
546 result = _lib.X509_NAME_cmp(self._name, other._name)
547 return op(result, 0)
548 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800549
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500550 __eq__ = _cmp(__eq__)
551 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800552
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500553 __lt__ = _cmp(__lt__)
554 __le__ = _cmp(__le__)
555
556 __gt__ = _cmp(__gt__)
557 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800558
559 def __repr__(self):
560 """
561 String representation of an X509Name
562 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500563 result_buffer = _ffi.new("char[]", 512);
564 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800565 self._name, result_buffer, len(result_buffer))
566
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500567 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500568 # TODO: This is untested.
569 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800570
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500571 return "<X509Name object '%s'>" % (
572 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800573
574
575 def hash(self):
576 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200577 Return an integer representation of the first four bytes of the
578 MD5 digest of the DER representation of the name.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800579
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200580 This is the Python equivalent of OpenSSL's ``X509_NAME_hash``.
581
582 :return: The (integer) hash of this name.
583 :rtype: :py:class:`int`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800584 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500585 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800586
587
588 def der(self):
589 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200590 Return the DER encoding of this name.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800591
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200592 :return: The DER encoded form of this name.
593 :rtype: :py:class:`bytes`
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800594 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500595 result_buffer = _ffi.new('unsigned char**')
596 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800597 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500598 # TODO: This is untested.
599 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800600
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500601 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
602 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800603 return string_result
604
605
606 def get_components(self):
607 """
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200608 Returns the components of this name, as a sequence of 2-tuples.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800609
Laurens Van Houtven196195b2014-06-17 17:06:34 +0200610 :return: The components of this name.
611 :rtype: :py:class:`list` of ``name, value`` tuples.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800612 """
613 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500614 for i in range(_lib.X509_NAME_entry_count(self._name)):
615 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800616
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500617 fname = _lib.X509_NAME_ENTRY_get_object(ent)
618 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800619
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500620 nid = _lib.OBJ_obj2nid(fname)
621 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800622
623 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500624 _ffi.string(name),
625 _ffi.string(
626 _lib.ASN1_STRING_data(fval),
627 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800628
629 return result
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200630
631
632
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800633X509NameType = X509Name
634
635
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200636
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800637class X509Extension(object):
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200638 """
639 An X.509 v3 certificate extension.
640 """
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800641 def __init__(self, type_name, critical, value, subject=None, issuer=None):
642 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200643 Initializes an X509 extension.
644
645 :param typename: The name of the type of extension to create. See
646 http://openssl.org/docs/apps/x509v3_config.html#STANDARD_EXTENSIONS
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800647 :type typename: :py:data:`str`
648
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200649 :param bool critical: A flag indicating whether this is a critical extension.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800650
651 :param value: The value of the extension.
652 :type value: :py:data:`str`
653
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200654 :param subject: Optional X509 certificate to use as subject.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800655 :type subject: :py:class:`X509`
656
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200657 :param issuer: Optional X509 certificate to use as issuer.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800658 :type issuer: :py:class:`X509`
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800659 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500660 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800661
662 # A context is necessary for any extension which uses the r2i conversion
663 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
664 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500665 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800666
667 # We have no configuration database - but perhaps we should (some
668 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500669 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800670
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800671 # Initialize the subject and issuer, if appropriate. ctx is a local,
672 # and as far as I can tell none of the X509V3_* APIs invoked here steal
673 # any references, so no need to mess with reference counts or duplicates.
674 if issuer is not None:
675 if not isinstance(issuer, X509):
676 raise TypeError("issuer must be an X509 instance")
677 ctx.issuer_cert = issuer._x509
678 if subject is not None:
679 if not isinstance(subject, X509):
680 raise TypeError("subject must be an X509 instance")
681 ctx.subject_cert = subject._x509
682
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800683 if critical:
684 # There are other OpenSSL APIs which would let us pass in critical
685 # separately, but they're harder to use, and since value is already
686 # a pile of crappy junk smuggling a ton of utterly important
687 # structured data, what's the point of trying to avoid nasty stuff
688 # with strings? (However, X509V3_EXT_i2d in particular seems like it
689 # would be a better API to invoke. I do not know where to get the
690 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500691 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800692
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500693 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
694 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800695 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500696 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800697
698
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400699 @property
700 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500701 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400702
703 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500704 _lib.GEN_EMAIL: "email",
705 _lib.GEN_DNS: "DNS",
706 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400707 }
708
709 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500710 method = _lib.X509V3_EXT_get(self._extension)
711 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500712 # TODO: This is untested.
713 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400714 payload = self._extension.value.data
715 length = self._extension.value.length
716
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500717 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400718 payloadptr[0] = payload
719
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500720 if method.it != _ffi.NULL:
721 ptr = _lib.ASN1_ITEM_ptr(method.it)
722 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
723 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400724 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500725 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400726 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500727 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400728
729 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500730 for i in range(_lib.sk_GENERAL_NAME_num(names)):
731 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400732 try:
733 label = self._prefixes[name.type]
734 except KeyError:
735 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500736 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500737 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400738 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500739 value = _native(
740 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
741 parts.append(label + ":" + value)
742 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400743
744
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800745 def __str__(self):
746 """
747 :return: a nice text representation of the extension
748 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500749 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400750 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800751
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400752 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500753 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800754 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500755 # TODO: This is untested.
756 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800757
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500758 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800759
760
761 def get_critical(self):
762 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200763 Returns the critical field of this X.509 extension.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800764
765 :return: The critical field.
766 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500767 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800768
769
770 def get_short_name(self):
771 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200772 Returns the short type name of this X.509 extension.
773
774 The result is a byte string such as :py:const:`b"basicConstraints"`.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800775
776 :return: The short type name.
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200777 :rtype: :py:data:`bytes`
778
779 .. versionadded:: 0.12
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800780 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500781 obj = _lib.X509_EXTENSION_get_object(self._extension)
782 nid = _lib.OBJ_obj2nid(obj)
783 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800784
785
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800786 def get_data(self):
787 """
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200788 Returns the data of the X509 extension, encoded as ASN.1.
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800789
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200790 :return: The ASN.1 encoded data of this X509 extension.
791 :rtype: :py:data:`bytes`
792
793 .. versionadded:: 0.12
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800794 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500795 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
796 string_result = _ffi.cast('ASN1_STRING*', octet_result)
797 char_result = _lib.ASN1_STRING_data(string_result)
798 result_length = _lib.ASN1_STRING_length(string_result)
799 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800800
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200801
802
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800803X509ExtensionType = X509Extension
804
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800805
Laurens Van Houtven2650de52014-06-18 13:47:47 +0200806
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800807class X509Req(object):
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200808 """
809 An X.509 certificate signing requests.
810 """
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800811 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500812 req = _lib.X509_REQ_new()
813 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800814
815
816 def set_pubkey(self, pkey):
817 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200818 Set the public key of the certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800819
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200820 :param pkey: The public key to use.
821 :type pkey: :py:class:`PKey`
822
823 :return: :py:data:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800824 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500825 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800826 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500827 # TODO: This is untested.
828 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800829
830
831 def get_pubkey(self):
832 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200833 Get the public key of the certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800834
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200835 :return: The public key.
836 :rtype: :py:class:`PKey`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800837 """
838 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500839 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
840 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500841 # TODO: This is untested.
842 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500843 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800844 pkey._only_public = True
845 return pkey
846
847
848 def set_version(self, version):
849 """
850 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
851 request.
852
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200853 :param int version: The version number.
854 :return: :py:data:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800855 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500856 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800857 if not set_result:
858 _raise_current_error()
859
860
861 def get_version(self):
862 """
863 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
864 request.
865
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200866 :return: The value of the version subfield.
867 :rtype: :py:class:`int`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800868 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500869 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800870
871
872 def get_subject(self):
873 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200874 Return the subject of this certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800875
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200876 This creates a new :py:class:`X509Name`: modifying it does not affect
877 this request.
878
879 :return: The subject of this certificate signing request.
880 :rtype: :py:class:`X509Name`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800881 """
882 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500883 name._name = _lib.X509_REQ_get_subject_name(self._req)
884 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500885 # TODO: This is untested.
886 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800887
888 # The name is owned by the X509Req structure. As long as the X509Name
889 # Python object is alive, keep the X509Req Python object alive.
890 name._owner = self
891
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800892 return name
893
894
895 def add_extensions(self, extensions):
896 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200897 Add extensions to the certificate signing request.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800898
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200899 :param extensions: The X.509 extensions to add.
900 :type extensions: iterable of :py:class:`X509Extension`
901 :return: :py:data:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800902 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500903 stack = _lib.sk_X509_EXTENSION_new_null()
904 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500905 # TODO: This is untested.
906 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800907
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500908 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800909
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800910 for ext in extensions:
911 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800912 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800913
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800914 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500915 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800916
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500917 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800918 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500919 # TODO: This is untested.
920 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800921
922
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800923 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800924 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200925 Get X.509 extensions in the certificate signing request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800926
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200927 :return: The X.509 extensions in this request.
928 :rtype: :py:class:`list` of :py:class:`X509Extension` objects.
929
930 .. versionadded:: 0.15
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800931 """
932 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500933 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500934 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800935 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500936 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800937 exts.append(ext)
938 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800939
940
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800941 def sign(self, pkey, digest):
942 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200943 Sign the certificate signing request using the supplied key and digest.
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800944
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200945 :param pkey: The key pair to sign with.
946 :type pkey: :py:class:`PKey`
947 :param digest: The name of the message digest to use for the signature,
948 e.g. :py:data:`b"sha1"`.
949 :type digest: :py:class:`bytes`
950 :return: :py:data:`None`
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800951 """
952 if pkey._only_public:
953 raise ValueError("Key has only public part")
954
955 if not pkey._initialized:
956 raise ValueError("Key is uninitialized")
957
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500958 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500959 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800960 raise ValueError("No such digest method")
961
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500962 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800963 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500964 # TODO: This is untested.
965 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800966
967
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800968 def verify(self, pkey):
969 """
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200970 Verifies the signature on this certificate signing request.
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800971
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200972 :param key: A public key.
973 :type key: :py:class:`PKey`
974 :return: :py:data:`True` if the signature is correct.
975 :rtype: :py:class:`bool`
976 :raises Error: If the signature is invalid or there is a
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800977 problem verifying the signature.
978 """
979 if not isinstance(pkey, PKey):
980 raise TypeError("pkey must be a PKey instance")
981
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500982 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800983 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500984 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800985
986 return result
987
988
Laurens Van Houtven3e83d242014-06-18 14:29:47 +0200989
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800990X509ReqType = X509Req
991
992
993
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800994class X509(object):
995 def __init__(self):
996 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500997 x509 = _lib.X509_new()
998 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800999
1000
1001 def set_version(self, version):
1002 """
1003 Set version number of the certificate
1004
1005 :param version: The version number
1006 :type version: :py:class:`int`
1007
1008 :return: None
1009 """
1010 if not isinstance(version, int):
1011 raise TypeError("version must be an integer")
1012
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001013 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001014
1015
1016 def get_version(self):
1017 """
1018 Return version number of the certificate
1019
1020 :return: Version number as a Python integer
1021 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001022 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001023
1024
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001025 def get_pubkey(self):
1026 """
1027 Get the public key of the certificate
1028
1029 :return: The public key
1030 """
1031 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001032 pkey._pkey = _lib.X509_get_pubkey(self._x509)
1033 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001034 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001035 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001036 pkey._only_public = True
1037 return pkey
1038
1039
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001040 def set_pubkey(self, pkey):
1041 """
1042 Set the public key of the certificate
1043
1044 :param pkey: The public key
1045
1046 :return: None
1047 """
1048 if not isinstance(pkey, PKey):
1049 raise TypeError("pkey must be a PKey instance")
1050
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001051 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001052 if not set_result:
1053 _raise_current_error()
1054
1055
1056 def sign(self, pkey, digest):
1057 """
1058 Sign the certificate using the supplied key and digest
1059
1060 :param pkey: The key to sign with
1061 :param digest: The message digest to use
1062 :return: None
1063 """
1064 if not isinstance(pkey, PKey):
1065 raise TypeError("pkey must be a PKey instance")
1066
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -08001067 if pkey._only_public:
1068 raise ValueError("Key only has public part")
1069
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -08001070 if not pkey._initialized:
1071 raise ValueError("Key is uninitialized")
1072
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001073 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001074 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001075 raise ValueError("No such digest method")
1076
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001077 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -08001078 if not sign_result:
1079 _raise_current_error()
1080
1081
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001082 def get_signature_algorithm(self):
1083 """
1084 Retrieve the signature algorithm used in the certificate
1085
1086 :return: A byte string giving the name of the signature algorithm used in
1087 the certificate.
1088 :raise ValueError: If the signature algorithm is undefined.
1089 """
1090 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001091 nid = _lib.OBJ_obj2nid(alg)
1092 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001093 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001094 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -08001095
1096
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001097 def digest(self, digest_name):
1098 """
1099 Return the digest of the X509 object.
1100
1101 :param digest_name: The name of the digest algorithm to use.
1102 :type digest_name: :py:class:`bytes`
1103
1104 :return: The digest of the object
1105 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001106 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001107 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001108 raise ValueError("No such digest method")
1109
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001110 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
1111 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001112 result_length[0] = len(result_buffer)
1113
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001114 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001115 self._x509, digest, result_buffer, result_length)
1116
1117 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001118 # TODO: This is untested.
1119 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001120
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001121 return b":".join([
1122 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001123 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -08001124
1125
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001126 def subject_name_hash(self):
1127 """
1128 Return the hash of the X509 subject.
1129
1130 :return: The hash of the subject.
1131 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001132 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001133
1134
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001135 def set_serial_number(self, serial):
1136 """
1137 Set serial number of the certificate
1138
1139 :param serial: The serial number
1140 :type serial: :py:class:`int`
1141
1142 :return: None
1143 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001144 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001145 raise TypeError("serial must be an integer")
1146
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001147 hex_serial = hex(serial)[2:]
1148 if not isinstance(hex_serial, bytes):
1149 hex_serial = hex_serial.encode('ascii')
1150
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001151 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001152
1153 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
1154 # it. If bignum is still NULL after this call, then the return value is
1155 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001156 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001157
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001158 if bignum_serial[0] == _ffi.NULL:
1159 set_result = _lib.ASN1_INTEGER_set(
1160 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001161 if set_result:
1162 # TODO Not tested
1163 _raise_current_error()
1164 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001165 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1166 _lib.BN_free(bignum_serial[0])
1167 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001168 # TODO Not tested
1169 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001170 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1171 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001172 if not set_result:
1173 # TODO Not tested
1174 _raise_current_error()
1175
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001176
1177 def get_serial_number(self):
1178 """
1179 Return serial number of the certificate
1180
1181 :return: Serial number as a Python integer
1182 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001183 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1184 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001185 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001186 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001187 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001188 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001189 serial = int(hexstring_serial, 16)
1190 return serial
1191 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001192 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001193 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001194 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001195
1196
1197 def gmtime_adj_notAfter(self, amount):
1198 """
1199 Adjust the time stamp for when the certificate stops being valid
1200
1201 :param amount: The number of seconds by which to adjust the ending
1202 validity time.
1203 :type amount: :py:class:`int`
1204
1205 :return: None
1206 """
1207 if not isinstance(amount, int):
1208 raise TypeError("amount must be an integer")
1209
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001210 notAfter = _lib.X509_get_notAfter(self._x509)
1211 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001212
1213
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001214 def gmtime_adj_notBefore(self, amount):
1215 """
1216 Change the timestamp for when the certificate starts being valid to the current
1217 time plus an offset.
1218
1219 :param amount: The number of seconds by which to adjust the starting validity
1220 time.
1221 :return: None
1222 """
1223 if not isinstance(amount, int):
1224 raise TypeError("amount must be an integer")
1225
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001226 notBefore = _lib.X509_get_notBefore(self._x509)
1227 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001228
1229
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001230 def has_expired(self):
1231 """
1232 Check whether the certificate has expired.
1233
1234 :return: True if the certificate has expired, false otherwise
1235 """
1236 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001237 notAfter = _lib.X509_get_notAfter(self._x509)
1238 return _lib.ASN1_UTCTIME_cmp_time_t(
1239 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001240
1241
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001242 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001243 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001244
1245
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001246 def get_notBefore(self):
1247 """
1248 Retrieve the time stamp for when the certificate starts being valid
1249
1250 :return: A string giving the timestamp, in the format::
1251
1252 YYYYMMDDhhmmssZ
1253 YYYYMMDDhhmmss+hhmm
1254 YYYYMMDDhhmmss-hhmm
1255
1256 or None if there is no value set.
1257 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001258 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001259
1260
1261 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001262 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001263
1264
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001265 def set_notBefore(self, when):
1266 """
1267 Set the time stamp for when the certificate starts being valid
1268
1269 :param when: A string giving the timestamp, in the format:
1270
1271 YYYYMMDDhhmmssZ
1272 YYYYMMDDhhmmss+hhmm
1273 YYYYMMDDhhmmss-hhmm
1274 :type when: :py:class:`bytes`
1275
1276 :return: None
1277 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001278 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001279
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001280
1281 def get_notAfter(self):
1282 """
1283 Retrieve the time stamp for when the certificate stops being valid
1284
1285 :return: A string giving the timestamp, in the format::
1286
1287 YYYYMMDDhhmmssZ
1288 YYYYMMDDhhmmss+hhmm
1289 YYYYMMDDhhmmss-hhmm
1290
1291 or None if there is no value set.
1292 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001293 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001294
1295
1296 def set_notAfter(self, when):
1297 """
1298 Set the time stamp for when the certificate stops being valid
1299
1300 :param when: A string giving the timestamp, in the format:
1301
1302 YYYYMMDDhhmmssZ
1303 YYYYMMDDhhmmss+hhmm
1304 YYYYMMDDhhmmss-hhmm
1305 :type when: :py:class:`bytes`
1306
1307 :return: None
1308 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001309 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001310
1311
1312 def _get_name(self, which):
1313 name = X509Name.__new__(X509Name)
1314 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001315 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001316 # TODO: This is untested.
1317 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001318
1319 # The name is owned by the X509 structure. As long as the X509Name
1320 # Python object is alive, keep the X509 Python object alive.
1321 name._owner = self
1322
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001323 return name
1324
1325
1326 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001327 if not isinstance(name, X509Name):
1328 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001329 set_result = which(self._x509, name._name)
1330 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001331 # TODO: This is untested.
1332 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001333
1334
1335 def get_issuer(self):
1336 """
1337 Create an X509Name object for the issuer of the certificate
1338
1339 :return: An X509Name object
1340 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001341 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001342
1343
1344 def set_issuer(self, issuer):
1345 """
1346 Set the issuer of the certificate
1347
1348 :param issuer: The issuer name
1349 :type issuer: :py:class:`X509Name`
1350
1351 :return: None
1352 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001353 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001354
1355
1356 def get_subject(self):
1357 """
1358 Create an X509Name object for the subject of the certificate
1359
1360 :return: An X509Name object
1361 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001362 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001363
1364
1365 def set_subject(self, subject):
1366 """
1367 Set the subject of the certificate
1368
1369 :param subject: The subject name
1370 :type subject: :py:class:`X509Name`
1371 :return: None
1372 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001373 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001374
1375
1376 def get_extension_count(self):
1377 """
1378 Get the number of extensions on the certificate.
1379
1380 :return: The number of extensions as an integer.
1381 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001382 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001383
1384
1385 def add_extensions(self, extensions):
1386 """
1387 Add extensions to the certificate.
1388
1389 :param extensions: a sequence of X509Extension objects
1390 :return: None
1391 """
1392 for ext in extensions:
1393 if not isinstance(ext, X509Extension):
1394 raise ValueError("One of the elements is not an X509Extension")
1395
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001396 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001397 if not add_result:
1398 _raise_current_error()
1399
1400
1401 def get_extension(self, index):
1402 """
1403 Get a specific extension of the certificate by index.
1404
1405 :param index: The index of the extension to retrieve.
1406 :return: The X509Extension object at the specified index.
1407 """
1408 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001409 ext._extension = _lib.X509_get_ext(self._x509, index)
1410 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001411 raise IndexError("extension index out of bounds")
1412
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001413 extension = _lib.X509_EXTENSION_dup(ext._extension)
1414 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001415 return ext
1416
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001417
1418
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001419X509Type = X509
1420
1421
1422
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001423class X509Store(object):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001424 """
1425 An X509 certificate store.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001426 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001427 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001428 store = _lib.X509_STORE_new()
1429 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001430
1431
1432 def add_cert(self, cert):
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001433 """
1434 Adds the certificate :py:data:`cert` to this store.
1435
Laurens Van Houtven6e7dd432014-06-17 16:10:57 +02001436 This is the Python equivalent of OpenSSL's ``X509_STORE_add_cert``.
Laurens Van Houtvenef5c83d2014-06-17 15:32:27 +02001437
1438 :param X509 cert: The certificate to add to this store.
1439 :raises TypeError: If the certificate is not an :py:class:`X509`.
1440 :raises Error: If OpenSSL was unhappy with your certificate.
1441 :return: py:data:`None` if the certificate was added successfully.
1442 """
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001443 if not isinstance(cert, X509):
1444 raise TypeError()
1445
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001446 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001447 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001448 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001449
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001450
1451X509StoreType = X509Store
1452
1453
1454
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001455def load_certificate(type, buffer):
1456 """
1457 Load a certificate from a buffer
1458
1459 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1460
1461 :param buffer: The buffer the certificate is stored in
1462 :type buffer: :py:class:`bytes`
1463
1464 :return: The X509 object
1465 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001466 if isinstance(buffer, _text_type):
1467 buffer = buffer.encode("ascii")
1468
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001469 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001470
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001471 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001472 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001473 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001474 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001475 else:
1476 raise ValueError(
1477 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001478
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001479 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001480 _raise_current_error()
1481
1482 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001483 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001484 return cert
1485
1486
1487def dump_certificate(type, cert):
1488 """
1489 Dump a certificate to a buffer
1490
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001491 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1492 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001493 :param cert: The certificate to dump
1494 :return: The buffer with the dumped certificate in
1495 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001496 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001497
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001498 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001499 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001500 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001501 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001502 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001503 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001504 else:
1505 raise ValueError(
1506 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1507 "FILETYPE_TEXT")
1508
1509 return _bio_to_string(bio)
1510
1511
1512
1513def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1514 """
1515 Dump a private key to a buffer
1516
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001517 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1518 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001519 :param pkey: The PKey to dump
1520 :param cipher: (optional) if encrypted PEM format, the cipher to
1521 use
1522 :param passphrase: (optional) if encrypted PEM format, this can be either
1523 the passphrase to use, or a callback for providing the
1524 passphrase.
1525 :return: The buffer with the dumped key in
1526 :rtype: :py:data:`str`
1527 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001528 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001529
1530 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001531 if passphrase is None:
1532 raise TypeError(
1533 "if a value is given for cipher "
1534 "one must also be given for passphrase")
1535 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001536 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001537 raise ValueError("Invalid cipher name")
1538 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001539 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001540
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001541 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001542 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001543 result_code = _lib.PEM_write_bio_PrivateKey(
1544 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001545 helper.callback, helper.callback_args)
1546 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001547 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001548 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001549 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001550 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1551 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001552 # TODO RSA_free(rsa)?
1553 else:
1554 raise ValueError(
1555 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1556 "FILETYPE_TEXT")
1557
1558 if result_code == 0:
1559 _raise_current_error()
1560
1561 return _bio_to_string(bio)
1562
1563
1564
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001565def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001566 copy = _lib.X509_REVOKED_new()
1567 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001568 # TODO: This is untested.
1569 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001570
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001571 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001572 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001573 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001574
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001575 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001576 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001577 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001578
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001579 if original.extensions != _ffi.NULL:
1580 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1581 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1582 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1583 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1584 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001585 copy.extensions = extension_stack
1586
1587 copy.sequence = original.sequence
1588 return copy
1589
1590
1591
1592class Revoked(object):
1593 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1594 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1595 # OCSP_crl_reason_str. We use the latter, just like the command line
1596 # program.
1597 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001598 b"unspecified",
1599 b"keyCompromise",
1600 b"CACompromise",
1601 b"affiliationChanged",
1602 b"superseded",
1603 b"cessationOfOperation",
1604 b"certificateHold",
1605 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001606 ]
1607
1608 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001609 revoked = _lib.X509_REVOKED_new()
1610 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001611
1612
1613 def set_serial(self, hex_str):
1614 """
1615 Set the serial number of a revoked Revoked structure
1616
1617 :param hex_str: The new serial number.
1618 :type hex_str: :py:data:`str`
1619 :return: None
1620 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001621 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1622 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001623 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001624 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001625 if not bn_result:
1626 raise ValueError("bad hex string")
1627
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001628 asn1_serial = _ffi.gc(
1629 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1630 _lib.ASN1_INTEGER_free)
1631 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001632
1633
1634 def get_serial(self):
1635 """
1636 Return the serial number of a Revoked structure
1637
1638 :return: The serial number as a string
1639 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001640 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001641
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001642 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001643 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001644 # TODO: This is untested.
1645 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001646
1647 return _bio_to_string(bio)
1648
1649
1650 def _delete_reason(self):
1651 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001652 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1653 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1654 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1655 _lib.X509_EXTENSION_free(ext)
1656 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001657 break
1658
1659
1660 def set_reason(self, reason):
1661 """
1662 Set the reason of a Revoked object.
1663
1664 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1665
1666 :param reason: The reason string.
1667 :type reason: :py:class:`str` or :py:class:`NoneType`
1668 :return: None
1669 """
1670 if reason is None:
1671 self._delete_reason()
1672 elif not isinstance(reason, bytes):
1673 raise TypeError("reason must be None or a byte string")
1674 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001675 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001676 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1677
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001678 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1679 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001680 # TODO: This is untested.
1681 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001682 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001683
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001684 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1685 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001686 # TODO: This is untested.
1687 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001688
1689 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001690 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1691 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001692
1693 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001694 # TODO: This is untested.
1695 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001696
1697
1698 def get_reason(self):
1699 """
1700 Return the reason of a Revoked object.
1701
1702 :return: The reason as a string
1703 """
1704 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001705 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1706 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1707 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001708 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001709
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001710 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001711 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001712 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001713 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001714 # TODO: This is untested.
1715 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001716
1717 return _bio_to_string(bio)
1718
1719
1720 def all_reasons(self):
1721 """
1722 Return a list of all the supported reason strings.
1723
1724 :return: A list of reason strings.
1725 """
1726 return self._crl_reasons[:]
1727
1728
1729 def set_rev_date(self, when):
1730 """
1731 Set the revocation timestamp
1732
1733 :param when: A string giving the timestamp, in the format:
1734
1735 YYYYMMDDhhmmssZ
1736 YYYYMMDDhhmmss+hhmm
1737 YYYYMMDDhhmmss-hhmm
1738
1739 :return: None
1740 """
1741 return _set_asn1_time(self._revoked.revocationDate, when)
1742
1743
1744 def get_rev_date(self):
1745 """
1746 Retrieve the revocation date
1747
1748 :return: A string giving the timestamp, in the format:
1749
1750 YYYYMMDDhhmmssZ
1751 YYYYMMDDhhmmss+hhmm
1752 YYYYMMDDhhmmss-hhmm
1753 """
1754 return _get_asn1_time(self._revoked.revocationDate)
1755
1756
1757
1758class CRL(object):
1759 def __init__(self):
1760 """
1761 Create a new empty CRL object.
1762 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001763 crl = _lib.X509_CRL_new()
1764 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001765
1766
1767 def get_revoked(self):
1768 """
1769 Return revoked portion of the CRL structure (by value not reference).
1770
1771 :return: A tuple of Revoked objects.
1772 """
1773 results = []
1774 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001775 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1776 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001777 revoked_copy = _X509_REVOKED_dup(revoked)
1778 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001779 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001780 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001781 if results:
1782 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001783
1784
1785 def add_revoked(self, revoked):
1786 """
1787 Add a revoked (by value not reference) to the CRL structure
1788
1789 :param revoked: The new revoked.
1790 :type revoked: :class:`X509`
1791
1792 :return: None
1793 """
1794 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001795 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001796 # TODO: This is untested.
1797 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001798
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001799 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001800 if add_result == 0:
1801 # TODO: This is untested.
1802 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001803
1804
1805 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1806 """
1807 export a CRL as a string
1808
1809 :param cert: Used to sign CRL.
1810 :type cert: :class:`X509`
1811
1812 :param key: Used to sign CRL.
1813 :type key: :class:`PKey`
1814
1815 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1816
1817 :param days: The number of days until the next update of this CRL.
1818 :type days: :py:data:`int`
1819
1820 :return: :py:data:`str`
1821 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001822 if not isinstance(cert, X509):
1823 raise TypeError("cert must be an X509 instance")
1824 if not isinstance(key, PKey):
1825 raise TypeError("key must be a PKey instance")
1826 if not isinstance(type, int):
1827 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001828
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001829 bio = _lib.BIO_new(_lib.BIO_s_mem())
1830 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001831 # TODO: This is untested.
1832 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001833
1834 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001835 sometime = _lib.ASN1_TIME_new()
1836 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001837 # TODO: This is untested.
1838 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001839
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001840 _lib.X509_gmtime_adj(sometime, 0)
1841 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001842
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001843 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1844 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001845
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001846 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001847
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001848 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001849 if not sign_result:
1850 _raise_current_error()
1851
1852 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001853 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001854 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001855 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001856 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001857 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001858 else:
1859 raise ValueError(
1860 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1861
1862 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001863 # TODO: This is untested.
1864 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001865
1866 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001867CRLType = CRL
1868
1869
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001870
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001871class PKCS7(object):
1872 def type_is_signed(self):
1873 """
1874 Check if this NID_pkcs7_signed object
1875
1876 :return: True if the PKCS7 is of type signed
1877 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001878 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001879 return True
1880 return False
1881
1882
1883 def type_is_enveloped(self):
1884 """
1885 Check if this NID_pkcs7_enveloped object
1886
1887 :returns: True if the PKCS7 is of type enveloped
1888 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001889 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001890 return True
1891 return False
1892
1893
1894 def type_is_signedAndEnveloped(self):
1895 """
1896 Check if this NID_pkcs7_signedAndEnveloped object
1897
1898 :returns: True if the PKCS7 is of type signedAndEnveloped
1899 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001900 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001901 return True
1902 return False
1903
1904
1905 def type_is_data(self):
1906 """
1907 Check if this NID_pkcs7_data object
1908
1909 :return: True if the PKCS7 is of type data
1910 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001911 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001912 return True
1913 return False
1914
1915
1916 def get_type_name(self):
1917 """
1918 Returns the type name of the PKCS7 structure
1919
1920 :return: A string with the typename
1921 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001922 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1923 string_type = _lib.OBJ_nid2sn(nid)
1924 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001925
1926PKCS7Type = PKCS7
1927
1928
1929
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001930class PKCS12(object):
1931 def __init__(self):
1932 self._pkey = None
1933 self._cert = None
1934 self._cacerts = None
1935 self._friendlyname = None
1936
1937
1938 def get_certificate(self):
1939 """
1940 Return certificate portion of the PKCS12 structure
1941
1942 :return: X509 object containing the certificate
1943 """
1944 return self._cert
1945
1946
1947 def set_certificate(self, cert):
1948 """
1949 Replace the certificate portion of the PKCS12 structure
1950
1951 :param cert: The new certificate.
1952 :type cert: :py:class:`X509` or :py:data:`None`
1953 :return: None
1954 """
1955 if not isinstance(cert, X509):
1956 raise TypeError("cert must be an X509 instance")
1957 self._cert = cert
1958
1959
1960 def get_privatekey(self):
1961 """
1962 Return private key portion of the PKCS12 structure
1963
1964 :returns: PKey object containing the private key
1965 """
1966 return self._pkey
1967
1968
1969 def set_privatekey(self, pkey):
1970 """
1971 Replace or set the certificate portion of the PKCS12 structure
1972
1973 :param pkey: The new private key.
1974 :type pkey: :py:class:`PKey`
1975 :return: None
1976 """
1977 if not isinstance(pkey, PKey):
1978 raise TypeError("pkey must be a PKey instance")
1979 self._pkey = pkey
1980
1981
1982 def get_ca_certificates(self):
1983 """
1984 Return CA certificates within of the PKCS12 object
1985
1986 :return: A newly created tuple containing the CA certificates in the chain,
1987 if any are present, or None if no CA certificates are present.
1988 """
1989 if self._cacerts is not None:
1990 return tuple(self._cacerts)
1991
1992
1993 def set_ca_certificates(self, cacerts):
1994 """
1995 Replace or set the CA certificates withing the PKCS12 object.
1996
1997 :param cacerts: The new CA certificates.
1998 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1999 :return: None
2000 """
2001 if cacerts is None:
2002 self._cacerts = None
2003 else:
2004 cacerts = list(cacerts)
2005 for cert in cacerts:
2006 if not isinstance(cert, X509):
2007 raise TypeError("iterable must only contain X509 instances")
2008 self._cacerts = cacerts
2009
2010
2011 def set_friendlyname(self, name):
2012 """
2013 Replace or set the certificate portion of the PKCS12 structure
2014
2015 :param name: The new friendly name.
2016 :type name: :py:class:`bytes`
2017 :return: None
2018 """
2019 if name is None:
2020 self._friendlyname = None
2021 elif not isinstance(name, bytes):
2022 raise TypeError("name must be a byte string or None (not %r)" % (name,))
2023 self._friendlyname = name
2024
2025
2026 def get_friendlyname(self):
2027 """
2028 Return friendly name portion of the PKCS12 structure
2029
2030 :returns: String containing the friendlyname
2031 """
2032 return self._friendlyname
2033
2034
2035 def export(self, passphrase=None, iter=2048, maciter=1):
2036 """
2037 Dump a PKCS12 object as a string. See also "man PKCS12_create".
2038
2039 :param passphrase: used to encrypt the PKCS12
2040 :type passphrase: :py:data:`bytes`
2041
2042 :param iter: How many times to repeat the encryption
2043 :type iter: :py:data:`int`
2044
2045 :param maciter: How many times to repeat the MAC
2046 :type maciter: :py:data:`int`
2047
2048 :return: The string containing the PKCS12
2049 """
2050 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002051 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002052 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002053 cacerts = _lib.sk_X509_new_null()
2054 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002055 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002056 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002057
2058 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002059 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002060
2061 friendlyname = self._friendlyname
2062 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002063 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002064
2065 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002066 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002067 else:
2068 pkey = self._pkey._pkey
2069
2070 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002071 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002072 else:
2073 cert = self._cert._x509
2074
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002075 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002076 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002077 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
2078 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002079 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002080 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002081 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002082 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002083
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002084 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002085 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002086 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002087
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002088PKCS12Type = PKCS12
2089
2090
2091
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002092class NetscapeSPKI(object):
2093 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002094 spki = _lib.NETSCAPE_SPKI_new()
2095 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002096
2097
2098 def sign(self, pkey, digest):
2099 """
2100 Sign the certificate request using the supplied key and digest
2101
2102 :param pkey: The key to sign with
2103 :param digest: The message digest to use
2104 :return: None
2105 """
2106 if pkey._only_public:
2107 raise ValueError("Key has only public part")
2108
2109 if not pkey._initialized:
2110 raise ValueError("Key is uninitialized")
2111
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002112 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002113 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002114 raise ValueError("No such digest method")
2115
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002116 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002117 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002118 # TODO: This is untested.
2119 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002120
2121
2122 def verify(self, key):
2123 """
2124 Verifies a certificate request using the supplied public key
2125
2126 :param key: a public key
2127 :return: True if the signature is correct.
2128 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
2129 problem verifying the signature.
2130 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002131 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002132 if answer <= 0:
2133 _raise_current_error()
2134 return True
2135
2136
2137 def b64_encode(self):
2138 """
2139 Generate a base64 encoded string from an SPKI
2140
2141 :return: The base64 encoded string
2142 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002143 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
2144 result = _ffi.string(encoded)
2145 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08002146 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002147
2148
2149 def get_pubkey(self):
2150 """
2151 Get the public key of the certificate
2152
2153 :return: The public key
2154 """
2155 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002156 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
2157 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002158 # TODO: This is untested.
2159 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002160 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002161 pkey._only_public = True
2162 return pkey
2163
2164
2165 def set_pubkey(self, pkey):
2166 """
2167 Set the public key of the certificate
2168
2169 :param pkey: The public key
2170 :return: None
2171 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002172 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002173 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002174 # TODO: This is untested.
2175 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08002176NetscapeSPKIType = NetscapeSPKI
2177
2178
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002179class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002180 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002181 if type != FILETYPE_PEM and passphrase is not None:
2182 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002183 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002184 self._more_args = more_args
2185 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002186 self._problems = []
2187
2188
2189 @property
2190 def callback(self):
2191 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002192 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002193 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002194 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002195 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002196 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002197 else:
2198 raise TypeError("Last argument must be string or callable")
2199
2200
2201 @property
2202 def callback_args(self):
2203 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002204 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002205 elif isinstance(self._passphrase, bytes):
2206 return self._passphrase
2207 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002208 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002209 else:
2210 raise TypeError("Last argument must be string or callable")
2211
2212
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002213 def raise_if_problem(self, exceptionType=Error):
2214 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002215 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002216 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002217 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002218 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002219 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002220 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002221
2222
2223 def _read_passphrase(self, buf, size, rwflag, userdata):
2224 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002225 if self._more_args:
2226 result = self._passphrase(size, rwflag, userdata)
2227 else:
2228 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002229 if not isinstance(result, bytes):
2230 raise ValueError("String expected")
2231 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002232 if self._truncate:
2233 result = result[:size]
2234 else:
2235 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002236 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002237 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002238 return len(result)
2239 except Exception as e:
2240 self._problems.append(e)
2241 return 0
2242
2243
2244
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002245def load_privatekey(type, buffer, passphrase=None):
2246 """
2247 Load a private key from a buffer
2248
2249 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2250 :param buffer: The buffer the key is stored in
2251 :param passphrase: (optional) if encrypted PEM format, this can be
2252 either the passphrase to use, or a callback for
2253 providing the passphrase.
2254
2255 :return: The PKey object
2256 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002257 if isinstance(buffer, _text_type):
2258 buffer = buffer.encode("ascii")
2259
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002260 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002261
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002262 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002263 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002264 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2265 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002266 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002267 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002268 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002269 else:
2270 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2271
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002272 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002273 _raise_current_error()
2274
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002275 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002276 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002277 return pkey
2278
2279
2280
2281def dump_certificate_request(type, req):
2282 """
2283 Dump a certificate request to a buffer
2284
2285 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2286 :param req: The certificate request to dump
2287 :return: The buffer with the dumped certificate request in
2288 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002289 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002290
2291 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002292 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002293 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002294 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002295 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002296 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002297 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002298 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002299
2300 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002301 # TODO: This is untested.
2302 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002303
2304 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002305
2306
2307
2308def load_certificate_request(type, buffer):
2309 """
2310 Load a certificate request from a buffer
2311
2312 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2313 :param buffer: The buffer the certificate request is stored in
2314 :return: The X509Req object
2315 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002316 if isinstance(buffer, _text_type):
2317 buffer = buffer.encode("ascii")
2318
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002319 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002320
2321 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002322 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002323 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002324 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002325 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002326 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002327
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002328 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002329 # TODO: This is untested.
2330 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002331
2332 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002333 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002334 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002335
2336
2337
2338def sign(pkey, data, digest):
2339 """
2340 Sign data with a digest
2341
2342 :param pkey: Pkey to sign with
2343 :param data: data to be signed
2344 :param digest: message digest to use
2345 :return: signature
2346 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002347 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002348 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002349 raise ValueError("No such digest method")
2350
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002351 md_ctx = _ffi.new("EVP_MD_CTX*")
2352 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002353
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002354 _lib.EVP_SignInit(md_ctx, digest_obj)
2355 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002356
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002357 signature_buffer = _ffi.new("unsigned char[]", 512)
2358 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002359 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002360 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002361 md_ctx, signature_buffer, signature_length, pkey._pkey)
2362
2363 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002364 # TODO: This is untested.
2365 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002366
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002367 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002368
2369
2370
2371def verify(cert, signature, data, digest):
2372 """
2373 Verify a signature
2374
2375 :param cert: signing certificate (X509 object)
2376 :param signature: signature returned by sign function
2377 :param data: data to be verified
2378 :param digest: message digest to use
2379 :return: None if the signature is correct, raise exception otherwise
2380 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002381 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002382 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002383 raise ValueError("No such digest method")
2384
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002385 pkey = _lib.X509_get_pubkey(cert._x509)
2386 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002387 # TODO: This is untested.
2388 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002389 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002390
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002391 md_ctx = _ffi.new("EVP_MD_CTX*")
2392 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002393
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002394 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2395 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2396 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002397
2398 if verify_result != 1:
2399 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002400
2401
2402
2403def load_crl(type, buffer):
2404 """
2405 Load a certificate revocation list from a buffer
2406
2407 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2408 :param buffer: The buffer the CRL is stored in
2409
2410 :return: The PKey object
2411 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002412 if isinstance(buffer, _text_type):
2413 buffer = buffer.encode("ascii")
2414
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002415 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002416
2417 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002418 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002419 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002420 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002421 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002422 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2423
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002424 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002425 _raise_current_error()
2426
2427 result = CRL.__new__(CRL)
2428 result._crl = crl
2429 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002430
2431
2432
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002433def load_pkcs7_data(type, buffer):
2434 """
2435 Load pkcs7 data from a buffer
2436
2437 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2438 :param buffer: The buffer with the pkcs7 data.
2439 :return: The PKCS7 object
2440 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002441 if isinstance(buffer, _text_type):
2442 buffer = buffer.encode("ascii")
2443
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002444 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002445
2446 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002447 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002448 elif type == FILETYPE_ASN1:
2449 pass
2450 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002451 # TODO: This is untested.
2452 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002453 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2454
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002455 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002456 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002457
2458 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002459 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002460 return pypkcs7
2461
2462
2463
Stephen Holsapple38482622014-04-05 20:29:34 -07002464def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002465 """
2466 Load a PKCS12 object from a buffer
2467
2468 :param buffer: The buffer the certificate is stored in
2469 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2470 :returns: The PKCS12 object
2471 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002472 if isinstance(buffer, _text_type):
2473 buffer = buffer.encode("ascii")
2474
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002475 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002476
Stephen Holsapple38482622014-04-05 20:29:34 -07002477 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2478 # password based encryption no password and a zero length password are two
2479 # different things, but OpenSSL implementation will try both to figure out
2480 # which one works.
2481 if not passphrase:
2482 passphrase = _ffi.NULL
2483
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002484 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2485 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002486 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002487 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002488
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002489 pkey = _ffi.new("EVP_PKEY**")
2490 cert = _ffi.new("X509**")
2491 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002492
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002493 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002494 if not parse_result:
2495 _raise_current_error()
2496
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002497 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002498
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002499 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2500 # queue for no particular reason. This error isn't interesting to anyone
2501 # outside this function. It's not even interesting to us. Get rid of it.
2502 try:
2503 _raise_current_error()
2504 except Error:
2505 pass
2506
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002507 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002508 pykey = None
2509 else:
2510 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002511 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002512
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002513 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002514 pycert = None
2515 friendlyname = None
2516 else:
2517 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002518 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002519
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002520 friendlyname_length = _ffi.new("int*")
2521 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2522 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2523 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002524 friendlyname = None
2525
2526 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002527 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002528 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002529 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002530 pycacerts.append(pycacert)
2531 if not pycacerts:
2532 pycacerts = None
2533
2534 pkcs12 = PKCS12.__new__(PKCS12)
2535 pkcs12._pkey = pykey
2536 pkcs12._cert = pycert
2537 pkcs12._cacerts = pycacerts
2538 pkcs12._friendlyname = friendlyname
2539 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002540
2541
2542def _initialize_openssl_threads(get_ident, Lock):
2543 import _ssl
2544 return
2545
2546 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2547
2548 def locking_function(mode, index, filename, line):
2549 if mode & _lib.CRYPTO_LOCK:
2550 locks[index].acquire()
2551 else:
2552 locks[index].release()
2553
2554 _lib.CRYPTO_set_id_callback(
2555 _ffi.callback("unsigned long (*)(void)", get_ident))
2556
2557 _lib.CRYPTO_set_locking_callback(
2558 _ffi.callback(
2559 "void (*)(int, int, const char*, int)", locking_function))
2560
2561
2562try:
2563 from thread import get_ident
2564 from threading import Lock
2565except ImportError:
2566 pass
2567else:
2568 _initialize_openssl_threads(get_ident, Lock)
2569 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002570
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002571# There are no direct unit tests for this initialization. It is tested
2572# indirectly since it is necessary for functions like dump_privatekey when
2573# using encryption.
2574#
2575# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2576# and some other similar tests may fail without this (though they may not if
2577# the Python runtime has already done some initialization of the underlying
2578# OpenSSL library (and is linked against the same one that cryptography is
2579# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002580_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002581
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002582# This is similar but exercised mainly by exception_from_error_queue. It calls
2583# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2584_lib.SSL_load_error_strings()