blob: 103deab8e9ce2414c9fbc1cd920b424395a26ead [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05002from functools import partial
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08003
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05004from OpenSSL._util import (
5 ffi as _ffi,
6 lib as _lib,
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05007 exception_from_error_queue as _exception_from_error_queue)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08008
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05009FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
10FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080011
12# TODO This was an API mistake. OpenSSL has no such constant.
13FILETYPE_TEXT = 2 ** 16 - 1
14
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050015TYPE_RSA = _lib.EVP_PKEY_RSA
16TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080017
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080018
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050019class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050020 """
21 An error occurred in an `OpenSSL.crypto` API.
22 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050023
24
25_raise_current_error = partial(_exception_from_error_queue, Error)
26
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050027def _untested_error(where):
28 """
29 An OpenSSL API failed somehow. Additionally, the failure which was
30 encountered isn't one that's exercised by the test suite so future behavior
31 of pyOpenSSL is now somewhat less predictable.
32 """
33 raise RuntimeError("Unknown %s failure" % (where,))
34
35
36
37def _new_mem_buf(buffer=None):
38 """
39 Allocate a new OpenSSL memory BIO.
40
41 Arrange for the garbage collector to clean it up automatically.
42
43 :param buffer: None or some bytes to use to put into the BIO so that they
44 can be read out.
45 """
46 if buffer is None:
47 bio = _lib.BIO_new(_lib.BIO_s_mem())
48 free = _lib.BIO_free
49 else:
50 data = _ffi.new("char[]", buffer)
51 bio = _lib.BIO_new_mem_buf(data, len(buffer))
52 # Keep the memory alive as long as the bio is alive!
53 def free(bio, ref=data):
54 return _lib.BIO_free(bio)
55
56 if bio == _ffi.NULL:
57 # TODO: This is untested.
58 _raise_current_error()
59
60 bio = _ffi.gc(bio, free)
61 return bio
62
63
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050064
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080065def _bio_to_string(bio):
66 """
67 Copy the contents of an OpenSSL BIO object into a Python byte string.
68 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050069 result_buffer = _ffi.new('char**')
70 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
71 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080072
73
74
Jean-Paul Calderone57122982013-02-21 08:47:05 -080075def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050076 """
77 The the time value of an ASN1 time object.
78
79 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
80 castable to that type) which will have its value set.
81 @param when: A string representation of the desired time value.
82
83 @raise TypeError: If C{when} is not a L{bytes} string.
84 @raise ValueError: If C{when} does not represent a time in the required
85 format.
86 @raise RuntimeError: If the time value cannot be set for some other
87 (unspecified) reason.
88 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080089 if not isinstance(when, bytes):
90 raise TypeError("when must be a byte string")
91
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050092 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
93 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -080094 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050095 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
96 _lib.ASN1_STRING_set(dummy, when, len(when))
97 check_result = _lib.ASN1_GENERALIZEDTIME_check(
98 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -080099 if not check_result:
100 raise ValueError("Invalid string")
101 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500102 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800103
104
105
106def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500107 """
108 Retrieve the time value of an ASN1 time object.
109
110 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
111 that type) from which the time value will be retrieved.
112
113 @return: The time value from C{timestamp} as a L{bytes} string in a certain
114 format. Or C{None} if the object contains no time value.
115 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500116 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
117 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800118 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500119 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
120 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800121 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500122 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
123 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
124 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500125 # This may happen:
126 # - if timestamp was not an ASN1_TIME
127 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
128 # - if a copy of the time data from timestamp cannot be made for
129 # the newly allocated ASN1_GENERALIZEDTIME
130 #
131 # These are difficult to test. cffi enforces the ASN1_TIME type.
132 # Memory allocation failures are a pain to trigger
133 # deterministically.
134 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800135 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500136 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800137 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500138 string_data = _lib.ASN1_STRING_data(string_timestamp)
139 string_result = _ffi.string(string_data)
140 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800141 return string_result
142
143
144
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800145class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800146 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800147 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800148
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800149 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500150 pkey = _lib.EVP_PKEY_new()
151 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800152 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800153
154
155 def generate_key(self, type, bits):
156 """
157 Generate a key of a given type, with a given number of a bits
158
159 :param type: The key type (TYPE_RSA or TYPE_DSA)
160 :param bits: The number of bits
161
162 :return: None
163 """
164 if not isinstance(type, int):
165 raise TypeError("type must be an integer")
166
167 if not isinstance(bits, int):
168 raise TypeError("bits must be an integer")
169
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800170 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500171 exponent = _lib.BN_new()
172 exponent = _ffi.gc(exponent, _lib.BN_free)
173 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800174
175 if type == TYPE_RSA:
176 if bits <= 0:
177 raise ValueError("Invalid number of bits")
178
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500179 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800180
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500181 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500182 if result == 0:
183 # TODO: The test for this case is commented out. Different
184 # builds of OpenSSL appear to have different failure modes that
185 # make it hard to test. Visual inspection of the OpenSSL
186 # source reveals that a return value of 0 signals an error.
187 # Manual testing on a particular build of OpenSSL suggests that
188 # this is probably the appropriate way to handle those errors.
189 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800190
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500191 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800192 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500193 # TODO: It appears as though this can fail if an engine is in
194 # use which does not support RSA.
195 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800196
197 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500198 dsa = _lib.DSA_generate_parameters(
199 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
200 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500201 # TODO: This is untested.
202 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500203 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500204 # TODO: This is untested.
205 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500206 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500207 # TODO: This is untested.
208 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800209 else:
210 raise Error("No such key type")
211
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800212 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800213
214
215 def check(self):
216 """
217 Check the consistency of an RSA private key.
218
219 :return: True if key is consistent.
220 :raise Error: if the key is inconsistent.
221 :raise TypeError: if the key is of a type which cannot be checked.
222 Only RSA keys can currently be checked.
223 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800224 if self._only_public:
225 raise TypeError("public key only")
226
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500227 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800228 raise TypeError("key type unsupported")
229
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500230 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
231 rsa = _ffi.gc(rsa, _lib.RSA_free)
232 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800233 if result:
234 return True
235 _raise_current_error()
236
237
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800238 def type(self):
239 """
240 Returns the type of the key
241
242 :return: The type of the key.
243 """
244 return self._pkey.type
245
246
247 def bits(self):
248 """
249 Returns the number of bits of the key
250
251 :return: The number of bits of the key.
252 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500253 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800254PKeyType = PKey
255
256
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800257
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800258class X509Name(object):
259 def __init__(self, name):
260 """
261 Create a new X509Name, copying the given X509Name instance.
262
263 :param name: An X509Name object to copy
264 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500265 name = _lib.X509_NAME_dup(name._name)
266 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800267
268
269 def __setattr__(self, name, value):
270 if name.startswith('_'):
271 return super(X509Name, self).__setattr__(name, value)
272
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800273 # Note: we really do not want str subclasses here, so we do not use
274 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800275 if type(name) is not str:
276 raise TypeError("attribute name must be string, not '%.200s'" % (
277 type(value).__name__,))
278
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500279 nid = _lib.OBJ_txt2nid(name)
280 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800281 try:
282 _raise_current_error()
283 except Error:
284 pass
285 raise AttributeError("No such attribute")
286
287 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500288 for i in range(_lib.X509_NAME_entry_count(self._name)):
289 ent = _lib.X509_NAME_get_entry(self._name, i)
290 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
291 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800292 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500293 ent = _lib.X509_NAME_delete_entry(self._name, i)
294 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800295 break
296
297 if isinstance(value, unicode):
298 value = value.encode('utf-8')
299
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500300 add_result = _lib.X509_NAME_add_entry_by_NID(
301 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800302 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500303 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800304
305
306 def __getattr__(self, name):
307 """
308 Find attribute. An X509Name object has the following attributes:
309 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
310 organization (alias O), organizationalUnit (alias OU), commonName (alias
311 CN) and more...
312 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500313 nid = _lib.OBJ_txt2nid(name)
314 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800315 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
316 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
317 # push something onto the error queue. If we don't clean that up
318 # now, someone else will bump into it later and be quite confused.
319 # See lp#314814.
320 try:
321 _raise_current_error()
322 except Error:
323 pass
324 return super(X509Name, self).__getattr__(name)
325
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500326 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800327 if entry_index == -1:
328 return None
329
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500330 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
331 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800332
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500333 result_buffer = _ffi.new("unsigned char**")
334 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800335 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500336 # TODO: This is untested.
337 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800338
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700339 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500340 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700341 finally:
342 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500343 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800344 return result
345
346
347 def __cmp__(self, other):
348 if not isinstance(other, X509Name):
349 return NotImplemented
350
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500351 result = _lib.X509_NAME_cmp(self._name, other._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800352 # TODO result == -2 is an error case that maybe should be checked for
353 return result
354
355
356 def __repr__(self):
357 """
358 String representation of an X509Name
359 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500360 result_buffer = _ffi.new("char[]", 512);
361 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800362 self._name, result_buffer, len(result_buffer))
363
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500364 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500365 # TODO: This is untested.
366 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800367
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500368 return "<X509Name object '%s'>" % (_ffi.string(result_buffer),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800369
370
371 def hash(self):
372 """
373 Return the hash value of this name
374
375 :return: None
376 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500377 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800378
379
380 def der(self):
381 """
382 Return the DER encoding of this name
383
384 :return: A :py:class:`bytes` instance giving the DER encoded form of
385 this name.
386 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500387 result_buffer = _ffi.new('unsigned char**')
388 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800389 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500390 # TODO: This is untested.
391 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800392
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500393 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
394 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800395 return string_result
396
397
398 def get_components(self):
399 """
400 Returns the split-up components of this name.
401
402 :return: List of tuples (name, value).
403 """
404 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500405 for i in range(_lib.X509_NAME_entry_count(self._name)):
406 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800407
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500408 fname = _lib.X509_NAME_ENTRY_get_object(ent)
409 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800410
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500411 nid = _lib.OBJ_obj2nid(fname)
412 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800413
414 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500415 _ffi.string(name),
416 _ffi.string(
417 _lib.ASN1_STRING_data(fval),
418 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800419
420 return result
421X509NameType = X509Name
422
423
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800424class X509Extension(object):
425 def __init__(self, type_name, critical, value, subject=None, issuer=None):
426 """
427 :param typename: The name of the extension to create.
428 :type typename: :py:data:`str`
429
430 :param critical: A flag indicating whether this is a critical extension.
431
432 :param value: The value of the extension.
433 :type value: :py:data:`str`
434
435 :param subject: Optional X509 cert to use as subject.
436 :type subject: :py:class:`X509`
437
438 :param issuer: Optional X509 cert to use as issuer.
439 :type issuer: :py:class:`X509`
440
441 :return: The X509Extension object
442 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500443 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800444
445 # A context is necessary for any extension which uses the r2i conversion
446 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
447 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500448 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800449
450 # We have no configuration database - but perhaps we should (some
451 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500452 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800453
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800454 # Initialize the subject and issuer, if appropriate. ctx is a local,
455 # and as far as I can tell none of the X509V3_* APIs invoked here steal
456 # any references, so no need to mess with reference counts or duplicates.
457 if issuer is not None:
458 if not isinstance(issuer, X509):
459 raise TypeError("issuer must be an X509 instance")
460 ctx.issuer_cert = issuer._x509
461 if subject is not None:
462 if not isinstance(subject, X509):
463 raise TypeError("subject must be an X509 instance")
464 ctx.subject_cert = subject._x509
465
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800466 if critical:
467 # There are other OpenSSL APIs which would let us pass in critical
468 # separately, but they're harder to use, and since value is already
469 # a pile of crappy junk smuggling a ton of utterly important
470 # structured data, what's the point of trying to avoid nasty stuff
471 # with strings? (However, X509V3_EXT_i2d in particular seems like it
472 # would be a better API to invoke. I do not know where to get the
473 # ext_struc it desires for its last parameter, though.)
474 value = "critical," + value
475
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500476 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
477 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800478 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500479 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800480
481
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400482 @property
483 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500484 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400485
486 _prefixes = {
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500487 _lib.GEN_EMAIL: b"email",
488 _lib.GEN_DNS: b"DNS",
489 _lib.GEN_URI: b"URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400490 }
491
492 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500493 method = _lib.X509V3_EXT_get(self._extension)
494 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500495 # TODO: This is untested.
496 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400497 payload = self._extension.value.data
498 length = self._extension.value.length
499
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500500 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400501 payloadptr[0] = payload
502
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500503 if method.it != _ffi.NULL:
504 ptr = _lib.ASN1_ITEM_ptr(method.it)
505 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
506 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400507 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500508 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400509 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500510 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400511
512 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500513 for i in range(_lib.sk_GENERAL_NAME_num(names)):
514 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400515 try:
516 label = self._prefixes[name.type]
517 except KeyError:
518 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500519 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400520 parts.append(_bio_to_string(bio))
521 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500522 value = _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:]
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400523 parts.append(label + b":" + value)
524 return b", ".join(parts)
525
526
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800527 def __str__(self):
528 """
529 :return: a nice text representation of the extension
530 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500531 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400532 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800533
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400534 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500535 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800536 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500537 # TODO: This is untested.
538 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800539
540 return _bio_to_string(bio)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800541
542
543 def get_critical(self):
544 """
545 Returns the critical field of the X509Extension
546
547 :return: The critical field.
548 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500549 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800550
551
552 def get_short_name(self):
553 """
554 Returns the short version of the type name of the X509Extension
555
556 :return: The short type name.
557 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500558 obj = _lib.X509_EXTENSION_get_object(self._extension)
559 nid = _lib.OBJ_obj2nid(obj)
560 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800561
562
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800563 def get_data(self):
564 """
565 Returns the data of the X509Extension
566
567 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
568 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500569 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
570 string_result = _ffi.cast('ASN1_STRING*', octet_result)
571 char_result = _lib.ASN1_STRING_data(string_result)
572 result_length = _lib.ASN1_STRING_length(string_result)
573 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800574
575X509ExtensionType = X509Extension
576
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800577
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800578class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800579 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500580 req = _lib.X509_REQ_new()
581 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800582
583
584 def set_pubkey(self, pkey):
585 """
586 Set the public key of the certificate request
587
588 :param pkey: The public key to use
589 :return: None
590 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500591 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800592 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500593 # TODO: This is untested.
594 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800595
596
597 def get_pubkey(self):
598 """
599 Get the public key from the certificate request
600
601 :return: The public key
602 """
603 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500604 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
605 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500606 # TODO: This is untested.
607 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500608 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800609 pkey._only_public = True
610 return pkey
611
612
613 def set_version(self, version):
614 """
615 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
616 request.
617
618 :param version: The version number
619 :return: None
620 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500621 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800622 if not set_result:
623 _raise_current_error()
624
625
626 def get_version(self):
627 """
628 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
629 request.
630
631 :return: an integer giving the value of the version subfield
632 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500633 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800634
635
636 def get_subject(self):
637 """
638 Create an X509Name object for the subject of the certificate request
639
640 :return: An X509Name object
641 """
642 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500643 name._name = _lib.X509_REQ_get_subject_name(self._req)
644 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500645 # TODO: This is untested.
646 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800647
648 # The name is owned by the X509Req structure. As long as the X509Name
649 # Python object is alive, keep the X509Req Python object alive.
650 name._owner = self
651
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800652 return name
653
654
655 def add_extensions(self, extensions):
656 """
657 Add extensions to the request.
658
659 :param extensions: a sequence of X509Extension objects
660 :return: None
661 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500662 stack = _lib.sk_X509_EXTENSION_new_null()
663 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500664 # TODO: This is untested.
665 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800666
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500667 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800668
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800669 for ext in extensions:
670 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800671 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800672
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800673 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500674 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800675
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500676 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800677 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500678 # TODO: This is untested.
679 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800680
681
682 def sign(self, pkey, digest):
683 """
684 Sign the certificate request using the supplied key and digest
685
686 :param pkey: The key to sign with
687 :param digest: The message digest to use
688 :return: None
689 """
690 if pkey._only_public:
691 raise ValueError("Key has only public part")
692
693 if not pkey._initialized:
694 raise ValueError("Key is uninitialized")
695
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500696 digest_obj = _lib.EVP_get_digestbyname(digest)
697 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800698 raise ValueError("No such digest method")
699
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500700 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800701 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500702 # TODO: This is untested.
703 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800704
705
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800706 def verify(self, pkey):
707 """
708 Verifies a certificate request using the supplied public key
709
710 :param key: a public key
711 :return: True if the signature is correct.
712
713 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
714 problem verifying the signature.
715 """
716 if not isinstance(pkey, PKey):
717 raise TypeError("pkey must be a PKey instance")
718
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500719 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800720 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500721 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800722
723 return result
724
725
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800726X509ReqType = X509Req
727
728
729
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800730class X509(object):
731 def __init__(self):
732 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500733 x509 = _lib.X509_new()
734 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800735
736
737 def set_version(self, version):
738 """
739 Set version number of the certificate
740
741 :param version: The version number
742 :type version: :py:class:`int`
743
744 :return: None
745 """
746 if not isinstance(version, int):
747 raise TypeError("version must be an integer")
748
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500749 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800750
751
752 def get_version(self):
753 """
754 Return version number of the certificate
755
756 :return: Version number as a Python integer
757 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500758 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800759
760
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800761 def get_pubkey(self):
762 """
763 Get the public key of the certificate
764
765 :return: The public key
766 """
767 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500768 pkey._pkey = _lib.X509_get_pubkey(self._x509)
769 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800770 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500771 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800772 pkey._only_public = True
773 return pkey
774
775
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800776 def set_pubkey(self, pkey):
777 """
778 Set the public key of the certificate
779
780 :param pkey: The public key
781
782 :return: None
783 """
784 if not isinstance(pkey, PKey):
785 raise TypeError("pkey must be a PKey instance")
786
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500787 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800788 if not set_result:
789 _raise_current_error()
790
791
792 def sign(self, pkey, digest):
793 """
794 Sign the certificate using the supplied key and digest
795
796 :param pkey: The key to sign with
797 :param digest: The message digest to use
798 :return: None
799 """
800 if not isinstance(pkey, PKey):
801 raise TypeError("pkey must be a PKey instance")
802
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800803 if pkey._only_public:
804 raise ValueError("Key only has public part")
805
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800806 if not pkey._initialized:
807 raise ValueError("Key is uninitialized")
808
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500809 evp_md = _lib.EVP_get_digestbyname(digest)
810 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800811 raise ValueError("No such digest method")
812
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500813 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800814 if not sign_result:
815 _raise_current_error()
816
817
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800818 def get_signature_algorithm(self):
819 """
820 Retrieve the signature algorithm used in the certificate
821
822 :return: A byte string giving the name of the signature algorithm used in
823 the certificate.
824 :raise ValueError: If the signature algorithm is undefined.
825 """
826 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500827 nid = _lib.OBJ_obj2nid(alg)
828 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800829 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500830 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800831
832
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800833 def digest(self, digest_name):
834 """
835 Return the digest of the X509 object.
836
837 :param digest_name: The name of the digest algorithm to use.
838 :type digest_name: :py:class:`bytes`
839
840 :return: The digest of the object
841 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500842 digest = _lib.EVP_get_digestbyname(digest_name)
843 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800844 raise ValueError("No such digest method")
845
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500846 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
847 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800848 result_length[0] = len(result_buffer)
849
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500850 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800851 self._x509, digest, result_buffer, result_length)
852
853 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500854 # TODO: This is untested.
855 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800856
857 return ':'.join([
858 ch.encode('hex').upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500859 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800860
861
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800862 def subject_name_hash(self):
863 """
864 Return the hash of the X509 subject.
865
866 :return: The hash of the subject.
867 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500868 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800869
870
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800871 def set_serial_number(self, serial):
872 """
873 Set serial number of the certificate
874
875 :param serial: The serial number
876 :type serial: :py:class:`int`
877
878 :return: None
879 """
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800880 if not isinstance(serial, (int, long)):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800881 raise TypeError("serial must be an integer")
882
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800883 hex_serial = hex(serial)[2:]
884 if not isinstance(hex_serial, bytes):
885 hex_serial = hex_serial.encode('ascii')
886
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500887 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800888
889 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
890 # it. If bignum is still NULL after this call, then the return value is
891 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500892 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800893
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500894 if bignum_serial[0] == _ffi.NULL:
895 set_result = _lib.ASN1_INTEGER_set(
896 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800897 if set_result:
898 # TODO Not tested
899 _raise_current_error()
900 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500901 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
902 _lib.BN_free(bignum_serial[0])
903 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800904 # TODO Not tested
905 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500906 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
907 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800908 if not set_result:
909 # TODO Not tested
910 _raise_current_error()
911
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800912
913 def get_serial_number(self):
914 """
915 Return serial number of the certificate
916
917 :return: Serial number as a Python integer
918 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500919 asn1_serial = _lib.X509_get_serialNumber(self._x509)
920 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800921 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500922 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800923 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500924 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800925 serial = int(hexstring_serial, 16)
926 return serial
927 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500928 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800929 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500930 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800931
932
933 def gmtime_adj_notAfter(self, amount):
934 """
935 Adjust the time stamp for when the certificate stops being valid
936
937 :param amount: The number of seconds by which to adjust the ending
938 validity time.
939 :type amount: :py:class:`int`
940
941 :return: None
942 """
943 if not isinstance(amount, int):
944 raise TypeError("amount must be an integer")
945
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500946 notAfter = _lib.X509_get_notAfter(self._x509)
947 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800948
949
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800950 def gmtime_adj_notBefore(self, amount):
951 """
952 Change the timestamp for when the certificate starts being valid to the current
953 time plus an offset.
954
955 :param amount: The number of seconds by which to adjust the starting validity
956 time.
957 :return: None
958 """
959 if not isinstance(amount, int):
960 raise TypeError("amount must be an integer")
961
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500962 notBefore = _lib.X509_get_notBefore(self._x509)
963 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800964
965
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800966 def has_expired(self):
967 """
968 Check whether the certificate has expired.
969
970 :return: True if the certificate has expired, false otherwise
971 """
972 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500973 notAfter = _lib.X509_get_notAfter(self._x509)
974 return _lib.ASN1_UTCTIME_cmp_time_t(
975 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800976
977
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800978 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800979 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800980
981
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800982 def get_notBefore(self):
983 """
984 Retrieve the time stamp for when the certificate starts being valid
985
986 :return: A string giving the timestamp, in the format::
987
988 YYYYMMDDhhmmssZ
989 YYYYMMDDhhmmss+hhmm
990 YYYYMMDDhhmmss-hhmm
991
992 or None if there is no value set.
993 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500994 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800995
996
997 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800998 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800999
1000
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001001 def set_notBefore(self, when):
1002 """
1003 Set the time stamp for when the certificate starts being valid
1004
1005 :param when: A string giving the timestamp, in the format:
1006
1007 YYYYMMDDhhmmssZ
1008 YYYYMMDDhhmmss+hhmm
1009 YYYYMMDDhhmmss-hhmm
1010 :type when: :py:class:`bytes`
1011
1012 :return: None
1013 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001014 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001015
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001016
1017 def get_notAfter(self):
1018 """
1019 Retrieve the time stamp for when the certificate stops being valid
1020
1021 :return: A string giving the timestamp, in the format::
1022
1023 YYYYMMDDhhmmssZ
1024 YYYYMMDDhhmmss+hhmm
1025 YYYYMMDDhhmmss-hhmm
1026
1027 or None if there is no value set.
1028 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001029 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001030
1031
1032 def set_notAfter(self, when):
1033 """
1034 Set the time stamp for when the certificate stops being valid
1035
1036 :param when: A string giving the timestamp, in the format:
1037
1038 YYYYMMDDhhmmssZ
1039 YYYYMMDDhhmmss+hhmm
1040 YYYYMMDDhhmmss-hhmm
1041 :type when: :py:class:`bytes`
1042
1043 :return: None
1044 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001045 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001046
1047
1048 def _get_name(self, which):
1049 name = X509Name.__new__(X509Name)
1050 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001051 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001052 # TODO: This is untested.
1053 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001054
1055 # The name is owned by the X509 structure. As long as the X509Name
1056 # Python object is alive, keep the X509 Python object alive.
1057 name._owner = self
1058
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001059 return name
1060
1061
1062 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001063 if not isinstance(name, X509Name):
1064 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001065 set_result = which(self._x509, name._name)
1066 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001067 # TODO: This is untested.
1068 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001069
1070
1071 def get_issuer(self):
1072 """
1073 Create an X509Name object for the issuer of the certificate
1074
1075 :return: An X509Name object
1076 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001077 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001078
1079
1080 def set_issuer(self, issuer):
1081 """
1082 Set the issuer of the certificate
1083
1084 :param issuer: The issuer name
1085 :type issuer: :py:class:`X509Name`
1086
1087 :return: None
1088 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001089 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001090
1091
1092 def get_subject(self):
1093 """
1094 Create an X509Name object for the subject of the certificate
1095
1096 :return: An X509Name object
1097 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001098 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001099
1100
1101 def set_subject(self, subject):
1102 """
1103 Set the subject of the certificate
1104
1105 :param subject: The subject name
1106 :type subject: :py:class:`X509Name`
1107 :return: None
1108 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001109 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001110
1111
1112 def get_extension_count(self):
1113 """
1114 Get the number of extensions on the certificate.
1115
1116 :return: The number of extensions as an integer.
1117 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001118 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001119
1120
1121 def add_extensions(self, extensions):
1122 """
1123 Add extensions to the certificate.
1124
1125 :param extensions: a sequence of X509Extension objects
1126 :return: None
1127 """
1128 for ext in extensions:
1129 if not isinstance(ext, X509Extension):
1130 raise ValueError("One of the elements is not an X509Extension")
1131
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001132 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001133 if not add_result:
1134 _raise_current_error()
1135
1136
1137 def get_extension(self, index):
1138 """
1139 Get a specific extension of the certificate by index.
1140
1141 :param index: The index of the extension to retrieve.
1142 :return: The X509Extension object at the specified index.
1143 """
1144 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001145 ext._extension = _lib.X509_get_ext(self._x509, index)
1146 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001147 raise IndexError("extension index out of bounds")
1148
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001149 extension = _lib.X509_EXTENSION_dup(ext._extension)
1150 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001151 return ext
1152
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001153X509Type = X509
1154
1155
1156
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001157class X509Store(object):
1158 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001159 store = _lib.X509_STORE_new()
1160 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001161
1162
1163 def add_cert(self, cert):
1164 if not isinstance(cert, X509):
1165 raise TypeError()
1166
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001167 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001168 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001169 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001170
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001171
1172X509StoreType = X509Store
1173
1174
1175
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001176def load_certificate(type, buffer):
1177 """
1178 Load a certificate from a buffer
1179
1180 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1181
1182 :param buffer: The buffer the certificate is stored in
1183 :type buffer: :py:class:`bytes`
1184
1185 :return: The X509 object
1186 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001187 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001188
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001189 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001190 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001191 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001192 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001193 else:
1194 raise ValueError(
1195 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001196
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001197 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001198 _raise_current_error()
1199
1200 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001201 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001202 return cert
1203
1204
1205def dump_certificate(type, cert):
1206 """
1207 Dump a certificate to a buffer
1208
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001209 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1210 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001211 :param cert: The certificate to dump
1212 :return: The buffer with the dumped certificate in
1213 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001214 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001215
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001216 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001217 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001218 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001219 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001220 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001221 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001222 else:
1223 raise ValueError(
1224 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1225 "FILETYPE_TEXT")
1226
1227 return _bio_to_string(bio)
1228
1229
1230
1231def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1232 """
1233 Dump a private key to a buffer
1234
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001235 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1236 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001237 :param pkey: The PKey to dump
1238 :param cipher: (optional) if encrypted PEM format, the cipher to
1239 use
1240 :param passphrase: (optional) if encrypted PEM format, this can be either
1241 the passphrase to use, or a callback for providing the
1242 passphrase.
1243 :return: The buffer with the dumped key in
1244 :rtype: :py:data:`str`
1245 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001246 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001247
1248 if cipher is not None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001249 cipher_obj = _lib.EVP_get_cipherbyname(cipher)
1250 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001251 raise ValueError("Invalid cipher name")
1252 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001253 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001254
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001255 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001256 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001257 result_code = _lib.PEM_write_bio_PrivateKey(
1258 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001259 helper.callback, helper.callback_args)
1260 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001261 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001262 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001263 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001264 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1265 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001266 # TODO RSA_free(rsa)?
1267 else:
1268 raise ValueError(
1269 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1270 "FILETYPE_TEXT")
1271
1272 if result_code == 0:
1273 _raise_current_error()
1274
1275 return _bio_to_string(bio)
1276
1277
1278
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001279def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001280 copy = _lib.X509_REVOKED_new()
1281 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001282 # TODO: This is untested.
1283 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001284
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001285 if original.serialNumber != _ffi.NULL:
1286 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001287
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001288 if original.revocationDate != _ffi.NULL:
1289 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001290
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001291 if original.extensions != _ffi.NULL:
1292 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1293 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1294 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1295 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1296 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001297 copy.extensions = extension_stack
1298
1299 copy.sequence = original.sequence
1300 return copy
1301
1302
1303
1304class Revoked(object):
1305 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1306 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1307 # OCSP_crl_reason_str. We use the latter, just like the command line
1308 # program.
1309 _crl_reasons = [
1310 "unspecified",
1311 "keyCompromise",
1312 "CACompromise",
1313 "affiliationChanged",
1314 "superseded",
1315 "cessationOfOperation",
1316 "certificateHold",
1317 # "removeFromCRL",
1318 ]
1319
1320 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001321 revoked = _lib.X509_REVOKED_new()
1322 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001323
1324
1325 def set_serial(self, hex_str):
1326 """
1327 Set the serial number of a revoked Revoked structure
1328
1329 :param hex_str: The new serial number.
1330 :type hex_str: :py:data:`str`
1331 :return: None
1332 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001333 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1334 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001335 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001336 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001337 if not bn_result:
1338 raise ValueError("bad hex string")
1339
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001340 asn1_serial = _ffi.gc(
1341 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1342 _lib.ASN1_INTEGER_free)
1343 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001344
1345
1346 def get_serial(self):
1347 """
1348 Return the serial number of a Revoked structure
1349
1350 :return: The serial number as a string
1351 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001352 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001353
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001354 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001355 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001356 # TODO: This is untested.
1357 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001358
1359 return _bio_to_string(bio)
1360
1361
1362 def _delete_reason(self):
1363 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001364 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1365 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1366 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1367 _lib.X509_EXTENSION_free(ext)
1368 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001369 break
1370
1371
1372 def set_reason(self, reason):
1373 """
1374 Set the reason of a Revoked object.
1375
1376 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1377
1378 :param reason: The reason string.
1379 :type reason: :py:class:`str` or :py:class:`NoneType`
1380 :return: None
1381 """
1382 if reason is None:
1383 self._delete_reason()
1384 elif not isinstance(reason, bytes):
1385 raise TypeError("reason must be None or a byte string")
1386 else:
1387 reason = reason.lower().replace(' ', '')
1388 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1389
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001390 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1391 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001392 # TODO: This is untested.
1393 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001394 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001395
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001396 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1397 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001398 # TODO: This is untested.
1399 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001400
1401 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001402 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1403 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001404
1405 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001406 # TODO: This is untested.
1407 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001408
1409
1410 def get_reason(self):
1411 """
1412 Return the reason of a Revoked object.
1413
1414 :return: The reason as a string
1415 """
1416 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001417 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1418 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1419 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001420 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001421
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001422 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001423 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001424 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001425 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001426 # TODO: This is untested.
1427 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001428
1429 return _bio_to_string(bio)
1430
1431
1432 def all_reasons(self):
1433 """
1434 Return a list of all the supported reason strings.
1435
1436 :return: A list of reason strings.
1437 """
1438 return self._crl_reasons[:]
1439
1440
1441 def set_rev_date(self, when):
1442 """
1443 Set the revocation timestamp
1444
1445 :param when: A string giving the timestamp, in the format:
1446
1447 YYYYMMDDhhmmssZ
1448 YYYYMMDDhhmmss+hhmm
1449 YYYYMMDDhhmmss-hhmm
1450
1451 :return: None
1452 """
1453 return _set_asn1_time(self._revoked.revocationDate, when)
1454
1455
1456 def get_rev_date(self):
1457 """
1458 Retrieve the revocation date
1459
1460 :return: A string giving the timestamp, in the format:
1461
1462 YYYYMMDDhhmmssZ
1463 YYYYMMDDhhmmss+hhmm
1464 YYYYMMDDhhmmss-hhmm
1465 """
1466 return _get_asn1_time(self._revoked.revocationDate)
1467
1468
1469
1470class CRL(object):
1471 def __init__(self):
1472 """
1473 Create a new empty CRL object.
1474 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001475 crl = _lib.X509_CRL_new()
1476 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001477
1478
1479 def get_revoked(self):
1480 """
1481 Return revoked portion of the CRL structure (by value not reference).
1482
1483 :return: A tuple of Revoked objects.
1484 """
1485 results = []
1486 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001487 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1488 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001489 revoked_copy = _X509_REVOKED_dup(revoked)
1490 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001491 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001492 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001493 if results:
1494 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001495
1496
1497 def add_revoked(self, revoked):
1498 """
1499 Add a revoked (by value not reference) to the CRL structure
1500
1501 :param revoked: The new revoked.
1502 :type revoked: :class:`X509`
1503
1504 :return: None
1505 """
1506 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001507 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001508 # TODO: This is untested.
1509 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001510
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001511 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001512 if add_result == 0:
1513 # TODO: This is untested.
1514 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001515
1516
1517 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1518 """
1519 export a CRL as a string
1520
1521 :param cert: Used to sign CRL.
1522 :type cert: :class:`X509`
1523
1524 :param key: Used to sign CRL.
1525 :type key: :class:`PKey`
1526
1527 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1528
1529 :param days: The number of days until the next update of this CRL.
1530 :type days: :py:data:`int`
1531
1532 :return: :py:data:`str`
1533 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001534 if not isinstance(cert, X509):
1535 raise TypeError("cert must be an X509 instance")
1536 if not isinstance(key, PKey):
1537 raise TypeError("key must be a PKey instance")
1538 if not isinstance(type, int):
1539 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001540
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001541 bio = _lib.BIO_new(_lib.BIO_s_mem())
1542 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001543 # TODO: This is untested.
1544 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001545
1546 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001547 sometime = _lib.ASN1_TIME_new()
1548 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001549 # TODO: This is untested.
1550 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001551
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001552 _lib.X509_gmtime_adj(sometime, 0)
1553 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001554
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001555 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1556 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001557
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001558 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001559
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001560 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001561 if not sign_result:
1562 _raise_current_error()
1563
1564 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001565 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001566 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001567 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001568 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001569 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001570 else:
1571 raise ValueError(
1572 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1573
1574 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001575 # TODO: This is untested.
1576 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001577
1578 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001579CRLType = CRL
1580
1581
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001582
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001583class PKCS7(object):
1584 def type_is_signed(self):
1585 """
1586 Check if this NID_pkcs7_signed object
1587
1588 :return: True if the PKCS7 is of type signed
1589 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001590 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001591 return True
1592 return False
1593
1594
1595 def type_is_enveloped(self):
1596 """
1597 Check if this NID_pkcs7_enveloped object
1598
1599 :returns: True if the PKCS7 is of type enveloped
1600 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001601 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001602 return True
1603 return False
1604
1605
1606 def type_is_signedAndEnveloped(self):
1607 """
1608 Check if this NID_pkcs7_signedAndEnveloped object
1609
1610 :returns: True if the PKCS7 is of type signedAndEnveloped
1611 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001612 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001613 return True
1614 return False
1615
1616
1617 def type_is_data(self):
1618 """
1619 Check if this NID_pkcs7_data object
1620
1621 :return: True if the PKCS7 is of type data
1622 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001623 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001624 return True
1625 return False
1626
1627
1628 def get_type_name(self):
1629 """
1630 Returns the type name of the PKCS7 structure
1631
1632 :return: A string with the typename
1633 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001634 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1635 string_type = _lib.OBJ_nid2sn(nid)
1636 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001637
1638PKCS7Type = PKCS7
1639
1640
1641
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001642class PKCS12(object):
1643 def __init__(self):
1644 self._pkey = None
1645 self._cert = None
1646 self._cacerts = None
1647 self._friendlyname = None
1648
1649
1650 def get_certificate(self):
1651 """
1652 Return certificate portion of the PKCS12 structure
1653
1654 :return: X509 object containing the certificate
1655 """
1656 return self._cert
1657
1658
1659 def set_certificate(self, cert):
1660 """
1661 Replace the certificate portion of the PKCS12 structure
1662
1663 :param cert: The new certificate.
1664 :type cert: :py:class:`X509` or :py:data:`None`
1665 :return: None
1666 """
1667 if not isinstance(cert, X509):
1668 raise TypeError("cert must be an X509 instance")
1669 self._cert = cert
1670
1671
1672 def get_privatekey(self):
1673 """
1674 Return private key portion of the PKCS12 structure
1675
1676 :returns: PKey object containing the private key
1677 """
1678 return self._pkey
1679
1680
1681 def set_privatekey(self, pkey):
1682 """
1683 Replace or set the certificate portion of the PKCS12 structure
1684
1685 :param pkey: The new private key.
1686 :type pkey: :py:class:`PKey`
1687 :return: None
1688 """
1689 if not isinstance(pkey, PKey):
1690 raise TypeError("pkey must be a PKey instance")
1691 self._pkey = pkey
1692
1693
1694 def get_ca_certificates(self):
1695 """
1696 Return CA certificates within of the PKCS12 object
1697
1698 :return: A newly created tuple containing the CA certificates in the chain,
1699 if any are present, or None if no CA certificates are present.
1700 """
1701 if self._cacerts is not None:
1702 return tuple(self._cacerts)
1703
1704
1705 def set_ca_certificates(self, cacerts):
1706 """
1707 Replace or set the CA certificates withing the PKCS12 object.
1708
1709 :param cacerts: The new CA certificates.
1710 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1711 :return: None
1712 """
1713 if cacerts is None:
1714 self._cacerts = None
1715 else:
1716 cacerts = list(cacerts)
1717 for cert in cacerts:
1718 if not isinstance(cert, X509):
1719 raise TypeError("iterable must only contain X509 instances")
1720 self._cacerts = cacerts
1721
1722
1723 def set_friendlyname(self, name):
1724 """
1725 Replace or set the certificate portion of the PKCS12 structure
1726
1727 :param name: The new friendly name.
1728 :type name: :py:class:`bytes`
1729 :return: None
1730 """
1731 if name is None:
1732 self._friendlyname = None
1733 elif not isinstance(name, bytes):
1734 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1735 self._friendlyname = name
1736
1737
1738 def get_friendlyname(self):
1739 """
1740 Return friendly name portion of the PKCS12 structure
1741
1742 :returns: String containing the friendlyname
1743 """
1744 return self._friendlyname
1745
1746
1747 def export(self, passphrase=None, iter=2048, maciter=1):
1748 """
1749 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1750
1751 :param passphrase: used to encrypt the PKCS12
1752 :type passphrase: :py:data:`bytes`
1753
1754 :param iter: How many times to repeat the encryption
1755 :type iter: :py:data:`int`
1756
1757 :param maciter: How many times to repeat the MAC
1758 :type maciter: :py:data:`int`
1759
1760 :return: The string containing the PKCS12
1761 """
1762 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001763 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001764 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001765 cacerts = _lib.sk_X509_new_null()
1766 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001767 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001768 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001769
1770 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001771 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001772
1773 friendlyname = self._friendlyname
1774 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001775 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001776
1777 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001778 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001779 else:
1780 pkey = self._pkey._pkey
1781
1782 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001783 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001784 else:
1785 cert = self._cert._x509
1786
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001787 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001788 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001789 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1790 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001791 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001792 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001793 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001794 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001795
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001796 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001797 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001798 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001799
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001800PKCS12Type = PKCS12
1801
1802
1803
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001804class NetscapeSPKI(object):
1805 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001806 spki = _lib.NETSCAPE_SPKI_new()
1807 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001808
1809
1810 def sign(self, pkey, digest):
1811 """
1812 Sign the certificate request using the supplied key and digest
1813
1814 :param pkey: The key to sign with
1815 :param digest: The message digest to use
1816 :return: None
1817 """
1818 if pkey._only_public:
1819 raise ValueError("Key has only public part")
1820
1821 if not pkey._initialized:
1822 raise ValueError("Key is uninitialized")
1823
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001824 digest_obj = _lib.EVP_get_digestbyname(digest)
1825 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001826 raise ValueError("No such digest method")
1827
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001828 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001829 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001830 # TODO: This is untested.
1831 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001832
1833
1834 def verify(self, key):
1835 """
1836 Verifies a certificate request using the supplied public key
1837
1838 :param key: a public key
1839 :return: True if the signature is correct.
1840 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1841 problem verifying the signature.
1842 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001843 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001844 if answer <= 0:
1845 _raise_current_error()
1846 return True
1847
1848
1849 def b64_encode(self):
1850 """
1851 Generate a base64 encoded string from an SPKI
1852
1853 :return: The base64 encoded string
1854 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001855 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
1856 result = _ffi.string(encoded)
1857 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001858 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001859
1860
1861 def get_pubkey(self):
1862 """
1863 Get the public key of the certificate
1864
1865 :return: The public key
1866 """
1867 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001868 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
1869 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001870 # TODO: This is untested.
1871 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001872 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001873 pkey._only_public = True
1874 return pkey
1875
1876
1877 def set_pubkey(self, pkey):
1878 """
1879 Set the public key of the certificate
1880
1881 :param pkey: The public key
1882 :return: None
1883 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001884 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001885 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001886 # TODO: This is untested.
1887 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001888NetscapeSPKIType = NetscapeSPKI
1889
1890
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001891class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001892 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001893 if type != FILETYPE_PEM and passphrase is not None:
1894 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001895 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001896 self._more_args = more_args
1897 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001898 self._problems = []
1899
1900
1901 @property
1902 def callback(self):
1903 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001904 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001905 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001906 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001907 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001908 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001909 else:
1910 raise TypeError("Last argument must be string or callable")
1911
1912
1913 @property
1914 def callback_args(self):
1915 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001916 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001917 elif isinstance(self._passphrase, bytes):
1918 return self._passphrase
1919 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001920 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001921 else:
1922 raise TypeError("Last argument must be string or callable")
1923
1924
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001925 def raise_if_problem(self, exceptionType=Error):
1926 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001927 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001928 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05001929 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001930 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001931 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05001932 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001933
1934
1935 def _read_passphrase(self, buf, size, rwflag, userdata):
1936 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001937 if self._more_args:
1938 result = self._passphrase(size, rwflag, userdata)
1939 else:
1940 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001941 if not isinstance(result, bytes):
1942 raise ValueError("String expected")
1943 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001944 if self._truncate:
1945 result = result[:size]
1946 else:
1947 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001948 for i in range(len(result)):
1949 buf[i] = result[i]
1950 return len(result)
1951 except Exception as e:
1952 self._problems.append(e)
1953 return 0
1954
1955
1956
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001957def load_privatekey(type, buffer, passphrase=None):
1958 """
1959 Load a private key from a buffer
1960
1961 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1962 :param buffer: The buffer the key is stored in
1963 :param passphrase: (optional) if encrypted PEM format, this can be
1964 either the passphrase to use, or a callback for
1965 providing the passphrase.
1966
1967 :return: The PKey object
1968 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001969 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001970
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001971 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001972 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001973 evp_pkey = _lib.PEM_read_bio_PrivateKey(
1974 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001975 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001976 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001977 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001978 else:
1979 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
1980
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001981 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08001982 _raise_current_error()
1983
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001984 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001985 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001986 return pkey
1987
1988
1989
1990def dump_certificate_request(type, req):
1991 """
1992 Dump a certificate request to a buffer
1993
1994 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1995 :param req: The certificate request to dump
1996 :return: The buffer with the dumped certificate request in
1997 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001998 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001999
2000 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002001 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002002 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002003 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002004 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002005 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002006 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002007 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002008
2009 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002010 # TODO: This is untested.
2011 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002012
2013 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002014
2015
2016
2017def load_certificate_request(type, buffer):
2018 """
2019 Load a certificate request from a buffer
2020
2021 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2022 :param buffer: The buffer the certificate request is stored in
2023 :return: The X509Req object
2024 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002025 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002026
2027 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002028 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002029 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002030 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002031 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002032 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002033
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002034 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002035 # TODO: This is untested.
2036 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002037
2038 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002039 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002040 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002041
2042
2043
2044def sign(pkey, data, digest):
2045 """
2046 Sign data with a digest
2047
2048 :param pkey: Pkey to sign with
2049 :param data: data to be signed
2050 :param digest: message digest to use
2051 :return: signature
2052 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002053 digest_obj = _lib.EVP_get_digestbyname(digest)
2054 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002055 raise ValueError("No such digest method")
2056
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002057 md_ctx = _ffi.new("EVP_MD_CTX*")
2058 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002059
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002060 _lib.EVP_SignInit(md_ctx, digest_obj)
2061 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002062
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002063 signature_buffer = _ffi.new("unsigned char[]", 512)
2064 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002065 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002066 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002067 md_ctx, signature_buffer, signature_length, pkey._pkey)
2068
2069 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002070 # TODO: This is untested.
2071 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002072
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002073 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002074
2075
2076
2077def verify(cert, signature, data, digest):
2078 """
2079 Verify a signature
2080
2081 :param cert: signing certificate (X509 object)
2082 :param signature: signature returned by sign function
2083 :param data: data to be verified
2084 :param digest: message digest to use
2085 :return: None if the signature is correct, raise exception otherwise
2086 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002087 digest_obj = _lib.EVP_get_digestbyname(digest)
2088 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002089 raise ValueError("No such digest method")
2090
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002091 pkey = _lib.X509_get_pubkey(cert._x509)
2092 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002093 # TODO: This is untested.
2094 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002095 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002096
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002097 md_ctx = _ffi.new("EVP_MD_CTX*")
2098 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002099
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002100 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2101 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2102 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002103
2104 if verify_result != 1:
2105 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002106
2107
2108
2109def load_crl(type, buffer):
2110 """
2111 Load a certificate revocation list from a buffer
2112
2113 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2114 :param buffer: The buffer the CRL is stored in
2115
2116 :return: The PKey object
2117 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002118 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002119
2120 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002121 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002122 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002123 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002124 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002125 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2126
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002127 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002128 _raise_current_error()
2129
2130 result = CRL.__new__(CRL)
2131 result._crl = crl
2132 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002133
2134
2135
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002136def load_pkcs7_data(type, buffer):
2137 """
2138 Load pkcs7 data from a buffer
2139
2140 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2141 :param buffer: The buffer with the pkcs7 data.
2142 :return: The PKCS7 object
2143 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002144 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002145
2146 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002147 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002148 elif type == FILETYPE_ASN1:
2149 pass
2150 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002151 # TODO: This is untested.
2152 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002153 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2154
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002155 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002156 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002157
2158 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002159 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002160 return pypkcs7
2161
2162
2163
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002164def load_pkcs12(buffer, passphrase):
2165 """
2166 Load a PKCS12 object from a buffer
2167
2168 :param buffer: The buffer the certificate is stored in
2169 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2170 :returns: The PKCS12 object
2171 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002172 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002173
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002174 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2175 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002176 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002177 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002178
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002179 pkey = _ffi.new("EVP_PKEY**")
2180 cert = _ffi.new("X509**")
2181 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002182
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002183 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002184 if not parse_result:
2185 _raise_current_error()
2186
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002187 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002188
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002189 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2190 # queue for no particular reason. This error isn't interesting to anyone
2191 # outside this function. It's not even interesting to us. Get rid of it.
2192 try:
2193 _raise_current_error()
2194 except Error:
2195 pass
2196
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002197 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002198 pykey = None
2199 else:
2200 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002201 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002202
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002203 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002204 pycert = None
2205 friendlyname = None
2206 else:
2207 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002208 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002209
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002210 friendlyname_length = _ffi.new("int*")
2211 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2212 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2213 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002214 friendlyname = None
2215
2216 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002217 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002218 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002219 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002220 pycacerts.append(pycacert)
2221 if not pycacerts:
2222 pycacerts = None
2223
2224 pkcs12 = PKCS12.__new__(PKCS12)
2225 pkcs12._pkey = pykey
2226 pkcs12._cert = pycert
2227 pkcs12._cacerts = pycacerts
2228 pkcs12._friendlyname = friendlyname
2229 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002230
2231
2232def _initialize_openssl_threads(get_ident, Lock):
2233 import _ssl
2234 return
2235
2236 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2237
2238 def locking_function(mode, index, filename, line):
2239 if mode & _lib.CRYPTO_LOCK:
2240 locks[index].acquire()
2241 else:
2242 locks[index].release()
2243
2244 _lib.CRYPTO_set_id_callback(
2245 _ffi.callback("unsigned long (*)(void)", get_ident))
2246
2247 _lib.CRYPTO_set_locking_callback(
2248 _ffi.callback(
2249 "void (*)(int, int, const char*, int)", locking_function))
2250
2251
2252try:
2253 from thread import get_ident
2254 from threading import Lock
2255except ImportError:
2256 pass
2257else:
2258 _initialize_openssl_threads(get_ident, Lock)
2259 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002260
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002261# There are no direct unit tests for this initialization. It is tested
2262# indirectly since it is necessary for functions like dump_privatekey when
2263# using encryption.
2264#
2265# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2266# and some other similar tests may fail without this (though they may not if
2267# the Python runtime has already done some initialization of the underlying
2268# OpenSSL library (and is linked against the same one that cryptography is
2269# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002270_lib.OpenSSL_add_all_algorithms()