blob: 09481a4ee548d2ada6622ef1c7ac304b140e65e6 [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
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800700 def get_extensions(self):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800701 """
702 Get extensions to the request.
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800703
Jean-Paul Calderoneda3e6c62014-03-02 08:06:11 -0500704 :return: A :py:class:`list` of :py:class:`X509Extension` objects.
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800705 """
706 exts = []
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500707 native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
708 for i in range(0, _lib.sk_X509_EXTENSION_num(native_exts_obj)):
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800709 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone9479d732014-03-02 08:04:54 -0500710 ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
Stephen Holsapple7fbdf642014-03-01 20:05:47 -0800711 exts.append(ext)
712 return exts
Stephen Holsappleadfd39d2014-01-28 17:58:31 -0800713
714
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800715 def sign(self, pkey, digest):
716 """
717 Sign the certificate request using the supplied key and digest
718
719 :param pkey: The key to sign with
720 :param digest: The message digest to use
721 :return: None
722 """
723 if pkey._only_public:
724 raise ValueError("Key has only public part")
725
726 if not pkey._initialized:
727 raise ValueError("Key is uninitialized")
728
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500729 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500730 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800731 raise ValueError("No such digest method")
732
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500733 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800734 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500735 # TODO: This is untested.
736 _raise_current_error()
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800737
738
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800739 def verify(self, pkey):
740 """
741 Verifies a certificate request using the supplied public key
742
743 :param key: a public key
744 :return: True if the signature is correct.
745
746 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
747 problem verifying the signature.
748 """
749 if not isinstance(pkey, PKey):
750 raise TypeError("pkey must be a PKey instance")
751
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500752 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800753 if result <= 0:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500754 _raise_current_error()
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800755
756 return result
757
758
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800759X509ReqType = X509Req
760
761
762
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800763class X509(object):
764 def __init__(self):
765 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500766 x509 = _lib.X509_new()
767 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800768
769
770 def set_version(self, version):
771 """
772 Set version number of the certificate
773
774 :param version: The version number
775 :type version: :py:class:`int`
776
777 :return: None
778 """
779 if not isinstance(version, int):
780 raise TypeError("version must be an integer")
781
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500782 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800783
784
785 def get_version(self):
786 """
787 Return version number of the certificate
788
789 :return: Version number as a Python integer
790 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500791 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800792
793
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800794 def get_pubkey(self):
795 """
796 Get the public key of the certificate
797
798 :return: The public key
799 """
800 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500801 pkey._pkey = _lib.X509_get_pubkey(self._x509)
802 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800803 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500804 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800805 pkey._only_public = True
806 return pkey
807
808
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800809 def set_pubkey(self, pkey):
810 """
811 Set the public key of the certificate
812
813 :param pkey: The public key
814
815 :return: None
816 """
817 if not isinstance(pkey, PKey):
818 raise TypeError("pkey must be a PKey instance")
819
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500820 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800821 if not set_result:
822 _raise_current_error()
823
824
825 def sign(self, pkey, digest):
826 """
827 Sign the certificate using the supplied key and digest
828
829 :param pkey: The key to sign with
830 :param digest: The message digest to use
831 :return: None
832 """
833 if not isinstance(pkey, PKey):
834 raise TypeError("pkey must be a PKey instance")
835
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800836 if pkey._only_public:
837 raise ValueError("Key only has public part")
838
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800839 if not pkey._initialized:
840 raise ValueError("Key is uninitialized")
841
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500842 evp_md = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500843 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800844 raise ValueError("No such digest method")
845
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500846 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800847 if not sign_result:
848 _raise_current_error()
849
850
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800851 def get_signature_algorithm(self):
852 """
853 Retrieve the signature algorithm used in the certificate
854
855 :return: A byte string giving the name of the signature algorithm used in
856 the certificate.
857 :raise ValueError: If the signature algorithm is undefined.
858 """
859 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500860 nid = _lib.OBJ_obj2nid(alg)
861 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800862 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500863 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800864
865
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800866 def digest(self, digest_name):
867 """
868 Return the digest of the X509 object.
869
870 :param digest_name: The name of the digest algorithm to use.
871 :type digest_name: :py:class:`bytes`
872
873 :return: The digest of the object
874 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500875 digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500876 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800877 raise ValueError("No such digest method")
878
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500879 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
880 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800881 result_length[0] = len(result_buffer)
882
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500883 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800884 self._x509, digest, result_buffer, result_length)
885
886 if not digest_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -0500887 # TODO: This is untested.
888 _raise_current_error()
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800889
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500890 return b":".join([
891 b16encode(ch).upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500892 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800893
894
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800895 def subject_name_hash(self):
896 """
897 Return the hash of the X509 subject.
898
899 :return: The hash of the subject.
900 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500901 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800902
903
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800904 def set_serial_number(self, serial):
905 """
906 Set serial number of the certificate
907
908 :param serial: The serial number
909 :type serial: :py:class:`int`
910
911 :return: None
912 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -0500913 if not isinstance(serial, _integer_types):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800914 raise TypeError("serial must be an integer")
915
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800916 hex_serial = hex(serial)[2:]
917 if not isinstance(hex_serial, bytes):
918 hex_serial = hex_serial.encode('ascii')
919
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500920 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800921
922 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
923 # it. If bignum is still NULL after this call, then the return value is
924 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500925 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800926
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500927 if bignum_serial[0] == _ffi.NULL:
928 set_result = _lib.ASN1_INTEGER_set(
929 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800930 if set_result:
931 # TODO Not tested
932 _raise_current_error()
933 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500934 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
935 _lib.BN_free(bignum_serial[0])
936 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800937 # TODO Not tested
938 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500939 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
940 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800941 if not set_result:
942 # TODO Not tested
943 _raise_current_error()
944
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800945
946 def get_serial_number(self):
947 """
948 Return serial number of the certificate
949
950 :return: Serial number as a Python integer
951 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500952 asn1_serial = _lib.X509_get_serialNumber(self._x509)
953 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800954 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500955 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800956 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500957 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800958 serial = int(hexstring_serial, 16)
959 return serial
960 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500961 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800962 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500963 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800964
965
966 def gmtime_adj_notAfter(self, amount):
967 """
968 Adjust the time stamp for when the certificate stops being valid
969
970 :param amount: The number of seconds by which to adjust the ending
971 validity time.
972 :type amount: :py:class:`int`
973
974 :return: None
975 """
976 if not isinstance(amount, int):
977 raise TypeError("amount must be an integer")
978
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500979 notAfter = _lib.X509_get_notAfter(self._x509)
980 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800981
982
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800983 def gmtime_adj_notBefore(self, amount):
984 """
985 Change the timestamp for when the certificate starts being valid to the current
986 time plus an offset.
987
988 :param amount: The number of seconds by which to adjust the starting validity
989 time.
990 :return: None
991 """
992 if not isinstance(amount, int):
993 raise TypeError("amount must be an integer")
994
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500995 notBefore = _lib.X509_get_notBefore(self._x509)
996 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800997
998
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800999 def has_expired(self):
1000 """
1001 Check whether the certificate has expired.
1002
1003 :return: True if the certificate has expired, false otherwise
1004 """
1005 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001006 notAfter = _lib.X509_get_notAfter(self._x509)
1007 return _lib.ASN1_UTCTIME_cmp_time_t(
1008 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001009
1010
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001011 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001012 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001013
1014
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001015 def get_notBefore(self):
1016 """
1017 Retrieve the time stamp for when the certificate starts being valid
1018
1019 :return: A string giving the timestamp, in the format::
1020
1021 YYYYMMDDhhmmssZ
1022 YYYYMMDDhhmmss+hhmm
1023 YYYYMMDDhhmmss-hhmm
1024
1025 or None if there is no value set.
1026 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001027 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001028
1029
1030 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001031 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001032
1033
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001034 def set_notBefore(self, when):
1035 """
1036 Set the time stamp for when the certificate starts being valid
1037
1038 :param when: A string giving the timestamp, in the format:
1039
1040 YYYYMMDDhhmmssZ
1041 YYYYMMDDhhmmss+hhmm
1042 YYYYMMDDhhmmss-hhmm
1043 :type when: :py:class:`bytes`
1044
1045 :return: None
1046 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001047 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -08001048
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001049
1050 def get_notAfter(self):
1051 """
1052 Retrieve the time stamp for when the certificate stops being valid
1053
1054 :return: A string giving the timestamp, in the format::
1055
1056 YYYYMMDDhhmmssZ
1057 YYYYMMDDhhmmss+hhmm
1058 YYYYMMDDhhmmss-hhmm
1059
1060 or None if there is no value set.
1061 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001062 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001063
1064
1065 def set_notAfter(self, when):
1066 """
1067 Set the time stamp for when the certificate stops being valid
1068
1069 :param when: A string giving the timestamp, in the format:
1070
1071 YYYYMMDDhhmmssZ
1072 YYYYMMDDhhmmss+hhmm
1073 YYYYMMDDhhmmss-hhmm
1074 :type when: :py:class:`bytes`
1075
1076 :return: None
1077 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001078 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001079
1080
1081 def _get_name(self, which):
1082 name = X509Name.__new__(X509Name)
1083 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001084 if name._name == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001085 # TODO: This is untested.
1086 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001087
1088 # The name is owned by the X509 structure. As long as the X509Name
1089 # Python object is alive, keep the X509 Python object alive.
1090 name._owner = self
1091
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001092 return name
1093
1094
1095 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001096 if not isinstance(name, X509Name):
1097 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001098 set_result = which(self._x509, name._name)
1099 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001100 # TODO: This is untested.
1101 _raise_current_error()
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001102
1103
1104 def get_issuer(self):
1105 """
1106 Create an X509Name object for the issuer of the certificate
1107
1108 :return: An X509Name object
1109 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001110 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001111
1112
1113 def set_issuer(self, issuer):
1114 """
1115 Set the issuer of the certificate
1116
1117 :param issuer: The issuer name
1118 :type issuer: :py:class:`X509Name`
1119
1120 :return: None
1121 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001122 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001123
1124
1125 def get_subject(self):
1126 """
1127 Create an X509Name object for the subject of the certificate
1128
1129 :return: An X509Name object
1130 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001131 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001132
1133
1134 def set_subject(self, subject):
1135 """
1136 Set the subject of the certificate
1137
1138 :param subject: The subject name
1139 :type subject: :py:class:`X509Name`
1140 :return: None
1141 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001142 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001143
1144
1145 def get_extension_count(self):
1146 """
1147 Get the number of extensions on the certificate.
1148
1149 :return: The number of extensions as an integer.
1150 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001151 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001152
1153
1154 def add_extensions(self, extensions):
1155 """
1156 Add extensions to the certificate.
1157
1158 :param extensions: a sequence of X509Extension objects
1159 :return: None
1160 """
1161 for ext in extensions:
1162 if not isinstance(ext, X509Extension):
1163 raise ValueError("One of the elements is not an X509Extension")
1164
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001165 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001166 if not add_result:
1167 _raise_current_error()
1168
1169
1170 def get_extension(self, index):
1171 """
1172 Get a specific extension of the certificate by index.
1173
1174 :param index: The index of the extension to retrieve.
1175 :return: The X509Extension object at the specified index.
1176 """
1177 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001178 ext._extension = _lib.X509_get_ext(self._x509, index)
1179 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001180 raise IndexError("extension index out of bounds")
1181
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001182 extension = _lib.X509_EXTENSION_dup(ext._extension)
1183 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001184 return ext
1185
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001186X509Type = X509
1187
1188
1189
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001190class X509Store(object):
1191 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001192 store = _lib.X509_STORE_new()
1193 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001194
1195
1196 def add_cert(self, cert):
1197 if not isinstance(cert, X509):
1198 raise TypeError()
1199
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001200 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001201 if not result:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001202 _raise_current_error()
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001203
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001204
1205X509StoreType = X509Store
1206
1207
1208
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001209def load_certificate(type, buffer):
1210 """
1211 Load a certificate from a buffer
1212
1213 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1214
1215 :param buffer: The buffer the certificate is stored in
1216 :type buffer: :py:class:`bytes`
1217
1218 :return: The X509 object
1219 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05001220 if isinstance(buffer, _text_type):
1221 buffer = buffer.encode("ascii")
1222
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001223 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001224
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001225 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001226 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001227 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001228 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001229 else:
1230 raise ValueError(
1231 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001232
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001233 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001234 _raise_current_error()
1235
1236 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001237 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001238 return cert
1239
1240
1241def dump_certificate(type, cert):
1242 """
1243 Dump a certificate to a buffer
1244
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001245 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1246 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001247 :param cert: The certificate to dump
1248 :return: The buffer with the dumped certificate in
1249 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001250 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001251
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001252 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001253 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001254 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001255 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001256 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001257 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001258 else:
1259 raise ValueError(
1260 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1261 "FILETYPE_TEXT")
1262
1263 return _bio_to_string(bio)
1264
1265
1266
1267def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1268 """
1269 Dump a private key to a buffer
1270
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001271 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1272 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001273 :param pkey: The PKey to dump
1274 :param cipher: (optional) if encrypted PEM format, the cipher to
1275 use
1276 :param passphrase: (optional) if encrypted PEM format, this can be either
1277 the passphrase to use, or a callback for providing the
1278 passphrase.
1279 :return: The buffer with the dumped key in
1280 :rtype: :py:data:`str`
1281 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001282 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001283
1284 if cipher is not None:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001285 if passphrase is None:
1286 raise TypeError(
1287 "if a value is given for cipher "
1288 "one must also be given for passphrase")
1289 cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001290 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001291 raise ValueError("Invalid cipher name")
1292 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001293 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001294
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001295 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001296 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001297 result_code = _lib.PEM_write_bio_PrivateKey(
1298 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001299 helper.callback, helper.callback_args)
1300 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001301 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001302 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001303 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001304 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1305 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001306 # TODO RSA_free(rsa)?
1307 else:
1308 raise ValueError(
1309 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1310 "FILETYPE_TEXT")
1311
1312 if result_code == 0:
1313 _raise_current_error()
1314
1315 return _bio_to_string(bio)
1316
1317
1318
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001319def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001320 copy = _lib.X509_REVOKED_new()
1321 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001322 # TODO: This is untested.
1323 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001324
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001325 if original.serialNumber != _ffi.NULL:
1326 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001327
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001328 if original.revocationDate != _ffi.NULL:
1329 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001330
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001331 if original.extensions != _ffi.NULL:
1332 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1333 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1334 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1335 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1336 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001337 copy.extensions = extension_stack
1338
1339 copy.sequence = original.sequence
1340 return copy
1341
1342
1343
1344class Revoked(object):
1345 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1346 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1347 # OCSP_crl_reason_str. We use the latter, just like the command line
1348 # program.
1349 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001350 b"unspecified",
1351 b"keyCompromise",
1352 b"CACompromise",
1353 b"affiliationChanged",
1354 b"superseded",
1355 b"cessationOfOperation",
1356 b"certificateHold",
1357 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001358 ]
1359
1360 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001361 revoked = _lib.X509_REVOKED_new()
1362 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001363
1364
1365 def set_serial(self, hex_str):
1366 """
1367 Set the serial number of a revoked Revoked structure
1368
1369 :param hex_str: The new serial number.
1370 :type hex_str: :py:data:`str`
1371 :return: None
1372 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001373 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1374 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001375 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001376 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001377 if not bn_result:
1378 raise ValueError("bad hex string")
1379
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001380 asn1_serial = _ffi.gc(
1381 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1382 _lib.ASN1_INTEGER_free)
1383 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001384
1385
1386 def get_serial(self):
1387 """
1388 Return the serial number of a Revoked structure
1389
1390 :return: The serial number as a string
1391 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001392 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001393
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001394 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001395 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001396 # TODO: This is untested.
1397 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001398
1399 return _bio_to_string(bio)
1400
1401
1402 def _delete_reason(self):
1403 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001404 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1405 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1406 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1407 _lib.X509_EXTENSION_free(ext)
1408 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001409 break
1410
1411
1412 def set_reason(self, reason):
1413 """
1414 Set the reason of a Revoked object.
1415
1416 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1417
1418 :param reason: The reason string.
1419 :type reason: :py:class:`str` or :py:class:`NoneType`
1420 :return: None
1421 """
1422 if reason is None:
1423 self._delete_reason()
1424 elif not isinstance(reason, bytes):
1425 raise TypeError("reason must be None or a byte string")
1426 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001427 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001428 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1429
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001430 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1431 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001432 # TODO: This is untested.
1433 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001434 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001435
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001436 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1437 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001438 # TODO: This is untested.
1439 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001440
1441 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001442 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1443 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001444
1445 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001446 # TODO: This is untested.
1447 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001448
1449
1450 def get_reason(self):
1451 """
1452 Return the reason of a Revoked object.
1453
1454 :return: The reason as a string
1455 """
1456 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001457 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1458 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1459 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001460 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001461
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001462 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001463 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001464 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001465 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001466 # TODO: This is untested.
1467 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001468
1469 return _bio_to_string(bio)
1470
1471
1472 def all_reasons(self):
1473 """
1474 Return a list of all the supported reason strings.
1475
1476 :return: A list of reason strings.
1477 """
1478 return self._crl_reasons[:]
1479
1480
1481 def set_rev_date(self, when):
1482 """
1483 Set the revocation timestamp
1484
1485 :param when: A string giving the timestamp, in the format:
1486
1487 YYYYMMDDhhmmssZ
1488 YYYYMMDDhhmmss+hhmm
1489 YYYYMMDDhhmmss-hhmm
1490
1491 :return: None
1492 """
1493 return _set_asn1_time(self._revoked.revocationDate, when)
1494
1495
1496 def get_rev_date(self):
1497 """
1498 Retrieve the revocation date
1499
1500 :return: A string giving the timestamp, in the format:
1501
1502 YYYYMMDDhhmmssZ
1503 YYYYMMDDhhmmss+hhmm
1504 YYYYMMDDhhmmss-hhmm
1505 """
1506 return _get_asn1_time(self._revoked.revocationDate)
1507
1508
1509
1510class CRL(object):
1511 def __init__(self):
1512 """
1513 Create a new empty CRL object.
1514 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001515 crl = _lib.X509_CRL_new()
1516 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001517
1518
1519 def get_revoked(self):
1520 """
1521 Return revoked portion of the CRL structure (by value not reference).
1522
1523 :return: A tuple of Revoked objects.
1524 """
1525 results = []
1526 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001527 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1528 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001529 revoked_copy = _X509_REVOKED_dup(revoked)
1530 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001531 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001532 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001533 if results:
1534 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001535
1536
1537 def add_revoked(self, revoked):
1538 """
1539 Add a revoked (by value not reference) to the CRL structure
1540
1541 :param revoked: The new revoked.
1542 :type revoked: :class:`X509`
1543
1544 :return: None
1545 """
1546 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001547 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001548 # TODO: This is untested.
1549 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001550
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001551 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001552 if add_result == 0:
1553 # TODO: This is untested.
1554 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001555
1556
1557 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1558 """
1559 export a CRL as a string
1560
1561 :param cert: Used to sign CRL.
1562 :type cert: :class:`X509`
1563
1564 :param key: Used to sign CRL.
1565 :type key: :class:`PKey`
1566
1567 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1568
1569 :param days: The number of days until the next update of this CRL.
1570 :type days: :py:data:`int`
1571
1572 :return: :py:data:`str`
1573 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001574 if not isinstance(cert, X509):
1575 raise TypeError("cert must be an X509 instance")
1576 if not isinstance(key, PKey):
1577 raise TypeError("key must be a PKey instance")
1578 if not isinstance(type, int):
1579 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001580
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001581 bio = _lib.BIO_new(_lib.BIO_s_mem())
1582 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001583 # TODO: This is untested.
1584 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001585
1586 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001587 sometime = _lib.ASN1_TIME_new()
1588 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001589 # TODO: This is untested.
1590 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001591
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001592 _lib.X509_gmtime_adj(sometime, 0)
1593 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001594
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001595 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1596 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001597
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001598 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001599
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001600 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001601 if not sign_result:
1602 _raise_current_error()
1603
1604 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001605 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001606 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001607 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001608 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001609 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001610 else:
1611 raise ValueError(
1612 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1613
1614 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001615 # TODO: This is untested.
1616 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001617
1618 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001619CRLType = CRL
1620
1621
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001622
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001623class PKCS7(object):
1624 def type_is_signed(self):
1625 """
1626 Check if this NID_pkcs7_signed object
1627
1628 :return: True if the PKCS7 is of type signed
1629 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001630 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001631 return True
1632 return False
1633
1634
1635 def type_is_enveloped(self):
1636 """
1637 Check if this NID_pkcs7_enveloped object
1638
1639 :returns: True if the PKCS7 is of type enveloped
1640 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001641 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001642 return True
1643 return False
1644
1645
1646 def type_is_signedAndEnveloped(self):
1647 """
1648 Check if this NID_pkcs7_signedAndEnveloped object
1649
1650 :returns: True if the PKCS7 is of type signedAndEnveloped
1651 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001652 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001653 return True
1654 return False
1655
1656
1657 def type_is_data(self):
1658 """
1659 Check if this NID_pkcs7_data object
1660
1661 :return: True if the PKCS7 is of type data
1662 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001663 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001664 return True
1665 return False
1666
1667
1668 def get_type_name(self):
1669 """
1670 Returns the type name of the PKCS7 structure
1671
1672 :return: A string with the typename
1673 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001674 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1675 string_type = _lib.OBJ_nid2sn(nid)
1676 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001677
1678PKCS7Type = PKCS7
1679
1680
1681
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001682class PKCS12(object):
1683 def __init__(self):
1684 self._pkey = None
1685 self._cert = None
1686 self._cacerts = None
1687 self._friendlyname = None
1688
1689
1690 def get_certificate(self):
1691 """
1692 Return certificate portion of the PKCS12 structure
1693
1694 :return: X509 object containing the certificate
1695 """
1696 return self._cert
1697
1698
1699 def set_certificate(self, cert):
1700 """
1701 Replace the certificate portion of the PKCS12 structure
1702
1703 :param cert: The new certificate.
1704 :type cert: :py:class:`X509` or :py:data:`None`
1705 :return: None
1706 """
1707 if not isinstance(cert, X509):
1708 raise TypeError("cert must be an X509 instance")
1709 self._cert = cert
1710
1711
1712 def get_privatekey(self):
1713 """
1714 Return private key portion of the PKCS12 structure
1715
1716 :returns: PKey object containing the private key
1717 """
1718 return self._pkey
1719
1720
1721 def set_privatekey(self, pkey):
1722 """
1723 Replace or set the certificate portion of the PKCS12 structure
1724
1725 :param pkey: The new private key.
1726 :type pkey: :py:class:`PKey`
1727 :return: None
1728 """
1729 if not isinstance(pkey, PKey):
1730 raise TypeError("pkey must be a PKey instance")
1731 self._pkey = pkey
1732
1733
1734 def get_ca_certificates(self):
1735 """
1736 Return CA certificates within of the PKCS12 object
1737
1738 :return: A newly created tuple containing the CA certificates in the chain,
1739 if any are present, or None if no CA certificates are present.
1740 """
1741 if self._cacerts is not None:
1742 return tuple(self._cacerts)
1743
1744
1745 def set_ca_certificates(self, cacerts):
1746 """
1747 Replace or set the CA certificates withing the PKCS12 object.
1748
1749 :param cacerts: The new CA certificates.
1750 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1751 :return: None
1752 """
1753 if cacerts is None:
1754 self._cacerts = None
1755 else:
1756 cacerts = list(cacerts)
1757 for cert in cacerts:
1758 if not isinstance(cert, X509):
1759 raise TypeError("iterable must only contain X509 instances")
1760 self._cacerts = cacerts
1761
1762
1763 def set_friendlyname(self, name):
1764 """
1765 Replace or set the certificate portion of the PKCS12 structure
1766
1767 :param name: The new friendly name.
1768 :type name: :py:class:`bytes`
1769 :return: None
1770 """
1771 if name is None:
1772 self._friendlyname = None
1773 elif not isinstance(name, bytes):
1774 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1775 self._friendlyname = name
1776
1777
1778 def get_friendlyname(self):
1779 """
1780 Return friendly name portion of the PKCS12 structure
1781
1782 :returns: String containing the friendlyname
1783 """
1784 return self._friendlyname
1785
1786
1787 def export(self, passphrase=None, iter=2048, maciter=1):
1788 """
1789 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1790
1791 :param passphrase: used to encrypt the PKCS12
1792 :type passphrase: :py:data:`bytes`
1793
1794 :param iter: How many times to repeat the encryption
1795 :type iter: :py:data:`int`
1796
1797 :param maciter: How many times to repeat the MAC
1798 :type maciter: :py:data:`int`
1799
1800 :return: The string containing the PKCS12
1801 """
1802 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001803 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001804 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001805 cacerts = _lib.sk_X509_new_null()
1806 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001807 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001808 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001809
1810 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001811 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001812
1813 friendlyname = self._friendlyname
1814 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001815 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001816
1817 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001818 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001819 else:
1820 pkey = self._pkey._pkey
1821
1822 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001823 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001824 else:
1825 cert = self._cert._x509
1826
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001827 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001828 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001829 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1830 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001831 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001832 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001833 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001834 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001835
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001836 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001837 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001838 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001839
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001840PKCS12Type = PKCS12
1841
1842
1843
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001844class NetscapeSPKI(object):
1845 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001846 spki = _lib.NETSCAPE_SPKI_new()
1847 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001848
1849
1850 def sign(self, pkey, digest):
1851 """
1852 Sign the certificate request using the supplied key and digest
1853
1854 :param pkey: The key to sign with
1855 :param digest: The message digest to use
1856 :return: None
1857 """
1858 if pkey._only_public:
1859 raise ValueError("Key has only public part")
1860
1861 if not pkey._initialized:
1862 raise ValueError("Key is uninitialized")
1863
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001864 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001865 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001866 raise ValueError("No such digest method")
1867
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001868 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001869 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001870 # TODO: This is untested.
1871 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001872
1873
1874 def verify(self, key):
1875 """
1876 Verifies a certificate request using the supplied public key
1877
1878 :param key: a public key
1879 :return: True if the signature is correct.
1880 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1881 problem verifying the signature.
1882 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001883 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001884 if answer <= 0:
1885 _raise_current_error()
1886 return True
1887
1888
1889 def b64_encode(self):
1890 """
1891 Generate a base64 encoded string from an SPKI
1892
1893 :return: The base64 encoded string
1894 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001895 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
1896 result = _ffi.string(encoded)
1897 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001898 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001899
1900
1901 def get_pubkey(self):
1902 """
1903 Get the public key of the certificate
1904
1905 :return: The public key
1906 """
1907 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001908 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
1909 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001910 # TODO: This is untested.
1911 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001912 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001913 pkey._only_public = True
1914 return pkey
1915
1916
1917 def set_pubkey(self, pkey):
1918 """
1919 Set the public key of the certificate
1920
1921 :param pkey: The public key
1922 :return: None
1923 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001924 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001925 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001926 # TODO: This is untested.
1927 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001928NetscapeSPKIType = NetscapeSPKI
1929
1930
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001931class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001932 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001933 if type != FILETYPE_PEM and passphrase is not None:
1934 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001935 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001936 self._more_args = more_args
1937 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001938 self._problems = []
1939
1940
1941 @property
1942 def callback(self):
1943 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001944 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001945 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001946 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001947 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001948 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001949 else:
1950 raise TypeError("Last argument must be string or callable")
1951
1952
1953 @property
1954 def callback_args(self):
1955 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001956 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001957 elif isinstance(self._passphrase, bytes):
1958 return self._passphrase
1959 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001960 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001961 else:
1962 raise TypeError("Last argument must be string or callable")
1963
1964
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001965 def raise_if_problem(self, exceptionType=Error):
1966 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001967 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001968 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05001969 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001970 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001971 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05001972 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001973
1974
1975 def _read_passphrase(self, buf, size, rwflag, userdata):
1976 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001977 if self._more_args:
1978 result = self._passphrase(size, rwflag, userdata)
1979 else:
1980 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001981 if not isinstance(result, bytes):
1982 raise ValueError("String expected")
1983 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001984 if self._truncate:
1985 result = result[:size]
1986 else:
1987 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001988 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001989 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001990 return len(result)
1991 except Exception as e:
1992 self._problems.append(e)
1993 return 0
1994
1995
1996
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001997def load_privatekey(type, buffer, passphrase=None):
1998 """
1999 Load a private key from a buffer
2000
2001 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2002 :param buffer: The buffer the key is stored in
2003 :param passphrase: (optional) if encrypted PEM format, this can be
2004 either the passphrase to use, or a callback for
2005 providing the passphrase.
2006
2007 :return: The PKey object
2008 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002009 if isinstance(buffer, _text_type):
2010 buffer = buffer.encode("ascii")
2011
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002012 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002013
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002014 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002015 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002016 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2017 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002018 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002019 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002020 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002021 else:
2022 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2023
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002024 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002025 _raise_current_error()
2026
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002027 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002028 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002029 return pkey
2030
2031
2032
2033def dump_certificate_request(type, req):
2034 """
2035 Dump a certificate request to a buffer
2036
2037 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2038 :param req: The certificate request to dump
2039 :return: The buffer with the dumped certificate request in
2040 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002041 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002042
2043 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002044 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002045 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002046 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002047 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002048 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002049 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002050 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002051
2052 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002053 # TODO: This is untested.
2054 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002055
2056 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002057
2058
2059
2060def load_certificate_request(type, buffer):
2061 """
2062 Load a certificate request from a buffer
2063
2064 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2065 :param buffer: The buffer the certificate request is stored in
2066 :return: The X509Req object
2067 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002068 if isinstance(buffer, _text_type):
2069 buffer = buffer.encode("ascii")
2070
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002071 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002072
2073 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002074 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002075 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002076 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002077 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002078 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002079
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002080 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002081 # TODO: This is untested.
2082 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002083
2084 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002085 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002086 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002087
2088
2089
2090def sign(pkey, data, digest):
2091 """
2092 Sign data with a digest
2093
2094 :param pkey: Pkey to sign with
2095 :param data: data to be signed
2096 :param digest: message digest to use
2097 :return: signature
2098 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002099 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002100 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002101 raise ValueError("No such digest method")
2102
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002103 md_ctx = _ffi.new("EVP_MD_CTX*")
2104 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002105
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002106 _lib.EVP_SignInit(md_ctx, digest_obj)
2107 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002108
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002109 signature_buffer = _ffi.new("unsigned char[]", 512)
2110 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002111 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002112 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002113 md_ctx, signature_buffer, signature_length, pkey._pkey)
2114
2115 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002116 # TODO: This is untested.
2117 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002118
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002119 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002120
2121
2122
2123def verify(cert, signature, data, digest):
2124 """
2125 Verify a signature
2126
2127 :param cert: signing certificate (X509 object)
2128 :param signature: signature returned by sign function
2129 :param data: data to be verified
2130 :param digest: message digest to use
2131 :return: None if the signature is correct, raise exception otherwise
2132 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002133 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002134 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002135 raise ValueError("No such digest method")
2136
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002137 pkey = _lib.X509_get_pubkey(cert._x509)
2138 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002139 # TODO: This is untested.
2140 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002141 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002142
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002143 md_ctx = _ffi.new("EVP_MD_CTX*")
2144 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002145
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002146 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2147 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2148 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002149
2150 if verify_result != 1:
2151 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002152
2153
2154
2155def load_crl(type, buffer):
2156 """
2157 Load a certificate revocation list from a buffer
2158
2159 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2160 :param buffer: The buffer the CRL is stored in
2161
2162 :return: The PKey object
2163 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002164 if isinstance(buffer, _text_type):
2165 buffer = buffer.encode("ascii")
2166
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002167 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002168
2169 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002170 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002171 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002172 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002173 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002174 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2175
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002176 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002177 _raise_current_error()
2178
2179 result = CRL.__new__(CRL)
2180 result._crl = crl
2181 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002182
2183
2184
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002185def load_pkcs7_data(type, buffer):
2186 """
2187 Load pkcs7 data from a buffer
2188
2189 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2190 :param buffer: The buffer with the pkcs7 data.
2191 :return: The PKCS7 object
2192 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002193 if isinstance(buffer, _text_type):
2194 buffer = buffer.encode("ascii")
2195
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002196 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002197
2198 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002199 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002200 elif type == FILETYPE_ASN1:
2201 pass
2202 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002203 # TODO: This is untested.
2204 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002205 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2206
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002207 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002208 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002209
2210 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002211 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002212 return pypkcs7
2213
2214
2215
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002216def load_pkcs12(buffer, passphrase):
2217 """
2218 Load a PKCS12 object from a buffer
2219
2220 :param buffer: The buffer the certificate is stored in
2221 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2222 :returns: The PKCS12 object
2223 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002224 if isinstance(buffer, _text_type):
2225 buffer = buffer.encode("ascii")
2226
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002227 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002228
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002229 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2230 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002231 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002232 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002233
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002234 pkey = _ffi.new("EVP_PKEY**")
2235 cert = _ffi.new("X509**")
2236 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002237
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002238 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002239 if not parse_result:
2240 _raise_current_error()
2241
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002242 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002243
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002244 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2245 # queue for no particular reason. This error isn't interesting to anyone
2246 # outside this function. It's not even interesting to us. Get rid of it.
2247 try:
2248 _raise_current_error()
2249 except Error:
2250 pass
2251
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002252 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002253 pykey = None
2254 else:
2255 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002256 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002257
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002258 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002259 pycert = None
2260 friendlyname = None
2261 else:
2262 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002263 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002264
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002265 friendlyname_length = _ffi.new("int*")
2266 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2267 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2268 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002269 friendlyname = None
2270
2271 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002272 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002273 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002274 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002275 pycacerts.append(pycacert)
2276 if not pycacerts:
2277 pycacerts = None
2278
2279 pkcs12 = PKCS12.__new__(PKCS12)
2280 pkcs12._pkey = pykey
2281 pkcs12._cert = pycert
2282 pkcs12._cacerts = pycacerts
2283 pkcs12._friendlyname = friendlyname
2284 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002285
2286
2287def _initialize_openssl_threads(get_ident, Lock):
2288 import _ssl
2289 return
2290
2291 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2292
2293 def locking_function(mode, index, filename, line):
2294 if mode & _lib.CRYPTO_LOCK:
2295 locks[index].acquire()
2296 else:
2297 locks[index].release()
2298
2299 _lib.CRYPTO_set_id_callback(
2300 _ffi.callback("unsigned long (*)(void)", get_ident))
2301
2302 _lib.CRYPTO_set_locking_callback(
2303 _ffi.callback(
2304 "void (*)(int, int, const char*, int)", locking_function))
2305
2306
2307try:
2308 from thread import get_ident
2309 from threading import Lock
2310except ImportError:
2311 pass
2312else:
2313 _initialize_openssl_threads(get_ident, Lock)
2314 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002315
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002316# There are no direct unit tests for this initialization. It is tested
2317# indirectly since it is necessary for functions like dump_privatekey when
2318# using encryption.
2319#
2320# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2321# and some other similar tests may fail without this (though they may not if
2322# the Python runtime has already done some initialization of the underlying
2323# OpenSSL library (and is linked against the same one that cryptography is
2324# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002325_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002326
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002327# This is similar but exercised mainly by exception_from_error_queue. It calls
2328# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2329_lib.SSL_load_error_strings()