blob: 948402c845b6aaf9e35600124c0b7a81aeb96db5 [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05003from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
5
6from six import (
7 integer_types as _integer_types,
8 text_type as _text_type)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08009
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050010from OpenSSL._util import (
11 ffi as _ffi,
12 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050013 exception_from_error_queue as _exception_from_error_queue,
14 byte_string as _byte_string,
15 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080016
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050017FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
18FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080019
20# TODO This was an API mistake. OpenSSL has no such constant.
21FILETYPE_TEXT = 2 ** 16 - 1
22
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050023TYPE_RSA = _lib.EVP_PKEY_RSA
24TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080025
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080026
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050027class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050028 """
29 An error occurred in an `OpenSSL.crypto` API.
30 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050031
32
33_raise_current_error = partial(_exception_from_error_queue, Error)
34
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050035def _untested_error(where):
36 """
37 An OpenSSL API failed somehow. Additionally, the failure which was
38 encountered isn't one that's exercised by the test suite so future behavior
39 of pyOpenSSL is now somewhat less predictable.
40 """
41 raise RuntimeError("Unknown %s failure" % (where,))
42
43
44
45def _new_mem_buf(buffer=None):
46 """
47 Allocate a new OpenSSL memory BIO.
48
49 Arrange for the garbage collector to clean it up automatically.
50
51 :param buffer: None or some bytes to use to put into the BIO so that they
52 can be read out.
53 """
54 if buffer is None:
55 bio = _lib.BIO_new(_lib.BIO_s_mem())
56 free = _lib.BIO_free
57 else:
58 data = _ffi.new("char[]", buffer)
59 bio = _lib.BIO_new_mem_buf(data, len(buffer))
60 # Keep the memory alive as long as the bio is alive!
61 def free(bio, ref=data):
62 return _lib.BIO_free(bio)
63
64 if bio == _ffi.NULL:
65 # TODO: This is untested.
66 _raise_current_error()
67
68 bio = _ffi.gc(bio, free)
69 return bio
70
71
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050072
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080073def _bio_to_string(bio):
74 """
75 Copy the contents of an OpenSSL BIO object into a Python byte string.
76 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050077 result_buffer = _ffi.new('char**')
78 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
79 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080080
81
82
Jean-Paul Calderone57122982013-02-21 08:47:05 -080083def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050084 """
85 The the time value of an ASN1 time object.
86
87 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
88 castable to that type) which will have its value set.
89 @param when: A string representation of the desired time value.
90
91 @raise TypeError: If C{when} is not a L{bytes} string.
92 @raise ValueError: If C{when} does not represent a time in the required
93 format.
94 @raise RuntimeError: If the time value cannot be set for some other
95 (unspecified) reason.
96 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080097 if not isinstance(when, bytes):
98 raise TypeError("when must be a byte string")
99
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500100 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
101 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800102 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500103 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
104 _lib.ASN1_STRING_set(dummy, when, len(when))
105 check_result = _lib.ASN1_GENERALIZEDTIME_check(
106 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800107 if not check_result:
108 raise ValueError("Invalid string")
109 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500110 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800111
112
113
114def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500115 """
116 Retrieve the time value of an ASN1 time object.
117
118 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
119 that type) from which the time value will be retrieved.
120
121 @return: The time value from C{timestamp} as a L{bytes} string in a certain
122 format. Or C{None} if the object contains no time value.
123 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500124 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
125 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800126 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500127 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
128 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800129 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500130 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
131 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
132 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500133 # This may happen:
134 # - if timestamp was not an ASN1_TIME
135 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
136 # - if a copy of the time data from timestamp cannot be made for
137 # the newly allocated ASN1_GENERALIZEDTIME
138 #
139 # These are difficult to test. cffi enforces the ASN1_TIME type.
140 # Memory allocation failures are a pain to trigger
141 # deterministically.
142 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800143 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500144 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800145 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500146 string_data = _lib.ASN1_STRING_data(string_timestamp)
147 string_result = _ffi.string(string_data)
148 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800149 return string_result
150
151
152
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800153class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800154 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800155 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800156
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800157 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500158 pkey = _lib.EVP_PKEY_new()
159 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800160 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800161
162
163 def generate_key(self, type, bits):
164 """
165 Generate a key of a given type, with a given number of a bits
166
167 :param type: The key type (TYPE_RSA or TYPE_DSA)
168 :param bits: The number of bits
169
170 :return: None
171 """
172 if not isinstance(type, int):
173 raise TypeError("type must be an integer")
174
175 if not isinstance(bits, int):
176 raise TypeError("bits must be an integer")
177
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800178 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500179 exponent = _lib.BN_new()
180 exponent = _ffi.gc(exponent, _lib.BN_free)
181 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800182
183 if type == TYPE_RSA:
184 if bits <= 0:
185 raise ValueError("Invalid number of bits")
186
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500187 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800188
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500189 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500190 if result == 0:
191 # TODO: The test for this case is commented out. Different
192 # builds of OpenSSL appear to have different failure modes that
193 # make it hard to test. Visual inspection of the OpenSSL
194 # source reveals that a return value of 0 signals an error.
195 # Manual testing on a particular build of OpenSSL suggests that
196 # this is probably the appropriate way to handle those errors.
197 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800198
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500199 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800200 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500201 # TODO: It appears as though this can fail if an engine is in
202 # use which does not support RSA.
203 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800204
205 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500206 dsa = _lib.DSA_generate_parameters(
207 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
208 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500209 # TODO: This is untested.
210 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500211 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500212 # TODO: This is untested.
213 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500214 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500215 # TODO: This is untested.
216 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800217 else:
218 raise Error("No such key type")
219
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800220 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800221
222
223 def check(self):
224 """
225 Check the consistency of an RSA private key.
226
227 :return: True if key is consistent.
228 :raise Error: if the key is inconsistent.
229 :raise TypeError: if the key is of a type which cannot be checked.
230 Only RSA keys can currently be checked.
231 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800232 if self._only_public:
233 raise TypeError("public key only")
234
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500235 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800236 raise TypeError("key type unsupported")
237
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500238 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
239 rsa = _ffi.gc(rsa, _lib.RSA_free)
240 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800241 if result:
242 return True
243 _raise_current_error()
244
245
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800246 def type(self):
247 """
248 Returns the type of the key
249
250 :return: The type of the key.
251 """
252 return self._pkey.type
253
254
255 def bits(self):
256 """
257 Returns the number of bits of the key
258
259 :return: The number of bits of the key.
260 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500261 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800262PKeyType = PKey
263
264
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800265
Jean-Paul Calderonec09fd582014-04-18 22:00:10 -0400266class _EllipticCurve(object):
267 """
268 :ivar _lib: The :py:mod:`cryptography` binding instance used to interface
269 with OpenSSL.
270
271 :ivar _nid: The OpenSSL NID identifying the curve this object represents.
272 :type _nid: :py:class:`int`
273
274 :ivar name: The OpenSSL short name identifying the curve this object
275 represents.
276 :type name: :py:class:`unicode`
277 """
278 @classmethod
279 def _get_elliptic_curves(cls, lib):
280 """
281 Load the names of the supported elliptic curves from OpenSSL.
282
283 :param lib: The OpenSSL library binding object.
284 :return: A set of :py:obj:`unicode` giving the names of the elliptic curves
285 the underlying library supports.
286 """
287 if lib.Cryptography_HAS_EC:
288 num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
289 builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
290 # The return value on this call should be num_curves again. We could
291 # check it to make sure but if it *isn't* then.. what could we do?
292 # Abort the whole process, I suppose...? -exarkun
293 lib.EC_get_builtin_curves(builtin_curves, num_curves)
294 return set(
295 cls.from_nid(lib, c.nid)
296 for c in builtin_curves)
297 else:
298 return set()
299
300
301 @classmethod
302 def from_nid(cls, lib, nid):
303 return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
304
305
306 def __init__(self, lib, nid, name):
307 self._lib = lib
308 self._nid = nid
309 self.name = name
310
311
312 def __repr__(self):
313 return "<Curve %r>" % (self.name,)
314
315
316 def _to_EC_KEY(self):
317 key = self._lib.EC_KEY_new_by_curve_name(self._nid)
318 return _ffi.gc(key, _lib.EC_KEY_free)
319
320
321
322def get_elliptic_curves():
323 return _EllipticCurve._get_elliptic_curves(_lib)
324
325
326
327def get_elliptic_curve(name):
328 for curve in get_elliptic_curves():
329 if curve.name == name:
330 return curve
331 raise ValueError("unknown curve name", name)
332
333
334
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800335class X509Name(object):
336 def __init__(self, name):
337 """
338 Create a new X509Name, copying the given X509Name instance.
339
340 :param name: An X509Name object to copy
341 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500342 name = _lib.X509_NAME_dup(name._name)
343 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800344
345
346 def __setattr__(self, name, value):
347 if name.startswith('_'):
348 return super(X509Name, self).__setattr__(name, value)
349
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800350 # Note: we really do not want str subclasses here, so we do not use
351 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800352 if type(name) is not str:
353 raise TypeError("attribute name must be string, not '%.200s'" % (
354 type(value).__name__,))
355
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500356 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500357 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800358 try:
359 _raise_current_error()
360 except Error:
361 pass
362 raise AttributeError("No such attribute")
363
364 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500365 for i in range(_lib.X509_NAME_entry_count(self._name)):
366 ent = _lib.X509_NAME_get_entry(self._name, i)
367 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
368 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800369 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500370 ent = _lib.X509_NAME_delete_entry(self._name, i)
371 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800372 break
373
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500374 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800375 value = value.encode('utf-8')
376
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500377 add_result = _lib.X509_NAME_add_entry_by_NID(
378 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800379 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500380 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800381
382
383 def __getattr__(self, name):
384 """
385 Find attribute. An X509Name object has the following attributes:
386 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
387 organization (alias O), organizationalUnit (alias OU), commonName (alias
388 CN) and more...
389 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500390 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500391 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800392 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
393 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
394 # push something onto the error queue. If we don't clean that up
395 # now, someone else will bump into it later and be quite confused.
396 # See lp#314814.
397 try:
398 _raise_current_error()
399 except Error:
400 pass
401 return super(X509Name, self).__getattr__(name)
402
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500403 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800404 if entry_index == -1:
405 return None
406
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500407 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
408 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800409
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500410 result_buffer = _ffi.new("unsigned char**")
411 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800412 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500413 # TODO: This is untested.
414 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800415
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700416 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500417 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700418 finally:
419 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500420 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800421 return result
422
423
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500424 def _cmp(op):
425 def f(self, other):
426 if not isinstance(other, X509Name):
427 return NotImplemented
428 result = _lib.X509_NAME_cmp(self._name, other._name)
429 return op(result, 0)
430 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800431
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500432 __eq__ = _cmp(__eq__)
433 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800434
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500435 __lt__ = _cmp(__lt__)
436 __le__ = _cmp(__le__)
437
438 __gt__ = _cmp(__gt__)
439 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800440
441 def __repr__(self):
442 """
443 String representation of an X509Name
444 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500445 result_buffer = _ffi.new("char[]", 512);
446 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800447 self._name, result_buffer, len(result_buffer))
448
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500449 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500450 # TODO: This is untested.
451 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800452
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500453 return "<X509Name object '%s'>" % (
454 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800455
456
457 def hash(self):
458 """
459 Return the hash value of this name
460
461 :return: None
462 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500463 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800464
465
466 def der(self):
467 """
468 Return the DER encoding of this name
469
470 :return: A :py:class:`bytes` instance giving the DER encoded form of
471 this name.
472 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500473 result_buffer = _ffi.new('unsigned char**')
474 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800475 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500476 # TODO: This is untested.
477 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800478
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500479 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
480 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800481 return string_result
482
483
484 def get_components(self):
485 """
486 Returns the split-up components of this name.
487
488 :return: List of tuples (name, value).
489 """
490 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500491 for i in range(_lib.X509_NAME_entry_count(self._name)):
492 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800493
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500494 fname = _lib.X509_NAME_ENTRY_get_object(ent)
495 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800496
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500497 nid = _lib.OBJ_obj2nid(fname)
498 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800499
500 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500501 _ffi.string(name),
502 _ffi.string(
503 _lib.ASN1_STRING_data(fval),
504 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800505
506 return result
507X509NameType = X509Name
508
509
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800510class X509Extension(object):
511 def __init__(self, type_name, critical, value, subject=None, issuer=None):
512 """
513 :param typename: The name of the extension to create.
514 :type typename: :py:data:`str`
515
516 :param critical: A flag indicating whether this is a critical extension.
517
518 :param value: The value of the extension.
519 :type value: :py:data:`str`
520
521 :param subject: Optional X509 cert to use as subject.
522 :type subject: :py:class:`X509`
523
524 :param issuer: Optional X509 cert to use as issuer.
525 :type issuer: :py:class:`X509`
526
527 :return: The X509Extension object
528 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500529 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800530
531 # A context is necessary for any extension which uses the r2i conversion
532 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
533 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500534 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800535
536 # We have no configuration database - but perhaps we should (some
537 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500538 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800539
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800540 # Initialize the subject and issuer, if appropriate. ctx is a local,
541 # and as far as I can tell none of the X509V3_* APIs invoked here steal
542 # any references, so no need to mess with reference counts or duplicates.
543 if issuer is not None:
544 if not isinstance(issuer, X509):
545 raise TypeError("issuer must be an X509 instance")
546 ctx.issuer_cert = issuer._x509
547 if subject is not None:
548 if not isinstance(subject, X509):
549 raise TypeError("subject must be an X509 instance")
550 ctx.subject_cert = subject._x509
551
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800552 if critical:
553 # There are other OpenSSL APIs which would let us pass in critical
554 # separately, but they're harder to use, and since value is already
555 # a pile of crappy junk smuggling a ton of utterly important
556 # structured data, what's the point of trying to avoid nasty stuff
557 # with strings? (However, X509V3_EXT_i2d in particular seems like it
558 # would be a better API to invoke. I do not know where to get the
559 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500560 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800561
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500562 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
563 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800564 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500565 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800566
567
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400568 @property
569 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500570 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400571
572 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500573 _lib.GEN_EMAIL: "email",
574 _lib.GEN_DNS: "DNS",
575 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400576 }
577
578 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500579 method = _lib.X509V3_EXT_get(self._extension)
580 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500581 # TODO: This is untested.
582 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400583 payload = self._extension.value.data
584 length = self._extension.value.length
585
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500586 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400587 payloadptr[0] = payload
588
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500589 if method.it != _ffi.NULL:
590 ptr = _lib.ASN1_ITEM_ptr(method.it)
591 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
592 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400593 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500594 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400595 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500596 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400597
598 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500599 for i in range(_lib.sk_GENERAL_NAME_num(names)):
600 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400601 try:
602 label = self._prefixes[name.type]
603 except KeyError:
604 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500605 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500606 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400607 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500608 value = _native(
609 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
610 parts.append(label + ":" + value)
611 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400612
613
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800614 def __str__(self):
615 """
616 :return: a nice text representation of the extension
617 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500618 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400619 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800620
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400621 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500622 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800623 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500624 # TODO: This is untested.
625 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800626
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500627 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800628
629
630 def get_critical(self):
631 """
632 Returns the critical field of the X509Extension
633
634 :return: The critical field.
635 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500636 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800637
638
639 def get_short_name(self):
640 """
641 Returns the short version of the type name of the X509Extension
642
643 :return: The short type name.
644 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500645 obj = _lib.X509_EXTENSION_get_object(self._extension)
646 nid = _lib.OBJ_obj2nid(obj)
647 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800648
649
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800650 def get_data(self):
651 """
652 Returns the data of the X509Extension
653
654 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
655 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500656 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
657 string_result = _ffi.cast('ASN1_STRING*', octet_result)
658 char_result = _lib.ASN1_STRING_data(string_result)
659 result_length = _lib.ASN1_STRING_length(string_result)
660 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800661
662X509ExtensionType = X509Extension
663
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800664
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800665class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800666 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500667 req = _lib.X509_REQ_new()
668 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800669
670
671 def set_pubkey(self, pkey):
672 """
673 Set the public key of the certificate request
674
675 :param pkey: The public key to use
676 :return: None
677 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500678 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800679 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500680 # TODO: This is untested.
681 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800682
683
684 def get_pubkey(self):
685 """
686 Get the public key from the certificate request
687
688 :return: The public key
689 """
690 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500691 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
692 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500693 # TODO: This is untested.
694 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500695 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800696 pkey._only_public = True
697 return pkey
698
699
700 def set_version(self, version):
701 """
702 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
703 request.
704
705 :param version: The version number
706 :return: None
707 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500708 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800709 if not set_result:
710 _raise_current_error()
711
712
713 def get_version(self):
714 """
715 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
716 request.
717
718 :return: an integer giving the value of the version subfield
719 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500720 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800721
722
723 def get_subject(self):
724 """
725 Create an X509Name object for the subject of the certificate request
726
727 :return: An X509Name object
728 """
729 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500730 name._name = _lib.X509_REQ_get_subject_name(self._req)
731 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500732 # TODO: This is untested.
733 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800734
735 # The name is owned by the X509Req structure. As long as the X509Name
736 # Python object is alive, keep the X509Req Python object alive.
737 name._owner = self
738
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800739 return name
740
741
742 def add_extensions(self, extensions):
743 """
744 Add extensions to the request.
745
746 :param extensions: a sequence of X509Extension objects
747 :return: None
748 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500749 stack = _lib.sk_X509_EXTENSION_new_null()
750 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500751 # TODO: This is untested.
752 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800753
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500754 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800755
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800756 for ext in extensions:
757 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800758 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800759
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800760 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500761 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800762
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500763 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800764 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500765 # TODO: This is untested.
766 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800767
768
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800769 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800770 """
771 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800772
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500773 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800774 """
775 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500776 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500777 for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800778 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500779 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800780 exts.append(ext)
781 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800782
783
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800784 def sign(self, pkey, digest):
785 """
786 Sign the certificate request using the supplied key and digest
787
788 :param pkey: The key to sign with
789 :param digest: The message digest to use
790 :return: None
791 """
792 if pkey._only_public:
793 raise ValueError("Key has only public part")
794
795 if not pkey._initialized:
796 raise ValueError("Key is uninitialized")
797
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500798 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500799 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800800 raise ValueError("No such digest method")
801
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500802 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800803 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500804 # TODO: This is untested.
805 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800806
807
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800808 def verify(self, pkey):
809 """
810 Verifies a certificate request using the supplied public key
811
812 :param key: a public key
813 :return: True if the signature is correct.
814
815 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
816 problem verifying the signature.
817 """
818 if not isinstance(pkey, PKey):
819 raise TypeError("pkey must be a PKey instance")
820
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500821 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800822 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500823 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800824
825 return result
826
827
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800828X509ReqType = X509Req
829
830
831
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800832class X509(object):
833 def __init__(self):
834 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500835 x509 = _lib.X509_new()
836 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800837
838
839 def set_version(self, version):
840 """
841 Set version number of the certificate
842
843 :param version: The version number
844 :type version: :py:class:`int`
845
846 :return: None
847 """
848 if not isinstance(version, int):
849 raise TypeError("version must be an integer")
850
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500851 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800852
853
854 def get_version(self):
855 """
856 Return version number of the certificate
857
858 :return: Version number as a Python integer
859 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500860 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800861
862
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800863 def get_pubkey(self):
864 """
865 Get the public key of the certificate
866
867 :return: The public key
868 """
869 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500870 pkey._pkey = _lib.X509_get_pubkey(self._x509)
871 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800872 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500873 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800874 pkey._only_public = True
875 return pkey
876
877
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800878 def set_pubkey(self, pkey):
879 """
880 Set the public key of the certificate
881
882 :param pkey: The public key
883
884 :return: None
885 """
886 if not isinstance(pkey, PKey):
887 raise TypeError("pkey must be a PKey instance")
888
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500889 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800890 if not set_result:
891 _raise_current_error()
892
893
894 def sign(self, pkey, digest):
895 """
896 Sign the certificate using the supplied key and digest
897
898 :param pkey: The key to sign with
899 :param digest: The message digest to use
900 :return: None
901 """
902 if not isinstance(pkey, PKey):
903 raise TypeError("pkey must be a PKey instance")
904
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800905 if pkey._only_public:
906 raise ValueError("Key only has public part")
907
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800908 if not pkey._initialized:
909 raise ValueError("Key is uninitialized")
910
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500911 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500912 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800913 raise ValueError("No such digest method")
914
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500915 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800916 if not sign_result:
917 _raise_current_error()
918
919
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800920 def get_signature_algorithm(self):
921 """
922 Retrieve the signature algorithm used in the certificate
923
924 :return: A byte string giving the name of the signature algorithm used in
925 the certificate.
926 :raise ValueError: If the signature algorithm is undefined.
927 """
928 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500929 nid = _lib.OBJ_obj2nid(alg)
930 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800931 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500932 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800933
934
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800935 def digest(self, digest_name):
936 """
937 Return the digest of the X509 object.
938
939 :param digest_name: The name of the digest algorithm to use.
940 :type digest_name: :py:class:`bytes`
941
942 :return: The digest of the object
943 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500944 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500945 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800946 raise ValueError("No such digest method")
947
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500948 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
949 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800950 result_length[0] = len(result_buffer)
951
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500952 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800953 self._x509, digest, result_buffer, result_length)
954
955 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500956 # TODO: This is untested.
957 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800958
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500959 return b":".join([
960 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500961 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800962
963
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800964 def subject_name_hash(self):
965 """
966 Return the hash of the X509 subject.
967
968 :return: The hash of the subject.
969 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500970 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800971
972
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800973 def set_serial_number(self, serial):
974 """
975 Set serial number of the certificate
976
977 :param serial: The serial number
978 :type serial: :py:class:`int`
979
980 :return: None
981 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500982 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800983 raise TypeError("serial must be an integer")
984
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800985 hex_serial = hex(serial)[2:]
986 if not isinstance(hex_serial, bytes):
987 hex_serial = hex_serial.encode('ascii')
988
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500989 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800990
991 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
992 # it. If bignum is still NULL after this call, then the return value is
993 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500994 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800995
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500996 if bignum_serial[0] == _ffi.NULL:
997 set_result = _lib.ASN1_INTEGER_set(
998 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800999 if set_result:
1000 # TODO Not tested
1001 _raise_current_error()
1002 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001003 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
1004 _lib.BN_free(bignum_serial[0])
1005 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001006 # TODO Not tested
1007 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001008 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
1009 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001010 if not set_result:
1011 # TODO Not tested
1012 _raise_current_error()
1013
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001014
1015 def get_serial_number(self):
1016 """
1017 Return serial number of the certificate
1018
1019 :return: Serial number as a Python integer
1020 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001021 asn1_serial = _lib.X509_get_serialNumber(self._x509)
1022 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001023 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001024 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001025 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001026 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001027 serial = int(hexstring_serial, 16)
1028 return serial
1029 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001030 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -08001031 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001032 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001033
1034
1035 def gmtime_adj_notAfter(self, amount):
1036 """
1037 Adjust the time stamp for when the certificate stops being valid
1038
1039 :param amount: The number of seconds by which to adjust the ending
1040 validity time.
1041 :type amount: :py:class:`int`
1042
1043 :return: None
1044 """
1045 if not isinstance(amount, int):
1046 raise TypeError("amount must be an integer")
1047
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001048 notAfter = _lib.X509_get_notAfter(self._x509)
1049 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001050
1051
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001052 def gmtime_adj_notBefore(self, amount):
1053 """
1054 Change the timestamp for when the certificate starts being valid to the current
1055 time plus an offset.
1056
1057 :param amount: The number of seconds by which to adjust the starting validity
1058 time.
1059 :return: None
1060 """
1061 if not isinstance(amount, int):
1062 raise TypeError("amount must be an integer")
1063
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001064 notBefore = _lib.X509_get_notBefore(self._x509)
1065 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -08001066
1067
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001068 def has_expired(self):
1069 """
1070 Check whether the certificate has expired.
1071
1072 :return: True if the certificate has expired, false otherwise
1073 """
1074 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001075 notAfter = _lib.X509_get_notAfter(self._x509)
1076 return _lib.ASN1_UTCTIME_cmp_time_t(
1077 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001078
1079
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001080 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001081 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001082
1083
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001084 def get_notBefore(self):
1085 """
1086 Retrieve the time stamp for when the certificate starts being valid
1087
1088 :return: A string giving the timestamp, in the format::
1089
1090 YYYYMMDDhhmmssZ
1091 YYYYMMDDhhmmss+hhmm
1092 YYYYMMDDhhmmss-hhmm
1093
1094 or None if there is no value set.
1095 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001096 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001097
1098
1099 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001100 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001101
1102
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001103 def set_notBefore(self, when):
1104 """
1105 Set the time stamp for when the certificate starts being valid
1106
1107 :param when: A string giving the timestamp, in the format:
1108
1109 YYYYMMDDhhmmssZ
1110 YYYYMMDDhhmmss+hhmm
1111 YYYYMMDDhhmmss-hhmm
1112 :type when: :py:class:`bytes`
1113
1114 :return: None
1115 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001116 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001117
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001118
1119 def get_notAfter(self):
1120 """
1121 Retrieve the time stamp for when the certificate stops being valid
1122
1123 :return: A string giving the timestamp, in the format::
1124
1125 YYYYMMDDhhmmssZ
1126 YYYYMMDDhhmmss+hhmm
1127 YYYYMMDDhhmmss-hhmm
1128
1129 or None if there is no value set.
1130 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001131 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001132
1133
1134 def set_notAfter(self, when):
1135 """
1136 Set the time stamp for when the certificate stops being valid
1137
1138 :param when: A string giving the timestamp, in the format:
1139
1140 YYYYMMDDhhmmssZ
1141 YYYYMMDDhhmmss+hhmm
1142 YYYYMMDDhhmmss-hhmm
1143 :type when: :py:class:`bytes`
1144
1145 :return: None
1146 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001147 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001148
1149
1150 def _get_name(self, which):
1151 name = X509Name.__new__(X509Name)
1152 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001153 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001154 # TODO: This is untested.
1155 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001156
1157 # The name is owned by the X509 structure. As long as the X509Name
1158 # Python object is alive, keep the X509 Python object alive.
1159 name._owner = self
1160
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001161 return name
1162
1163
1164 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001165 if not isinstance(name, X509Name):
1166 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001167 set_result = which(self._x509, name._name)
1168 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001169 # TODO: This is untested.
1170 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001171
1172
1173 def get_issuer(self):
1174 """
1175 Create an X509Name object for the issuer of the certificate
1176
1177 :return: An X509Name object
1178 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001179 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001180
1181
1182 def set_issuer(self, issuer):
1183 """
1184 Set the issuer of the certificate
1185
1186 :param issuer: The issuer name
1187 :type issuer: :py:class:`X509Name`
1188
1189 :return: None
1190 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001191 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001192
1193
1194 def get_subject(self):
1195 """
1196 Create an X509Name object for the subject of the certificate
1197
1198 :return: An X509Name object
1199 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001200 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001201
1202
1203 def set_subject(self, subject):
1204 """
1205 Set the subject of the certificate
1206
1207 :param subject: The subject name
1208 :type subject: :py:class:`X509Name`
1209 :return: None
1210 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001211 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001212
1213
1214 def get_extension_count(self):
1215 """
1216 Get the number of extensions on the certificate.
1217
1218 :return: The number of extensions as an integer.
1219 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001220 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001221
1222
1223 def add_extensions(self, extensions):
1224 """
1225 Add extensions to the certificate.
1226
1227 :param extensions: a sequence of X509Extension objects
1228 :return: None
1229 """
1230 for ext in extensions:
1231 if not isinstance(ext, X509Extension):
1232 raise ValueError("One of the elements is not an X509Extension")
1233
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001234 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001235 if not add_result:
1236 _raise_current_error()
1237
1238
1239 def get_extension(self, index):
1240 """
1241 Get a specific extension of the certificate by index.
1242
1243 :param index: The index of the extension to retrieve.
1244 :return: The X509Extension object at the specified index.
1245 """
1246 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001247 ext._extension = _lib.X509_get_ext(self._x509, index)
1248 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001249 raise IndexError("extension index out of bounds")
1250
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001251 extension = _lib.X509_EXTENSION_dup(ext._extension)
1252 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001253 return ext
1254
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001255X509Type = X509
1256
1257
1258
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001259class X509Store(object):
1260 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001261 store = _lib.X509_STORE_new()
1262 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001263
1264
1265 def add_cert(self, cert):
1266 if not isinstance(cert, X509):
1267 raise TypeError()
1268
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001269 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001270 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001271 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001272
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001273
1274X509StoreType = X509Store
1275
1276
1277
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001278def load_certificate(type, buffer):
1279 """
1280 Load a certificate from a buffer
1281
1282 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1283
1284 :param buffer: The buffer the certificate is stored in
1285 :type buffer: :py:class:`bytes`
1286
1287 :return: The X509 object
1288 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001289 if isinstance(buffer, _text_type):
1290 buffer = buffer.encode("ascii")
1291
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001292 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001293
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001294 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001295 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001296 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001297 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001298 else:
1299 raise ValueError(
1300 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001301
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001302 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001303 _raise_current_error()
1304
1305 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001306 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001307 return cert
1308
1309
1310def dump_certificate(type, cert):
1311 """
1312 Dump a certificate to a buffer
1313
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001314 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1315 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001316 :param cert: The certificate to dump
1317 :return: The buffer with the dumped certificate in
1318 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001319 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001320
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001321 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001322 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001323 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001324 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001325 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001326 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001327 else:
1328 raise ValueError(
1329 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1330 "FILETYPE_TEXT")
1331
1332 return _bio_to_string(bio)
1333
1334
1335
1336def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1337 """
1338 Dump a private key to a buffer
1339
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001340 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1341 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001342 :param pkey: The PKey to dump
1343 :param cipher: (optional) if encrypted PEM format, the cipher to
1344 use
1345 :param passphrase: (optional) if encrypted PEM format, this can be either
1346 the passphrase to use, or a callback for providing the
1347 passphrase.
1348 :return: The buffer with the dumped key in
1349 :rtype: :py:data:`str`
1350 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001351 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001352
1353 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001354 if passphrase is None:
1355 raise TypeError(
1356 "if a value is given for cipher "
1357 "one must also be given for passphrase")
1358 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001359 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001360 raise ValueError("Invalid cipher name")
1361 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001362 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001363
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001364 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001365 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001366 result_code = _lib.PEM_write_bio_PrivateKey(
1367 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001368 helper.callback, helper.callback_args)
1369 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001370 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001371 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001372 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001373 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1374 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001375 # TODO RSA_free(rsa)?
1376 else:
1377 raise ValueError(
1378 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1379 "FILETYPE_TEXT")
1380
1381 if result_code == 0:
1382 _raise_current_error()
1383
1384 return _bio_to_string(bio)
1385
1386
1387
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001388def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001389 copy = _lib.X509_REVOKED_new()
1390 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001391 # TODO: This is untested.
1392 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001393
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001394 if original.serialNumber != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001395 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001396 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001397
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001398 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001399 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001400 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001401
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001402 if original.extensions != _ffi.NULL:
1403 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1404 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1405 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1406 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1407 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001408 copy.extensions = extension_stack
1409
1410 copy.sequence = original.sequence
1411 return copy
1412
1413
1414
1415class Revoked(object):
1416 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1417 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1418 # OCSP_crl_reason_str. We use the latter, just like the command line
1419 # program.
1420 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001421 b"unspecified",
1422 b"keyCompromise",
1423 b"CACompromise",
1424 b"affiliationChanged",
1425 b"superseded",
1426 b"cessationOfOperation",
1427 b"certificateHold",
1428 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001429 ]
1430
1431 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001432 revoked = _lib.X509_REVOKED_new()
1433 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001434
1435
1436 def set_serial(self, hex_str):
1437 """
1438 Set the serial number of a revoked Revoked structure
1439
1440 :param hex_str: The new serial number.
1441 :type hex_str: :py:data:`str`
1442 :return: None
1443 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001444 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1445 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001446 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001447 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001448 if not bn_result:
1449 raise ValueError("bad hex string")
1450
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001451 asn1_serial = _ffi.gc(
1452 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1453 _lib.ASN1_INTEGER_free)
1454 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001455
1456
1457 def get_serial(self):
1458 """
1459 Return the serial number of a Revoked structure
1460
1461 :return: The serial number as a string
1462 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001463 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001464
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001465 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001466 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001467 # TODO: This is untested.
1468 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001469
1470 return _bio_to_string(bio)
1471
1472
1473 def _delete_reason(self):
1474 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001475 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1476 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1477 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1478 _lib.X509_EXTENSION_free(ext)
1479 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001480 break
1481
1482
1483 def set_reason(self, reason):
1484 """
1485 Set the reason of a Revoked object.
1486
1487 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1488
1489 :param reason: The reason string.
1490 :type reason: :py:class:`str` or :py:class:`NoneType`
1491 :return: None
1492 """
1493 if reason is None:
1494 self._delete_reason()
1495 elif not isinstance(reason, bytes):
1496 raise TypeError("reason must be None or a byte string")
1497 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001498 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001499 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1500
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001501 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1502 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001503 # TODO: This is untested.
1504 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001505 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001506
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001507 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1508 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001509 # TODO: This is untested.
1510 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001511
1512 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001513 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1514 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001515
1516 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001517 # TODO: This is untested.
1518 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001519
1520
1521 def get_reason(self):
1522 """
1523 Return the reason of a Revoked object.
1524
1525 :return: The reason as a string
1526 """
1527 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001528 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1529 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1530 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001531 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001532
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001533 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001534 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001535 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001536 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001537 # TODO: This is untested.
1538 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001539
1540 return _bio_to_string(bio)
1541
1542
1543 def all_reasons(self):
1544 """
1545 Return a list of all the supported reason strings.
1546
1547 :return: A list of reason strings.
1548 """
1549 return self._crl_reasons[:]
1550
1551
1552 def set_rev_date(self, when):
1553 """
1554 Set the revocation timestamp
1555
1556 :param when: A string giving the timestamp, in the format:
1557
1558 YYYYMMDDhhmmssZ
1559 YYYYMMDDhhmmss+hhmm
1560 YYYYMMDDhhmmss-hhmm
1561
1562 :return: None
1563 """
1564 return _set_asn1_time(self._revoked.revocationDate, when)
1565
1566
1567 def get_rev_date(self):
1568 """
1569 Retrieve the revocation date
1570
1571 :return: A string giving the timestamp, in the format:
1572
1573 YYYYMMDDhhmmssZ
1574 YYYYMMDDhhmmss+hhmm
1575 YYYYMMDDhhmmss-hhmm
1576 """
1577 return _get_asn1_time(self._revoked.revocationDate)
1578
1579
1580
1581class CRL(object):
1582 def __init__(self):
1583 """
1584 Create a new empty CRL object.
1585 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001586 crl = _lib.X509_CRL_new()
1587 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001588
1589
1590 def get_revoked(self):
1591 """
1592 Return revoked portion of the CRL structure (by value not reference).
1593
1594 :return: A tuple of Revoked objects.
1595 """
1596 results = []
1597 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001598 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1599 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001600 revoked_copy = _X509_REVOKED_dup(revoked)
1601 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001602 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001603 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001604 if results:
1605 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001606
1607
1608 def add_revoked(self, revoked):
1609 """
1610 Add a revoked (by value not reference) to the CRL structure
1611
1612 :param revoked: The new revoked.
1613 :type revoked: :class:`X509`
1614
1615 :return: None
1616 """
1617 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001618 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001619 # TODO: This is untested.
1620 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001621
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001622 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001623 if add_result == 0:
1624 # TODO: This is untested.
1625 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001626
1627
1628 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1629 """
1630 export a CRL as a string
1631
1632 :param cert: Used to sign CRL.
1633 :type cert: :class:`X509`
1634
1635 :param key: Used to sign CRL.
1636 :type key: :class:`PKey`
1637
1638 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1639
1640 :param days: The number of days until the next update of this CRL.
1641 :type days: :py:data:`int`
1642
1643 :return: :py:data:`str`
1644 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001645 if not isinstance(cert, X509):
1646 raise TypeError("cert must be an X509 instance")
1647 if not isinstance(key, PKey):
1648 raise TypeError("key must be a PKey instance")
1649 if not isinstance(type, int):
1650 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001651
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001652 bio = _lib.BIO_new(_lib.BIO_s_mem())
1653 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001654 # TODO: This is untested.
1655 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001656
1657 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001658 sometime = _lib.ASN1_TIME_new()
1659 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001660 # TODO: This is untested.
1661 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001662
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001663 _lib.X509_gmtime_adj(sometime, 0)
1664 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001665
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001666 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1667 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001668
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001669 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001670
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001671 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001672 if not sign_result:
1673 _raise_current_error()
1674
1675 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001676 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001677 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001678 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001679 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001680 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001681 else:
1682 raise ValueError(
1683 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1684
1685 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001686 # TODO: This is untested.
1687 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001688
1689 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001690CRLType = CRL
1691
1692
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001693
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001694class PKCS7(object):
1695 def type_is_signed(self):
1696 """
1697 Check if this NID_pkcs7_signed object
1698
1699 :return: True if the PKCS7 is of type signed
1700 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001701 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001702 return True
1703 return False
1704
1705
1706 def type_is_enveloped(self):
1707 """
1708 Check if this NID_pkcs7_enveloped object
1709
1710 :returns: True if the PKCS7 is of type enveloped
1711 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001712 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001713 return True
1714 return False
1715
1716
1717 def type_is_signedAndEnveloped(self):
1718 """
1719 Check if this NID_pkcs7_signedAndEnveloped object
1720
1721 :returns: True if the PKCS7 is of type signedAndEnveloped
1722 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001723 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001724 return True
1725 return False
1726
1727
1728 def type_is_data(self):
1729 """
1730 Check if this NID_pkcs7_data object
1731
1732 :return: True if the PKCS7 is of type data
1733 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001734 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001735 return True
1736 return False
1737
1738
1739 def get_type_name(self):
1740 """
1741 Returns the type name of the PKCS7 structure
1742
1743 :return: A string with the typename
1744 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001745 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1746 string_type = _lib.OBJ_nid2sn(nid)
1747 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001748
1749PKCS7Type = PKCS7
1750
1751
1752
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001753class PKCS12(object):
1754 def __init__(self):
1755 self._pkey = None
1756 self._cert = None
1757 self._cacerts = None
1758 self._friendlyname = None
1759
1760
1761 def get_certificate(self):
1762 """
1763 Return certificate portion of the PKCS12 structure
1764
1765 :return: X509 object containing the certificate
1766 """
1767 return self._cert
1768
1769
1770 def set_certificate(self, cert):
1771 """
1772 Replace the certificate portion of the PKCS12 structure
1773
1774 :param cert: The new certificate.
1775 :type cert: :py:class:`X509` or :py:data:`None`
1776 :return: None
1777 """
1778 if not isinstance(cert, X509):
1779 raise TypeError("cert must be an X509 instance")
1780 self._cert = cert
1781
1782
1783 def get_privatekey(self):
1784 """
1785 Return private key portion of the PKCS12 structure
1786
1787 :returns: PKey object containing the private key
1788 """
1789 return self._pkey
1790
1791
1792 def set_privatekey(self, pkey):
1793 """
1794 Replace or set the certificate portion of the PKCS12 structure
1795
1796 :param pkey: The new private key.
1797 :type pkey: :py:class:`PKey`
1798 :return: None
1799 """
1800 if not isinstance(pkey, PKey):
1801 raise TypeError("pkey must be a PKey instance")
1802 self._pkey = pkey
1803
1804
1805 def get_ca_certificates(self):
1806 """
1807 Return CA certificates within of the PKCS12 object
1808
1809 :return: A newly created tuple containing the CA certificates in the chain,
1810 if any are present, or None if no CA certificates are present.
1811 """
1812 if self._cacerts is not None:
1813 return tuple(self._cacerts)
1814
1815
1816 def set_ca_certificates(self, cacerts):
1817 """
1818 Replace or set the CA certificates withing the PKCS12 object.
1819
1820 :param cacerts: The new CA certificates.
1821 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1822 :return: None
1823 """
1824 if cacerts is None:
1825 self._cacerts = None
1826 else:
1827 cacerts = list(cacerts)
1828 for cert in cacerts:
1829 if not isinstance(cert, X509):
1830 raise TypeError("iterable must only contain X509 instances")
1831 self._cacerts = cacerts
1832
1833
1834 def set_friendlyname(self, name):
1835 """
1836 Replace or set the certificate portion of the PKCS12 structure
1837
1838 :param name: The new friendly name.
1839 :type name: :py:class:`bytes`
1840 :return: None
1841 """
1842 if name is None:
1843 self._friendlyname = None
1844 elif not isinstance(name, bytes):
1845 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1846 self._friendlyname = name
1847
1848
1849 def get_friendlyname(self):
1850 """
1851 Return friendly name portion of the PKCS12 structure
1852
1853 :returns: String containing the friendlyname
1854 """
1855 return self._friendlyname
1856
1857
1858 def export(self, passphrase=None, iter=2048, maciter=1):
1859 """
1860 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1861
1862 :param passphrase: used to encrypt the PKCS12
1863 :type passphrase: :py:data:`bytes`
1864
1865 :param iter: How many times to repeat the encryption
1866 :type iter: :py:data:`int`
1867
1868 :param maciter: How many times to repeat the MAC
1869 :type maciter: :py:data:`int`
1870
1871 :return: The string containing the PKCS12
1872 """
1873 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001874 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001875 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001876 cacerts = _lib.sk_X509_new_null()
1877 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001878 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001879 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001880
1881 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001882 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001883
1884 friendlyname = self._friendlyname
1885 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001886 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001887
1888 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001889 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001890 else:
1891 pkey = self._pkey._pkey
1892
1893 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001894 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001895 else:
1896 cert = self._cert._x509
1897
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001898 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001899 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001900 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1901 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001902 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001903 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001904 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001905 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001906
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001907 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001908 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001909 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001910
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001911PKCS12Type = PKCS12
1912
1913
1914
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001915class NetscapeSPKI(object):
1916 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001917 spki = _lib.NETSCAPE_SPKI_new()
1918 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001919
1920
1921 def sign(self, pkey, digest):
1922 """
1923 Sign the certificate request using the supplied key and digest
1924
1925 :param pkey: The key to sign with
1926 :param digest: The message digest to use
1927 :return: None
1928 """
1929 if pkey._only_public:
1930 raise ValueError("Key has only public part")
1931
1932 if not pkey._initialized:
1933 raise ValueError("Key is uninitialized")
1934
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001935 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001936 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001937 raise ValueError("No such digest method")
1938
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001939 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001940 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001941 # TODO: This is untested.
1942 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001943
1944
1945 def verify(self, key):
1946 """
1947 Verifies a certificate request using the supplied public key
1948
1949 :param key: a public key
1950 :return: True if the signature is correct.
1951 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1952 problem verifying the signature.
1953 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001954 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001955 if answer <= 0:
1956 _raise_current_error()
1957 return True
1958
1959
1960 def b64_encode(self):
1961 """
1962 Generate a base64 encoded string from an SPKI
1963
1964 :return: The base64 encoded string
1965 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001966 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
1967 result = _ffi.string(encoded)
1968 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001969 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001970
1971
1972 def get_pubkey(self):
1973 """
1974 Get the public key of the certificate
1975
1976 :return: The public key
1977 """
1978 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001979 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
1980 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001981 # TODO: This is untested.
1982 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001983 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001984 pkey._only_public = True
1985 return pkey
1986
1987
1988 def set_pubkey(self, pkey):
1989 """
1990 Set the public key of the certificate
1991
1992 :param pkey: The public key
1993 :return: None
1994 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001995 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001996 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001997 # TODO: This is untested.
1998 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001999NetscapeSPKIType = NetscapeSPKI
2000
2001
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002002class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002003 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002004 if type != FILETYPE_PEM and passphrase is not None:
2005 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002006 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002007 self._more_args = more_args
2008 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002009 self._problems = []
2010
2011
2012 @property
2013 def callback(self):
2014 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002015 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002016 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002017 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002018 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002019 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002020 else:
2021 raise TypeError("Last argument must be string or callable")
2022
2023
2024 @property
2025 def callback_args(self):
2026 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002027 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002028 elif isinstance(self._passphrase, bytes):
2029 return self._passphrase
2030 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002031 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002032 else:
2033 raise TypeError("Last argument must be string or callable")
2034
2035
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002036 def raise_if_problem(self, exceptionType=Error):
2037 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002038 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002039 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002040 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002041 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002042 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05002043 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002044
2045
2046 def _read_passphrase(self, buf, size, rwflag, userdata):
2047 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002048 if self._more_args:
2049 result = self._passphrase(size, rwflag, userdata)
2050 else:
2051 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002052 if not isinstance(result, bytes):
2053 raise ValueError("String expected")
2054 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08002055 if self._truncate:
2056 result = result[:size]
2057 else:
2058 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002059 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002060 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002061 return len(result)
2062 except Exception as e:
2063 self._problems.append(e)
2064 return 0
2065
2066
2067
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002068def load_privatekey(type, buffer, passphrase=None):
2069 """
2070 Load a private key from a buffer
2071
2072 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2073 :param buffer: The buffer the key is stored in
2074 :param passphrase: (optional) if encrypted PEM format, this can be
2075 either the passphrase to use, or a callback for
2076 providing the passphrase.
2077
2078 :return: The PKey object
2079 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002080 if isinstance(buffer, _text_type):
2081 buffer = buffer.encode("ascii")
2082
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002083 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002084
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002085 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002086 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002087 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2088 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002089 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002090 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002091 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002092 else:
2093 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2094
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002095 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002096 _raise_current_error()
2097
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002098 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002099 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002100 return pkey
2101
2102
2103
2104def dump_certificate_request(type, req):
2105 """
2106 Dump a certificate request to a buffer
2107
2108 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2109 :param req: The certificate request to dump
2110 :return: The buffer with the dumped certificate request in
2111 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002112 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002113
2114 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002115 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002116 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002117 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002118 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002119 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002120 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002121 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002122
2123 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002124 # TODO: This is untested.
2125 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002126
2127 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002128
2129
2130
2131def load_certificate_request(type, buffer):
2132 """
2133 Load a certificate request from a buffer
2134
2135 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2136 :param buffer: The buffer the certificate request is stored in
2137 :return: The X509Req object
2138 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002139 if isinstance(buffer, _text_type):
2140 buffer = buffer.encode("ascii")
2141
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002142 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002143
2144 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002145 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002146 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002147 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002148 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002149 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002150
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002151 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002152 # TODO: This is untested.
2153 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002154
2155 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002156 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002157 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002158
2159
2160
2161def sign(pkey, data, digest):
2162 """
2163 Sign data with a digest
2164
2165 :param pkey: Pkey to sign with
2166 :param data: data to be signed
2167 :param digest: message digest to use
2168 :return: signature
2169 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002170 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002171 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002172 raise ValueError("No such digest method")
2173
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002174 md_ctx = _ffi.new("EVP_MD_CTX*")
2175 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002176
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002177 _lib.EVP_SignInit(md_ctx, digest_obj)
2178 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002179
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002180 signature_buffer = _ffi.new("unsigned char[]", 512)
2181 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002182 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002183 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002184 md_ctx, signature_buffer, signature_length, pkey._pkey)
2185
2186 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002187 # TODO: This is untested.
2188 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002189
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002190 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002191
2192
2193
2194def verify(cert, signature, data, digest):
2195 """
2196 Verify a signature
2197
2198 :param cert: signing certificate (X509 object)
2199 :param signature: signature returned by sign function
2200 :param data: data to be verified
2201 :param digest: message digest to use
2202 :return: None if the signature is correct, raise exception otherwise
2203 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002204 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002205 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002206 raise ValueError("No such digest method")
2207
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002208 pkey = _lib.X509_get_pubkey(cert._x509)
2209 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002210 # TODO: This is untested.
2211 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002212 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002213
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002214 md_ctx = _ffi.new("EVP_MD_CTX*")
2215 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002216
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002217 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2218 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2219 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002220
2221 if verify_result != 1:
2222 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002223
2224
2225
2226def load_crl(type, buffer):
2227 """
2228 Load a certificate revocation list from a buffer
2229
2230 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2231 :param buffer: The buffer the CRL is stored in
2232
2233 :return: The PKey object
2234 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002235 if isinstance(buffer, _text_type):
2236 buffer = buffer.encode("ascii")
2237
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002238 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002239
2240 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002241 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002242 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002243 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002244 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002245 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2246
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002247 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002248 _raise_current_error()
2249
2250 result = CRL.__new__(CRL)
2251 result._crl = crl
2252 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002253
2254
2255
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002256def load_pkcs7_data(type, buffer):
2257 """
2258 Load pkcs7 data from a buffer
2259
2260 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2261 :param buffer: The buffer with the pkcs7 data.
2262 :return: The PKCS7 object
2263 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002264 if isinstance(buffer, _text_type):
2265 buffer = buffer.encode("ascii")
2266
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002267 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002268
2269 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002270 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002271 elif type == FILETYPE_ASN1:
2272 pass
2273 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002274 # TODO: This is untested.
2275 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002276 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2277
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002278 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002279 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002280
2281 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002282 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002283 return pypkcs7
2284
2285
2286
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002287def load_pkcs12(buffer, passphrase):
2288 """
2289 Load a PKCS12 object from a buffer
2290
2291 :param buffer: The buffer the certificate is stored in
2292 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2293 :returns: The PKCS12 object
2294 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002295 if isinstance(buffer, _text_type):
2296 buffer = buffer.encode("ascii")
2297
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002298 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002299
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002300 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2301 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002302 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002303 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002304
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002305 pkey = _ffi.new("EVP_PKEY**")
2306 cert = _ffi.new("X509**")
2307 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002308
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002309 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002310 if not parse_result:
2311 _raise_current_error()
2312
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002313 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002314
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002315 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2316 # queue for no particular reason. This error isn't interesting to anyone
2317 # outside this function. It's not even interesting to us. Get rid of it.
2318 try:
2319 _raise_current_error()
2320 except Error:
2321 pass
2322
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002323 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002324 pykey = None
2325 else:
2326 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002327 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002328
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002329 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002330 pycert = None
2331 friendlyname = None
2332 else:
2333 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002334 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002335
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002336 friendlyname_length = _ffi.new("int*")
2337 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2338 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2339 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002340 friendlyname = None
2341
2342 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002343 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002344 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002345 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002346 pycacerts.append(pycacert)
2347 if not pycacerts:
2348 pycacerts = None
2349
2350 pkcs12 = PKCS12.__new__(PKCS12)
2351 pkcs12._pkey = pykey
2352 pkcs12._cert = pycert
2353 pkcs12._cacerts = pycacerts
2354 pkcs12._friendlyname = friendlyname
2355 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002356
2357
2358def _initialize_openssl_threads(get_ident, Lock):
2359 import _ssl
2360 return
2361
2362 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2363
2364 def locking_function(mode, index, filename, line):
2365 if mode & _lib.CRYPTO_LOCK:
2366 locks[index].acquire()
2367 else:
2368 locks[index].release()
2369
2370 _lib.CRYPTO_set_id_callback(
2371 _ffi.callback("unsigned long (*)(void)", get_ident))
2372
2373 _lib.CRYPTO_set_locking_callback(
2374 _ffi.callback(
2375 "void (*)(int, int, const char*, int)", locking_function))
2376
2377
2378try:
2379 from thread import get_ident
2380 from threading import Lock
2381except ImportError:
2382 pass
2383else:
2384 _initialize_openssl_threads(get_ident, Lock)
2385 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002386
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002387# There are no direct unit tests for this initialization. It is tested
2388# indirectly since it is necessary for functions like dump_privatekey when
2389# using encryption.
2390#
2391# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2392# and some other similar tests may fail without this (though they may not if
2393# the Python runtime has already done some initialization of the underlying
2394# OpenSSL library (and is linked against the same one that cryptography is
2395# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002396_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002397
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002398# This is similar but exercised mainly by exception_from_error_queue. It calls
2399# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2400_lib.SSL_load_error_strings()