blob: 736227b7fe5eec829806ea396c15f8902b4afdb4 [file] [log] [blame]
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001from time import time
2
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08003from tls.c import api as _api
4
5FILETYPE_PEM = _api.SSL_FILETYPE_PEM
6FILETYPE_ASN1 = _api.SSL_FILETYPE_ASN1
7
8# TODO This was an API mistake. OpenSSL has no such constant.
9FILETYPE_TEXT = 2 ** 16 - 1
10
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080011TYPE_RSA = _api.EVP_PKEY_RSA
12TYPE_DSA = _api.EVP_PKEY_DSA
13
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080014
15def _bio_to_string(bio):
16 """
17 Copy the contents of an OpenSSL BIO object into a Python byte string.
18 """
19 result_buffer = _api.new('char**')
20 buffer_length = _api.BIO_get_mem_data(bio, result_buffer)
21 return _api.buffer(result_buffer[0], buffer_length)[:]
22
23
24
Jean-Paul Calderonefd371362013-03-01 20:53:58 -080025def _new_mem_buf(buffer=None):
26 if buffer is None:
27 bio = _api.BIO_new(_api.BIO_s_mem())
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -080028 free = _api.BIO_free
Jean-Paul Calderonefd371362013-03-01 20:53:58 -080029 else:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -080030 data = _api.ffi.new("char[]", buffer)
31 bio = _api.BIO_new_mem_buf(data, len(buffer))
32 # Keep the memory alive as long as the bio is alive!
33 def free(bio, ref=data):
34 return _api.BIO_free(bio)
35
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080036 if bio == _api.NULL:
37 1/0
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -080038
39 bio = _api.ffi.gc(bio, free)
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080040 return bio
41
42
43
Jean-Paul Calderone57122982013-02-21 08:47:05 -080044def _set_asn1_time(boundary, when):
45 if not isinstance(when, bytes):
46 raise TypeError("when must be a byte string")
47
48 set_result = _api.ASN1_GENERALIZEDTIME_set_string(
49 _api.cast('ASN1_GENERALIZEDTIME*', boundary), when)
50 if set_result == 0:
Jean-Paul Calderone3a90aa52013-03-02 07:33:36 -080051 dummy = _api.ffi.gc(_api.ASN1_STRING_new(), _api.ASN1_STRING_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -080052 _api.ASN1_STRING_set(dummy, when, len(when))
53 check_result = _api.ASN1_GENERALIZEDTIME_check(
54 _api.cast('ASN1_GENERALIZEDTIME*', dummy))
55 if not check_result:
56 raise ValueError("Invalid string")
57 else:
58 # TODO No tests for this case
59 raise RuntimeError("Unknown ASN1_GENERALIZEDTIME_set_string failure")
60
61
62
63def _get_asn1_time(timestamp):
64 string_timestamp = _api.cast('ASN1_STRING*', timestamp)
65 if _api.ASN1_STRING_length(string_timestamp) == 0:
66 return None
67 elif _api.ASN1_STRING_type(string_timestamp) == _api.V_ASN1_GENERALIZEDTIME:
68 return _api.string(_api.ASN1_STRING_data(string_timestamp))
69 else:
70 generalized_timestamp = _api.new("ASN1_GENERALIZEDTIME**")
71 _api.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
72 if generalized_timestamp[0] == _api.NULL:
73 1/0
74 else:
75 string_timestamp = _api.cast(
76 "ASN1_STRING*", generalized_timestamp[0])
77 string_data = _api.ASN1_STRING_data(string_timestamp)
78 string_result = _api.string(string_data)
79 _api.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
80 return string_result
81
82
83
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -080084class Error(Exception):
85 pass
86
87
88
89def _raise_current_error(exceptionType=Error):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080090 errors = []
91 while True:
92 error = _api.ERR_get_error()
93 if error == 0:
94 break
95 errors.append((
96 _api.string(_api.ERR_lib_error_string(error)),
97 _api.string(_api.ERR_func_error_string(error)),
98 _api.string(_api.ERR_reason_error_string(error))))
99
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -0800100 raise exceptionType(errors)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800101
102_exception_from_error_queue = _raise_current_error
103
104
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800105
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800106class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800107 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800108 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800109
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800110 def __init__(self):
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800111 pkey = _api.EVP_PKEY_new()
112 self._pkey = _api.ffi.gc(pkey, _api.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800113 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800114
115
116 def generate_key(self, type, bits):
117 """
118 Generate a key of a given type, with a given number of a bits
119
120 :param type: The key type (TYPE_RSA or TYPE_DSA)
121 :param bits: The number of bits
122
123 :return: None
124 """
125 if not isinstance(type, int):
126 raise TypeError("type must be an integer")
127
128 if not isinstance(bits, int):
129 raise TypeError("bits must be an integer")
130
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800131 # TODO Check error return
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800132 exponent = _api.BN_new()
133 exponent = _api.ffi.gc(exponent, _api.BN_free)
134 _api.BN_set_word(exponent, _api.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800135
136 if type == TYPE_RSA:
137 if bits <= 0:
138 raise ValueError("Invalid number of bits")
139
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800140 rsa = _api.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800141
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800142 result = _api.RSA_generate_key_ex(rsa, bits, exponent, _api.NULL)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800143 if result == -1:
144 1/0
145
146 result = _api.EVP_PKEY_assign_RSA(self._pkey, rsa)
147 if not result:
148 1/0
149
150 elif type == TYPE_DSA:
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800151 dsa = _api.DSA_generate_parameters(
152 bits, _api.NULL, 0, _api.NULL, _api.NULL, _api.NULL, _api.NULL)
153 if dsa == _api.NULL:
154 1/0
155 if not _api.DSA_generate_key(dsa):
156 1/0
157 if not _api.EVP_PKEY_assign_DSA(self._pkey, dsa):
158 1/0
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800159 else:
160 raise Error("No such key type")
161
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800162 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800163
164
165 def check(self):
166 """
167 Check the consistency of an RSA private key.
168
169 :return: True if key is consistent.
170 :raise Error: if the key is inconsistent.
171 :raise TypeError: if the key is of a type which cannot be checked.
172 Only RSA keys can currently be checked.
173 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800174 if self._only_public:
175 raise TypeError("public key only")
176
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800177 if _api.EVP_PKEY_type(self._pkey.type) != _api.EVP_PKEY_RSA:
178 raise TypeError("key type unsupported")
179
180 rsa = _api.EVP_PKEY_get1_RSA(self._pkey)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800181 rsa = _api.ffi.gc(rsa, _api.RSA_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800182 result = _api.RSA_check_key(rsa)
183 if result:
184 return True
185 _raise_current_error()
186
187
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800188 def type(self):
189 """
190 Returns the type of the key
191
192 :return: The type of the key.
193 """
194 return self._pkey.type
195
196
197 def bits(self):
198 """
199 Returns the number of bits of the key
200
201 :return: The number of bits of the key.
202 """
203 return _api.EVP_PKEY_bits(self._pkey)
204PKeyType = PKey
205
206
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800207
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800208class X509Name(object):
209 def __init__(self, name):
210 """
211 Create a new X509Name, copying the given X509Name instance.
212
213 :param name: An X509Name object to copy
214 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800215 name = _api.X509_NAME_dup(name._name)
216 self._name = _api.ffi.gc(name, _api.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800217
218
219 def __setattr__(self, name, value):
220 if name.startswith('_'):
221 return super(X509Name, self).__setattr__(name, value)
222
Jean-Paul Calderoneff363be2013-03-03 10:21:23 -0800223 # Note: we really do not want str subclasses here, so we do not use
224 # isinstance.
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800225 if type(name) is not str:
226 raise TypeError("attribute name must be string, not '%.200s'" % (
227 type(value).__name__,))
228
229 nid = _api.OBJ_txt2nid(name)
230 if nid == _api.NID_undef:
231 try:
232 _raise_current_error()
233 except Error:
234 pass
235 raise AttributeError("No such attribute")
236
237 # If there's an old entry for this NID, remove it
238 for i in range(_api.X509_NAME_entry_count(self._name)):
239 ent = _api.X509_NAME_get_entry(self._name, i)
240 ent_obj = _api.X509_NAME_ENTRY_get_object(ent)
241 ent_nid = _api.OBJ_obj2nid(ent_obj)
242 if nid == ent_nid:
243 ent = _api.X509_NAME_delete_entry(self._name, i)
244 _api.X509_NAME_ENTRY_free(ent)
245 break
246
247 if isinstance(value, unicode):
248 value = value.encode('utf-8')
249
250 add_result = _api.X509_NAME_add_entry_by_NID(
251 self._name, nid, _api.MBSTRING_UTF8, value, -1, -1, 0)
252 if not add_result:
253 # TODO Untested
254 1/0
255
256
257 def __getattr__(self, name):
258 """
259 Find attribute. An X509Name object has the following attributes:
260 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
261 organization (alias O), organizationalUnit (alias OU), commonName (alias
262 CN) and more...
263 """
264 nid = _api.OBJ_txt2nid(name)
265 if nid == _api.NID_undef:
266 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
267 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
268 # push something onto the error queue. If we don't clean that up
269 # now, someone else will bump into it later and be quite confused.
270 # See lp#314814.
271 try:
272 _raise_current_error()
273 except Error:
274 pass
275 return super(X509Name, self).__getattr__(name)
276
277 entry_index = _api.X509_NAME_get_index_by_NID(self._name, nid, -1)
278 if entry_index == -1:
279 return None
280
281 entry = _api.X509_NAME_get_entry(self._name, entry_index)
282 data = _api.X509_NAME_ENTRY_get_data(entry)
283
284 result_buffer = _api.new("unsigned char**")
285 data_length = _api.ASN1_STRING_to_UTF8(result_buffer, data)
286 if data_length < 0:
287 1/0
288
289 result = _api.buffer(result_buffer[0], data_length)[:].decode('utf-8')
290 _api.OPENSSL_free(result_buffer[0])
291 return result
292
293
294 def __cmp__(self, other):
295 if not isinstance(other, X509Name):
296 return NotImplemented
297
298 result = _api.X509_NAME_cmp(self._name, other._name)
299 # TODO result == -2 is an error case that maybe should be checked for
300 return result
301
302
303 def __repr__(self):
304 """
305 String representation of an X509Name
306 """
307 result_buffer = _api.new("char[]", 512);
308 format_result = _api.X509_NAME_oneline(
309 self._name, result_buffer, len(result_buffer))
310
311 if format_result == _api.NULL:
312 1/0
313
314 return "<X509Name object '%s'>" % (_api.string(result_buffer),)
315
316
317 def hash(self):
318 """
319 Return the hash value of this name
320
321 :return: None
322 """
323 return _api.X509_NAME_hash(self._name)
324
325
326 def der(self):
327 """
328 Return the DER encoding of this name
329
330 :return: A :py:class:`bytes` instance giving the DER encoded form of
331 this name.
332 """
333 result_buffer = _api.new('unsigned char**')
334 encode_result = _api.i2d_X509_NAME(self._name, result_buffer)
335 if encode_result < 0:
336 1/0
337
338 string_result = _api.buffer(result_buffer[0], encode_result)[:]
339 _api.OPENSSL_free(result_buffer[0])
340 return string_result
341
342
343 def get_components(self):
344 """
345 Returns the split-up components of this name.
346
347 :return: List of tuples (name, value).
348 """
349 result = []
350 for i in range(_api.X509_NAME_entry_count(self._name)):
351 ent = _api.X509_NAME_get_entry(self._name, i)
352
353 fname = _api.X509_NAME_ENTRY_get_object(ent)
354 fval = _api.X509_NAME_ENTRY_get_data(ent)
355
356 nid = _api.OBJ_obj2nid(fname)
357 name = _api.OBJ_nid2sn(nid)
358
359 result.append((
360 _api.string(name),
361 _api.string(
362 _api.ASN1_STRING_data(fval),
363 _api.ASN1_STRING_length(fval))))
364
365 return result
366X509NameType = X509Name
367
368
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800369class X509Extension(object):
370 def __init__(self, type_name, critical, value, subject=None, issuer=None):
371 """
372 :param typename: The name of the extension to create.
373 :type typename: :py:data:`str`
374
375 :param critical: A flag indicating whether this is a critical extension.
376
377 :param value: The value of the extension.
378 :type value: :py:data:`str`
379
380 :param subject: Optional X509 cert to use as subject.
381 :type subject: :py:class:`X509`
382
383 :param issuer: Optional X509 cert to use as issuer.
384 :type issuer: :py:class:`X509`
385
386 :return: The X509Extension object
387 """
388 ctx = _api.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800389
390 # A context is necessary for any extension which uses the r2i conversion
391 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
392 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800393 _api.X509V3_set_ctx(ctx, _api.NULL, _api.NULL, _api.NULL, _api.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800394
395 # We have no configuration database - but perhaps we should (some
396 # extensions may require it).
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800397 _api.X509V3_set_ctx_nodb(ctx)
398
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800399 # Initialize the subject and issuer, if appropriate. ctx is a local,
400 # and as far as I can tell none of the X509V3_* APIs invoked here steal
401 # any references, so no need to mess with reference counts or duplicates.
402 if issuer is not None:
403 if not isinstance(issuer, X509):
404 raise TypeError("issuer must be an X509 instance")
405 ctx.issuer_cert = issuer._x509
406 if subject is not None:
407 if not isinstance(subject, X509):
408 raise TypeError("subject must be an X509 instance")
409 ctx.subject_cert = subject._x509
410
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800411 if critical:
412 # There are other OpenSSL APIs which would let us pass in critical
413 # separately, but they're harder to use, and since value is already
414 # a pile of crappy junk smuggling a ton of utterly important
415 # structured data, what's the point of trying to avoid nasty stuff
416 # with strings? (However, X509V3_EXT_i2d in particular seems like it
417 # would be a better API to invoke. I do not know where to get the
418 # ext_struc it desires for its last parameter, though.)
419 value = "critical," + value
420
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800421 extension = _api.X509V3_EXT_nconf(_api.NULL, ctx, type_name, value)
422 if extension == _api.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800423 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800424 self._extension = _api.ffi.gc(extension, _api.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800425
426
427 def __str__(self):
428 """
429 :return: a nice text representation of the extension
430 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -0800431 bio = _new_mem_buf()
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800432
433 print_result = _api.X509V3_EXT_print(bio, self._extension, 0, 0)
434 if not print_result:
435 1/0
436
437 return _bio_to_string(bio)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800438
439
440 def get_critical(self):
441 """
442 Returns the critical field of the X509Extension
443
444 :return: The critical field.
445 """
446 return _api.X509_EXTENSION_get_critical(self._extension)
447
448
449 def get_short_name(self):
450 """
451 Returns the short version of the type name of the X509Extension
452
453 :return: The short type name.
454 """
455 obj = _api.X509_EXTENSION_get_object(self._extension)
456 nid = _api.OBJ_obj2nid(obj)
457 return _api.string(_api.OBJ_nid2sn(nid))
458
459
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800460 def get_data(self):
461 """
462 Returns the data of the X509Extension
463
464 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
465 """
466 octet_result = _api.X509_EXTENSION_get_data(self._extension)
467 string_result = _api.cast('ASN1_STRING*', octet_result)
468 char_result = _api.ASN1_STRING_data(string_result)
469 result_length = _api.ASN1_STRING_length(string_result)
470 return _api.buffer(char_result, result_length)[:]
471
472X509ExtensionType = X509Extension
473
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800474
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800475class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800476 def __init__(self):
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800477 req = _api.X509_REQ_new()
478 self._req = _api.ffi.gc(req, _api.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800479
480
481 def set_pubkey(self, pkey):
482 """
483 Set the public key of the certificate request
484
485 :param pkey: The public key to use
486 :return: None
487 """
488 set_result = _api.X509_REQ_set_pubkey(self._req, pkey._pkey)
489 if not set_result:
490 1/0
491
492
493 def get_pubkey(self):
494 """
495 Get the public key from the certificate request
496
497 :return: The public key
498 """
499 pkey = PKey.__new__(PKey)
500 pkey._pkey = _api.X509_REQ_get_pubkey(self._req)
501 if pkey._pkey == _api.NULL:
502 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800503 pkey._pkey = _api.ffi.gc(pkey._pkey, _api.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800504 pkey._only_public = True
505 return pkey
506
507
508 def set_version(self, version):
509 """
510 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
511 request.
512
513 :param version: The version number
514 :return: None
515 """
516 set_result = _api.X509_REQ_set_version(self._req, version)
517 if not set_result:
518 _raise_current_error()
519
520
521 def get_version(self):
522 """
523 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
524 request.
525
526 :return: an integer giving the value of the version subfield
527 """
528 return _api.X509_REQ_get_version(self._req)
529
530
531 def get_subject(self):
532 """
533 Create an X509Name object for the subject of the certificate request
534
535 :return: An X509Name object
536 """
537 name = X509Name.__new__(X509Name)
538 name._name = _api.X509_REQ_get_subject_name(self._req)
539 if name._name == _api.NULL:
540 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800541
542 # The name is owned by the X509Req structure. As long as the X509Name
543 # Python object is alive, keep the X509Req Python object alive.
544 name._owner = self
545
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800546 return name
547
548
549 def add_extensions(self, extensions):
550 """
551 Add extensions to the request.
552
553 :param extensions: a sequence of X509Extension objects
554 :return: None
555 """
556 stack = _api.sk_X509_EXTENSION_new_null()
557 if stack == _api.NULL:
558 1/0
559
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800560 stack = _api.ffi.gc(stack, _api.sk_X509_EXTENSION_free)
561
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800562 for ext in extensions:
563 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800564 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800565
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -0800566 # TODO push can fail (here and elsewhere)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800567 _api.sk_X509_EXTENSION_push(stack, ext._extension)
568
569 add_result = _api.X509_REQ_add_extensions(self._req, stack)
570 if not add_result:
571 1/0
572
573
574 def sign(self, pkey, digest):
575 """
576 Sign the certificate request using the supplied key and digest
577
578 :param pkey: The key to sign with
579 :param digest: The message digest to use
580 :return: None
581 """
582 if pkey._only_public:
583 raise ValueError("Key has only public part")
584
585 if not pkey._initialized:
586 raise ValueError("Key is uninitialized")
587
588 digest_obj = _api.EVP_get_digestbyname(digest)
589 if digest_obj == _api.NULL:
590 raise ValueError("No such digest method")
591
592 sign_result = _api.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
593 if not sign_result:
594 1/0
595
596
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800597X509ReqType = X509Req
598
599
600
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800601class X509(object):
602 def __init__(self):
603 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800604 x509 = _api.X509_new()
605 self._x509 = _api.ffi.gc(x509, _api.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800606
607
608 def set_version(self, version):
609 """
610 Set version number of the certificate
611
612 :param version: The version number
613 :type version: :py:class:`int`
614
615 :return: None
616 """
617 if not isinstance(version, int):
618 raise TypeError("version must be an integer")
619
620 _api.X509_set_version(self._x509, version)
621
622
623 def get_version(self):
624 """
625 Return version number of the certificate
626
627 :return: Version number as a Python integer
628 """
629 return _api.X509_get_version(self._x509)
630
631
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800632 def get_pubkey(self):
633 """
634 Get the public key of the certificate
635
636 :return: The public key
637 """
638 pkey = PKey.__new__(PKey)
639 pkey._pkey = _api.X509_get_pubkey(self._x509)
640 if pkey._pkey == _api.NULL:
641 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800642 pkey._pkey = _api.ffi.gc(pkey._pkey, _api.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800643 pkey._only_public = True
644 return pkey
645
646
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800647 def set_pubkey(self, pkey):
648 """
649 Set the public key of the certificate
650
651 :param pkey: The public key
652
653 :return: None
654 """
655 if not isinstance(pkey, PKey):
656 raise TypeError("pkey must be a PKey instance")
657
658 set_result = _api.X509_set_pubkey(self._x509, pkey._pkey)
659 if not set_result:
660 _raise_current_error()
661
662
663 def sign(self, pkey, digest):
664 """
665 Sign the certificate using the supplied key and digest
666
667 :param pkey: The key to sign with
668 :param digest: The message digest to use
669 :return: None
670 """
671 if not isinstance(pkey, PKey):
672 raise TypeError("pkey must be a PKey instance")
673
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800674 if pkey._only_public:
675 raise ValueError("Key only has public part")
676
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800677 if not pkey._initialized:
678 raise ValueError("Key is uninitialized")
679
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800680 evp_md = _api.EVP_get_digestbyname(digest)
681 if evp_md == _api.NULL:
682 raise ValueError("No such digest method")
683
684 sign_result = _api.X509_sign(self._x509, pkey._pkey, evp_md)
685 if not sign_result:
686 _raise_current_error()
687
688
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800689 def get_signature_algorithm(self):
690 """
691 Retrieve the signature algorithm used in the certificate
692
693 :return: A byte string giving the name of the signature algorithm used in
694 the certificate.
695 :raise ValueError: If the signature algorithm is undefined.
696 """
697 alg = self._x509.cert_info.signature.algorithm
698 nid = _api.OBJ_obj2nid(alg)
699 if nid == _api.NID_undef:
700 raise ValueError("Undefined signature algorithm")
701 return _api.string(_api.OBJ_nid2ln(nid))
702
703
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800704 def digest(self, digest_name):
705 """
706 Return the digest of the X509 object.
707
708 :param digest_name: The name of the digest algorithm to use.
709 :type digest_name: :py:class:`bytes`
710
711 :return: The digest of the object
712 """
713 digest = _api.EVP_get_digestbyname(digest_name)
714 if digest == _api.NULL:
715 raise ValueError("No such digest method")
716
717 result_buffer = _api.new("char[]", _api.EVP_MAX_MD_SIZE)
718 result_length = _api.new("unsigned int[]", 1)
719 result_length[0] = len(result_buffer)
720
721 digest_result = _api.X509_digest(
722 self._x509, digest, result_buffer, result_length)
723
724 if not digest_result:
725 1/0
726
727 return ':'.join([
728 ch.encode('hex').upper() for ch
729 in _api.buffer(result_buffer, result_length[0])])
730
731
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800732 def subject_name_hash(self):
733 """
734 Return the hash of the X509 subject.
735
736 :return: The hash of the subject.
737 """
738 return _api.X509_subject_name_hash(self._x509)
739
740
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800741 def set_serial_number(self, serial):
742 """
743 Set serial number of the certificate
744
745 :param serial: The serial number
746 :type serial: :py:class:`int`
747
748 :return: None
749 """
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800750 if not isinstance(serial, (int, long)):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800751 raise TypeError("serial must be an integer")
752
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800753 hex_serial = hex(serial)[2:]
754 if not isinstance(hex_serial, bytes):
755 hex_serial = hex_serial.encode('ascii')
756
757 bignum_serial = _api.new("BIGNUM**")
758
759 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
760 # it. If bignum is still NULL after this call, then the return value is
761 # actually the result. I hope. -exarkun
762 small_serial = _api.BN_hex2bn(bignum_serial, hex_serial)
763
764 if bignum_serial[0] == _api.NULL:
765 set_result = ASN1_INTEGER_set(
766 _api.X509_get_serialNumber(self._x509), small_serial)
767 if set_result:
768 # TODO Not tested
769 _raise_current_error()
770 else:
771 asn1_serial = _api.BN_to_ASN1_INTEGER(bignum_serial[0], _api.NULL)
772 _api.BN_free(bignum_serial[0])
773 if asn1_serial == _api.NULL:
774 # TODO Not tested
775 _raise_current_error()
Jean-Paul Calderone3a90aa52013-03-02 07:33:36 -0800776 asn1_serial = _api.ffi.gc(asn1_serial, _api.ASN1_INTEGER_free)
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800777 set_result = _api.X509_set_serialNumber(self._x509, asn1_serial)
778 if not set_result:
779 # TODO Not tested
780 _raise_current_error()
781
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800782
783 def get_serial_number(self):
784 """
785 Return serial number of the certificate
786
787 :return: Serial number as a Python integer
788 """
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800789 asn1_serial = _api.X509_get_serialNumber(self._x509)
790 bignum_serial = _api.ASN1_INTEGER_to_BN(asn1_serial, _api.NULL)
791 try:
792 hex_serial = _api.BN_bn2hex(bignum_serial)
793 try:
794 hexstring_serial = _api.string(hex_serial)
795 serial = int(hexstring_serial, 16)
796 return serial
797 finally:
798 _api.OPENSSL_free(hex_serial)
799 finally:
800 _api.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800801
802
803 def gmtime_adj_notAfter(self, amount):
804 """
805 Adjust the time stamp for when the certificate stops being valid
806
807 :param amount: The number of seconds by which to adjust the ending
808 validity time.
809 :type amount: :py:class:`int`
810
811 :return: None
812 """
813 if not isinstance(amount, int):
814 raise TypeError("amount must be an integer")
815
816 notAfter = _api.X509_get_notAfter(self._x509)
817 _api.X509_gmtime_adj(notAfter, amount)
818
819
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800820 def gmtime_adj_notBefore(self, amount):
821 """
822 Change the timestamp for when the certificate starts being valid to the current
823 time plus an offset.
824
825 :param amount: The number of seconds by which to adjust the starting validity
826 time.
827 :return: None
828 """
829 if not isinstance(amount, int):
830 raise TypeError("amount must be an integer")
831
832 notBefore = _api.X509_get_notBefore(self._x509)
833 _api.X509_gmtime_adj(notBefore, amount)
834
835
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800836 def has_expired(self):
837 """
838 Check whether the certificate has expired.
839
840 :return: True if the certificate has expired, false otherwise
841 """
842 now = int(time())
843 notAfter = _api.X509_get_notAfter(self._x509)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -0800844 return _api.ASN1_UTCTIME_cmp_time_t(
845 _api.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800846
847
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800848 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800849 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800850
851
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800852 def get_notBefore(self):
853 """
854 Retrieve the time stamp for when the certificate starts being valid
855
856 :return: A string giving the timestamp, in the format::
857
858 YYYYMMDDhhmmssZ
859 YYYYMMDDhhmmss+hhmm
860 YYYYMMDDhhmmss-hhmm
861
862 or None if there is no value set.
863 """
864 return self._get_boundary_time(_api.X509_get_notBefore)
865
866
867 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800868 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800869
870
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800871 def set_notBefore(self, when):
872 """
873 Set the time stamp for when the certificate starts being valid
874
875 :param when: A string giving the timestamp, in the format:
876
877 YYYYMMDDhhmmssZ
878 YYYYMMDDhhmmss+hhmm
879 YYYYMMDDhhmmss-hhmm
880 :type when: :py:class:`bytes`
881
882 :return: None
883 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800884 return self._set_boundary_time(_api.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -0800885
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800886
887 def get_notAfter(self):
888 """
889 Retrieve the time stamp for when the certificate stops being valid
890
891 :return: A string giving the timestamp, in the format::
892
893 YYYYMMDDhhmmssZ
894 YYYYMMDDhhmmss+hhmm
895 YYYYMMDDhhmmss-hhmm
896
897 or None if there is no value set.
898 """
899 return self._get_boundary_time(_api.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800900
901
902 def set_notAfter(self, when):
903 """
904 Set the time stamp for when the certificate stops being valid
905
906 :param when: A string giving the timestamp, in the format:
907
908 YYYYMMDDhhmmssZ
909 YYYYMMDDhhmmss+hhmm
910 YYYYMMDDhhmmss-hhmm
911 :type when: :py:class:`bytes`
912
913 :return: None
914 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800915 return self._set_boundary_time(_api.X509_get_notAfter, when)
916
917
918 def _get_name(self, which):
919 name = X509Name.__new__(X509Name)
920 name._name = which(self._x509)
921 if name._name == _api.NULL:
922 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800923
924 # The name is owned by the X509 structure. As long as the X509Name
925 # Python object is alive, keep the X509 Python object alive.
926 name._owner = self
927
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800928 return name
929
930
931 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800932 if not isinstance(name, X509Name):
933 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800934 set_result = which(self._x509, name._name)
935 if not set_result:
936 1/0
937
938
939 def get_issuer(self):
940 """
941 Create an X509Name object for the issuer of the certificate
942
943 :return: An X509Name object
944 """
945 return self._get_name(_api.X509_get_issuer_name)
946
947
948 def set_issuer(self, issuer):
949 """
950 Set the issuer of the certificate
951
952 :param issuer: The issuer name
953 :type issuer: :py:class:`X509Name`
954
955 :return: None
956 """
957 return self._set_name(_api.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800958
959
960 def get_subject(self):
961 """
962 Create an X509Name object for the subject of the certificate
963
964 :return: An X509Name object
965 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800966 return self._get_name(_api.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800967
968
969 def set_subject(self, subject):
970 """
971 Set the subject of the certificate
972
973 :param subject: The subject name
974 :type subject: :py:class:`X509Name`
975 :return: None
976 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800977 return self._set_name(_api.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800978
979
980 def get_extension_count(self):
981 """
982 Get the number of extensions on the certificate.
983
984 :return: The number of extensions as an integer.
985 """
986 return _api.X509_get_ext_count(self._x509)
987
988
989 def add_extensions(self, extensions):
990 """
991 Add extensions to the certificate.
992
993 :param extensions: a sequence of X509Extension objects
994 :return: None
995 """
996 for ext in extensions:
997 if not isinstance(ext, X509Extension):
998 raise ValueError("One of the elements is not an X509Extension")
999
1000 add_result = _api.X509_add_ext(self._x509, ext._extension, -1)
1001 if not add_result:
1002 _raise_current_error()
1003
1004
1005 def get_extension(self, index):
1006 """
1007 Get a specific extension of the certificate by index.
1008
1009 :param index: The index of the extension to retrieve.
1010 :return: The X509Extension object at the specified index.
1011 """
1012 ext = X509Extension.__new__(X509Extension)
1013 ext._extension = _api.X509_get_ext(self._x509, index)
1014 if ext._extension == _api.NULL:
1015 raise IndexError("extension index out of bounds")
1016
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001017 extension = _api.X509_EXTENSION_dup(ext._extension)
1018 ext._extension = _api.ffi.gc(extension, _api.X509_EXTENSION_free)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -08001019 return ext
1020
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001021X509Type = X509
1022
1023
1024
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001025class X509Store(object):
1026 def __init__(self):
1027 store = _api.X509_STORE_new()
1028 self._store = _api.ffi.gc(store, _api.X509_STORE_free)
1029
1030
1031 def add_cert(self, cert):
1032 if not isinstance(cert, X509):
1033 raise TypeError()
1034
1035 result = _api.X509_STORE_add_cert(self._store, cert._x509)
1036 if not result:
Jean-Paul Calderonee6f32b82013-03-06 10:27:57 -08001037 _raise_current_error(Error)
1038
Jean-Paul Calderonea63714c2013-03-05 17:02:26 -08001039
1040X509StoreType = X509Store
1041
1042
1043
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001044def load_certificate(type, buffer):
1045 """
1046 Load a certificate from a buffer
1047
1048 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1049
1050 :param buffer: The buffer the certificate is stored in
1051 :type buffer: :py:class:`bytes`
1052
1053 :return: The X509 object
1054 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001055 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001056
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001057 if type == FILETYPE_PEM:
1058 x509 = _api.PEM_read_bio_X509(bio, _api.NULL, _api.NULL, _api.NULL)
1059 elif type == FILETYPE_ASN1:
1060 x509 = _api.d2i_X509_bio(bio, _api.NULL);
1061 else:
1062 raise ValueError(
1063 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001064
1065 if x509 == _api.NULL:
1066 _raise_current_error()
1067
1068 cert = X509.__new__(X509)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001069 cert._x509 = _api.ffi.gc(x509, _api.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001070 return cert
1071
1072
1073def dump_certificate(type, cert):
1074 """
1075 Dump a certificate to a buffer
1076
1077 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1078 :param cert: The certificate to dump
1079 :return: The buffer with the dumped certificate in
1080 """
Jean-Paul Calderone0c73aff2013-03-02 07:45:12 -08001081 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001082
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001083 if type == FILETYPE_PEM:
1084 result_code = _api.PEM_write_bio_X509(bio, cert._x509)
1085 elif type == FILETYPE_ASN1:
1086 result_code = _api.i2d_X509_bio(bio, cert._x509)
1087 elif type == FILETYPE_TEXT:
1088 result_code = _api.X509_print_ex(bio, cert._x509, 0, 0)
1089 else:
1090 raise ValueError(
1091 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1092 "FILETYPE_TEXT")
1093
1094 return _bio_to_string(bio)
1095
1096
1097
1098def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1099 """
1100 Dump a private key to a buffer
1101
1102 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1103 :param pkey: The PKey to dump
1104 :param cipher: (optional) if encrypted PEM format, the cipher to
1105 use
1106 :param passphrase: (optional) if encrypted PEM format, this can be either
1107 the passphrase to use, or a callback for providing the
1108 passphrase.
1109 :return: The buffer with the dumped key in
1110 :rtype: :py:data:`str`
1111 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001112 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001113
1114 if cipher is not None:
1115 cipher_obj = _api.EVP_get_cipherbyname(cipher)
1116 if cipher_obj == _api.NULL:
1117 raise ValueError("Invalid cipher name")
1118 else:
1119 cipher_obj = _api.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001120
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001121 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001122 if type == FILETYPE_PEM:
1123 result_code = _api.PEM_write_bio_PrivateKey(
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001124 bio, pkey._pkey, cipher_obj, _api.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001125 helper.callback, helper.callback_args)
1126 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001127 elif type == FILETYPE_ASN1:
1128 result_code = _api.i2d_PrivateKey_bio(bio, pkey._pkey)
1129 elif type == FILETYPE_TEXT:
1130 rsa = _api.EVP_PKEY_get1_RSA(pkey._pkey)
1131 result_code = _api.RSA_print(bio, rsa, 0)
1132 # TODO RSA_free(rsa)?
1133 else:
1134 raise ValueError(
1135 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1136 "FILETYPE_TEXT")
1137
1138 if result_code == 0:
1139 _raise_current_error()
1140
1141 return _bio_to_string(bio)
1142
1143
1144
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001145def _X509_REVOKED_dup(original):
1146 copy = _api.X509_REVOKED_new()
1147 if copy == _api.NULL:
1148 1/0
1149
1150 if original.serialNumber != _api.NULL:
1151 copy.serialNumber = _api.ASN1_INTEGER_dup(original.serialNumber)
1152
1153 if original.revocationDate != _api.NULL:
1154 copy.revocationDate = _api.M_ASN1_TIME_dup(original.revocationDate)
1155
1156 if original.extensions != _api.NULL:
1157 extension_stack = _api.sk_X509_EXTENSION_new_null()
1158 for i in range(_api.sk_X509_EXTENSION_num(original.extensions)):
1159 original_ext = _api.sk_X509_EXTENSION_value(original.extensions, i)
1160 copy_ext = _api.X509_EXTENSION_dup(original_ext)
1161 _api.sk_X509_EXTENSION_push(extension_stack, copy_ext)
1162 copy.extensions = extension_stack
1163
1164 copy.sequence = original.sequence
1165 return copy
1166
1167
1168
1169class Revoked(object):
1170 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1171 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1172 # OCSP_crl_reason_str. We use the latter, just like the command line
1173 # program.
1174 _crl_reasons = [
1175 "unspecified",
1176 "keyCompromise",
1177 "CACompromise",
1178 "affiliationChanged",
1179 "superseded",
1180 "cessationOfOperation",
1181 "certificateHold",
1182 # "removeFromCRL",
1183 ]
1184
1185 def __init__(self):
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001186 revoked = _api.X509_REVOKED_new()
1187 self._revoked = _api.ffi.gc(revoked, _api.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001188
1189
1190 def set_serial(self, hex_str):
1191 """
1192 Set the serial number of a revoked Revoked structure
1193
1194 :param hex_str: The new serial number.
1195 :type hex_str: :py:data:`str`
1196 :return: None
1197 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001198 bignum_serial = _api.ffi.gc(_api.BN_new(), _api.BN_free)
1199 bignum_ptr = _api.new("BIGNUM**")
1200 bignum_ptr[0] = bignum_serial
1201 bn_result = _api.BN_hex2bn(bignum_ptr, hex_str)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001202 if not bn_result:
1203 raise ValueError("bad hex string")
1204
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001205 asn1_serial = _api.ffi.gc(
1206 _api.BN_to_ASN1_INTEGER(bignum_serial, _api.NULL),
1207 _api.ASN1_INTEGER_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001208 _api.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
1209
1210
1211 def get_serial(self):
1212 """
1213 Return the serial number of a Revoked structure
1214
1215 :return: The serial number as a string
1216 """
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001217 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001218
1219 result = _api.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
1220 if result < 0:
1221 1/0
1222
1223 return _bio_to_string(bio)
1224
1225
1226 def _delete_reason(self):
1227 stack = self._revoked.extensions
1228 for i in range(_api.sk_X509_EXTENSION_num(stack)):
1229 ext = _api.sk_X509_EXTENSION_value(stack, i)
1230 if _api.OBJ_obj2nid(ext.object) == _api.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001231 _api.X509_EXTENSION_free(ext)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001232 _api.sk_X509_EXTENSION_delete(stack, i)
1233 break
1234
1235
1236 def set_reason(self, reason):
1237 """
1238 Set the reason of a Revoked object.
1239
1240 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1241
1242 :param reason: The reason string.
1243 :type reason: :py:class:`str` or :py:class:`NoneType`
1244 :return: None
1245 """
1246 if reason is None:
1247 self._delete_reason()
1248 elif not isinstance(reason, bytes):
1249 raise TypeError("reason must be None or a byte string")
1250 else:
1251 reason = reason.lower().replace(' ', '')
1252 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1253
1254 new_reason_ext = _api.ASN1_ENUMERATED_new()
1255 if new_reason_ext == _api.NULL:
1256 1/0
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001257 new_reason_ext = _api.ffi.gc(new_reason_ext, _api.ASN1_ENUMERATED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001258
1259 set_result = _api.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1260 if set_result == _api.NULL:
1261 1/0
1262
1263 self._delete_reason()
1264 add_result = _api.X509_REVOKED_add1_ext_i2d(
1265 self._revoked, _api.NID_crl_reason, new_reason_ext, 0, 0)
1266
1267 if not add_result:
1268 1/0
1269
1270
1271 def get_reason(self):
1272 """
1273 Return the reason of a Revoked object.
1274
1275 :return: The reason as a string
1276 """
1277 extensions = self._revoked.extensions
1278 for i in range(_api.sk_X509_EXTENSION_num(extensions)):
1279 ext = _api.sk_X509_EXTENSION_value(extensions, i)
1280 if _api.OBJ_obj2nid(ext.object) == _api.NID_crl_reason:
Jean-Paul Calderonefd371362013-03-01 20:53:58 -08001281 bio = _new_mem_buf()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001282
1283 print_result = _api.X509V3_EXT_print(bio, ext, 0, 0)
1284 if not print_result:
1285 print_result = _api.M_ASN1_OCTET_STRING_print(bio, ext.value)
1286 if print_result == 0:
1287 1/0
1288
1289 return _bio_to_string(bio)
1290
1291
1292 def all_reasons(self):
1293 """
1294 Return a list of all the supported reason strings.
1295
1296 :return: A list of reason strings.
1297 """
1298 return self._crl_reasons[:]
1299
1300
1301 def set_rev_date(self, when):
1302 """
1303 Set the revocation timestamp
1304
1305 :param when: A string giving the timestamp, in the format:
1306
1307 YYYYMMDDhhmmssZ
1308 YYYYMMDDhhmmss+hhmm
1309 YYYYMMDDhhmmss-hhmm
1310
1311 :return: None
1312 """
1313 return _set_asn1_time(self._revoked.revocationDate, when)
1314
1315
1316 def get_rev_date(self):
1317 """
1318 Retrieve the revocation date
1319
1320 :return: A string giving the timestamp, in the format:
1321
1322 YYYYMMDDhhmmssZ
1323 YYYYMMDDhhmmss+hhmm
1324 YYYYMMDDhhmmss-hhmm
1325 """
1326 return _get_asn1_time(self._revoked.revocationDate)
1327
1328
1329
1330class CRL(object):
1331 def __init__(self):
1332 """
1333 Create a new empty CRL object.
1334 """
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001335 crl = _api.X509_CRL_new()
1336 self._crl = _api.ffi.gc(crl, _api.X509_CRL_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001337
1338
1339 def get_revoked(self):
1340 """
1341 Return revoked portion of the CRL structure (by value not reference).
1342
1343 :return: A tuple of Revoked objects.
1344 """
1345 results = []
1346 revoked_stack = self._crl.crl.revoked
1347 for i in range(_api.sk_X509_REVOKED_num(revoked_stack)):
1348 revoked = _api.sk_X509_REVOKED_value(revoked_stack, i)
1349 revoked_copy = _X509_REVOKED_dup(revoked)
1350 pyrev = Revoked.__new__(Revoked)
Jean-Paul Calderone9a194eb2013-03-02 22:17:07 -08001351 pyrev._revoked = _api.ffi.gc(revoked_copy, _api.X509_REVOKED_free)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001352 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001353 if results:
1354 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001355
1356
1357 def add_revoked(self, revoked):
1358 """
1359 Add a revoked (by value not reference) to the CRL structure
1360
1361 :param revoked: The new revoked.
1362 :type revoked: :class:`X509`
1363
1364 :return: None
1365 """
1366 copy = _X509_REVOKED_dup(revoked._revoked)
1367 if copy == _api.NULL:
1368 1/0
1369
1370 add_result = _api.X509_CRL_add0_revoked(self._crl, copy)
1371 # TODO what check on add_result?
1372
1373
1374 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1375 """
1376 export a CRL as a string
1377
1378 :param cert: Used to sign CRL.
1379 :type cert: :class:`X509`
1380
1381 :param key: Used to sign CRL.
1382 :type key: :class:`PKey`
1383
1384 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1385
1386 :param days: The number of days until the next update of this CRL.
1387 :type days: :py:data:`int`
1388
1389 :return: :py:data:`str`
1390 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001391 if not isinstance(cert, X509):
1392 raise TypeError("cert must be an X509 instance")
1393 if not isinstance(key, PKey):
1394 raise TypeError("key must be a PKey instance")
1395 if not isinstance(type, int):
1396 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001397
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001398 bio = _api.BIO_new(_api.BIO_s_mem())
1399 if bio == _api.NULL:
1400 1/0
1401
1402 # A scratch time object to give different values to different CRL fields
1403 sometime = _api.ASN1_TIME_new()
1404 if sometime == _api.NULL:
1405 1/0
1406
1407 _api.X509_gmtime_adj(sometime, 0)
1408 _api.X509_CRL_set_lastUpdate(self._crl, sometime)
1409
1410 _api.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1411 _api.X509_CRL_set_nextUpdate(self._crl, sometime)
1412
1413 _api.X509_CRL_set_issuer_name(self._crl, _api.X509_get_subject_name(cert._x509))
1414
1415 sign_result = _api.X509_CRL_sign(self._crl, key._pkey, _api.EVP_md5())
1416 if not sign_result:
1417 _raise_current_error()
1418
1419 if type == FILETYPE_PEM:
1420 ret = _api.PEM_write_bio_X509_CRL(bio, self._crl)
1421 elif type == FILETYPE_ASN1:
1422 ret = _api.i2d_X509_CRL_bio(bio, self._crl)
1423 elif type == FILETYPE_TEXT:
1424 ret = _api.X509_CRL_print(bio, self._crl)
1425 else:
1426 raise ValueError(
1427 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1428
1429 if not ret:
1430 1/0
1431
1432 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001433CRLType = CRL
1434
1435
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001436
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001437class PKCS7(object):
1438 def type_is_signed(self):
1439 """
1440 Check if this NID_pkcs7_signed object
1441
1442 :return: True if the PKCS7 is of type signed
1443 """
1444 if _api.PKCS7_type_is_signed(self._pkcs7):
1445 return True
1446 return False
1447
1448
1449 def type_is_enveloped(self):
1450 """
1451 Check if this NID_pkcs7_enveloped object
1452
1453 :returns: True if the PKCS7 is of type enveloped
1454 """
1455 if _api.PKCS7_type_is_enveloped(self._pkcs7):
1456 return True
1457 return False
1458
1459
1460 def type_is_signedAndEnveloped(self):
1461 """
1462 Check if this NID_pkcs7_signedAndEnveloped object
1463
1464 :returns: True if the PKCS7 is of type signedAndEnveloped
1465 """
1466 if _api.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
1467 return True
1468 return False
1469
1470
1471 def type_is_data(self):
1472 """
1473 Check if this NID_pkcs7_data object
1474
1475 :return: True if the PKCS7 is of type data
1476 """
1477 if _api.PKCS7_type_is_data(self._pkcs7):
1478 return True
1479 return False
1480
1481
1482 def get_type_name(self):
1483 """
1484 Returns the type name of the PKCS7 structure
1485
1486 :return: A string with the typename
1487 """
1488 nid = _api.OBJ_obj2nid(self._pkcs7.type)
1489 string_type = _api.OBJ_nid2sn(nid)
1490 return _api.string(string_type)
1491
1492PKCS7Type = PKCS7
1493
1494
1495
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001496class PKCS12(object):
1497 def __init__(self):
1498 self._pkey = None
1499 self._cert = None
1500 self._cacerts = None
1501 self._friendlyname = None
1502
1503
1504 def get_certificate(self):
1505 """
1506 Return certificate portion of the PKCS12 structure
1507
1508 :return: X509 object containing the certificate
1509 """
1510 return self._cert
1511
1512
1513 def set_certificate(self, cert):
1514 """
1515 Replace the certificate portion of the PKCS12 structure
1516
1517 :param cert: The new certificate.
1518 :type cert: :py:class:`X509` or :py:data:`None`
1519 :return: None
1520 """
1521 if not isinstance(cert, X509):
1522 raise TypeError("cert must be an X509 instance")
1523 self._cert = cert
1524
1525
1526 def get_privatekey(self):
1527 """
1528 Return private key portion of the PKCS12 structure
1529
1530 :returns: PKey object containing the private key
1531 """
1532 return self._pkey
1533
1534
1535 def set_privatekey(self, pkey):
1536 """
1537 Replace or set the certificate portion of the PKCS12 structure
1538
1539 :param pkey: The new private key.
1540 :type pkey: :py:class:`PKey`
1541 :return: None
1542 """
1543 if not isinstance(pkey, PKey):
1544 raise TypeError("pkey must be a PKey instance")
1545 self._pkey = pkey
1546
1547
1548 def get_ca_certificates(self):
1549 """
1550 Return CA certificates within of the PKCS12 object
1551
1552 :return: A newly created tuple containing the CA certificates in the chain,
1553 if any are present, or None if no CA certificates are present.
1554 """
1555 if self._cacerts is not None:
1556 return tuple(self._cacerts)
1557
1558
1559 def set_ca_certificates(self, cacerts):
1560 """
1561 Replace or set the CA certificates withing the PKCS12 object.
1562
1563 :param cacerts: The new CA certificates.
1564 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1565 :return: None
1566 """
1567 if cacerts is None:
1568 self._cacerts = None
1569 else:
1570 cacerts = list(cacerts)
1571 for cert in cacerts:
1572 if not isinstance(cert, X509):
1573 raise TypeError("iterable must only contain X509 instances")
1574 self._cacerts = cacerts
1575
1576
1577 def set_friendlyname(self, name):
1578 """
1579 Replace or set the certificate portion of the PKCS12 structure
1580
1581 :param name: The new friendly name.
1582 :type name: :py:class:`bytes`
1583 :return: None
1584 """
1585 if name is None:
1586 self._friendlyname = None
1587 elif not isinstance(name, bytes):
1588 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1589 self._friendlyname = name
1590
1591
1592 def get_friendlyname(self):
1593 """
1594 Return friendly name portion of the PKCS12 structure
1595
1596 :returns: String containing the friendlyname
1597 """
1598 return self._friendlyname
1599
1600
1601 def export(self, passphrase=None, iter=2048, maciter=1):
1602 """
1603 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1604
1605 :param passphrase: used to encrypt the PKCS12
1606 :type passphrase: :py:data:`bytes`
1607
1608 :param iter: How many times to repeat the encryption
1609 :type iter: :py:data:`int`
1610
1611 :param maciter: How many times to repeat the MAC
1612 :type maciter: :py:data:`int`
1613
1614 :return: The string containing the PKCS12
1615 """
1616 if self._cacerts is None:
1617 cacerts = _api.NULL
1618 else:
1619 cacerts = _api.sk_X509_new_null()
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001620 cacerts = _api.ffi.gc(cacerts, _api.sk_X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001621 for cert in self._cacerts:
1622 _api.sk_X509_push(cacerts, cert._x509)
1623
1624 if passphrase is None:
1625 passphrase = _api.NULL
1626
1627 friendlyname = self._friendlyname
1628 if friendlyname is None:
1629 friendlyname = _api.NULL
1630
1631 if self._pkey is None:
1632 pkey = _api.NULL
1633 else:
1634 pkey = self._pkey._pkey
1635
1636 if self._cert is None:
1637 cert = _api.NULL
1638 else:
1639 cert = self._cert._x509
1640
1641 pkcs12 = _api.PKCS12_create(
1642 passphrase, friendlyname, pkey, cert, cacerts,
1643 _api.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1644 _api.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1645 iter, maciter, 0)
1646 if pkcs12 == _api.NULL:
1647 _raise_current_error()
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001648 pkcs12 = _api.ffi.gc(pkcs12, _api.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001649
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001650 bio = _new_mem_buf()
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001651 _api.i2d_PKCS12_bio(bio, pkcs12)
1652 return _bio_to_string(bio)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001653
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001654PKCS12Type = PKCS12
1655
1656
1657
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001658class NetscapeSPKI(object):
1659 def __init__(self):
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001660 spki = _api.NETSCAPE_SPKI_new()
1661 self._spki = _api.ffi.gc(spki, _api.NETSCAPE_SPKI_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001662
1663
1664 def sign(self, pkey, digest):
1665 """
1666 Sign the certificate request using the supplied key and digest
1667
1668 :param pkey: The key to sign with
1669 :param digest: The message digest to use
1670 :return: None
1671 """
1672 if pkey._only_public:
1673 raise ValueError("Key has only public part")
1674
1675 if not pkey._initialized:
1676 raise ValueError("Key is uninitialized")
1677
1678 digest_obj = _api.EVP_get_digestbyname(digest)
1679 if digest_obj == _api.NULL:
1680 raise ValueError("No such digest method")
1681
1682 sign_result = _api.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
1683 if not sign_result:
1684 1/0
1685
1686
1687 def verify(self, key):
1688 """
1689 Verifies a certificate request using the supplied public key
1690
1691 :param key: a public key
1692 :return: True if the signature is correct.
1693 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1694 problem verifying the signature.
1695 """
1696 answer = _api.NETSCAPE_SPKI_verify(self._spki, key._pkey)
1697 if answer <= 0:
1698 _raise_current_error()
1699 return True
1700
1701
1702 def b64_encode(self):
1703 """
1704 Generate a base64 encoded string from an SPKI
1705
1706 :return: The base64 encoded string
1707 """
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001708 encoded = _api.NETSCAPE_SPKI_b64_encode(self._spki)
1709 result = _api.string(encoded)
1710 _api.CRYPTO_free(encoded)
1711 return result
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001712
1713
1714 def get_pubkey(self):
1715 """
1716 Get the public key of the certificate
1717
1718 :return: The public key
1719 """
1720 pkey = PKey.__new__(PKey)
1721 pkey._pkey = _api.NETSCAPE_SPKI_get_pubkey(self._spki)
1722 if pkey._pkey == _api.NULL:
1723 1/0
Jean-Paul Calderone2c2e21d2013-03-02 16:50:35 -08001724 pkey._pkey = _api.ffi.gc(pkey._pkey, _api.EVP_PKEY_free)
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001725 pkey._only_public = True
1726 return pkey
1727
1728
1729 def set_pubkey(self, pkey):
1730 """
1731 Set the public key of the certificate
1732
1733 :param pkey: The public key
1734 :return: None
1735 """
1736 set_result = _api.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
1737 if not set_result:
1738 1/0
1739NetscapeSPKIType = NetscapeSPKI
1740
1741
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001742class _PassphraseHelper(object):
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001743 def __init__(self, type, passphrase, more_args=False, truncate=False):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001744 if type != FILETYPE_PEM and passphrase is not None:
1745 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001746 self._passphrase = passphrase
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001747 self._more_args = more_args
1748 self._truncate = truncate
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001749 self._problems = []
1750
1751
1752 @property
1753 def callback(self):
1754 if self._passphrase is None:
1755 return _api.NULL
1756 elif isinstance(self._passphrase, bytes):
1757 return _api.NULL
1758 elif callable(self._passphrase):
1759 return _api.callback("pem_password_cb", self._read_passphrase)
1760 else:
1761 raise TypeError("Last argument must be string or callable")
1762
1763
1764 @property
1765 def callback_args(self):
1766 if self._passphrase is None:
1767 return _api.NULL
1768 elif isinstance(self._passphrase, bytes):
1769 return self._passphrase
1770 elif callable(self._passphrase):
1771 return _api.NULL
1772 else:
1773 raise TypeError("Last argument must be string or callable")
1774
1775
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001776 def raise_if_problem(self, exceptionType=Error):
1777 try:
1778 _raise_current_error(exceptionType)
1779 except exceptionType as e:
1780 pass
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001781 if self._problems:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001782 raise self._problems[0]
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001783 return e
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001784
1785
1786 def _read_passphrase(self, buf, size, rwflag, userdata):
1787 try:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001788 if self._more_args:
1789 result = self._passphrase(size, rwflag, userdata)
1790 else:
1791 result = self._passphrase(rwflag)
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001792 if not isinstance(result, bytes):
1793 raise ValueError("String expected")
1794 if len(result) > size:
Jean-Paul Calderone8a1bea52013-03-05 07:57:57 -08001795 if self._truncate:
1796 result = result[:size]
1797 else:
1798 raise ValueError("passphrase returned by callback is too long")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001799 for i in range(len(result)):
1800 buf[i] = result[i]
1801 return len(result)
1802 except Exception as e:
1803 self._problems.append(e)
1804 return 0
1805
1806
1807
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001808def load_privatekey(type, buffer, passphrase=None):
1809 """
1810 Load a private key from a buffer
1811
1812 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1813 :param buffer: The buffer the key is stored in
1814 :param passphrase: (optional) if encrypted PEM format, this can be
1815 either the passphrase to use, or a callback for
1816 providing the passphrase.
1817
1818 :return: The PKey object
1819 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001820 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001821
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001822 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001823 if type == FILETYPE_PEM:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001824 evp_pkey = _api.PEM_read_bio_PrivateKey(
1825 bio, _api.NULL, helper.callback, helper.callback_args)
1826 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001827 elif type == FILETYPE_ASN1:
1828 evp_pkey = _api.d2i_PrivateKey_bio(bio, _api.NULL)
1829 else:
1830 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
1831
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08001832 if evp_pkey == _api.NULL:
1833 _raise_current_error()
1834
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001835 pkey = PKey.__new__(PKey)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001836 pkey._pkey = _api.ffi.gc(evp_pkey, _api.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001837 return pkey
1838
1839
1840
1841def dump_certificate_request(type, req):
1842 """
1843 Dump a certificate request to a buffer
1844
1845 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1846 :param req: The certificate request to dump
1847 :return: The buffer with the dumped certificate request in
1848 """
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001849 bio = _new_mem_buf()
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001850
1851 if type == FILETYPE_PEM:
1852 result_code = _api.PEM_write_bio_X509_REQ(bio, req._req)
1853 elif type == FILETYPE_ASN1:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08001854 result_code = _api.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001855 elif type == FILETYPE_TEXT:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08001856 result_code = _api.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001857 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08001858 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001859
1860 if result_code == 0:
1861 1/0
1862
1863 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001864
1865
1866
1867def load_certificate_request(type, buffer):
1868 """
1869 Load a certificate request from a buffer
1870
1871 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1872 :param buffer: The buffer the certificate request is stored in
1873 :return: The X509Req object
1874 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001875 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001876
1877 if type == FILETYPE_PEM:
1878 req = _api.PEM_read_bio_X509_REQ(bio, _api.NULL, _api.NULL, _api.NULL)
1879 elif type == FILETYPE_ASN1:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08001880 req = _api.d2i_X509_REQ_bio(bio, _api.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001881 else:
1882 1/0
1883
1884 if req == _api.NULL:
1885 1/0
1886
1887 x509req = X509Req.__new__(X509Req)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001888 x509req._req = _api.ffi.gc(req, _api.X509_REQ_free)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001889 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001890
1891
1892
1893def sign(pkey, data, digest):
1894 """
1895 Sign data with a digest
1896
1897 :param pkey: Pkey to sign with
1898 :param data: data to be signed
1899 :param digest: message digest to use
1900 :return: signature
1901 """
1902 digest_obj = _api.EVP_get_digestbyname(digest)
1903 if digest_obj == _api.NULL:
1904 raise ValueError("No such digest method")
1905
1906 md_ctx = _api.new("EVP_MD_CTX*")
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001907 md_ctx = _api.ffi.gc(md_ctx, _api.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001908
1909 _api.EVP_SignInit(md_ctx, digest_obj)
1910 _api.EVP_SignUpdate(md_ctx, data, len(data))
1911
1912 signature_buffer = _api.new("unsigned char[]", 512)
1913 signature_length = _api.new("unsigned int*")
1914 signature_length[0] = len(signature_buffer)
1915 final_result = _api.EVP_SignFinal(
1916 md_ctx, signature_buffer, signature_length, pkey._pkey)
1917
1918 if final_result != 1:
1919 1/0
1920
1921 return _api.buffer(signature_buffer, signature_length[0])[:]
1922
1923
1924
1925def verify(cert, signature, data, digest):
1926 """
1927 Verify a signature
1928
1929 :param cert: signing certificate (X509 object)
1930 :param signature: signature returned by sign function
1931 :param data: data to be verified
1932 :param digest: message digest to use
1933 :return: None if the signature is correct, raise exception otherwise
1934 """
1935 digest_obj = _api.EVP_get_digestbyname(digest)
1936 if digest_obj == _api.NULL:
1937 raise ValueError("No such digest method")
1938
1939 pkey = _api.X509_get_pubkey(cert._x509)
1940 if pkey == _api.NULL:
1941 1/0
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001942 pkey = _api.ffi.gc(pkey, _api.EVP_PKEY_free)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001943
1944 md_ctx = _api.new("EVP_MD_CTX*")
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08001945 md_ctx = _api.ffi.gc(md_ctx, _api.EVP_MD_CTX_cleanup)
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001946
1947 _api.EVP_VerifyInit(md_ctx, digest_obj)
1948 _api.EVP_VerifyUpdate(md_ctx, data, len(data))
1949 verify_result = _api.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
1950
1951 if verify_result != 1:
1952 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001953
1954
1955
1956def load_crl(type, buffer):
1957 """
1958 Load a certificate revocation list from a buffer
1959
1960 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1961 :param buffer: The buffer the CRL is stored in
1962
1963 :return: The PKey object
1964 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001965 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001966
1967 if type == FILETYPE_PEM:
1968 crl = _api.PEM_read_bio_X509_CRL(bio, _api.NULL, _api.NULL, _api.NULL)
1969 elif type == FILETYPE_ASN1:
1970 crl = _api.d2i_X509_CRL_bio(bio, _api.NULL)
1971 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001972 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
1973
1974 if crl == _api.NULL:
1975 _raise_current_error()
1976
1977 result = CRL.__new__(CRL)
1978 result._crl = crl
1979 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001980
1981
1982
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001983def load_pkcs7_data(type, buffer):
1984 """
1985 Load pkcs7 data from a buffer
1986
1987 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
1988 :param buffer: The buffer with the pkcs7 data.
1989 :return: The PKCS7 object
1990 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001991 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001992
1993 if type == FILETYPE_PEM:
1994 pkcs7 = _api.PEM_read_bio_PKCS7(bio, _api.NULL, _api.NULL, _api.NULL)
1995 elif type == FILETYPE_ASN1:
1996 pass
1997 else:
1998 1/0
1999 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
2000
2001 if pkcs7 == _api.NULL:
Jean-Paul Calderoneb0f64712013-03-03 10:15:39 -08002002 _raise_current_error()
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002003
2004 pypkcs7 = PKCS7.__new__(PKCS7)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002005 pypkcs7._pkcs7 = _api.ffi.gc(pkcs7, _api.PKCS7_free)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08002006 return pypkcs7
2007
2008
2009
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002010def load_pkcs12(buffer, passphrase):
2011 """
2012 Load a PKCS12 object from a buffer
2013
2014 :param buffer: The buffer the certificate is stored in
2015 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
2016 :returns: The PKCS12 object
2017 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08002018 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002019
2020 p12 = _api.d2i_PKCS12_bio(bio, _api.NULL)
2021 if p12 == _api.NULL:
2022 _raise_current_error()
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002023 p12 = _api.ffi.gc(p12, _api.PKCS12_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002024
2025 pkey = _api.new("EVP_PKEY**")
2026 cert = _api.new("X509**")
2027 cacerts = _api.new("struct stack_st_X509**")
2028
2029 parse_result = _api.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
2030 if not parse_result:
2031 _raise_current_error()
2032
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002033 cacerts = _api.ffi.gc(cacerts[0], _api.sk_X509_free)
2034
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002035 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
2036 # queue for no particular reason. This error isn't interesting to anyone
2037 # outside this function. It's not even interesting to us. Get rid of it.
2038 try:
2039 _raise_current_error()
2040 except Error:
2041 pass
2042
2043 if pkey[0] == _api.NULL:
2044 pykey = None
2045 else:
2046 pykey = PKey.__new__(PKey)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002047 pykey._pkey = _api.ffi.gc(pkey[0], _api.EVP_PKEY_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002048
2049 if cert[0] == _api.NULL:
2050 pycert = None
2051 friendlyname = None
2052 else:
2053 pycert = X509.__new__(X509)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002054 pycert._x509 = _api.ffi.gc(cert[0], _api.X509_free)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002055
2056 friendlyname_length = _api.new("int*")
2057 friendlyname_buffer = _api.X509_alias_get0(cert[0], friendlyname_length)
2058 friendlyname = _api.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2059 if friendlyname_buffer == _api.NULL:
2060 friendlyname = None
2061
2062 pycacerts = []
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002063 for i in range(_api.sk_X509_num(cacerts)):
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002064 pycacert = X509.__new__(X509)
Jean-Paul Calderoneef9a3dc2013-03-02 16:33:32 -08002065 pycacert._x509 = _api.sk_X509_value(cacerts, i)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08002066 pycacerts.append(pycacert)
2067 if not pycacerts:
2068 pycacerts = None
2069
2070 pkcs12 = PKCS12.__new__(PKCS12)
2071 pkcs12._pkey = pykey
2072 pkcs12._cert = pycert
2073 pkcs12._cacerts = pycacerts
2074 pkcs12._friendlyname = friendlyname
2075 return pkcs12