blob: 1a4db380a6adb9c29342d2d984d7411b2af39863 [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
2
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05003from cryptography.hazmat.backends.openssl import backend
4_ffi = backend.ffi
5_lib = backend.lib
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08006
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05007FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
8FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08009
10# TODO This was an API mistake. OpenSSL has no such constant.
11FILETYPE_TEXT = 2 ** 16 - 1
12
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050013TYPE_RSA = _lib.EVP_PKEY_RSA
14TYPE_DSA = _lib.EVP_PKEY_DSA
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080015
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080016
17def _bio_to_string(bio):
18 """
19 Copy the contents of an OpenSSL BIO object into a Python byte string.
20 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050021 result_buffer = _ffi.new('char**')
22 buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
23 return _ffi.buffer(result_buffer[0], buffer_length)[:]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080024
25
26
Jean-Paul Calderonefd371362013-03-01 20:53:58 -080027def _new_mem_buf(buffer=None):
28 if buffer is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050029 bio = _lib.BIO_new(_lib.BIO_s_mem())
30 free = _lib.BIO_free
Jean-Paul Calderonefd371362013-03-01 20:53:58 -080031 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050032 data = _ffi.new("char[]", buffer)
33 bio = _lib.BIO_new_mem_buf(data, len(buffer))
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -080034 # Keep the memory alive as long as the bio is alive!
35 def free(bio, ref=data):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050036 return _lib.BIO_free(bio)
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -080037
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050038 if bio == _ffi.NULL:
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080039 1/0
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -080040
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050041 bio = _ffi.gc(bio, free)
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080042 return bio
43
44
45
Jean-Paul Calderone57122982013-02-21 08:47:05 -080046def _set_asn1_time(boundary, when):
47 if not isinstance(when, bytes):
48 raise TypeError("when must be a byte string")
49
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050050 set_result = _lib.ASN1_GENERALIZEDTIME_set_string(
51 _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when)
Jean-Paul Calderone57122982013-02-21 08:47:05 -080052 if set_result == 0:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050053 dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free)
54 _lib.ASN1_STRING_set(dummy, when, len(when))
55 check_result = _lib.ASN1_GENERALIZEDTIME_check(
56 _ffi.cast('ASN1_GENERALIZEDTIME*', dummy))
Jean-Paul Calderone57122982013-02-21 08:47:05 -080057 if not check_result:
58 raise ValueError("Invalid string")
59 else:
60 # TODO No tests for this case
61 raise RuntimeError("Unknown ASN1_GENERALIZEDTIME_set_string failure")
62
63
64
65def _get_asn1_time(timestamp):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050066 string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
67 if _lib.ASN1_STRING_length(string_timestamp) == 0:
Jean-Paul Calderone57122982013-02-21 08:47:05 -080068 return None
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050069 elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME:
70 return _ffi.string(_lib.ASN1_STRING_data(string_timestamp))
Jean-Paul Calderone57122982013-02-21 08:47:05 -080071 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050072 generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**")
73 _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
74 if generalized_timestamp[0] == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -080075 1/0
76 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050077 string_timestamp = _ffi.cast(
Jean-Paul Calderone57122982013-02-21 08:47:05 -080078 "ASN1_STRING*", generalized_timestamp[0])
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050079 string_data = _lib.ASN1_STRING_data(string_timestamp)
80 string_result = _ffi.string(string_data)
81 _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
Jean-Paul Calderone57122982013-02-21 08:47:05 -080082 return string_result
83
84
85
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -080086class Error(Exception):
87 pass
88
89
90
91def _raise_current_error(exceptionType=Error):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080092 errors = []
93 while True:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050094 error = _lib.ERR_get_error()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080095 if error == 0:
96 break
97 errors.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -050098 _ffi.string(_lib.ERR_lib_error_string(error)),
99 _ffi.string(_lib.ERR_func_error_string(error)),
100 _ffi.string(_lib.ERR_reason_error_string(error))))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800101
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -0800102 raise exceptionType(errors)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800103
104_exception_from_error_queue = _raise_current_error
105
106
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800107
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800108class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800109 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800110 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800111
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800112 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500113 pkey = _lib.EVP_PKEY_new()
114 self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800115 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800116
117
118 def generate_key(self, type, bits):
119 """
120 Generate a key of a given type, with a given number of a bits
121
122 :param type: The key type (TYPE_RSA or TYPE_DSA)
123 :param bits: The number of bits
124
125 :return: None
126 """
127 if not isinstance(type, int):
128 raise TypeError("type must be an integer")
129
130 if not isinstance(bits, int):
131 raise TypeError("bits must be an integer")
132
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800133 # TODO Check error return
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500134 exponent = _lib.BN_new()
135 exponent = _ffi.gc(exponent, _lib.BN_free)
136 _lib.BN_set_word(exponent, _lib.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800137
138 if type == TYPE_RSA:
139 if bits <= 0:
140 raise ValueError("Invalid number of bits")
141
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500142 rsa = _lib.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800143
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500144 result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800145 if result == -1:
146 1/0
147
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500148 result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800149 if not result:
150 1/0
151
152 elif type == TYPE_DSA:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500153 dsa = _lib.DSA_generate_parameters(
154 bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL)
155 if dsa == _ffi.NULL:
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800156 1/0
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500157 if not _lib.DSA_generate_key(dsa):
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800158 1/0
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500159 if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa):
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800160 1/0
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800161 else:
162 raise Error("No such key type")
163
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800164 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800165
166
167 def check(self):
168 """
169 Check the consistency of an RSA private key.
170
171 :return: True if key is consistent.
172 :raise Error: if the key is inconsistent.
173 :raise TypeError: if the key is of a type which cannot be checked.
174 Only RSA keys can currently be checked.
175 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800176 if self._only_public:
177 raise TypeError("public key only")
178
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500179 if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800180 raise TypeError("key type unsupported")
181
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500182 rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
183 rsa = _ffi.gc(rsa, _lib.RSA_free)
184 result = _lib.RSA_check_key(rsa)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800185 if result:
186 return True
187 _raise_current_error()
188
189
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800190 def type(self):
191 """
192 Returns the type of the key
193
194 :return: The type of the key.
195 """
196 return self._pkey.type
197
198
199 def bits(self):
200 """
201 Returns the number of bits of the key
202
203 :return: The number of bits of the key.
204 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500205 return _lib.EVP_PKEY_bits(self._pkey)
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800206PKeyType = PKey
207
208
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800209
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800210class X509Name(object):
211 def __init__(self, name):
212 """
213 Create a new X509Name, copying the given X509Name instance.
214
215 :param name: An X509Name object to copy
216 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500217 name = _lib.X509_NAME_dup(name._name)
218 self._name = _ffi.gc(name, _lib.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800219
220
221 def __setattr__(self, name, value):
222 if name.startswith('_'):
223 return super(X509Name, self).__setattr__(name, value)
224
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800225 # Note: we really do not want str subclasses here, so we do not use
226 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800227 if type(name) is not str:
228 raise TypeError("attribute name must be string, not '%.200s'" % (
229 type(value).__name__,))
230
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500231 nid = _lib.OBJ_txt2nid(name)
232 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800233 try:
234 _raise_current_error()
235 except Error:
236 pass
237 raise AttributeError("No such attribute")
238
239 # If there's an old entry for this NID, remove it
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500240 for i in range(_lib.X509_NAME_entry_count(self._name)):
241 ent = _lib.X509_NAME_get_entry(self._name, i)
242 ent_obj = _lib.X509_NAME_ENTRY_get_object(ent)
243 ent_nid = _lib.OBJ_obj2nid(ent_obj)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800244 if nid == ent_nid:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500245 ent = _lib.X509_NAME_delete_entry(self._name, i)
246 _lib.X509_NAME_ENTRY_free(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800247 break
248
249 if isinstance(value, unicode):
250 value = value.encode('utf-8')
251
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500252 add_result = _lib.X509_NAME_add_entry_by_NID(
253 self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800254 if not add_result:
255 # TODO Untested
256 1/0
257
258
259 def __getattr__(self, name):
260 """
261 Find attribute. An X509Name object has the following attributes:
262 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
263 organization (alias O), organizationalUnit (alias OU), commonName (alias
264 CN) and more...
265 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500266 nid = _lib.OBJ_txt2nid(name)
267 if nid == _lib.NID_undef:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800268 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
269 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
270 # push something onto the error queue. If we don't clean that up
271 # now, someone else will bump into it later and be quite confused.
272 # See lp#314814.
273 try:
274 _raise_current_error()
275 except Error:
276 pass
277 return super(X509Name, self).__getattr__(name)
278
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500279 entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800280 if entry_index == -1:
281 return None
282
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500283 entry = _lib.X509_NAME_get_entry(self._name, entry_index)
284 data = _lib.X509_NAME_ENTRY_get_data(entry)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800285
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500286 result_buffer = _ffi.new("unsigned char**")
287 data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800288 if data_length < 0:
289 1/0
290
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700291 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500292 result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8')
Jean-Paul Calderoned899af02013-03-19 22:10:37 -0700293 finally:
294 # XXX untested
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500295 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800296 return result
297
298
299 def __cmp__(self, other):
300 if not isinstance(other, X509Name):
301 return NotImplemented
302
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500303 result = _lib.X509_NAME_cmp(self._name, other._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800304 # TODO result == -2 is an error case that maybe should be checked for
305 return result
306
307
308 def __repr__(self):
309 """
310 String representation of an X509Name
311 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500312 result_buffer = _ffi.new("char[]", 512);
313 format_result = _lib.X509_NAME_oneline(
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800314 self._name, result_buffer, len(result_buffer))
315
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500316 if format_result == _ffi.NULL:
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800317 1/0
318
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500319 return "<X509Name object '%s'>" % (_ffi.string(result_buffer),)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800320
321
322 def hash(self):
323 """
324 Return the hash value of this name
325
326 :return: None
327 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500328 return _lib.X509_NAME_hash(self._name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800329
330
331 def der(self):
332 """
333 Return the DER encoding of this name
334
335 :return: A :py:class:`bytes` instance giving the DER encoded form of
336 this name.
337 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500338 result_buffer = _ffi.new('unsigned char**')
339 encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800340 if encode_result < 0:
341 1/0
342
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500343 string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
344 _lib.OPENSSL_free(result_buffer[0])
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800345 return string_result
346
347
348 def get_components(self):
349 """
350 Returns the split-up components of this name.
351
352 :return: List of tuples (name, value).
353 """
354 result = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500355 for i in range(_lib.X509_NAME_entry_count(self._name)):
356 ent = _lib.X509_NAME_get_entry(self._name, i)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800357
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500358 fname = _lib.X509_NAME_ENTRY_get_object(ent)
359 fval = _lib.X509_NAME_ENTRY_get_data(ent)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800360
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500361 nid = _lib.OBJ_obj2nid(fname)
362 name = _lib.OBJ_nid2sn(nid)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800363
364 result.append((
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500365 _ffi.string(name),
366 _ffi.string(
367 _lib.ASN1_STRING_data(fval),
368 _lib.ASN1_STRING_length(fval))))
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800369
370 return result
371X509NameType = X509Name
372
373
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800374class X509Extension(object):
375 def __init__(self, type_name, critical, value, subject=None, issuer=None):
376 """
377 :param typename: The name of the extension to create.
378 :type typename: :py:data:`str`
379
380 :param critical: A flag indicating whether this is a critical extension.
381
382 :param value: The value of the extension.
383 :type value: :py:data:`str`
384
385 :param subject: Optional X509 cert to use as subject.
386 :type subject: :py:class:`X509`
387
388 :param issuer: Optional X509 cert to use as issuer.
389 :type issuer: :py:class:`X509`
390
391 :return: The X509Extension object
392 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500393 ctx = _ffi.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800394
395 # A context is necessary for any extension which uses the r2i conversion
396 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
397 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500398 _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800399
400 # We have no configuration database - but perhaps we should (some
401 # extensions may require it).
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500402 _lib.X509V3_set_ctx_nodb(ctx)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800403
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800404 # Initialize the subject and issuer, if appropriate. ctx is a local,
405 # and as far as I can tell none of the X509V3_* APIs invoked here steal
406 # any references, so no need to mess with reference counts or duplicates.
407 if issuer is not None:
408 if not isinstance(issuer, X509):
409 raise TypeError("issuer must be an X509 instance")
410 ctx.issuer_cert = issuer._x509
411 if subject is not None:
412 if not isinstance(subject, X509):
413 raise TypeError("subject must be an X509 instance")
414 ctx.subject_cert = subject._x509
415
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800416 if critical:
417 # There are other OpenSSL APIs which would let us pass in critical
418 # separately, but they're harder to use, and since value is already
419 # a pile of crappy junk smuggling a ton of utterly important
420 # structured data, what's the point of trying to avoid nasty stuff
421 # with strings? (However, X509V3_EXT_i2d in particular seems like it
422 # would be a better API to invoke. I do not know where to get the
423 # ext_struc it desires for its last parameter, though.)
424 value = "critical," + value
425
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500426 extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
427 if extension == _ffi.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800428 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500429 self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800430
431
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400432 @property
433 def _nid(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500434 return _lib.OBJ_obj2nid(self._extension.object)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400435
436 _prefixes = {
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500437 _lib.GEN_EMAIL: b"email",
438 _lib.GEN_DNS: b"DNS",
439 _lib.GEN_URI: b"URI",
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400440 }
441
442 def _subjectAltNameString(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500443 method = _lib.X509V3_EXT_get(self._extension)
444 if method == _ffi.NULL:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400445 1/0
446 payload = self._extension.value.data
447 length = self._extension.value.length
448
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500449 payloadptr = _ffi.new("unsigned char**")
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400450 payloadptr[0] = payload
451
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500452 if method.it != _ffi.NULL:
453 ptr = _lib.ASN1_ITEM_ptr(method.it)
454 data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr)
455 names = _ffi.cast("GENERAL_NAMES*", data)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400456 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500457 names = _ffi.cast(
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400458 "GENERAL_NAMES*",
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500459 method.d2i(_ffi.NULL, payloadptr, length))
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400460
461 parts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500462 for i in range(_lib.sk_GENERAL_NAME_num(names)):
463 name = _lib.sk_GENERAL_NAME_value(names, i)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400464 try:
465 label = self._prefixes[name.type]
466 except KeyError:
467 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500468 _lib.GENERAL_NAME_print(bio, name)
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400469 parts.append(_bio_to_string(bio))
470 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500471 value = _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:]
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400472 parts.append(label + b":" + value)
473 return b", ".join(parts)
474
475
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800476 def __str__(self):
477 """
478 :return: a nice text representation of the extension
479 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500480 if _lib.NID_subject_alt_name == self._nid:
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400481 return self._subjectAltNameString()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800482
Jean-Paul Calderoneed0c57b2013-10-06 08:31:40 -0400483 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500484 print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800485 if not print_result:
486 1/0
487
488 return _bio_to_string(bio)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800489
490
491 def get_critical(self):
492 """
493 Returns the critical field of the X509Extension
494
495 :return: The critical field.
496 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500497 return _lib.X509_EXTENSION_get_critical(self._extension)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800498
499
500 def get_short_name(self):
501 """
502 Returns the short version of the type name of the X509Extension
503
504 :return: The short type name.
505 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500506 obj = _lib.X509_EXTENSION_get_object(self._extension)
507 nid = _lib.OBJ_obj2nid(obj)
508 return _ffi.string(_lib.OBJ_nid2sn(nid))
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800509
510
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800511 def get_data(self):
512 """
513 Returns the data of the X509Extension
514
515 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
516 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500517 octet_result = _lib.X509_EXTENSION_get_data(self._extension)
518 string_result = _ffi.cast('ASN1_STRING*', octet_result)
519 char_result = _lib.ASN1_STRING_data(string_result)
520 result_length = _lib.ASN1_STRING_length(string_result)
521 return _ffi.buffer(char_result, result_length)[:]
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800522
523X509ExtensionType = X509Extension
524
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800525
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800526class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800527 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500528 req = _lib.X509_REQ_new()
529 self._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800530
531
532 def set_pubkey(self, pkey):
533 """
534 Set the public key of the certificate request
535
536 :param pkey: The public key to use
537 :return: None
538 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500539 set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800540 if not set_result:
541 1/0
542
543
544 def get_pubkey(self):
545 """
546 Get the public key from the certificate request
547
548 :return: The public key
549 """
550 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500551 pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
552 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800553 1/0
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500554 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800555 pkey._only_public = True
556 return pkey
557
558
559 def set_version(self, version):
560 """
561 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
562 request.
563
564 :param version: The version number
565 :return: None
566 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500567 set_result = _lib.X509_REQ_set_version(self._req, version)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800568 if not set_result:
569 _raise_current_error()
570
571
572 def get_version(self):
573 """
574 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
575 request.
576
577 :return: an integer giving the value of the version subfield
578 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500579 return _lib.X509_REQ_get_version(self._req)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800580
581
582 def get_subject(self):
583 """
584 Create an X509Name object for the subject of the certificate request
585
586 :return: An X509Name object
587 """
588 name = X509Name.__new__(X509Name)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500589 name._name = _lib.X509_REQ_get_subject_name(self._req)
590 if name._name == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800591 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800592
593 # The name is owned by the X509Req structure. As long as the X509Name
594 # Python object is alive, keep the X509Req Python object alive.
595 name._owner = self
596
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800597 return name
598
599
600 def add_extensions(self, extensions):
601 """
602 Add extensions to the request.
603
604 :param extensions: a sequence of X509Extension objects
605 :return: None
606 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500607 stack = _lib.sk_X509_EXTENSION_new_null()
608 if stack == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800609 1/0
610
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500611 stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800612
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800613 for ext in extensions:
614 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800615 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800616
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800617 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500618 _lib.sk_X509_EXTENSION_push(stack, ext._extension)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800619
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500620 add_result = _lib.X509_REQ_add_extensions(self._req, stack)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800621 if not add_result:
622 1/0
623
624
625 def sign(self, pkey, digest):
626 """
627 Sign the certificate request using the supplied key and digest
628
629 :param pkey: The key to sign with
630 :param digest: The message digest to use
631 :return: None
632 """
633 if pkey._only_public:
634 raise ValueError("Key has only public part")
635
636 if not pkey._initialized:
637 raise ValueError("Key is uninitialized")
638
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500639 digest_obj = _lib.EVP_get_digestbyname(digest)
640 if digest_obj == _ffi.NULL:
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800641 raise ValueError("No such digest method")
642
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500643 sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800644 if not sign_result:
645 1/0
646
647
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800648 def verify(self, pkey):
649 """
650 Verifies a certificate request using the supplied public key
651
652 :param key: a public key
653 :return: True if the signature is correct.
654
655 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
656 problem verifying the signature.
657 """
658 if not isinstance(pkey, PKey):
659 raise TypeError("pkey must be a PKey instance")
660
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500661 result = _lib.X509_REQ_verify(self._req, pkey._pkey)
Jean-Paul Calderone5565f0f2013-03-06 11:10:20 -0800662 if result <= 0:
663 _raise_current_error(Error)
664
665 return result
666
667
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800668X509ReqType = X509Req
669
670
671
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800672class X509(object):
673 def __init__(self):
674 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500675 x509 = _lib.X509_new()
676 self._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800677
678
679 def set_version(self, version):
680 """
681 Set version number of the certificate
682
683 :param version: The version number
684 :type version: :py:class:`int`
685
686 :return: None
687 """
688 if not isinstance(version, int):
689 raise TypeError("version must be an integer")
690
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500691 _lib.X509_set_version(self._x509, version)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800692
693
694 def get_version(self):
695 """
696 Return version number of the certificate
697
698 :return: Version number as a Python integer
699 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500700 return _lib.X509_get_version(self._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800701
702
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800703 def get_pubkey(self):
704 """
705 Get the public key of the certificate
706
707 :return: The public key
708 """
709 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500710 pkey._pkey = _lib.X509_get_pubkey(self._x509)
711 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800712 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500713 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800714 pkey._only_public = True
715 return pkey
716
717
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800718 def set_pubkey(self, pkey):
719 """
720 Set the public key of the certificate
721
722 :param pkey: The public key
723
724 :return: None
725 """
726 if not isinstance(pkey, PKey):
727 raise TypeError("pkey must be a PKey instance")
728
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500729 set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800730 if not set_result:
731 _raise_current_error()
732
733
734 def sign(self, pkey, digest):
735 """
736 Sign the certificate using the supplied key and digest
737
738 :param pkey: The key to sign with
739 :param digest: The message digest to use
740 :return: None
741 """
742 if not isinstance(pkey, PKey):
743 raise TypeError("pkey must be a PKey instance")
744
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800745 if pkey._only_public:
746 raise ValueError("Key only has public part")
747
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800748 if not pkey._initialized:
749 raise ValueError("Key is uninitialized")
750
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500751 evp_md = _lib.EVP_get_digestbyname(digest)
752 if evp_md == _ffi.NULL:
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800753 raise ValueError("No such digest method")
754
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500755 sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800756 if not sign_result:
757 _raise_current_error()
758
759
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800760 def get_signature_algorithm(self):
761 """
762 Retrieve the signature algorithm used in the certificate
763
764 :return: A byte string giving the name of the signature algorithm used in
765 the certificate.
766 :raise ValueError: If the signature algorithm is undefined.
767 """
768 alg = self._x509.cert_info.signature.algorithm
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500769 nid = _lib.OBJ_obj2nid(alg)
770 if nid == _lib.NID_undef:
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800771 raise ValueError("Undefined signature algorithm")
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500772 return _ffi.string(_lib.OBJ_nid2ln(nid))
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800773
774
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800775 def digest(self, digest_name):
776 """
777 Return the digest of the X509 object.
778
779 :param digest_name: The name of the digest algorithm to use.
780 :type digest_name: :py:class:`bytes`
781
782 :return: The digest of the object
783 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500784 digest = _lib.EVP_get_digestbyname(digest_name)
785 if digest == _ffi.NULL:
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800786 raise ValueError("No such digest method")
787
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500788 result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE)
789 result_length = _ffi.new("unsigned int[]", 1)
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800790 result_length[0] = len(result_buffer)
791
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500792 digest_result = _lib.X509_digest(
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800793 self._x509, digest, result_buffer, result_length)
794
795 if not digest_result:
796 1/0
797
798 return ':'.join([
799 ch.encode('hex').upper() for ch
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500800 in _ffi.buffer(result_buffer, result_length[0])])
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800801
802
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800803 def subject_name_hash(self):
804 """
805 Return the hash of the X509 subject.
806
807 :return: The hash of the subject.
808 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500809 return _lib.X509_subject_name_hash(self._x509)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800810
811
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800812 def set_serial_number(self, serial):
813 """
814 Set serial number of the certificate
815
816 :param serial: The serial number
817 :type serial: :py:class:`int`
818
819 :return: None
820 """
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800821 if not isinstance(serial, (int, long)):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800822 raise TypeError("serial must be an integer")
823
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800824 hex_serial = hex(serial)[2:]
825 if not isinstance(hex_serial, bytes):
826 hex_serial = hex_serial.encode('ascii')
827
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500828 bignum_serial = _ffi.new("BIGNUM**")
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800829
830 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
831 # it. If bignum is still NULL after this call, then the return value is
832 # actually the result. I hope. -exarkun
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500833 small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800834
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500835 if bignum_serial[0] == _ffi.NULL:
836 set_result = _lib.ASN1_INTEGER_set(
837 _lib.X509_get_serialNumber(self._x509), small_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800838 if set_result:
839 # TODO Not tested
840 _raise_current_error()
841 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500842 asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL)
843 _lib.BN_free(bignum_serial[0])
844 if asn1_serial == _ffi.NULL:
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800845 # TODO Not tested
846 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500847 asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free)
848 set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800849 if not set_result:
850 # TODO Not tested
851 _raise_current_error()
852
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800853
854 def get_serial_number(self):
855 """
856 Return serial number of the certificate
857
858 :return: Serial number as a Python integer
859 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500860 asn1_serial = _lib.X509_get_serialNumber(self._x509)
861 bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800862 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500863 hex_serial = _lib.BN_bn2hex(bignum_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800864 try:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500865 hexstring_serial = _ffi.string(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800866 serial = int(hexstring_serial, 16)
867 return serial
868 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500869 _lib.OPENSSL_free(hex_serial)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800870 finally:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500871 _lib.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800872
873
874 def gmtime_adj_notAfter(self, amount):
875 """
876 Adjust the time stamp for when the certificate stops being valid
877
878 :param amount: The number of seconds by which to adjust the ending
879 validity time.
880 :type amount: :py:class:`int`
881
882 :return: None
883 """
884 if not isinstance(amount, int):
885 raise TypeError("amount must be an integer")
886
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500887 notAfter = _lib.X509_get_notAfter(self._x509)
888 _lib.X509_gmtime_adj(notAfter, amount)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800889
890
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800891 def gmtime_adj_notBefore(self, amount):
892 """
893 Change the timestamp for when the certificate starts being valid to the current
894 time plus an offset.
895
896 :param amount: The number of seconds by which to adjust the starting validity
897 time.
898 :return: None
899 """
900 if not isinstance(amount, int):
901 raise TypeError("amount must be an integer")
902
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500903 notBefore = _lib.X509_get_notBefore(self._x509)
904 _lib.X509_gmtime_adj(notBefore, amount)
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800905
906
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800907 def has_expired(self):
908 """
909 Check whether the certificate has expired.
910
911 :return: True if the certificate has expired, false otherwise
912 """
913 now = int(time())
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500914 notAfter = _lib.X509_get_notAfter(self._x509)
915 return _lib.ASN1_UTCTIME_cmp_time_t(
916 _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800917
918
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800919 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800920 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800921
922
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800923 def get_notBefore(self):
924 """
925 Retrieve the time stamp for when the certificate starts being valid
926
927 :return: A string giving the timestamp, in the format::
928
929 YYYYMMDDhhmmssZ
930 YYYYMMDDhhmmss+hhmm
931 YYYYMMDDhhmmss-hhmm
932
933 or None if there is no value set.
934 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500935 return self._get_boundary_time(_lib.X509_get_notBefore)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800936
937
938 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800939 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800940
941
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800942 def set_notBefore(self, when):
943 """
944 Set the time stamp for when the certificate starts being valid
945
946 :param when: A string giving the timestamp, in the format:
947
948 YYYYMMDDhhmmssZ
949 YYYYMMDDhhmmss+hhmm
950 YYYYMMDDhhmmss-hhmm
951 :type when: :py:class:`bytes`
952
953 :return: None
954 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500955 return self._set_boundary_time(_lib.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -0800956
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800957
958 def get_notAfter(self):
959 """
960 Retrieve the time stamp for when the certificate stops being valid
961
962 :return: A string giving the timestamp, in the format::
963
964 YYYYMMDDhhmmssZ
965 YYYYMMDDhhmmss+hhmm
966 YYYYMMDDhhmmss-hhmm
967
968 or None if there is no value set.
969 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500970 return self._get_boundary_time(_lib.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800971
972
973 def set_notAfter(self, when):
974 """
975 Set the time stamp for when the certificate stops being valid
976
977 :param when: A string giving the timestamp, in the format:
978
979 YYYYMMDDhhmmssZ
980 YYYYMMDDhhmmss+hhmm
981 YYYYMMDDhhmmss-hhmm
982 :type when: :py:class:`bytes`
983
984 :return: None
985 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500986 return self._set_boundary_time(_lib.X509_get_notAfter, when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800987
988
989 def _get_name(self, which):
990 name = X509Name.__new__(X509Name)
991 name._name = which(self._x509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -0500992 if name._name == _ffi.NULL:
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800993 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800994
995 # The name is owned by the X509 structure. As long as the X509Name
996 # Python object is alive, keep the X509 Python object alive.
997 name._owner = self
998
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800999 return name
1000
1001
1002 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001003 if not isinstance(name, X509Name):
1004 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001005 set_result = which(self._x509, name._name)
1006 if not set_result:
1007 1/0
1008
1009
1010 def get_issuer(self):
1011 """
1012 Create an X509Name object for the issuer of the certificate
1013
1014 :return: An X509Name object
1015 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001016 return self._get_name(_lib.X509_get_issuer_name)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -08001017
1018
1019 def set_issuer(self, issuer):
1020 """
1021 Set the issuer of the certificate
1022
1023 :param issuer: The issuer name
1024 :type issuer: :py:class:`X509Name`
1025
1026 :return: None
1027 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001028 return self._set_name(_lib.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001029
1030
1031 def get_subject(self):
1032 """
1033 Create an X509Name object for the subject of the certificate
1034
1035 :return: An X509Name object
1036 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001037 return self._get_name(_lib.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -08001038
1039
1040 def set_subject(self, subject):
1041 """
1042 Set the subject of the certificate
1043
1044 :param subject: The subject name
1045 :type subject: :py:class:`X509Name`
1046 :return: None
1047 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001048 return self._set_name(_lib.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001049
1050
1051 def get_extension_count(self):
1052 """
1053 Get the number of extensions on the certificate.
1054
1055 :return: The number of extensions as an integer.
1056 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001057 return _lib.X509_get_ext_count(self._x509)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001058
1059
1060 def add_extensions(self, extensions):
1061 """
1062 Add extensions to the certificate.
1063
1064 :param extensions: a sequence of X509Extension objects
1065 :return: None
1066 """
1067 for ext in extensions:
1068 if not isinstance(ext, X509Extension):
1069 raise ValueError("One of the elements is not an X509Extension")
1070
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001071 add_result = _lib.X509_add_ext(self._x509, ext._extension, -1)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001072 if not add_result:
1073 _raise_current_error()
1074
1075
1076 def get_extension(self, index):
1077 """
1078 Get a specific extension of the certificate by index.
1079
1080 :param index: The index of the extension to retrieve.
1081 :return: The X509Extension object at the specified index.
1082 """
1083 ext = X509Extension.__new__(X509Extension)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001084 ext._extension = _lib.X509_get_ext(self._x509, index)
1085 if ext._extension == _ffi.NULL:
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001086 raise IndexError("extension index out of bounds")
1087
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001088 extension = _lib.X509_EXTENSION_dup(ext._extension)
1089 ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001090 return ext
1091
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001092X509Type = X509
1093
1094
1095
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001096class X509Store(object):
1097 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001098 store = _lib.X509_STORE_new()
1099 self._store = _ffi.gc(store, _lib.X509_STORE_free)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001100
1101
1102 def add_cert(self, cert):
1103 if not isinstance(cert, X509):
1104 raise TypeError()
1105
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001106 result = _lib.X509_STORE_add_cert(self._store, cert._x509)
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001107 if not result:
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001108 _raise_current_error(Error)
1109
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001110
1111X509StoreType = X509Store
1112
1113
1114
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001115def load_certificate(type, buffer):
1116 """
1117 Load a certificate from a buffer
1118
1119 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1120
1121 :param buffer: The buffer the certificate is stored in
1122 :type buffer: :py:class:`bytes`
1123
1124 :return: The X509 object
1125 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001126 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001127
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001128 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001129 x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001130 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001131 x509 = _lib.d2i_X509_bio(bio, _ffi.NULL);
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001132 else:
1133 raise ValueError(
1134 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001135
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001136 if x509 == _ffi.NULL:
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001137 _raise_current_error()
1138
1139 cert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001140 cert._x509 = _ffi.gc(x509, _lib.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001141 return cert
1142
1143
1144def dump_certificate(type, cert):
1145 """
1146 Dump a certificate to a buffer
1147
Jean-Paul Calderonea12e7d22013-04-03 08:17:34 -04001148 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1149 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001150 :param cert: The certificate to dump
1151 :return: The buffer with the dumped certificate in
1152 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001153 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001154
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001155 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001156 result_code = _lib.PEM_write_bio_X509(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001157 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001158 result_code = _lib.i2d_X509_bio(bio, cert._x509)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001159 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001160 result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001161 else:
1162 raise ValueError(
1163 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1164 "FILETYPE_TEXT")
1165
1166 return _bio_to_string(bio)
1167
1168
1169
1170def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1171 """
1172 Dump a private key to a buffer
1173
Jean-Paul Calderonee66fde22013-04-03 08:35:08 -04001174 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or
1175 FILETYPE_TEXT)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001176 :param pkey: The PKey to dump
1177 :param cipher: (optional) if encrypted PEM format, the cipher to
1178 use
1179 :param passphrase: (optional) if encrypted PEM format, this can be either
1180 the passphrase to use, or a callback for providing the
1181 passphrase.
1182 :return: The buffer with the dumped key in
1183 :rtype: :py:data:`str`
1184 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001185 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001186
1187 if cipher is not None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001188 cipher_obj = _lib.EVP_get_cipherbyname(cipher)
1189 if cipher_obj == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001190 raise ValueError("Invalid cipher name")
1191 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001192 cipher_obj = _ffi.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001193
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001194 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001195 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001196 result_code = _lib.PEM_write_bio_PrivateKey(
1197 bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001198 helper.callback, helper.callback_args)
1199 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001200 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001201 result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001202 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001203 rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey)
1204 result_code = _lib.RSA_print(bio, rsa, 0)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001205 # TODO RSA_free(rsa)?
1206 else:
1207 raise ValueError(
1208 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1209 "FILETYPE_TEXT")
1210
1211 if result_code == 0:
1212 _raise_current_error()
1213
1214 return _bio_to_string(bio)
1215
1216
1217
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001218def _X509_REVOKED_dup(original):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001219 copy = _lib.X509_REVOKED_new()
1220 if copy == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001221 1/0
1222
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001223 if original.serialNumber != _ffi.NULL:
1224 copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001225
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001226 if original.revocationDate != _ffi.NULL:
1227 copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001228
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001229 if original.extensions != _ffi.NULL:
1230 extension_stack = _lib.sk_X509_EXTENSION_new_null()
1231 for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)):
1232 original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i)
1233 copy_ext = _lib.X509_EXTENSION_dup(original_ext)
1234 _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001235 copy.extensions = extension_stack
1236
1237 copy.sequence = original.sequence
1238 return copy
1239
1240
1241
1242class Revoked(object):
1243 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1244 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1245 # OCSP_crl_reason_str. We use the latter, just like the command line
1246 # program.
1247 _crl_reasons = [
1248 "unspecified",
1249 "keyCompromise",
1250 "CACompromise",
1251 "affiliationChanged",
1252 "superseded",
1253 "cessationOfOperation",
1254 "certificateHold",
1255 # "removeFromCRL",
1256 ]
1257
1258 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001259 revoked = _lib.X509_REVOKED_new()
1260 self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001261
1262
1263 def set_serial(self, hex_str):
1264 """
1265 Set the serial number of a revoked Revoked structure
1266
1267 :param hex_str: The new serial number.
1268 :type hex_str: :py:data:`str`
1269 :return: None
1270 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001271 bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free)
1272 bignum_ptr = _ffi.new("BIGNUM**")
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001273 bignum_ptr[0] = bignum_serial
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001274 bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001275 if not bn_result:
1276 raise ValueError("bad hex string")
1277
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001278 asn1_serial = _ffi.gc(
1279 _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
1280 _lib.ASN1_INTEGER_free)
1281 _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001282
1283
1284 def get_serial(self):
1285 """
1286 Return the serial number of a Revoked structure
1287
1288 :return: The serial number as a string
1289 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001290 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001291
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001292 result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001293 if result < 0:
1294 1/0
1295
1296 return _bio_to_string(bio)
1297
1298
1299 def _delete_reason(self):
1300 stack = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001301 for i in range(_lib.sk_X509_EXTENSION_num(stack)):
1302 ext = _lib.sk_X509_EXTENSION_value(stack, i)
1303 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
1304 _lib.X509_EXTENSION_free(ext)
1305 _lib.sk_X509_EXTENSION_delete(stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001306 break
1307
1308
1309 def set_reason(self, reason):
1310 """
1311 Set the reason of a Revoked object.
1312
1313 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1314
1315 :param reason: The reason string.
1316 :type reason: :py:class:`str` or :py:class:`NoneType`
1317 :return: None
1318 """
1319 if reason is None:
1320 self._delete_reason()
1321 elif not isinstance(reason, bytes):
1322 raise TypeError("reason must be None or a byte string")
1323 else:
1324 reason = reason.lower().replace(' ', '')
1325 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1326
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001327 new_reason_ext = _lib.ASN1_ENUMERATED_new()
1328 if new_reason_ext == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001329 1/0
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001330 new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001331
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001332 set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1333 if set_result == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001334 1/0
1335
1336 self._delete_reason()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001337 add_result = _lib.X509_REVOKED_add1_ext_i2d(
1338 self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001339
1340 if not add_result:
1341 1/0
1342
1343
1344 def get_reason(self):
1345 """
1346 Return the reason of a Revoked object.
1347
1348 :return: The reason as a string
1349 """
1350 extensions = self._revoked.extensions
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001351 for i in range(_lib.sk_X509_EXTENSION_num(extensions)):
1352 ext = _lib.sk_X509_EXTENSION_value(extensions, i)
1353 if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001354 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001355
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001356 print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001357 if not print_result:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001358 print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001359 if print_result == 0:
1360 1/0
1361
1362 return _bio_to_string(bio)
1363
1364
1365 def all_reasons(self):
1366 """
1367 Return a list of all the supported reason strings.
1368
1369 :return: A list of reason strings.
1370 """
1371 return self._crl_reasons[:]
1372
1373
1374 def set_rev_date(self, when):
1375 """
1376 Set the revocation timestamp
1377
1378 :param when: A string giving the timestamp, in the format:
1379
1380 YYYYMMDDhhmmssZ
1381 YYYYMMDDhhmmss+hhmm
1382 YYYYMMDDhhmmss-hhmm
1383
1384 :return: None
1385 """
1386 return _set_asn1_time(self._revoked.revocationDate, when)
1387
1388
1389 def get_rev_date(self):
1390 """
1391 Retrieve the revocation date
1392
1393 :return: A string giving the timestamp, in the format:
1394
1395 YYYYMMDDhhmmssZ
1396 YYYYMMDDhhmmss+hhmm
1397 YYYYMMDDhhmmss-hhmm
1398 """
1399 return _get_asn1_time(self._revoked.revocationDate)
1400
1401
1402
1403class CRL(object):
1404 def __init__(self):
1405 """
1406 Create a new empty CRL object.
1407 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001408 crl = _lib.X509_CRL_new()
1409 self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001410
1411
1412 def get_revoked(self):
1413 """
1414 Return revoked portion of the CRL structure (by value not reference).
1415
1416 :return: A tuple of Revoked objects.
1417 """
1418 results = []
1419 revoked_stack = self._crl.crl.revoked
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001420 for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
1421 revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001422 revoked_copy = _X509_REVOKED_dup(revoked)
1423 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001424 pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001425 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001426 if results:
1427 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001428
1429
1430 def add_revoked(self, revoked):
1431 """
1432 Add a revoked (by value not reference) to the CRL structure
1433
1434 :param revoked: The new revoked.
1435 :type revoked: :class:`X509`
1436
1437 :return: None
1438 """
1439 copy = _X509_REVOKED_dup(revoked._revoked)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001440 if copy == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001441 1/0
1442
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001443 add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001444 # TODO what check on add_result?
1445
1446
1447 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1448 """
1449 export a CRL as a string
1450
1451 :param cert: Used to sign CRL.
1452 :type cert: :class:`X509`
1453
1454 :param key: Used to sign CRL.
1455 :type key: :class:`PKey`
1456
1457 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1458
1459 :param days: The number of days until the next update of this CRL.
1460 :type days: :py:data:`int`
1461
1462 :return: :py:data:`str`
1463 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001464 if not isinstance(cert, X509):
1465 raise TypeError("cert must be an X509 instance")
1466 if not isinstance(key, PKey):
1467 raise TypeError("key must be a PKey instance")
1468 if not isinstance(type, int):
1469 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001470
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001471 bio = _lib.BIO_new(_lib.BIO_s_mem())
1472 if bio == _ffi.NULL:
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001473 1/0
1474
1475 # A scratch time object to give different values to different CRL fields
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001476 sometime = _lib.ASN1_TIME_new()
1477 if sometime == _ffi.NULL:
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001478 1/0
1479
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001480 _lib.X509_gmtime_adj(sometime, 0)
1481 _lib.X509_CRL_set_lastUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001482
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001483 _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1484 _lib.X509_CRL_set_nextUpdate(self._crl, sometime)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001485
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001486 _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509))
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001487
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001488 sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5())
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001489 if not sign_result:
1490 _raise_current_error()
1491
1492 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001493 ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001494 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001495 ret = _lib.i2d_X509_CRL_bio(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001496 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001497 ret = _lib.X509_CRL_print(bio, self._crl)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001498 else:
1499 raise ValueError(
1500 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1501
1502 if not ret:
1503 1/0
1504
1505 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001506CRLType = CRL
1507
1508
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001509
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001510class PKCS7(object):
1511 def type_is_signed(self):
1512 """
1513 Check if this NID_pkcs7_signed object
1514
1515 :return: True if the PKCS7 is of type signed
1516 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001517 if _lib.PKCS7_type_is_signed(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001518 return True
1519 return False
1520
1521
1522 def type_is_enveloped(self):
1523 """
1524 Check if this NID_pkcs7_enveloped object
1525
1526 :returns: True if the PKCS7 is of type enveloped
1527 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001528 if _lib.PKCS7_type_is_enveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001529 return True
1530 return False
1531
1532
1533 def type_is_signedAndEnveloped(self):
1534 """
1535 Check if this NID_pkcs7_signedAndEnveloped object
1536
1537 :returns: True if the PKCS7 is of type signedAndEnveloped
1538 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001539 if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001540 return True
1541 return False
1542
1543
1544 def type_is_data(self):
1545 """
1546 Check if this NID_pkcs7_data object
1547
1548 :return: True if the PKCS7 is of type data
1549 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001550 if _lib.PKCS7_type_is_data(self._pkcs7):
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001551 return True
1552 return False
1553
1554
1555 def get_type_name(self):
1556 """
1557 Returns the type name of the PKCS7 structure
1558
1559 :return: A string with the typename
1560 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001561 nid = _lib.OBJ_obj2nid(self._pkcs7.type)
1562 string_type = _lib.OBJ_nid2sn(nid)
1563 return _ffi.string(string_type)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001564
1565PKCS7Type = PKCS7
1566
1567
1568
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001569class PKCS12(object):
1570 def __init__(self):
1571 self._pkey = None
1572 self._cert = None
1573 self._cacerts = None
1574 self._friendlyname = None
1575
1576
1577 def get_certificate(self):
1578 """
1579 Return certificate portion of the PKCS12 structure
1580
1581 :return: X509 object containing the certificate
1582 """
1583 return self._cert
1584
1585
1586 def set_certificate(self, cert):
1587 """
1588 Replace the certificate portion of the PKCS12 structure
1589
1590 :param cert: The new certificate.
1591 :type cert: :py:class:`X509` or :py:data:`None`
1592 :return: None
1593 """
1594 if not isinstance(cert, X509):
1595 raise TypeError("cert must be an X509 instance")
1596 self._cert = cert
1597
1598
1599 def get_privatekey(self):
1600 """
1601 Return private key portion of the PKCS12 structure
1602
1603 :returns: PKey object containing the private key
1604 """
1605 return self._pkey
1606
1607
1608 def set_privatekey(self, pkey):
1609 """
1610 Replace or set the certificate portion of the PKCS12 structure
1611
1612 :param pkey: The new private key.
1613 :type pkey: :py:class:`PKey`
1614 :return: None
1615 """
1616 if not isinstance(pkey, PKey):
1617 raise TypeError("pkey must be a PKey instance")
1618 self._pkey = pkey
1619
1620
1621 def get_ca_certificates(self):
1622 """
1623 Return CA certificates within of the PKCS12 object
1624
1625 :return: A newly created tuple containing the CA certificates in the chain,
1626 if any are present, or None if no CA certificates are present.
1627 """
1628 if self._cacerts is not None:
1629 return tuple(self._cacerts)
1630
1631
1632 def set_ca_certificates(self, cacerts):
1633 """
1634 Replace or set the CA certificates withing the PKCS12 object.
1635
1636 :param cacerts: The new CA certificates.
1637 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1638 :return: None
1639 """
1640 if cacerts is None:
1641 self._cacerts = None
1642 else:
1643 cacerts = list(cacerts)
1644 for cert in cacerts:
1645 if not isinstance(cert, X509):
1646 raise TypeError("iterable must only contain X509 instances")
1647 self._cacerts = cacerts
1648
1649
1650 def set_friendlyname(self, name):
1651 """
1652 Replace or set the certificate portion of the PKCS12 structure
1653
1654 :param name: The new friendly name.
1655 :type name: :py:class:`bytes`
1656 :return: None
1657 """
1658 if name is None:
1659 self._friendlyname = None
1660 elif not isinstance(name, bytes):
1661 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1662 self._friendlyname = name
1663
1664
1665 def get_friendlyname(self):
1666 """
1667 Return friendly name portion of the PKCS12 structure
1668
1669 :returns: String containing the friendlyname
1670 """
1671 return self._friendlyname
1672
1673
1674 def export(self, passphrase=None, iter=2048, maciter=1):
1675 """
1676 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1677
1678 :param passphrase: used to encrypt the PKCS12
1679 :type passphrase: :py:data:`bytes`
1680
1681 :param iter: How many times to repeat the encryption
1682 :type iter: :py:data:`int`
1683
1684 :param maciter: How many times to repeat the MAC
1685 :type maciter: :py:data:`int`
1686
1687 :return: The string containing the PKCS12
1688 """
1689 if self._cacerts is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001690 cacerts = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001691 else:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001692 cacerts = _lib.sk_X509_new_null()
1693 cacerts = _ffi.gc(cacerts, _lib.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001694 for cert in self._cacerts:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001695 _lib.sk_X509_push(cacerts, cert._x509)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001696
1697 if passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001698 passphrase = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001699
1700 friendlyname = self._friendlyname
1701 if friendlyname is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001702 friendlyname = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001703
1704 if self._pkey is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001705 pkey = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001706 else:
1707 pkey = self._pkey._pkey
1708
1709 if self._cert is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001710 cert = _ffi.NULL
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001711 else:
1712 cert = self._cert._x509
1713
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001714 pkcs12 = _lib.PKCS12_create(
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001715 passphrase, friendlyname, pkey, cert, cacerts,
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001716 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1717 _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001718 iter, maciter, 0)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001719 if pkcs12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001720 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001721 pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001722
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001723 bio = _new_mem_buf()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001724 _lib.i2d_PKCS12_bio(bio, pkcs12)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001725 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001726
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001727PKCS12Type = PKCS12
1728
1729
1730
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001731class NetscapeSPKI(object):
1732 def __init__(self):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001733 spki = _lib.NETSCAPE_SPKI_new()
1734 self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001735
1736
1737 def sign(self, pkey, digest):
1738 """
1739 Sign the certificate request using the supplied key and digest
1740
1741 :param pkey: The key to sign with
1742 :param digest: The message digest to use
1743 :return: None
1744 """
1745 if pkey._only_public:
1746 raise ValueError("Key has only public part")
1747
1748 if not pkey._initialized:
1749 raise ValueError("Key is uninitialized")
1750
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001751 digest_obj = _lib.EVP_get_digestbyname(digest)
1752 if digest_obj == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001753 raise ValueError("No such digest method")
1754
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001755 sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001756 if not sign_result:
1757 1/0
1758
1759
1760 def verify(self, key):
1761 """
1762 Verifies a certificate request using the supplied public key
1763
1764 :param key: a public key
1765 :return: True if the signature is correct.
1766 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1767 problem verifying the signature.
1768 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001769 answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001770 if answer <= 0:
1771 _raise_current_error()
1772 return True
1773
1774
1775 def b64_encode(self):
1776 """
1777 Generate a base64 encoded string from an SPKI
1778
1779 :return: The base64 encoded string
1780 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001781 encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
1782 result = _ffi.string(encoded)
1783 _lib.CRYPTO_free(encoded)
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001784 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001785
1786
1787 def get_pubkey(self):
1788 """
1789 Get the public key of the certificate
1790
1791 :return: The public key
1792 """
1793 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001794 pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
1795 if pkey._pkey == _ffi.NULL:
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001796 1/0
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001797 pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001798 pkey._only_public = True
1799 return pkey
1800
1801
1802 def set_pubkey(self, pkey):
1803 """
1804 Set the public key of the certificate
1805
1806 :param pkey: The public key
1807 :return: None
1808 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001809 set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001810 if not set_result:
1811 1/0
1812NetscapeSPKIType = NetscapeSPKI
1813
1814
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001815class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001816 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001817 if type != FILETYPE_PEM and passphrase is not None:
1818 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001819 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001820 self._more_args = more_args
1821 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001822 self._problems = []
1823
1824
1825 @property
1826 def callback(self):
1827 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001828 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001829 elif isinstance(self._passphrase, bytes):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001830 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001831 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001832 return _ffi.callback("pem_password_cb", self._read_passphrase)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001833 else:
1834 raise TypeError("Last argument must be string or callable")
1835
1836
1837 @property
1838 def callback_args(self):
1839 if self._passphrase is None:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001840 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001841 elif isinstance(self._passphrase, bytes):
1842 return self._passphrase
1843 elif callable(self._passphrase):
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001844 return _ffi.NULL
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001845 else:
1846 raise TypeError("Last argument must be string or callable")
1847
1848
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001849 def raise_if_problem(self, exceptionType=Error):
1850 try:
1851 _raise_current_error(exceptionType)
1852 except exceptionType as e:
1853 pass
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001854 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001855 raise self._problems[0]
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001856 return e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001857
1858
1859 def _read_passphrase(self, buf, size, rwflag, userdata):
1860 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001861 if self._more_args:
1862 result = self._passphrase(size, rwflag, userdata)
1863 else:
1864 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001865 if not isinstance(result, bytes):
1866 raise ValueError("String expected")
1867 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001868 if self._truncate:
1869 result = result[:size]
1870 else:
1871 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001872 for i in range(len(result)):
1873 buf[i] = result[i]
1874 return len(result)
1875 except Exception as e:
1876 self._problems.append(e)
1877 return 0
1878
1879
1880
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001881def load_privatekey(type, buffer, passphrase=None):
1882 """
1883 Load a private key from a buffer
1884
1885 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1886 :param buffer: The buffer the key is stored in
1887 :param passphrase: (optional) if encrypted PEM format, this can be
1888 either the passphrase to use, or a callback for
1889 providing the passphrase.
1890
1891 :return: The PKey object
1892 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001893 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001894
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001895 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001896 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001897 evp_pkey = _lib.PEM_read_bio_PrivateKey(
1898 bio, _ffi.NULL, helper.callback, helper.callback_args)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001899 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001900 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001901 evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001902 else:
1903 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
1904
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001905 if evp_pkey == _ffi.NULL:
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08001906 _raise_current_error()
1907
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001908 pkey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001909 pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001910 return pkey
1911
1912
1913
1914def dump_certificate_request(type, req):
1915 """
1916 Dump a certificate request to a buffer
1917
1918 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1919 :param req: The certificate request to dump
1920 :return: The buffer with the dumped certificate request in
1921 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001922 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001923
1924 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001925 result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001926 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001927 result_code = _lib.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001928 elif type == FILETYPE_TEXT:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001929 result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001930 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08001931 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001932
1933 if result_code == 0:
1934 1/0
1935
1936 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001937
1938
1939
1940def load_certificate_request(type, buffer):
1941 """
1942 Load a certificate request from a buffer
1943
1944 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1945 :param buffer: The buffer the certificate request is stored in
1946 :return: The X509Req object
1947 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001948 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001949
1950 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001951 req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001952 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001953 req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001954 else:
1955 1/0
1956
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001957 if req == _ffi.NULL:
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001958 1/0
1959
1960 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001961 x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001962 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001963
1964
1965
1966def sign(pkey, data, digest):
1967 """
1968 Sign data with a digest
1969
1970 :param pkey: Pkey to sign with
1971 :param data: data to be signed
1972 :param digest: message digest to use
1973 :return: signature
1974 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001975 digest_obj = _lib.EVP_get_digestbyname(digest)
1976 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001977 raise ValueError("No such digest method")
1978
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001979 md_ctx = _ffi.new("EVP_MD_CTX*")
1980 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001981
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001982 _lib.EVP_SignInit(md_ctx, digest_obj)
1983 _lib.EVP_SignUpdate(md_ctx, data, len(data))
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001984
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001985 signature_buffer = _ffi.new("unsigned char[]", 512)
1986 signature_length = _ffi.new("unsigned int*")
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001987 signature_length[0] = len(signature_buffer)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001988 final_result = _lib.EVP_SignFinal(
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001989 md_ctx, signature_buffer, signature_length, pkey._pkey)
1990
1991 if final_result != 1:
1992 1/0
1993
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05001994 return _ffi.buffer(signature_buffer, signature_length[0])[:]
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001995
1996
1997
1998def verify(cert, signature, data, digest):
1999 """
2000 Verify a signature
2001
2002 :param cert: signing certificate (X509 object)
2003 :param signature: signature returned by sign function
2004 :param data: data to be verified
2005 :param digest: message digest to use
2006 :return: None if the signature is correct, raise exception otherwise
2007 """
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002008 digest_obj = _lib.EVP_get_digestbyname(digest)
2009 if digest_obj == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002010 raise ValueError("No such digest method")
2011
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002012 pkey = _lib.X509_get_pubkey(cert._x509)
2013 if pkey == _ffi.NULL:
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002014 1/0
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002015 pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002016
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002017 md_ctx = _ffi.new("EVP_MD_CTX*")
2018 md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002019
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002020 _lib.EVP_VerifyInit(md_ctx, digest_obj)
2021 _lib.EVP_VerifyUpdate(md_ctx, data, len(data))
2022 verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08002023
2024 if verify_result != 1:
2025 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002026
2027
2028
2029def load_crl(type, buffer):
2030 """
2031 Load a certificate revocation list from a buffer
2032
2033 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
2034 :param buffer: The buffer the CRL is stored in
2035
2036 :return: The PKey object
2037 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002038 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002039
2040 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002041 crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002042 elif type == FILETYPE_ASN1:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002043 crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002044 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002045 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2046
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002047 if crl == _ffi.NULL:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08002048 _raise_current_error()
2049
2050 result = CRL.__new__(CRL)
2051 result._crl = crl
2052 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002053
2054
2055
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002056def load_pkcs7_data(type, buffer):
2057 """
2058 Load pkcs7 data from a buffer
2059
2060 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
2061 :param buffer: The buffer with the pkcs7 data.
2062 :return: The PKCS7 object
2063 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002064 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002065
2066 if type == FILETYPE_PEM:
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002067 pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002068 elif type == FILETYPE_ASN1:
2069 pass
2070 else:
2071 1/0
2072 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2073
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002074 if pkcs7 == _ffi.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002075 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002076
2077 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002078 pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002079 return pypkcs7
2080
2081
2082
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002083def load_pkcs12(buffer, passphrase):
2084 """
2085 Load a PKCS12 object from a buffer
2086
2087 :param buffer: The buffer the certificate is stored in
2088 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2089 :returns: The PKCS12 object
2090 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002091 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002092
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002093 p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
2094 if p12 == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002095 _raise_current_error()
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002096 p12 = _ffi.gc(p12, _lib.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002097
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002098 pkey = _ffi.new("EVP_PKEY**")
2099 cert = _ffi.new("X509**")
2100 cacerts = _ffi.new("Cryptography_STACK_OF_X509**")
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002101
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002102 parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002103 if not parse_result:
2104 _raise_current_error()
2105
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002106 cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002107
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002108 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2109 # queue for no particular reason. This error isn't interesting to anyone
2110 # outside this function. It's not even interesting to us. Get rid of it.
2111 try:
2112 _raise_current_error()
2113 except Error:
2114 pass
2115
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002116 if pkey[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002117 pykey = None
2118 else:
2119 pykey = PKey.__new__(PKey)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002120 pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002121
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002122 if cert[0] == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002123 pycert = None
2124 friendlyname = None
2125 else:
2126 pycert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002127 pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002128
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002129 friendlyname_length = _ffi.new("int*")
2130 friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length)
2131 friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2132 if friendlyname_buffer == _ffi.NULL:
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002133 friendlyname = None
2134
2135 pycacerts = []
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002136 for i in range(_lib.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002137 pycacert = X509.__new__(X509)
Jean-Paul Calderone6037d072013-12-28 18:04:00 -05002138 pycacert._x509 = _lib.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002139 pycacerts.append(pycacert)
2140 if not pycacerts:
2141 pycacerts = None
2142
2143 pkcs12 = PKCS12.__new__(PKCS12)
2144 pkcs12._pkey = pykey
2145 pkcs12._cert = pycert
2146 pkcs12._cacerts = pycacerts
2147 pkcs12._friendlyname = friendlyname
2148 return pkcs12