blob: d0026bdfd65e25c248d96292890a428b9fc9b5c5 [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002from base64 import b16encode
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05003from functools import partial
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05004from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
5
6from six import (
7 integer_types as _integer_types,
8 text_type as _text_type)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08009
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050010from OpenSSL._util import (
11 ffi as _ffi,
12 lib as _lib,
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050013 exception_from_error_queue as _exception_from_error_queue,
14 byte_string as _byte_string,
15 native as _native)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080016
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050017FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
18FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080019
20# TODO This was an API mistake. OpenSSL has no such constant.
21FILETYPE_TEXT = 2 ** 16 - 1
22
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050023TYPE_RSA = _lib.EVP_PKEY_RSA
24TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080025
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080026
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050027class Error(Exception):
Jean-Paul Calderone511cde02013-12-29 10:31:13 -050028 """
29 An error occurred in an `OpenSSL.crypto` API.
30 """
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050031
32
33_raise_current_error = partial(_exception_from_error_queue, Error)
34
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -050035def _untested_error(where):
36 """
37 An OpenSSL API failed somehow. Additionally, the failure which was
38 encountered isn't one that's exercised by the test suite so future behavior
39 of pyOpenSSL is now somewhat less predictable.
40 """
41 raise RuntimeError("Unknown %s failure" % (where,))
42
43
44
45def _new_mem_buf(buffer=None):
46 """
47 Allocate a new OpenSSL memory BIO.
48
49 Arrange for the garbage collector to clean it up automatically.
50
51 :param buffer: None or some bytes to use to put into the BIO so that they
52 can be read out.
53 """
54 if buffer is None:
55 bio = _lib.BIO_new(_lib.BIO_s_mem())
56 free = _lib.BIO_free
57 else:
58 data = _ffi.new("char[]", buffer)
59 bio = _lib.BIO_new_mem_buf(data, len(buffer))
60 # Keep the memory alive as long as the bio is alive!
61 def free(bio, ref=data):
62 return _lib.BIO_free(bio)
63
64 if bio == _ffi.NULL:
65 # TODO: This is untested.
66 _raise_current_error()
67
68 bio = _ffi.gc(bio, free)
69 return bio
70
71
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050072
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080073def _bio_to_string(bio):
74 """
75 Copy the contents of an OpenSSL BIO object into a Python byte string.
76 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050077 result_buffer = _ffi.new('char**')
78 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
79 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080080
81
82
Jean-Paul Calderone57122982013-02-21 08:47:05 -080083def _set_asn1_time(boundary, when):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -050084 """
85 The the time value of an ASN1 time object.
86
87 @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely
88 castable to that type) which will have its value set.
89 @param when: A string representation of the desired time value.
90
91 @raise TypeError: If C{when} is not a L{bytes} string.
92 @raise ValueError: If C{when} does not represent a time in the required
93 format.
94 @raise RuntimeError: If the time value cannot be set for some other
95 (unspecified) reason.
96 """
Jean-Paul Calderone57122982013-02-21 08:47:05 -080097 if not isinstance(when, bytes):
98 raise TypeError("when must be a byte string")
99
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500100 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
101 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800102 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500103 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
104 _lib.ASN1_STRING_set(dummy, when, len(when))
105 check_result = _lib.ASN1_GENERALIZEDTIME_check(
106 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800107 if not check_result:
108 raise ValueError("Invalid string")
109 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500110 _untested_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800111
112
113
114def _get_asn1_time(timestamp):
Jean-Paul Calderonee728e872013-12-29 10:37:15 -0500115 """
116 Retrieve the time value of an ASN1 time object.
117
118 @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to
119 that type) from which the time value will be retrieved.
120
121 @return: The time value from C{timestamp} as a L{bytes} string in a certain
122 format. Or C{None} if the object contains no time value.
123 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500124 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
125 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800126 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500127 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
128 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800129 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500130 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
131 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
132 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500133 # This may happen:
134 # - if timestamp was not an ASN1_TIME
135 # - if allocating memory for the ASN1_GENERALIZEDTIME failed
136 # - if a copy of the time data from timestamp cannot be made for
137 # the newly allocated ASN1_GENERALIZEDTIME
138 #
139 # These are difficult to test. cffi enforces the ASN1_TIME type.
140 # Memory allocation failures are a pain to trigger
141 # deterministically.
142 _untested_error("ASN1_TIME_to_generalizedtime")
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800143 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500144 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800145 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500146 string_data = _lib.ASN1_STRING_data(string_timestamp)
147 string_result = _ffi.string(string_data)
148 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800149 return string_result
150
151
152
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800153class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800154 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800155 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800156
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800157 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500158 pkey = _lib.EVP_PKEY_new()
159 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800160 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800161
162
163 def generate_key(self, type, bits):
164 """
165 Generate a key of a given type, with a given number of a bits
166
167 :param type: The key type (TYPE_RSA or TYPE_DSA)
168 :param bits: The number of bits
169
170 :return: None
171 """
172 if not isinstance(type, int):
173 raise TypeError("type must be an integer")
174
175 if not isinstance(bits, int):
176 raise TypeError("bits must be an integer")
177
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800178 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500179 exponent = _lib.BN_new()
180 exponent = _ffi.gc(exponent, _lib.BN_free)
181 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800182
183 if type == TYPE_RSA:
184 if bits <= 0:
185 raise ValueError("Invalid number of bits")
186
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500187 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800188
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500189 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500190 if result == 0:
191 # TODO: The test for this case is commented out. Different
192 # builds of OpenSSL appear to have different failure modes that
193 # make it hard to test. Visual inspection of the OpenSSL
194 # source reveals that a return value of 0 signals an error.
195 # Manual testing on a particular build of OpenSSL suggests that
196 # this is probably the appropriate way to handle those errors.
197 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800198
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500199 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800200 if not result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500201 # TODO: It appears as though this can fail if an engine is in
202 # use which does not support RSA.
203 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800204
205 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500206 dsa = _lib.DSA_generate_parameters(
207 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
208 if dsa == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500209 # TODO: This is untested.
210 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500211 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500212 # TODO: This is untested.
213 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500214 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500215 # TODO: This is untested.
216 _raise_current_error()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800217 else:
218 raise Error("No such key type")
219
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800220 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800221
222
223 def check(self):
224 """
225 Check the consistency of an RSA private key.
226
227 :return: True if key is consistent.
228 :raise Error: if the key is inconsistent.
229 :raise TypeError: if the key is of a type which cannot be checked.
230 Only RSA keys can currently be checked.
231 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800232 if self._only_public:
233 raise TypeError("public key only")
234
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500235 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800236 raise TypeError("key type unsupported")
237
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500238 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
239 rsa = _ffi.gc(rsa, _lib.RSA_free)
240 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800241 if result:
242 return True
243 _raise_current_error()
244
245
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800246 def type(self):
247 """
248 Returns the type of the key
249
250 :return: The type of the key.
251 """
252 return self._pkey.type
253
254
255 def bits(self):
256 """
257 Returns the number of bits of the key
258
259 :return: The number of bits of the key.
260 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500261 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800262PKeyType = PKey
263
264
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800265
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800266class X509Name(object):
267 def __init__(self, name):
268 """
269 Create a new X509Name, copying the given X509Name instance.
270
271 :param name: An X509Name object to copy
272 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500273 name = _lib.X509_NAME_dup(name._name)
274 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800275
276
277 def __setattr__(self, name, value):
278 if name.startswith('_'):
279 return super(X509Name, self).__setattr__(name, value)
280
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800281 # Note: we really do not want str subclasses here, so we do not use
282 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800283 if type(name) is not str:
284 raise TypeError("attribute name must be string, not '%.200s'" % (
285 type(value).__name__,))
286
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500287 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500288 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800289 try:
290 _raise_current_error()
291 except Error:
292 pass
293 raise AttributeError("No such attribute")
294
295 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500296 for i in range(_lib.X509_NAME_entry_count(self._name)):
297 ent = _lib.X509_NAME_get_entry(self._name, i)
298 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
299 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800300 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500301 ent = _lib.X509_NAME_delete_entry(self._name, i)
302 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800303 break
304
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500305 if isinstance(value, _text_type):
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800306 value = value.encode('utf-8')
307
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500308 add_result = _lib.X509_NAME_add_entry_by_NID(
309 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800310 if not add_result:
Jean-Paul Calderone5300d6a2013-12-29 16:36:50 -0500311 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800312
313
314 def __getattr__(self, name):
315 """
316 Find attribute. An X509Name object has the following attributes:
317 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
318 organization (alias O), organizationalUnit (alias OU), commonName (alias
319 CN) and more...
320 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500321 nid = _lib.OBJ_txt2nid(_byte_string(name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500322 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800323 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
324 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
325 # push something onto the error queue. If we don't clean that up
326 # now, someone else will bump into it later and be quite confused.
327 # See lp#314814.
328 try:
329 _raise_current_error()
330 except Error:
331 pass
332 return super(X509Name, self).__getattr__(name)
333
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500334 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800335 if entry_index == -1:
336 return None
337
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500338 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
339 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800340
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500341 result_buffer = _ffi.new("unsigned char**")
342 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800343 if data_length < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500344 # TODO: This is untested.
345 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800346
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700347 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500348 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700349 finally:
350 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500351 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800352 return result
353
354
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500355 def _cmp(op):
356 def f(self, other):
357 if not isinstance(other, X509Name):
358 return NotImplemented
359 result = _lib.X509_NAME_cmp(self._name, other._name)
360 return op(result, 0)
361 return f
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800362
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500363 __eq__ = _cmp(__eq__)
364 __ne__ = _cmp(__ne__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800365
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500366 __lt__ = _cmp(__lt__)
367 __le__ = _cmp(__le__)
368
369 __gt__ = _cmp(__gt__)
370 __ge__ = _cmp(__ge__)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800371
372 def __repr__(self):
373 """
374 String representation of an X509Name
375 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500376 result_buffer = _ffi.new("char[]", 512);
377 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800378 self._name, result_buffer, len(result_buffer))
379
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500380 if format_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500381 # TODO: This is untested.
382 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800383
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500384 return "<X509Name object '%s'>" % (
385 _native(_ffi.string(result_buffer)),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800386
387
388 def hash(self):
389 """
390 Return the hash value of this name
391
392 :return: None
393 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500394 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800395
396
397 def der(self):
398 """
399 Return the DER encoding of this name
400
401 :return: A :py:class:`bytes` instance giving the DER encoded form of
402 this name.
403 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500404 result_buffer = _ffi.new('unsigned char**')
405 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800406 if encode_result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500407 # TODO: This is untested.
408 _raise_current_error()
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800409
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500410 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
411 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800412 return string_result
413
414
415 def get_components(self):
416 """
417 Returns the split-up components of this name.
418
419 :return: List of tuples (name, value).
420 """
421 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500422 for i in range(_lib.X509_NAME_entry_count(self._name)):
423 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800424
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500425 fname = _lib.X509_NAME_ENTRY_get_object(ent)
426 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800427
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500428 nid = _lib.OBJ_obj2nid(fname)
429 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800430
431 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500432 _ffi.string(name),
433 _ffi.string(
434 _lib.ASN1_STRING_data(fval),
435 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800436
437 return result
438X509NameType = X509Name
439
440
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800441class X509Extension(object):
442 def __init__(self, type_name, critical, value, subject=None, issuer=None):
443 """
444 :param typename: The name of the extension to create.
445 :type typename: :py:data:`str`
446
447 :param critical: A flag indicating whether this is a critical extension.
448
449 :param value: The value of the extension.
450 :type value: :py:data:`str`
451
452 :param subject: Optional X509 cert to use as subject.
453 :type subject: :py:class:`X509`
454
455 :param issuer: Optional X509 cert to use as issuer.
456 :type issuer: :py:class:`X509`
457
458 :return: The X509Extension object
459 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500460 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800461
462 # A context is necessary for any extension which uses the r2i conversion
463 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
464 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500465 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800466
467 # We have no configuration database - but perhaps we should (some
468 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500469 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800470
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800471 # Initialize the subject and issuer, if appropriate. ctx is a local,
472 # and as far as I can tell none of the X509V3_* APIs invoked here steal
473 # any references, so no need to mess with reference counts or duplicates.
474 if issuer is not None:
475 if not isinstance(issuer, X509):
476 raise TypeError("issuer must be an X509 instance")
477 ctx.issuer_cert = issuer._x509
478 if subject is not None:
479 if not isinstance(subject, X509):
480 raise TypeError("subject must be an X509 instance")
481 ctx.subject_cert = subject._x509
482
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800483 if critical:
484 # There are other OpenSSL APIs which would let us pass in critical
485 # separately, but they're harder to use, and since value is already
486 # a pile of crappy junk smuggling a ton of utterly important
487 # structured data, what's the point of trying to avoid nasty stuff
488 # with strings? (However, X509V3_EXT_i2d in particular seems like it
489 # would be a better API to invoke. I do not know where to get the
490 # ext_struc it desires for its last parameter, though.)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500491 value = b"critical," + value
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800492
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500493 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
494 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800495 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500496 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800497
498
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400499 @property
500 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500501 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400502
503 _prefixes = {
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500504 _lib.GEN_EMAIL: "email",
505 _lib.GEN_DNS: "DNS",
506 _lib.GEN_URI: "URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400507 }
508
509 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500510 method = _lib.X509V3_EXT_get(self._extension)
511 if method == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500512 # TODO: This is untested.
513 _raise_current_error()
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400514 payload = self._extension.value.data
515 length = self._extension.value.length
516
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500517 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400518 payloadptr[0] = payload
519
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500520 if method.it != _ffi.NULL:
521 ptr = _lib.ASN1_ITEM_ptr(method.it)
522 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
523 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400524 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500525 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400526 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500527 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400528
529 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500530 for i in range(_lib.sk_GENERAL_NAME_num(names)):
531 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400532 try:
533 label = self._prefixes[name.type]
534 except KeyError:
535 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500536 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500537 parts.append(_native(_bio_to_string(bio)))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400538 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500539 value = _native(
540 _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
541 parts.append(label + ":" + value)
542 return ", ".join(parts)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400543
544
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800545 def __str__(self):
546 """
547 :return: a nice text representation of the extension
548 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500549 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400550 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800551
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400552 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500553 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800554 if not print_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500555 # TODO: This is untested.
556 _raise_current_error()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800557
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500558 return _native(_bio_to_string(bio))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800559
560
561 def get_critical(self):
562 """
563 Returns the critical field of the X509Extension
564
565 :return: The critical field.
566 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500567 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800568
569
570 def get_short_name(self):
571 """
572 Returns the short version of the type name of the X509Extension
573
574 :return: The short type name.
575 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500576 obj = _lib.X509_EXTENSION_get_object(self._extension)
577 nid = _lib.OBJ_obj2nid(obj)
578 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800579
580
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800581 def get_data(self):
582 """
583 Returns the data of the X509Extension
584
585 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
586 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500587 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
588 string_result = _ffi.cast('ASN1_STRING*', octet_result)
589 char_result = _lib.ASN1_STRING_data(string_result)
590 result_length = _lib.ASN1_STRING_length(string_result)
591 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800592
593X509ExtensionType = X509Extension
594
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800595
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800596class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800597 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500598 req = _lib.X509_REQ_new()
599 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800600
601
602 def set_pubkey(self, pkey):
603 """
604 Set the public key of the certificate request
605
606 :param pkey: The public key to use
607 :return: None
608 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500609 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800610 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500611 # TODO: This is untested.
612 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800613
614
615 def get_pubkey(self):
616 """
617 Get the public key from the certificate request
618
619 :return: The public key
620 """
621 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500622 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
623 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500624 # TODO: This is untested.
625 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500626 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800627 pkey._only_public = True
628 return pkey
629
630
631 def set_version(self, version):
632 """
633 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
634 request.
635
636 :param version: The version number
637 :return: None
638 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500639 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800640 if not set_result:
641 _raise_current_error()
642
643
644 def get_version(self):
645 """
646 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
647 request.
648
649 :return: an integer giving the value of the version subfield
650 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500651 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800652
653
654 def get_subject(self):
655 """
656 Create an X509Name object for the subject of the certificate request
657
658 :return: An X509Name object
659 """
660 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500661 name._name = _lib.X509_REQ_get_subject_name(self._req)
662 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500663 # TODO: This is untested.
664 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800665
666 # The name is owned by the X509Req structure. As long as the X509Name
667 # Python object is alive, keep the X509Req Python object alive.
668 name._owner = self
669
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800670 return name
671
672
673 def add_extensions(self, extensions):
674 """
675 Add extensions to the request.
676
677 :param extensions: a sequence of X509Extension objects
678 :return: None
679 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500680 stack = _lib.sk_X509_EXTENSION_new_null()
681 if stack == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500682 # TODO: This is untested.
683 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800684
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500685 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800686
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800687 for ext in extensions:
688 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800689 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800690
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800691 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500692 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800693
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500694 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800695 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500696 # TODO: This is untested.
697 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800698
699
700 def sign(self, pkey, digest):
701 """
702 Sign the certificate request using the supplied key and digest
703
704 :param pkey: The key to sign with
705 :param digest: The message digest to use
706 :return: None
707 """
708 if pkey._only_public:
709 raise ValueError("Key has only public part")
710
711 if not pkey._initialized:
712 raise ValueError("Key is uninitialized")
713
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500714 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500715 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800716 raise ValueError("No such digest method")
717
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500718 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800719 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500720 # TODO: This is untested.
721 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800722
723
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800724 def verify(self, pkey):
725 """
726 Verifies a certificate request using the supplied public key
727
728 :param key: a public key
729 :return: True if the signature is correct.
730
731 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
732 problem verifying the signature.
733 """
734 if not isinstance(pkey, PKey):
735 raise TypeError("pkey must be a PKey instance")
736
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500737 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800738 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500739 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800740
741 return result
742
743
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800744X509ReqType = X509Req
745
746
747
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800748class X509(object):
749 def __init__(self):
750 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500751 x509 = _lib.X509_new()
752 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800753
754
755 def set_version(self, version):
756 """
757 Set version number of the certificate
758
759 :param version: The version number
760 :type version: :py:class:`int`
761
762 :return: None
763 """
764 if not isinstance(version, int):
765 raise TypeError("version must be an integer")
766
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500767 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800768
769
770 def get_version(self):
771 """
772 Return version number of the certificate
773
774 :return: Version number as a Python integer
775 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500776 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800777
778
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800779 def get_pubkey(self):
780 """
781 Get the public key of the certificate
782
783 :return: The public key
784 """
785 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500786 pkey._pkey = _lib.X509_get_pubkey(self._x509)
787 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800788 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500789 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800790 pkey._only_public = True
791 return pkey
792
793
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800794 def set_pubkey(self, pkey):
795 """
796 Set the public key of the certificate
797
798 :param pkey: The public key
799
800 :return: None
801 """
802 if not isinstance(pkey, PKey):
803 raise TypeError("pkey must be a PKey instance")
804
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500805 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800806 if not set_result:
807 _raise_current_error()
808
809
810 def sign(self, pkey, digest):
811 """
812 Sign the certificate using the supplied key and digest
813
814 :param pkey: The key to sign with
815 :param digest: The message digest to use
816 :return: None
817 """
818 if not isinstance(pkey, PKey):
819 raise TypeError("pkey must be a PKey instance")
820
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800821 if pkey._only_public:
822 raise ValueError("Key only has public part")
823
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800824 if not pkey._initialized:
825 raise ValueError("Key is uninitialized")
826
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500827 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500828 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800829 raise ValueError("No such digest method")
830
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500831 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800832 if not sign_result:
833 _raise_current_error()
834
835
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800836 def get_signature_algorithm(self):
837 """
838 Retrieve the signature algorithm used in the certificate
839
840 :return: A byte string giving the name of the signature algorithm used in
841 the certificate.
842 :raise ValueError: If the signature algorithm is undefined.
843 """
844 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500845 nid = _lib.OBJ_obj2nid(alg)
846 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800847 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500848 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800849
850
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800851 def digest(self, digest_name):
852 """
853 Return the digest of the X509 object.
854
855 :param digest_name: The name of the digest algorithm to use.
856 :type digest_name: :py:class:`bytes`
857
858 :return: The digest of the object
859 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500860 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500861 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800862 raise ValueError("No such digest method")
863
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500864 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
865 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800866 result_length[0] = len(result_buffer)
867
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500868 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800869 self._x509, digest, result_buffer, result_length)
870
871 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500872 # TODO: This is untested.
873 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800874
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500875 return b":".join([
876 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500877 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800878
879
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800880 def subject_name_hash(self):
881 """
882 Return the hash of the X509 subject.
883
884 :return: The hash of the subject.
885 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500886 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800887
888
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800889 def set_serial_number(self, serial):
890 """
891 Set serial number of the certificate
892
893 :param serial: The serial number
894 :type serial: :py:class:`int`
895
896 :return: None
897 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500898 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800899 raise TypeError("serial must be an integer")
900
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800901 hex_serial = hex(serial)[2:]
902 if not isinstance(hex_serial, bytes):
903 hex_serial = hex_serial.encode('ascii')
904
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500905 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800906
907 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
908 # it. If bignum is still NULL after this call, then the return value is
909 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500910 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800911
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500912 if bignum_serial[0] == _ffi.NULL:
913 set_result = _lib.ASN1_INTEGER_set(
914 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800915 if set_result:
916 # TODO Not tested
917 _raise_current_error()
918 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500919 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
920 _lib.BN_free(bignum_serial[0])
921 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800922 # TODO Not tested
923 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500924 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
925 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800926 if not set_result:
927 # TODO Not tested
928 _raise_current_error()
929
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800930
931 def get_serial_number(self):
932 """
933 Return serial number of the certificate
934
935 :return: Serial number as a Python integer
936 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500937 asn1_serial = _lib.X509_get_serialNumber(self._x509)
938 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800939 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500940 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800941 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500942 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800943 serial = int(hexstring_serial, 16)
944 return serial
945 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500946 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800947 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500948 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800949
950
951 def gmtime_adj_notAfter(self, amount):
952 """
953 Adjust the time stamp for when the certificate stops being valid
954
955 :param amount: The number of seconds by which to adjust the ending
956 validity time.
957 :type amount: :py:class:`int`
958
959 :return: None
960 """
961 if not isinstance(amount, int):
962 raise TypeError("amount must be an integer")
963
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500964 notAfter = _lib.X509_get_notAfter(self._x509)
965 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800966
967
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800968 def gmtime_adj_notBefore(self, amount):
969 """
970 Change the timestamp for when the certificate starts being valid to the current
971 time plus an offset.
972
973 :param amount: The number of seconds by which to adjust the starting validity
974 time.
975 :return: None
976 """
977 if not isinstance(amount, int):
978 raise TypeError("amount must be an integer")
979
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500980 notBefore = _lib.X509_get_notBefore(self._x509)
981 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800982
983
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800984 def has_expired(self):
985 """
986 Check whether the certificate has expired.
987
988 :return: True if the certificate has expired, false otherwise
989 """
990 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500991 notAfter = _lib.X509_get_notAfter(self._x509)
992 return _lib.ASN1_UTCTIME_cmp_time_t(
993 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800994
995
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800996 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800997 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800998
999
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001000 def get_notBefore(self):
1001 """
1002 Retrieve the time stamp for when the certificate starts being valid
1003
1004 :return: A string giving the timestamp, in the format::
1005
1006 YYYYMMDDhhmmssZ
1007 YYYYMMDDhhmmss+hhmm
1008 YYYYMMDDhhmmss-hhmm
1009
1010 or None if there is no value set.
1011 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001012 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001013
1014
1015 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001016 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001017
1018
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001019 def set_notBefore(self, when):
1020 """
1021 Set the time stamp for when the certificate starts being valid
1022
1023 :param when: A string giving the timestamp, in the format:
1024
1025 YYYYMMDDhhmmssZ
1026 YYYYMMDDhhmmss+hhmm
1027 YYYYMMDDhhmmss-hhmm
1028 :type when: :py:class:`bytes`
1029
1030 :return: None
1031 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001032 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001033
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001034
1035 def get_notAfter(self):
1036 """
1037 Retrieve the time stamp for when the certificate stops being valid
1038
1039 :return: A string giving the timestamp, in the format::
1040
1041 YYYYMMDDhhmmssZ
1042 YYYYMMDDhhmmss+hhmm
1043 YYYYMMDDhhmmss-hhmm
1044
1045 or None if there is no value set.
1046 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001047 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001048
1049
1050 def set_notAfter(self, when):
1051 """
1052 Set the time stamp for when the certificate stops being valid
1053
1054 :param when: A string giving the timestamp, in the format:
1055
1056 YYYYMMDDhhmmssZ
1057 YYYYMMDDhhmmss+hhmm
1058 YYYYMMDDhhmmss-hhmm
1059 :type when: :py:class:`bytes`
1060
1061 :return: None
1062 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001063 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001064
1065
1066 def _get_name(self, which):
1067 name = X509Name.__new__(X509Name)
1068 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001069 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001070 # TODO: This is untested.
1071 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001072
1073 # The name is owned by the X509 structure. As long as the X509Name
1074 # Python object is alive, keep the X509 Python object alive.
1075 name._owner = self
1076
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001077 return name
1078
1079
1080 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001081 if not isinstance(name, X509Name):
1082 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001083 set_result = which(self._x509, name._name)
1084 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001085 # TODO: This is untested.
1086 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001087
1088
1089 def get_issuer(self):
1090 """
1091 Create an X509Name object for the issuer of the certificate
1092
1093 :return: An X509Name object
1094 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001095 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001096
1097
1098 def set_issuer(self, issuer):
1099 """
1100 Set the issuer of the certificate
1101
1102 :param issuer: The issuer name
1103 :type issuer: :py:class:`X509Name`
1104
1105 :return: None
1106 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001107 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001108
1109
1110 def get_subject(self):
1111 """
1112 Create an X509Name object for the subject of the certificate
1113
1114 :return: An X509Name object
1115 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001116 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001117
1118
1119 def set_subject(self, subject):
1120 """
1121 Set the subject of the certificate
1122
1123 :param subject: The subject name
1124 :type subject: :py:class:`X509Name`
1125 :return: None
1126 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001127 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001128
1129
1130 def get_extension_count(self):
1131 """
1132 Get the number of extensions on the certificate.
1133
1134 :return: The number of extensions as an integer.
1135 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001136 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001137
1138
1139 def add_extensions(self, extensions):
1140 """
1141 Add extensions to the certificate.
1142
1143 :param extensions: a sequence of X509Extension objects
1144 :return: None
1145 """
1146 for ext in extensions:
1147 if not isinstance(ext, X509Extension):
1148 raise ValueError("One of the elements is not an X509Extension")
1149
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001150 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001151 if not add_result:
1152 _raise_current_error()
1153
1154
1155 def get_extension(self, index):
1156 """
1157 Get a specific extension of the certificate by index.
1158
1159 :param index: The index of the extension to retrieve.
1160 :return: The X509Extension object at the specified index.
1161 """
1162 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001163 ext._extension = _lib.X509_get_ext(self._x509, index)
1164 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001165 raise IndexError("extension index out of bounds")
1166
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001167 extension = _lib.X509_EXTENSION_dup(ext._extension)
1168 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001169 return ext
1170
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001171X509Type = X509
1172
1173
1174
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001175class X509Store(object):
1176 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001177 store = _lib.X509_STORE_new()
1178 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001179
1180
1181 def add_cert(self, cert):
1182 if not isinstance(cert, X509):
1183 raise TypeError()
1184
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001185 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001186 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001187 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001188
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001189
1190X509StoreType = X509Store
1191
1192
1193
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001194def load_certificate(type, buffer):
1195 """
1196 Load a certificate from a buffer
1197
1198 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1199
1200 :param buffer: The buffer the certificate is stored in
1201 :type buffer: :py:class:`bytes`
1202
1203 :return: The X509 object
1204 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001205 if isinstance(buffer, _text_type):
1206 buffer = buffer.encode("ascii")
1207
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001208 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001209
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001210 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001211 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001212 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001213 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001214 else:
1215 raise ValueError(
1216 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001217
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001218 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001219 _raise_current_error()
1220
1221 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001222 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001223 return cert
1224
1225
1226def dump_certificate(type, cert):
1227 """
1228 Dump a certificate to a buffer
1229
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001230 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1231 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001232 :param cert: The certificate to dump
1233 :return: The buffer with the dumped certificate in
1234 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001235 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001236
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001237 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001238 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001239 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001240 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001241 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001242 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001243 else:
1244 raise ValueError(
1245 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1246 "FILETYPE_TEXT")
1247
1248 return _bio_to_string(bio)
1249
1250
1251
1252def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1253 """
1254 Dump a private key to a buffer
1255
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001256 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1257 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001258 :param pkey: The PKey to dump
1259 :param cipher: (optional) if encrypted PEM format, the cipher to
1260 use
1261 :param passphrase: (optional) if encrypted PEM format, this can be either
1262 the passphrase to use, or a callback for providing the
1263 passphrase.
1264 :return: The buffer with the dumped key in
1265 :rtype: :py:data:`str`
1266 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001267 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001268
1269 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001270 if passphrase is None:
1271 raise TypeError(
1272 "if a value is given for cipher "
1273 "one must also be given for passphrase")
1274 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001275 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001276 raise ValueError("Invalid cipher name")
1277 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001278 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001279
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001280 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001281 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001282 result_code = _lib.PEM_write_bio_PrivateKey(
1283 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001284 helper.callback, helper.callback_args)
1285 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001286 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001287 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001288 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001289 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1290 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001291 # TODO RSA_free(rsa)?
1292 else:
1293 raise ValueError(
1294 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1295 "FILETYPE_TEXT")
1296
1297 if result_code == 0:
1298 _raise_current_error()
1299
1300 return _bio_to_string(bio)
1301
1302
1303
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001304def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001305 copy = _lib.X509_REVOKED_new()
1306 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001307 # TODO: This is untested.
1308 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001309
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001310 if original.serialNumber != _ffi.NULL:
1311 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001312
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001313 if original.revocationDate != _ffi.NULL:
1314 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001315
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001316 if original.extensions != _ffi.NULL:
1317 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1318 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1319 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1320 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1321 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001322 copy.extensions = extension_stack
1323
1324 copy.sequence = original.sequence
1325 return copy
1326
1327
1328
1329class Revoked(object):
1330 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1331 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1332 # OCSP_crl_reason_str. We use the latter, just like the command line
1333 # program.
1334 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001335 b"unspecified",
1336 b"keyCompromise",
1337 b"CACompromise",
1338 b"affiliationChanged",
1339 b"superseded",
1340 b"cessationOfOperation",
1341 b"certificateHold",
1342 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001343 ]
1344
1345 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001346 revoked = _lib.X509_REVOKED_new()
1347 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001348
1349
1350 def set_serial(self, hex_str):
1351 """
1352 Set the serial number of a revoked Revoked structure
1353
1354 :param hex_str: The new serial number.
1355 :type hex_str: :py:data:`str`
1356 :return: None
1357 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001358 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1359 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001360 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001361 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001362 if not bn_result:
1363 raise ValueError("bad hex string")
1364
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001365 asn1_serial = _ffi.gc(
1366 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1367 _lib.ASN1_INTEGER_free)
1368 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001369
1370
1371 def get_serial(self):
1372 """
1373 Return the serial number of a Revoked structure
1374
1375 :return: The serial number as a string
1376 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001377 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001378
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001379 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001380 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001381 # TODO: This is untested.
1382 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001383
1384 return _bio_to_string(bio)
1385
1386
1387 def _delete_reason(self):
1388 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001389 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1390 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1391 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1392 _lib.X509_EXTENSION_free(ext)
1393 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001394 break
1395
1396
1397 def set_reason(self, reason):
1398 """
1399 Set the reason of a Revoked object.
1400
1401 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1402
1403 :param reason: The reason string.
1404 :type reason: :py:class:`str` or :py:class:`NoneType`
1405 :return: None
1406 """
1407 if reason is None:
1408 self._delete_reason()
1409 elif not isinstance(reason, bytes):
1410 raise TypeError("reason must be None or a byte string")
1411 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001412 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001413 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1414
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001415 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1416 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001417 # TODO: This is untested.
1418 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001419 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001420
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001421 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1422 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001423 # TODO: This is untested.
1424 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001425
1426 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001427 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1428 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001429
1430 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001431 # TODO: This is untested.
1432 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001433
1434
1435 def get_reason(self):
1436 """
1437 Return the reason of a Revoked object.
1438
1439 :return: The reason as a string
1440 """
1441 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001442 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1443 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1444 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001445 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001446
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001447 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001448 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001449 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001450 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001451 # TODO: This is untested.
1452 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001453
1454 return _bio_to_string(bio)
1455
1456
1457 def all_reasons(self):
1458 """
1459 Return a list of all the supported reason strings.
1460
1461 :return: A list of reason strings.
1462 """
1463 return self._crl_reasons[:]
1464
1465
1466 def set_rev_date(self, when):
1467 """
1468 Set the revocation timestamp
1469
1470 :param when: A string giving the timestamp, in the format:
1471
1472 YYYYMMDDhhmmssZ
1473 YYYYMMDDhhmmss+hhmm
1474 YYYYMMDDhhmmss-hhmm
1475
1476 :return: None
1477 """
1478 return _set_asn1_time(self._revoked.revocationDate, when)
1479
1480
1481 def get_rev_date(self):
1482 """
1483 Retrieve the revocation date
1484
1485 :return: A string giving the timestamp, in the format:
1486
1487 YYYYMMDDhhmmssZ
1488 YYYYMMDDhhmmss+hhmm
1489 YYYYMMDDhhmmss-hhmm
1490 """
1491 return _get_asn1_time(self._revoked.revocationDate)
1492
1493
1494
1495class CRL(object):
1496 def __init__(self):
1497 """
1498 Create a new empty CRL object.
1499 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001500 crl = _lib.X509_CRL_new()
1501 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001502
1503
1504 def get_revoked(self):
1505 """
1506 Return revoked portion of the CRL structure (by value not reference).
1507
1508 :return: A tuple of Revoked objects.
1509 """
1510 results = []
1511 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001512 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1513 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001514 revoked_copy = _X509_REVOKED_dup(revoked)
1515 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001516 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001517 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001518 if results:
1519 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001520
1521
1522 def add_revoked(self, revoked):
1523 """
1524 Add a revoked (by value not reference) to the CRL structure
1525
1526 :param revoked: The new revoked.
1527 :type revoked: :class:`X509`
1528
1529 :return: None
1530 """
1531 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001532 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001533 # TODO: This is untested.
1534 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001535
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001536 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001537 if add_result == 0:
1538 # TODO: This is untested.
1539 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001540
1541
1542 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1543 """
1544 export a CRL as a string
1545
1546 :param cert: Used to sign CRL.
1547 :type cert: :class:`X509`
1548
1549 :param key: Used to sign CRL.
1550 :type key: :class:`PKey`
1551
1552 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1553
1554 :param days: The number of days until the next update of this CRL.
1555 :type days: :py:data:`int`
1556
1557 :return: :py:data:`str`
1558 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001559 if not isinstance(cert, X509):
1560 raise TypeError("cert must be an X509 instance")
1561 if not isinstance(key, PKey):
1562 raise TypeError("key must be a PKey instance")
1563 if not isinstance(type, int):
1564 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001565
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001566 bio = _lib.BIO_new(_lib.BIO_s_mem())
1567 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001568 # TODO: This is untested.
1569 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001570
1571 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001572 sometime = _lib.ASN1_TIME_new()
1573 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001574 # TODO: This is untested.
1575 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001576
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001577 _lib.X509_gmtime_adj(sometime, 0)
1578 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001579
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001580 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1581 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001582
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001583 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001584
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001585 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001586 if not sign_result:
1587 _raise_current_error()
1588
1589 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001590 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001591 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001592 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001593 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001594 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001595 else:
1596 raise ValueError(
1597 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1598
1599 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001600 # TODO: This is untested.
1601 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001602
1603 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001604CRLType = CRL
1605
1606
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001607
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001608class PKCS7(object):
1609 def type_is_signed(self):
1610 """
1611 Check if this NID_pkcs7_signed object
1612
1613 :return: True if the PKCS7 is of type signed
1614 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001615 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001616 return True
1617 return False
1618
1619
1620 def type_is_enveloped(self):
1621 """
1622 Check if this NID_pkcs7_enveloped object
1623
1624 :returns: True if the PKCS7 is of type enveloped
1625 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001626 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001627 return True
1628 return False
1629
1630
1631 def type_is_signedAndEnveloped(self):
1632 """
1633 Check if this NID_pkcs7_signedAndEnveloped object
1634
1635 :returns: True if the PKCS7 is of type signedAndEnveloped
1636 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001637 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001638 return True
1639 return False
1640
1641
1642 def type_is_data(self):
1643 """
1644 Check if this NID_pkcs7_data object
1645
1646 :return: True if the PKCS7 is of type data
1647 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001648 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001649 return True
1650 return False
1651
1652
1653 def get_type_name(self):
1654 """
1655 Returns the type name of the PKCS7 structure
1656
1657 :return: A string with the typename
1658 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001659 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1660 string_type = _lib.OBJ_nid2sn(nid)
1661 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001662
1663PKCS7Type = PKCS7
1664
1665
1666
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001667class PKCS12(object):
1668 def __init__(self):
1669 self._pkey = None
1670 self._cert = None
1671 self._cacerts = None
1672 self._friendlyname = None
1673
1674
1675 def get_certificate(self):
1676 """
1677 Return certificate portion of the PKCS12 structure
1678
1679 :return: X509 object containing the certificate
1680 """
1681 return self._cert
1682
1683
1684 def set_certificate(self, cert):
1685 """
1686 Replace the certificate portion of the PKCS12 structure
1687
1688 :param cert: The new certificate.
1689 :type cert: :py:class:`X509` or :py:data:`None`
1690 :return: None
1691 """
1692 if not isinstance(cert, X509):
1693 raise TypeError("cert must be an X509 instance")
1694 self._cert = cert
1695
1696
1697 def get_privatekey(self):
1698 """
1699 Return private key portion of the PKCS12 structure
1700
1701 :returns: PKey object containing the private key
1702 """
1703 return self._pkey
1704
1705
1706 def set_privatekey(self, pkey):
1707 """
1708 Replace or set the certificate portion of the PKCS12 structure
1709
1710 :param pkey: The new private key.
1711 :type pkey: :py:class:`PKey`
1712 :return: None
1713 """
1714 if not isinstance(pkey, PKey):
1715 raise TypeError("pkey must be a PKey instance")
1716 self._pkey = pkey
1717
1718
1719 def get_ca_certificates(self):
1720 """
1721 Return CA certificates within of the PKCS12 object
1722
1723 :return: A newly created tuple containing the CA certificates in the chain,
1724 if any are present, or None if no CA certificates are present.
1725 """
1726 if self._cacerts is not None:
1727 return tuple(self._cacerts)
1728
1729
1730 def set_ca_certificates(self, cacerts):
1731 """
1732 Replace or set the CA certificates withing the PKCS12 object.
1733
1734 :param cacerts: The new CA certificates.
1735 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1736 :return: None
1737 """
1738 if cacerts is None:
1739 self._cacerts = None
1740 else:
1741 cacerts = list(cacerts)
1742 for cert in cacerts:
1743 if not isinstance(cert, X509):
1744 raise TypeError("iterable must only contain X509 instances")
1745 self._cacerts = cacerts
1746
1747
1748 def set_friendlyname(self, name):
1749 """
1750 Replace or set the certificate portion of the PKCS12 structure
1751
1752 :param name: The new friendly name.
1753 :type name: :py:class:`bytes`
1754 :return: None
1755 """
1756 if name is None:
1757 self._friendlyname = None
1758 elif not isinstance(name, bytes):
1759 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1760 self._friendlyname = name
1761
1762
1763 def get_friendlyname(self):
1764 """
1765 Return friendly name portion of the PKCS12 structure
1766
1767 :returns: String containing the friendlyname
1768 """
1769 return self._friendlyname
1770
1771
1772 def export(self, passphrase=None, iter=2048, maciter=1):
1773 """
1774 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1775
1776 :param passphrase: used to encrypt the PKCS12
1777 :type passphrase: :py:data:`bytes`
1778
1779 :param iter: How many times to repeat the encryption
1780 :type iter: :py:data:`int`
1781
1782 :param maciter: How many times to repeat the MAC
1783 :type maciter: :py:data:`int`
1784
1785 :return: The string containing the PKCS12
1786 """
1787 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001788 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001789 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001790 cacerts = _lib.sk_X509_new_null()
1791 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001792 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001793 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001794
1795 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001796 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001797
1798 friendlyname = self._friendlyname
1799 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001800 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001801
1802 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001803 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001804 else:
1805 pkey = self._pkey._pkey
1806
1807 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001808 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001809 else:
1810 cert = self._cert._x509
1811
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001812 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001813 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001814 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1815 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001816 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001817 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001818 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001819 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001820
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001821 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001822 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001823 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001824
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001825PKCS12Type = PKCS12
1826
1827
1828
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001829class NetscapeSPKI(object):
1830 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001831 spki = _lib.NETSCAPE_SPKI_new()
1832 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001833
1834
1835 def sign(self, pkey, digest):
1836 """
1837 Sign the certificate request using the supplied key and digest
1838
1839 :param pkey: The key to sign with
1840 :param digest: The message digest to use
1841 :return: None
1842 """
1843 if pkey._only_public:
1844 raise ValueError("Key has only public part")
1845
1846 if not pkey._initialized:
1847 raise ValueError("Key is uninitialized")
1848
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001849 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001850 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001851 raise ValueError("No such digest method")
1852
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001853 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001854 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001855 # TODO: This is untested.
1856 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001857
1858
1859 def verify(self, key):
1860 """
1861 Verifies a certificate request using the supplied public key
1862
1863 :param key: a public key
1864 :return: True if the signature is correct.
1865 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1866 problem verifying the signature.
1867 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001868 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001869 if answer <= 0:
1870 _raise_current_error()
1871 return True
1872
1873
1874 def b64_encode(self):
1875 """
1876 Generate a base64 encoded string from an SPKI
1877
1878 :return: The base64 encoded string
1879 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001880 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
1881 result = _ffi.string(encoded)
1882 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001883 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001884
1885
1886 def get_pubkey(self):
1887 """
1888 Get the public key of the certificate
1889
1890 :return: The public key
1891 """
1892 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001893 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
1894 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001895 # TODO: This is untested.
1896 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001897 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001898 pkey._only_public = True
1899 return pkey
1900
1901
1902 def set_pubkey(self, pkey):
1903 """
1904 Set the public key of the certificate
1905
1906 :param pkey: The public key
1907 :return: None
1908 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001909 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001910 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001911 # TODO: This is untested.
1912 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001913NetscapeSPKIType = NetscapeSPKI
1914
1915
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001916class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001917 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001918 if type != FILETYPE_PEM and passphrase is not None:
1919 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001920 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001921 self._more_args = more_args
1922 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001923 self._problems = []
1924
1925
1926 @property
1927 def callback(self):
1928 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001929 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001930 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001931 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001932 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001933 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001934 else:
1935 raise TypeError("Last argument must be string or callable")
1936
1937
1938 @property
1939 def callback_args(self):
1940 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001941 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001942 elif isinstance(self._passphrase, bytes):
1943 return self._passphrase
1944 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001945 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001946 else:
1947 raise TypeError("Last argument must be string or callable")
1948
1949
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001950 def raise_if_problem(self, exceptionType=Error):
1951 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001952 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001953 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05001954 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001955 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001956 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05001957 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001958
1959
1960 def _read_passphrase(self, buf, size, rwflag, userdata):
1961 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001962 if self._more_args:
1963 result = self._passphrase(size, rwflag, userdata)
1964 else:
1965 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001966 if not isinstance(result, bytes):
1967 raise ValueError("String expected")
1968 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001969 if self._truncate:
1970 result = result[:size]
1971 else:
1972 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001973 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001974 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001975 return len(result)
1976 except Exception as e:
1977 self._problems.append(e)
1978 return 0
1979
1980
1981
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001982def load_privatekey(type, buffer, passphrase=None):
1983 """
1984 Load a private key from a buffer
1985
1986 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1987 :param buffer: The buffer the key is stored in
1988 :param passphrase: (optional) if encrypted PEM format, this can be
1989 either the passphrase to use, or a callback for
1990 providing the passphrase.
1991
1992 :return: The PKey object
1993 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001994 if isinstance(buffer, _text_type):
1995 buffer = buffer.encode("ascii")
1996
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001997 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001998
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001999 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002000 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002001 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2002 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002003 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002004 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002005 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002006 else:
2007 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2008
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002009 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002010 _raise_current_error()
2011
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002012 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002013 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002014 return pkey
2015
2016
2017
2018def dump_certificate_request(type, req):
2019 """
2020 Dump a certificate request to a buffer
2021
2022 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2023 :param req: The certificate request to dump
2024 :return: The buffer with the dumped certificate request in
2025 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002026 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002027
2028 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002029 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002030 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002031 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002032 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002033 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002034 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002035 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002036
2037 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002038 # TODO: This is untested.
2039 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002040
2041 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002042
2043
2044
2045def load_certificate_request(type, buffer):
2046 """
2047 Load a certificate request from a buffer
2048
2049 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2050 :param buffer: The buffer the certificate request is stored in
2051 :return: The X509Req object
2052 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002053 if isinstance(buffer, _text_type):
2054 buffer = buffer.encode("ascii")
2055
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002056 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002057
2058 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002059 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002060 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002061 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002062 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002063 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002064
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002065 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002066 # TODO: This is untested.
2067 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002068
2069 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002070 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002071 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002072
2073
2074
2075def sign(pkey, data, digest):
2076 """
2077 Sign data with a digest
2078
2079 :param pkey: Pkey to sign with
2080 :param data: data to be signed
2081 :param digest: message digest to use
2082 :return: signature
2083 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002084 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002085 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002086 raise ValueError("No such digest method")
2087
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002088 md_ctx = _ffi.new("EVP_MD_CTX*")
2089 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002090
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002091 _lib.EVP_SignInit(md_ctx, digest_obj)
2092 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002093
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002094 signature_buffer = _ffi.new("unsigned char[]", 512)
2095 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002096 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002097 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002098 md_ctx, signature_buffer, signature_length, pkey._pkey)
2099
2100 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002101 # TODO: This is untested.
2102 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002103
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002104 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002105
2106
2107
2108def verify(cert, signature, data, digest):
2109 """
2110 Verify a signature
2111
2112 :param cert: signing certificate (X509 object)
2113 :param signature: signature returned by sign function
2114 :param data: data to be verified
2115 :param digest: message digest to use
2116 :return: None if the signature is correct, raise exception otherwise
2117 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002118 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002119 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002120 raise ValueError("No such digest method")
2121
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002122 pkey = _lib.X509_get_pubkey(cert._x509)
2123 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002124 # TODO: This is untested.
2125 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002126 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002127
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002128 md_ctx = _ffi.new("EVP_MD_CTX*")
2129 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002130
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002131 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2132 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2133 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002134
2135 if verify_result != 1:
2136 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002137
2138
2139
2140def load_crl(type, buffer):
2141 """
2142 Load a certificate revocation list from a buffer
2143
2144 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2145 :param buffer: The buffer the CRL is stored in
2146
2147 :return: The PKey object
2148 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002149 if isinstance(buffer, _text_type):
2150 buffer = buffer.encode("ascii")
2151
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002152 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002153
2154 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002155 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002156 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002157 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002158 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002159 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2160
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002161 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002162 _raise_current_error()
2163
2164 result = CRL.__new__(CRL)
2165 result._crl = crl
2166 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002167
2168
2169
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002170def load_pkcs7_data(type, buffer):
2171 """
2172 Load pkcs7 data from a buffer
2173
2174 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2175 :param buffer: The buffer with the pkcs7 data.
2176 :return: The PKCS7 object
2177 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002178 if isinstance(buffer, _text_type):
2179 buffer = buffer.encode("ascii")
2180
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002181 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002182
2183 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002184 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002185 elif type == FILETYPE_ASN1:
2186 pass
2187 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002188 # TODO: This is untested.
2189 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002190 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2191
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002192 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002193 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002194
2195 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002196 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002197 return pypkcs7
2198
2199
2200
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002201def load_pkcs12(buffer, passphrase):
2202 """
2203 Load a PKCS12 object from a buffer
2204
2205 :param buffer: The buffer the certificate is stored in
2206 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2207 :returns: The PKCS12 object
2208 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002209 if isinstance(buffer, _text_type):
2210 buffer = buffer.encode("ascii")
2211
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002212 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002213
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002214 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2215 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002216 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002217 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002218
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002219 pkey = _ffi.new("EVP_PKEY**")
2220 cert = _ffi.new("X509**")
2221 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002222
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002223 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002224 if not parse_result:
2225 _raise_current_error()
2226
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002227 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002228
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002229 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2230 # queue for no particular reason. This error isn't interesting to anyone
2231 # outside this function. It's not even interesting to us. Get rid of it.
2232 try:
2233 _raise_current_error()
2234 except Error:
2235 pass
2236
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002237 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002238 pykey = None
2239 else:
2240 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002241 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002242
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002243 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002244 pycert = None
2245 friendlyname = None
2246 else:
2247 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002248 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002249
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002250 friendlyname_length = _ffi.new("int*")
2251 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2252 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2253 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002254 friendlyname = None
2255
2256 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002257 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002258 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002259 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002260 pycacerts.append(pycacert)
2261 if not pycacerts:
2262 pycacerts = None
2263
2264 pkcs12 = PKCS12.__new__(PKCS12)
2265 pkcs12._pkey = pykey
2266 pkcs12._cert = pycert
2267 pkcs12._cacerts = pycacerts
2268 pkcs12._friendlyname = friendlyname
2269 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002270
2271
2272def _initialize_openssl_threads(get_ident, Lock):
2273 import _ssl
2274 return
2275
2276 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2277
2278 def locking_function(mode, index, filename, line):
2279 if mode & _lib.CRYPTO_LOCK:
2280 locks[index].acquire()
2281 else:
2282 locks[index].release()
2283
2284 _lib.CRYPTO_set_id_callback(
2285 _ffi.callback("unsigned long (*)(void)", get_ident))
2286
2287 _lib.CRYPTO_set_locking_callback(
2288 _ffi.callback(
2289 "void (*)(int, int, const char*, int)", locking_function))
2290
2291
2292try:
2293 from thread import get_ident
2294 from threading import Lock
2295except ImportError:
2296 pass
2297else:
2298 _initialize_openssl_threads(get_ident, Lock)
2299 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002300
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002301# There are no direct unit tests for this initialization. It is tested
2302# indirectly since it is necessary for functions like dump_privatekey when
2303# using encryption.
2304#
2305# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2306# and some other similar tests may fail without this (though they may not if
2307# the Python runtime has already done some initialization of the underlying
2308# OpenSSL library (and is linked against the same one that cryptography is
2309# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002310_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002311
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002312# This is similar but exercised mainly by exception_from_error_queue. It calls
2313# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2314_lib.SSL_load_error_strings()