blob: 4d0867e799e5e137632af86486f6e2ddb97f99c0 [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)
Jean-Paul Calderoneb7a79b42014-03-02 08:06:47 -0500708 for i in range(_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:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001326 _lib.ASN1_INTEGER_free(copy.serialNumber)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001327 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001328
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001329 if original.revocationDate != _ffi.NULL:
Jonathan Giannuzzib5b93222014-03-20 15:54:29 +01001330 _lib.ASN1_TIME_free(copy.revocationDate)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001331 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001332
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001333 if original.extensions != _ffi.NULL:
1334 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1335 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1336 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1337 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1338 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001339 copy.extensions = extension_stack
1340
1341 copy.sequence = original.sequence
1342 return copy
1343
1344
1345
1346class Revoked(object):
1347 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1348 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1349 # OCSP_crl_reason_str. We use the latter, just like the command line
1350 # program.
1351 _crl_reasons = [
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001352 b"unspecified",
1353 b"keyCompromise",
1354 b"CACompromise",
1355 b"affiliationChanged",
1356 b"superseded",
1357 b"cessationOfOperation",
1358 b"certificateHold",
1359 # b"removeFromCRL",
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001360 ]
1361
1362 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001363 revoked = _lib.X509_REVOKED_new()
1364 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001365
1366
1367 def set_serial(self, hex_str):
1368 """
1369 Set the serial number of a revoked Revoked structure
1370
1371 :param hex_str: The new serial number.
1372 :type hex_str: :py:data:`str`
1373 :return: None
1374 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001375 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1376 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001377 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001378 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001379 if not bn_result:
1380 raise ValueError("bad hex string")
1381
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001382 asn1_serial = _ffi.gc(
1383 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1384 _lib.ASN1_INTEGER_free)
1385 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001386
1387
1388 def get_serial(self):
1389 """
1390 Return the serial number of a Revoked structure
1391
1392 :return: The serial number as a string
1393 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001394 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001395
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001396 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001397 if result < 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001398 # TODO: This is untested.
1399 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001400
1401 return _bio_to_string(bio)
1402
1403
1404 def _delete_reason(self):
1405 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001406 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1407 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1408 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1409 _lib.X509_EXTENSION_free(ext)
1410 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001411 break
1412
1413
1414 def set_reason(self, reason):
1415 """
1416 Set the reason of a Revoked object.
1417
1418 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1419
1420 :param reason: The reason string.
1421 :type reason: :py:class:`str` or :py:class:`NoneType`
1422 :return: None
1423 """
1424 if reason is None:
1425 self._delete_reason()
1426 elif not isinstance(reason, bytes):
1427 raise TypeError("reason must be None or a byte string")
1428 else:
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001429 reason = reason.lower().replace(b' ', b'')
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001430 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1431
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001432 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1433 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001434 # TODO: This is untested.
1435 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001436 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001437
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001438 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1439 if set_result == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001440 # TODO: This is untested.
1441 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001442
1443 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001444 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1445 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001446
1447 if not add_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001448 # TODO: This is untested.
1449 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001450
1451
1452 def get_reason(self):
1453 """
1454 Return the reason of a Revoked object.
1455
1456 :return: The reason as a string
1457 """
1458 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001459 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1460 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1461 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001462 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001463
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001464 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001465 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001466 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001467 if print_result == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001468 # TODO: This is untested.
1469 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001470
1471 return _bio_to_string(bio)
1472
1473
1474 def all_reasons(self):
1475 """
1476 Return a list of all the supported reason strings.
1477
1478 :return: A list of reason strings.
1479 """
1480 return self._crl_reasons[:]
1481
1482
1483 def set_rev_date(self, when):
1484 """
1485 Set the revocation timestamp
1486
1487 :param when: A string giving the timestamp, in the format:
1488
1489 YYYYMMDDhhmmssZ
1490 YYYYMMDDhhmmss+hhmm
1491 YYYYMMDDhhmmss-hhmm
1492
1493 :return: None
1494 """
1495 return _set_asn1_time(self._revoked.revocationDate, when)
1496
1497
1498 def get_rev_date(self):
1499 """
1500 Retrieve the revocation date
1501
1502 :return: A string giving the timestamp, in the format:
1503
1504 YYYYMMDDhhmmssZ
1505 YYYYMMDDhhmmss+hhmm
1506 YYYYMMDDhhmmss-hhmm
1507 """
1508 return _get_asn1_time(self._revoked.revocationDate)
1509
1510
1511
1512class CRL(object):
1513 def __init__(self):
1514 """
1515 Create a new empty CRL object.
1516 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001517 crl = _lib.X509_CRL_new()
1518 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001519
1520
1521 def get_revoked(self):
1522 """
1523 Return revoked portion of the CRL structure (by value not reference).
1524
1525 :return: A tuple of Revoked objects.
1526 """
1527 results = []
1528 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001529 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1530 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001531 revoked_copy = _X509_REVOKED_dup(revoked)
1532 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001533 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001534 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001535 if results:
1536 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001537
1538
1539 def add_revoked(self, revoked):
1540 """
1541 Add a revoked (by value not reference) to the CRL structure
1542
1543 :param revoked: The new revoked.
1544 :type revoked: :class:`X509`
1545
1546 :return: None
1547 """
1548 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001549 if copy == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001550 # TODO: This is untested.
1551 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001552
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001553 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001554 if add_result == 0:
1555 # TODO: This is untested.
1556 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001557
1558
1559 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1560 """
1561 export a CRL as a string
1562
1563 :param cert: Used to sign CRL.
1564 :type cert: :class:`X509`
1565
1566 :param key: Used to sign CRL.
1567 :type key: :class:`PKey`
1568
1569 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1570
1571 :param days: The number of days until the next update of this CRL.
1572 :type days: :py:data:`int`
1573
1574 :return: :py:data:`str`
1575 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001576 if not isinstance(cert, X509):
1577 raise TypeError("cert must be an X509 instance")
1578 if not isinstance(key, PKey):
1579 raise TypeError("key must be a PKey instance")
1580 if not isinstance(type, int):
1581 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001582
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001583 bio = _lib.BIO_new(_lib.BIO_s_mem())
1584 if bio == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001585 # TODO: This is untested.
1586 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001587
1588 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001589 sometime = _lib.ASN1_TIME_new()
1590 if sometime == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001591 # TODO: This is untested.
1592 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001593
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001594 _lib.X509_gmtime_adj(sometime, 0)
1595 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001596
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001597 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1598 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001599
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001600 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001601
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001602 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001603 if not sign_result:
1604 _raise_current_error()
1605
1606 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001607 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001608 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001609 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001610 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001611 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001612 else:
1613 raise ValueError(
1614 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1615
1616 if not ret:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001617 # TODO: This is untested.
1618 _raise_current_error()
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001619
1620 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001621CRLType = CRL
1622
1623
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001624
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001625class PKCS7(object):
1626 def type_is_signed(self):
1627 """
1628 Check if this NID_pkcs7_signed object
1629
1630 :return: True if the PKCS7 is of type signed
1631 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001632 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001633 return True
1634 return False
1635
1636
1637 def type_is_enveloped(self):
1638 """
1639 Check if this NID_pkcs7_enveloped object
1640
1641 :returns: True if the PKCS7 is of type enveloped
1642 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001643 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001644 return True
1645 return False
1646
1647
1648 def type_is_signedAndEnveloped(self):
1649 """
1650 Check if this NID_pkcs7_signedAndEnveloped object
1651
1652 :returns: True if the PKCS7 is of type signedAndEnveloped
1653 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001654 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001655 return True
1656 return False
1657
1658
1659 def type_is_data(self):
1660 """
1661 Check if this NID_pkcs7_data object
1662
1663 :return: True if the PKCS7 is of type data
1664 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001665 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001666 return True
1667 return False
1668
1669
1670 def get_type_name(self):
1671 """
1672 Returns the type name of the PKCS7 structure
1673
1674 :return: A string with the typename
1675 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001676 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1677 string_type = _lib.OBJ_nid2sn(nid)
1678 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001679
1680PKCS7Type = PKCS7
1681
1682
1683
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001684class PKCS12(object):
1685 def __init__(self):
1686 self._pkey = None
1687 self._cert = None
1688 self._cacerts = None
1689 self._friendlyname = None
1690
1691
1692 def get_certificate(self):
1693 """
1694 Return certificate portion of the PKCS12 structure
1695
1696 :return: X509 object containing the certificate
1697 """
1698 return self._cert
1699
1700
1701 def set_certificate(self, cert):
1702 """
1703 Replace the certificate portion of the PKCS12 structure
1704
1705 :param cert: The new certificate.
1706 :type cert: :py:class:`X509` or :py:data:`None`
1707 :return: None
1708 """
1709 if not isinstance(cert, X509):
1710 raise TypeError("cert must be an X509 instance")
1711 self._cert = cert
1712
1713
1714 def get_privatekey(self):
1715 """
1716 Return private key portion of the PKCS12 structure
1717
1718 :returns: PKey object containing the private key
1719 """
1720 return self._pkey
1721
1722
1723 def set_privatekey(self, pkey):
1724 """
1725 Replace or set the certificate portion of the PKCS12 structure
1726
1727 :param pkey: The new private key.
1728 :type pkey: :py:class:`PKey`
1729 :return: None
1730 """
1731 if not isinstance(pkey, PKey):
1732 raise TypeError("pkey must be a PKey instance")
1733 self._pkey = pkey
1734
1735
1736 def get_ca_certificates(self):
1737 """
1738 Return CA certificates within of the PKCS12 object
1739
1740 :return: A newly created tuple containing the CA certificates in the chain,
1741 if any are present, or None if no CA certificates are present.
1742 """
1743 if self._cacerts is not None:
1744 return tuple(self._cacerts)
1745
1746
1747 def set_ca_certificates(self, cacerts):
1748 """
1749 Replace or set the CA certificates withing the PKCS12 object.
1750
1751 :param cacerts: The new CA certificates.
1752 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1753 :return: None
1754 """
1755 if cacerts is None:
1756 self._cacerts = None
1757 else:
1758 cacerts = list(cacerts)
1759 for cert in cacerts:
1760 if not isinstance(cert, X509):
1761 raise TypeError("iterable must only contain X509 instances")
1762 self._cacerts = cacerts
1763
1764
1765 def set_friendlyname(self, name):
1766 """
1767 Replace or set the certificate portion of the PKCS12 structure
1768
1769 :param name: The new friendly name.
1770 :type name: :py:class:`bytes`
1771 :return: None
1772 """
1773 if name is None:
1774 self._friendlyname = None
1775 elif not isinstance(name, bytes):
1776 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1777 self._friendlyname = name
1778
1779
1780 def get_friendlyname(self):
1781 """
1782 Return friendly name portion of the PKCS12 structure
1783
1784 :returns: String containing the friendlyname
1785 """
1786 return self._friendlyname
1787
1788
1789 def export(self, passphrase=None, iter=2048, maciter=1):
1790 """
1791 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1792
1793 :param passphrase: used to encrypt the PKCS12
1794 :type passphrase: :py:data:`bytes`
1795
1796 :param iter: How many times to repeat the encryption
1797 :type iter: :py:data:`int`
1798
1799 :param maciter: How many times to repeat the MAC
1800 :type maciter: :py:data:`int`
1801
1802 :return: The string containing the PKCS12
1803 """
1804 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001805 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001806 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001807 cacerts = _lib.sk_X509_new_null()
1808 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001809 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001810 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001811
1812 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001813 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001814
1815 friendlyname = self._friendlyname
1816 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001817 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001818
1819 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001820 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001821 else:
1822 pkey = self._pkey._pkey
1823
1824 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001825 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001826 else:
1827 cert = self._cert._x509
1828
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001829 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001830 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001831 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1832 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001833 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001834 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001835 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001836 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001837
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001838 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001839 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001840 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001841
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001842PKCS12Type = PKCS12
1843
1844
1845
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001846class NetscapeSPKI(object):
1847 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001848 spki = _lib.NETSCAPE_SPKI_new()
1849 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001850
1851
1852 def sign(self, pkey, digest):
1853 """
1854 Sign the certificate request using the supplied key and digest
1855
1856 :param pkey: The key to sign with
1857 :param digest: The message digest to use
1858 :return: None
1859 """
1860 if pkey._only_public:
1861 raise ValueError("Key has only public part")
1862
1863 if not pkey._initialized:
1864 raise ValueError("Key is uninitialized")
1865
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001866 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001867 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001868 raise ValueError("No such digest method")
1869
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001870 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001871 if not sign_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001872 # TODO: This is untested.
1873 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001874
1875
1876 def verify(self, key):
1877 """
1878 Verifies a certificate request using the supplied public key
1879
1880 :param key: a public key
1881 :return: True if the signature is correct.
1882 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1883 problem verifying the signature.
1884 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001885 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001886 if answer <= 0:
1887 _raise_current_error()
1888 return True
1889
1890
1891 def b64_encode(self):
1892 """
1893 Generate a base64 encoded string from an SPKI
1894
1895 :return: The base64 encoded string
1896 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001897 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
1898 result = _ffi.string(encoded)
1899 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001900 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001901
1902
1903 def get_pubkey(self):
1904 """
1905 Get the public key of the certificate
1906
1907 :return: The public key
1908 """
1909 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001910 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
1911 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001912 # TODO: This is untested.
1913 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001914 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001915 pkey._only_public = True
1916 return pkey
1917
1918
1919 def set_pubkey(self, pkey):
1920 """
1921 Set the public key of the certificate
1922
1923 :param pkey: The public key
1924 :return: None
1925 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001926 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001927 if not set_result:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05001928 # TODO: This is untested.
1929 _raise_current_error()
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001930NetscapeSPKIType = NetscapeSPKI
1931
1932
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001933class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001934 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001935 if type != FILETYPE_PEM and passphrase is not None:
1936 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001937 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001938 self._more_args = more_args
1939 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001940 self._problems = []
1941
1942
1943 @property
1944 def callback(self):
1945 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001946 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001947 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001948 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001949 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001950 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001951 else:
1952 raise TypeError("Last argument must be string or callable")
1953
1954
1955 @property
1956 def callback_args(self):
1957 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001958 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001959 elif isinstance(self._passphrase, bytes):
1960 return self._passphrase
1961 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001962 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001963 else:
1964 raise TypeError("Last argument must be string or callable")
1965
1966
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001967 def raise_if_problem(self, exceptionType=Error):
1968 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -05001969 _exception_from_error_queue(exceptionType)
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001970 except exceptionType as e:
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05001971 from_queue = e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001972 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001973 raise self._problems[0]
Jean-Paul Calderone9b4115f2014-01-10 14:06:04 -05001974 return from_queue
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001975
1976
1977 def _read_passphrase(self, buf, size, rwflag, userdata):
1978 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001979 if self._more_args:
1980 result = self._passphrase(size, rwflag, userdata)
1981 else:
1982 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001983 if not isinstance(result, bytes):
1984 raise ValueError("String expected")
1985 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001986 if self._truncate:
1987 result = result[:size]
1988 else:
1989 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001990 for i in range(len(result)):
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05001991 buf[i] = result[i:i + 1]
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001992 return len(result)
1993 except Exception as e:
1994 self._problems.append(e)
1995 return 0
1996
1997
1998
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001999def load_privatekey(type, buffer, passphrase=None):
2000 """
2001 Load a private key from a buffer
2002
2003 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2004 :param buffer: The buffer the key is stored in
2005 :param passphrase: (optional) if encrypted PEM format, this can be
2006 either the passphrase to use, or a callback for
2007 providing the passphrase.
2008
2009 :return: The PKey object
2010 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002011 if isinstance(buffer, _text_type):
2012 buffer = buffer.encode("ascii")
2013
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002014 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002015
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08002016 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002017 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002018 evp_pkey = _lib.PEM_read_bio_PrivateKey(
2019 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08002020 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002021 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002022 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002023 else:
2024 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2025
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002026 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08002027 _raise_current_error()
2028
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002029 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002030 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002031 return pkey
2032
2033
2034
2035def dump_certificate_request(type, req):
2036 """
2037 Dump a certificate request to a buffer
2038
2039 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2040 :param req: The certificate request to dump
2041 :return: The buffer with the dumped certificate request in
2042 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002043 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002044
2045 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002046 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002047 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002048 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002049 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002050 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002051 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08002052 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002053
2054 if result_code == 0:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002055 # TODO: This is untested.
2056 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002057
2058 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08002059
2060
2061
2062def load_certificate_request(type, buffer):
2063 """
2064 Load a certificate request from a buffer
2065
2066 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2067 :param buffer: The buffer the certificate request is stored in
2068 :return: The X509Req object
2069 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002070 if isinstance(buffer, _text_type):
2071 buffer = buffer.encode("ascii")
2072
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002073 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002074
2075 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002076 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002077 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002078 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002079 else:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002080 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002081
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002082 if req == _ffi.NULL:
Jean-Paul Calderone4a68b402013-12-29 16:54:58 -05002083 # TODO: This is untested.
2084 _raise_current_error()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002085
2086 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002087 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08002088 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002089
2090
2091
2092def sign(pkey, data, digest):
2093 """
2094 Sign data with a digest
2095
2096 :param pkey: Pkey to sign with
2097 :param data: data to be signed
2098 :param digest: message digest to use
2099 :return: signature
2100 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002101 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002102 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002103 raise ValueError("No such digest method")
2104
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002105 md_ctx = _ffi.new("EVP_MD_CTX*")
2106 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002107
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002108 _lib.EVP_SignInit(md_ctx, digest_obj)
2109 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002110
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002111 signature_buffer = _ffi.new("unsigned char[]", 512)
2112 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002113 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002114 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002115 md_ctx, signature_buffer, signature_length, pkey._pkey)
2116
2117 if final_result != 1:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002118 # TODO: This is untested.
2119 _raise_current_error()
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002120
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002121 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002122
2123
2124
2125def verify(cert, signature, data, digest):
2126 """
2127 Verify a signature
2128
2129 :param cert: signing certificate (X509 object)
2130 :param signature: signature returned by sign function
2131 :param data: data to be verified
2132 :param digest: message digest to use
2133 :return: None if the signature is correct, raise exception otherwise
2134 """
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -05002135 digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002136 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002137 raise ValueError("No such digest method")
2138
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002139 pkey = _lib.X509_get_pubkey(cert._x509)
2140 if pkey == _ffi.NULL:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002141 # TODO: This is untested.
2142 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002143 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002144
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002145 md_ctx = _ffi.new("EVP_MD_CTX*")
2146 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002147
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002148 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2149 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2150 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002151
2152 if verify_result != 1:
2153 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002154
2155
2156
2157def load_crl(type, buffer):
2158 """
2159 Load a certificate revocation list from a buffer
2160
2161 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2162 :param buffer: The buffer the CRL is stored in
2163
2164 :return: The PKey object
2165 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002166 if isinstance(buffer, _text_type):
2167 buffer = buffer.encode("ascii")
2168
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002169 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002170
2171 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002172 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002173 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002174 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002175 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002176 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2177
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002178 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002179 _raise_current_error()
2180
2181 result = CRL.__new__(CRL)
2182 result._crl = crl
2183 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002184
2185
2186
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002187def load_pkcs7_data(type, buffer):
2188 """
2189 Load pkcs7 data from a buffer
2190
2191 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2192 :param buffer: The buffer with the pkcs7 data.
2193 :return: The PKCS7 object
2194 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002195 if isinstance(buffer, _text_type):
2196 buffer = buffer.encode("ascii")
2197
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002198 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002199
2200 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002201 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002202 elif type == FILETYPE_ASN1:
2203 pass
2204 else:
Jean-Paul Calderonedba578b2013-12-29 17:00:04 -05002205 # TODO: This is untested.
2206 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002207 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2208
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002209 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002210 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002211
2212 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002213 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002214 return pypkcs7
2215
2216
2217
Stephen Holsapple38482622014-04-05 20:29:34 -07002218def load_pkcs12(buffer, passphrase=None):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002219 """
2220 Load a PKCS12 object from a buffer
2221
2222 :param buffer: The buffer the certificate is stored in
2223 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2224 :returns: The PKCS12 object
2225 """
Jean-Paul Calderone6922a862014-01-18 10:38:28 -05002226 if isinstance(buffer, _text_type):
2227 buffer = buffer.encode("ascii")
2228
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002229 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002230
Stephen Holsapple38482622014-04-05 20:29:34 -07002231 # Use null passphrase if passphrase is None or empty string. With PKCS#12
2232 # password based encryption no password and a zero length password are two
2233 # different things, but OpenSSL implementation will try both to figure out
2234 # which one works.
2235 if not passphrase:
2236 passphrase = _ffi.NULL
2237
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002238 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2239 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002240 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002241 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002242
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002243 pkey = _ffi.new("EVP_PKEY**")
2244 cert = _ffi.new("X509**")
2245 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002246
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002247 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002248 if not parse_result:
2249 _raise_current_error()
2250
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002251 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002252
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002253 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2254 # queue for no particular reason. This error isn't interesting to anyone
2255 # outside this function. It's not even interesting to us. Get rid of it.
2256 try:
2257 _raise_current_error()
2258 except Error:
2259 pass
2260
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002261 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002262 pykey = None
2263 else:
2264 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002265 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002266
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002267 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002268 pycert = None
2269 friendlyname = None
2270 else:
2271 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002272 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002273
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002274 friendlyname_length = _ffi.new("int*")
2275 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2276 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2277 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002278 friendlyname = None
2279
2280 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002281 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002282 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002283 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002284 pycacerts.append(pycacert)
2285 if not pycacerts:
2286 pycacerts = None
2287
2288 pkcs12 = PKCS12.__new__(PKCS12)
2289 pkcs12._pkey = pykey
2290 pkcs12._cert = pycert
2291 pkcs12._cacerts = pycacerts
2292 pkcs12._friendlyname = friendlyname
2293 return pkcs12
Jean-Paul Calderone6bb40892014-01-01 12:21:34 -05002294
2295
2296def _initialize_openssl_threads(get_ident, Lock):
2297 import _ssl
2298 return
2299
2300 locks = list(Lock() for n in range(_lib.CRYPTO_num_locks()))
2301
2302 def locking_function(mode, index, filename, line):
2303 if mode & _lib.CRYPTO_LOCK:
2304 locks[index].acquire()
2305 else:
2306 locks[index].release()
2307
2308 _lib.CRYPTO_set_id_callback(
2309 _ffi.callback("unsigned long (*)(void)", get_ident))
2310
2311 _lib.CRYPTO_set_locking_callback(
2312 _ffi.callback(
2313 "void (*)(int, int, const char*, int)", locking_function))
2314
2315
2316try:
2317 from thread import get_ident
2318 from threading import Lock
2319except ImportError:
2320 pass
2321else:
2322 _initialize_openssl_threads(get_ident, Lock)
2323 del get_ident, Lock
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002324
Jean-Paul Calderoneb64e2a22014-01-11 08:06:35 -05002325# There are no direct unit tests for this initialization. It is tested
2326# indirectly since it is necessary for functions like dump_privatekey when
2327# using encryption.
2328#
2329# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase
2330# and some other similar tests may fail without this (though they may not if
2331# the Python runtime has already done some initialization of the underlying
2332# OpenSSL library (and is linked against the same one that cryptography is
2333# using)).
Jean-Paul Calderonee324fd62014-01-11 08:00:33 -05002334_lib.OpenSSL_add_all_algorithms()
Jean-Paul Calderone11ed8e82014-01-18 10:21:50 -05002335
Jean-Paul Calderonefab157b2014-01-18 11:21:38 -05002336# This is similar but exercised mainly by exception_from_error_queue. It calls
2337# both ERR_load_crypto_strings() and ERR_load_SSL_strings().
2338_lib.SSL_load_error_strings()