blob: abe9734e300a846df56c8db1448c15fb9354d666 [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 Calderone68a6f8f2013-03-01 17:56:22 -080025def _new_mem_buf(buffer):
26 bio = _api.BIO_new_mem_buf(buffer, len(buffer))
27 if bio == _api.NULL:
28 1/0
29 bio = _api.ffi.gc(bio, _api.BIO_free)
30 return bio
31
32
33
Jean-Paul Calderone57122982013-02-21 08:47:05 -080034def _set_asn1_time(boundary, when):
35 if not isinstance(when, bytes):
36 raise TypeError("when must be a byte string")
37
38 set_result = _api.ASN1_GENERALIZEDTIME_set_string(
39 _api.cast('ASN1_GENERALIZEDTIME*', boundary), when)
40 if set_result == 0:
41 dummy = _api.ASN1_STRING_new()
42 _api.ASN1_STRING_set(dummy, when, len(when))
43 check_result = _api.ASN1_GENERALIZEDTIME_check(
44 _api.cast('ASN1_GENERALIZEDTIME*', dummy))
45 if not check_result:
46 raise ValueError("Invalid string")
47 else:
48 # TODO No tests for this case
49 raise RuntimeError("Unknown ASN1_GENERALIZEDTIME_set_string failure")
50
51
52
53def _get_asn1_time(timestamp):
54 string_timestamp = _api.cast('ASN1_STRING*', timestamp)
55 if _api.ASN1_STRING_length(string_timestamp) == 0:
56 return None
57 elif _api.ASN1_STRING_type(string_timestamp) == _api.V_ASN1_GENERALIZEDTIME:
58 return _api.string(_api.ASN1_STRING_data(string_timestamp))
59 else:
60 generalized_timestamp = _api.new("ASN1_GENERALIZEDTIME**")
61 _api.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
62 if generalized_timestamp[0] == _api.NULL:
63 1/0
64 else:
65 string_timestamp = _api.cast(
66 "ASN1_STRING*", generalized_timestamp[0])
67 string_data = _api.ASN1_STRING_data(string_timestamp)
68 string_result = _api.string(string_data)
69 _api.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
70 return string_result
71
72
73
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080074def _raise_current_error():
75 errors = []
76 while True:
77 error = _api.ERR_get_error()
78 if error == 0:
79 break
80 errors.append((
81 _api.string(_api.ERR_lib_error_string(error)),
82 _api.string(_api.ERR_func_error_string(error)),
83 _api.string(_api.ERR_reason_error_string(error))))
84
85 raise Error(errors)
86
87_exception_from_error_queue = _raise_current_error
88
89
90class Error(Exception):
91 pass
92
93
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080094class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -080095 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -080096 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -080097
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080098 def __init__(self):
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080099 pkey = _api.EVP_PKEY_new()
100 self._pkey = _api.ffi.gc(pkey, _api.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800101 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800102
103
104 def generate_key(self, type, bits):
105 """
106 Generate a key of a given type, with a given number of a bits
107
108 :param type: The key type (TYPE_RSA or TYPE_DSA)
109 :param bits: The number of bits
110
111 :return: None
112 """
113 if not isinstance(type, int):
114 raise TypeError("type must be an integer")
115
116 if not isinstance(bits, int):
117 raise TypeError("bits must be an integer")
118
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800119 # TODO Check error return
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800120 exponent = _api.BN_new()
121 exponent = _api.ffi.gc(exponent, _api.BN_free)
122 _api.BN_set_word(exponent, _api.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800123
124 if type == TYPE_RSA:
125 if bits <= 0:
126 raise ValueError("Invalid number of bits")
127
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800128 rsa = _api.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800129
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800130 result = _api.RSA_generate_key_ex(rsa, bits, exponent, _api.NULL)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800131 if result == -1:
132 1/0
133
134 result = _api.EVP_PKEY_assign_RSA(self._pkey, rsa)
135 if not result:
136 1/0
137
138 elif type == TYPE_DSA:
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800139 dsa = _api.DSA_generate_parameters(
140 bits, _api.NULL, 0, _api.NULL, _api.NULL, _api.NULL, _api.NULL)
141 if dsa == _api.NULL:
142 1/0
143 if not _api.DSA_generate_key(dsa):
144 1/0
145 if not _api.EVP_PKEY_assign_DSA(self._pkey, dsa):
146 1/0
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800147 else:
148 raise Error("No such key type")
149
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800150 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800151
152
153 def check(self):
154 """
155 Check the consistency of an RSA private key.
156
157 :return: True if key is consistent.
158 :raise Error: if the key is inconsistent.
159 :raise TypeError: if the key is of a type which cannot be checked.
160 Only RSA keys can currently be checked.
161 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800162 if self._only_public:
163 raise TypeError("public key only")
164
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800165 if _api.EVP_PKEY_type(self._pkey.type) != _api.EVP_PKEY_RSA:
166 raise TypeError("key type unsupported")
167
168 rsa = _api.EVP_PKEY_get1_RSA(self._pkey)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800169 rsa = _api.ffi.gc(rsa, _api.RSA_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800170 result = _api.RSA_check_key(rsa)
171 if result:
172 return True
173 _raise_current_error()
174
175
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800176 def type(self):
177 """
178 Returns the type of the key
179
180 :return: The type of the key.
181 """
182 return self._pkey.type
183
184
185 def bits(self):
186 """
187 Returns the number of bits of the key
188
189 :return: The number of bits of the key.
190 """
191 return _api.EVP_PKEY_bits(self._pkey)
192PKeyType = PKey
193
194
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800195
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800196class X509Name(object):
197 def __init__(self, name):
198 """
199 Create a new X509Name, copying the given X509Name instance.
200
201 :param name: An X509Name object to copy
202 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800203 name = _api.X509_NAME_dup(name._name)
204 self._name = _api.ffi.gc(name, _api.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800205
206
207 def __setattr__(self, name, value):
208 if name.startswith('_'):
209 return super(X509Name, self).__setattr__(name, value)
210
211 if type(name) is not str:
212 raise TypeError("attribute name must be string, not '%.200s'" % (
213 type(value).__name__,))
214
215 nid = _api.OBJ_txt2nid(name)
216 if nid == _api.NID_undef:
217 try:
218 _raise_current_error()
219 except Error:
220 pass
221 raise AttributeError("No such attribute")
222
223 # If there's an old entry for this NID, remove it
224 for i in range(_api.X509_NAME_entry_count(self._name)):
225 ent = _api.X509_NAME_get_entry(self._name, i)
226 ent_obj = _api.X509_NAME_ENTRY_get_object(ent)
227 ent_nid = _api.OBJ_obj2nid(ent_obj)
228 if nid == ent_nid:
229 ent = _api.X509_NAME_delete_entry(self._name, i)
230 _api.X509_NAME_ENTRY_free(ent)
231 break
232
233 if isinstance(value, unicode):
234 value = value.encode('utf-8')
235
236 add_result = _api.X509_NAME_add_entry_by_NID(
237 self._name, nid, _api.MBSTRING_UTF8, value, -1, -1, 0)
238 if not add_result:
239 # TODO Untested
240 1/0
241
242
243 def __getattr__(self, name):
244 """
245 Find attribute. An X509Name object has the following attributes:
246 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
247 organization (alias O), organizationalUnit (alias OU), commonName (alias
248 CN) and more...
249 """
250 nid = _api.OBJ_txt2nid(name)
251 if nid == _api.NID_undef:
252 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
253 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
254 # push something onto the error queue. If we don't clean that up
255 # now, someone else will bump into it later and be quite confused.
256 # See lp#314814.
257 try:
258 _raise_current_error()
259 except Error:
260 pass
261 return super(X509Name, self).__getattr__(name)
262
263 entry_index = _api.X509_NAME_get_index_by_NID(self._name, nid, -1)
264 if entry_index == -1:
265 return None
266
267 entry = _api.X509_NAME_get_entry(self._name, entry_index)
268 data = _api.X509_NAME_ENTRY_get_data(entry)
269
270 result_buffer = _api.new("unsigned char**")
271 data_length = _api.ASN1_STRING_to_UTF8(result_buffer, data)
272 if data_length < 0:
273 1/0
274
275 result = _api.buffer(result_buffer[0], data_length)[:].decode('utf-8')
276 _api.OPENSSL_free(result_buffer[0])
277 return result
278
279
280 def __cmp__(self, other):
281 if not isinstance(other, X509Name):
282 return NotImplemented
283
284 result = _api.X509_NAME_cmp(self._name, other._name)
285 # TODO result == -2 is an error case that maybe should be checked for
286 return result
287
288
289 def __repr__(self):
290 """
291 String representation of an X509Name
292 """
293 result_buffer = _api.new("char[]", 512);
294 format_result = _api.X509_NAME_oneline(
295 self._name, result_buffer, len(result_buffer))
296
297 if format_result == _api.NULL:
298 1/0
299
300 return "<X509Name object '%s'>" % (_api.string(result_buffer),)
301
302
303 def hash(self):
304 """
305 Return the hash value of this name
306
307 :return: None
308 """
309 return _api.X509_NAME_hash(self._name)
310
311
312 def der(self):
313 """
314 Return the DER encoding of this name
315
316 :return: A :py:class:`bytes` instance giving the DER encoded form of
317 this name.
318 """
319 result_buffer = _api.new('unsigned char**')
320 encode_result = _api.i2d_X509_NAME(self._name, result_buffer)
321 if encode_result < 0:
322 1/0
323
324 string_result = _api.buffer(result_buffer[0], encode_result)[:]
325 _api.OPENSSL_free(result_buffer[0])
326 return string_result
327
328
329 def get_components(self):
330 """
331 Returns the split-up components of this name.
332
333 :return: List of tuples (name, value).
334 """
335 result = []
336 for i in range(_api.X509_NAME_entry_count(self._name)):
337 ent = _api.X509_NAME_get_entry(self._name, i)
338
339 fname = _api.X509_NAME_ENTRY_get_object(ent)
340 fval = _api.X509_NAME_ENTRY_get_data(ent)
341
342 nid = _api.OBJ_obj2nid(fname)
343 name = _api.OBJ_nid2sn(nid)
344
345 result.append((
346 _api.string(name),
347 _api.string(
348 _api.ASN1_STRING_data(fval),
349 _api.ASN1_STRING_length(fval))))
350
351 return result
352X509NameType = X509Name
353
354
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800355class X509Extension(object):
356 def __init__(self, type_name, critical, value, subject=None, issuer=None):
357 """
358 :param typename: The name of the extension to create.
359 :type typename: :py:data:`str`
360
361 :param critical: A flag indicating whether this is a critical extension.
362
363 :param value: The value of the extension.
364 :type value: :py:data:`str`
365
366 :param subject: Optional X509 cert to use as subject.
367 :type subject: :py:class:`X509`
368
369 :param issuer: Optional X509 cert to use as issuer.
370 :type issuer: :py:class:`X509`
371
372 :return: The X509Extension object
373 """
374 ctx = _api.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800375
376 # A context is necessary for any extension which uses the r2i conversion
377 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
378 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800379 _api.X509V3_set_ctx(ctx, _api.NULL, _api.NULL, _api.NULL, _api.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800380
381 # We have no configuration database - but perhaps we should (some
382 # extensions may require it).
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800383 _api.X509V3_set_ctx_nodb(ctx)
384
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800385 # Initialize the subject and issuer, if appropriate. ctx is a local,
386 # and as far as I can tell none of the X509V3_* APIs invoked here steal
387 # any references, so no need to mess with reference counts or duplicates.
388 if issuer is not None:
389 if not isinstance(issuer, X509):
390 raise TypeError("issuer must be an X509 instance")
391 ctx.issuer_cert = issuer._x509
392 if subject is not None:
393 if not isinstance(subject, X509):
394 raise TypeError("subject must be an X509 instance")
395 ctx.subject_cert = subject._x509
396
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800397 if critical:
398 # There are other OpenSSL APIs which would let us pass in critical
399 # separately, but they're harder to use, and since value is already
400 # a pile of crappy junk smuggling a ton of utterly important
401 # structured data, what's the point of trying to avoid nasty stuff
402 # with strings? (However, X509V3_EXT_i2d in particular seems like it
403 # would be a better API to invoke. I do not know where to get the
404 # ext_struc it desires for its last parameter, though.)
405 value = "critical," + value
406
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800407 extension = _api.X509V3_EXT_nconf(_api.NULL, ctx, type_name, value)
408 if extension == _api.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800409 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800410 self._extension = _api.ffi.gc(extension, _api.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800411
412
413 def __str__(self):
414 """
415 :return: a nice text representation of the extension
416 """
417 bio = _api.BIO_new(_api.BIO_s_mem())
418 if bio == _api.NULL:
419 1/0
420
421 print_result = _api.X509V3_EXT_print(bio, self._extension, 0, 0)
422 if not print_result:
423 1/0
424
425 return _bio_to_string(bio)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800426
427
428 def get_critical(self):
429 """
430 Returns the critical field of the X509Extension
431
432 :return: The critical field.
433 """
434 return _api.X509_EXTENSION_get_critical(self._extension)
435
436
437 def get_short_name(self):
438 """
439 Returns the short version of the type name of the X509Extension
440
441 :return: The short type name.
442 """
443 obj = _api.X509_EXTENSION_get_object(self._extension)
444 nid = _api.OBJ_obj2nid(obj)
445 return _api.string(_api.OBJ_nid2sn(nid))
446
447
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800448 def get_data(self):
449 """
450 Returns the data of the X509Extension
451
452 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
453 """
454 octet_result = _api.X509_EXTENSION_get_data(self._extension)
455 string_result = _api.cast('ASN1_STRING*', octet_result)
456 char_result = _api.ASN1_STRING_data(string_result)
457 result_length = _api.ASN1_STRING_length(string_result)
458 return _api.buffer(char_result, result_length)[:]
459
460X509ExtensionType = X509Extension
461
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800462
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800463class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800464 def __init__(self):
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800465 req = _api.X509_REQ_new()
466 self._req = _api.ffi.gc(req, _api.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800467
468
469 def set_pubkey(self, pkey):
470 """
471 Set the public key of the certificate request
472
473 :param pkey: The public key to use
474 :return: None
475 """
476 set_result = _api.X509_REQ_set_pubkey(self._req, pkey._pkey)
477 if not set_result:
478 1/0
479
480
481 def get_pubkey(self):
482 """
483 Get the public key from the certificate request
484
485 :return: The public key
486 """
487 pkey = PKey.__new__(PKey)
488 pkey._pkey = _api.X509_REQ_get_pubkey(self._req)
489 if pkey._pkey == _api.NULL:
490 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800491 pkey._pkey = _api.ffi.gc(pkey._pkey, _api.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800492 pkey._only_public = True
493 return pkey
494
495
496 def set_version(self, version):
497 """
498 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
499 request.
500
501 :param version: The version number
502 :return: None
503 """
504 set_result = _api.X509_REQ_set_version(self._req, version)
505 if not set_result:
506 _raise_current_error()
507
508
509 def get_version(self):
510 """
511 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
512 request.
513
514 :return: an integer giving the value of the version subfield
515 """
516 return _api.X509_REQ_get_version(self._req)
517
518
519 def get_subject(self):
520 """
521 Create an X509Name object for the subject of the certificate request
522
523 :return: An X509Name object
524 """
525 name = X509Name.__new__(X509Name)
526 name._name = _api.X509_REQ_get_subject_name(self._req)
527 if name._name == _api.NULL:
528 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800529
530 # The name is owned by the X509Req structure. As long as the X509Name
531 # Python object is alive, keep the X509Req Python object alive.
532 name._owner = self
533
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800534 return name
535
536
537 def add_extensions(self, extensions):
538 """
539 Add extensions to the request.
540
541 :param extensions: a sequence of X509Extension objects
542 :return: None
543 """
544 stack = _api.sk_X509_EXTENSION_new_null()
545 if stack == _api.NULL:
546 1/0
547
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800548 stack = _api.ffi.gc(stack, _api.sk_X509_EXTENSION_free)
549
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800550 for ext in extensions:
551 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800552 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800553
554 _api.sk_X509_EXTENSION_push(stack, ext._extension)
555
556 add_result = _api.X509_REQ_add_extensions(self._req, stack)
557 if not add_result:
558 1/0
559
560
561 def sign(self, pkey, digest):
562 """
563 Sign the certificate request using the supplied key and digest
564
565 :param pkey: The key to sign with
566 :param digest: The message digest to use
567 :return: None
568 """
569 if pkey._only_public:
570 raise ValueError("Key has only public part")
571
572 if not pkey._initialized:
573 raise ValueError("Key is uninitialized")
574
575 digest_obj = _api.EVP_get_digestbyname(digest)
576 if digest_obj == _api.NULL:
577 raise ValueError("No such digest method")
578
579 sign_result = _api.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
580 if not sign_result:
581 1/0
582
583
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800584X509ReqType = X509Req
585
586
587
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800588class X509(object):
589 def __init__(self):
590 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800591 x509 = _api.X509_new()
592 self._x509 = _api.ffi.gc(x509, _api.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800593
594
595 def set_version(self, version):
596 """
597 Set version number of the certificate
598
599 :param version: The version number
600 :type version: :py:class:`int`
601
602 :return: None
603 """
604 if not isinstance(version, int):
605 raise TypeError("version must be an integer")
606
607 _api.X509_set_version(self._x509, version)
608
609
610 def get_version(self):
611 """
612 Return version number of the certificate
613
614 :return: Version number as a Python integer
615 """
616 return _api.X509_get_version(self._x509)
617
618
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800619 def get_pubkey(self):
620 """
621 Get the public key of the certificate
622
623 :return: The public key
624 """
625 pkey = PKey.__new__(PKey)
626 pkey._pkey = _api.X509_get_pubkey(self._x509)
627 if pkey._pkey == _api.NULL:
628 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800629 pkey._pkey = _api.ffi.gc(pkey._pkey, _api.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800630 pkey._only_public = True
631 return pkey
632
633
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800634 def set_pubkey(self, pkey):
635 """
636 Set the public key of the certificate
637
638 :param pkey: The public key
639
640 :return: None
641 """
642 if not isinstance(pkey, PKey):
643 raise TypeError("pkey must be a PKey instance")
644
645 set_result = _api.X509_set_pubkey(self._x509, pkey._pkey)
646 if not set_result:
647 _raise_current_error()
648
649
650 def sign(self, pkey, digest):
651 """
652 Sign the certificate using the supplied key and digest
653
654 :param pkey: The key to sign with
655 :param digest: The message digest to use
656 :return: None
657 """
658 if not isinstance(pkey, PKey):
659 raise TypeError("pkey must be a PKey instance")
660
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800661 if pkey._only_public:
662 raise ValueError("Key only has public part")
663
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800664 if not pkey._initialized:
665 raise ValueError("Key is uninitialized")
666
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800667 evp_md = _api.EVP_get_digestbyname(digest)
668 if evp_md == _api.NULL:
669 raise ValueError("No such digest method")
670
671 sign_result = _api.X509_sign(self._x509, pkey._pkey, evp_md)
672 if not sign_result:
673 _raise_current_error()
674
675
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800676 def get_signature_algorithm(self):
677 """
678 Retrieve the signature algorithm used in the certificate
679
680 :return: A byte string giving the name of the signature algorithm used in
681 the certificate.
682 :raise ValueError: If the signature algorithm is undefined.
683 """
684 alg = self._x509.cert_info.signature.algorithm
685 nid = _api.OBJ_obj2nid(alg)
686 if nid == _api.NID_undef:
687 raise ValueError("Undefined signature algorithm")
688 return _api.string(_api.OBJ_nid2ln(nid))
689
690
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800691 def digest(self, digest_name):
692 """
693 Return the digest of the X509 object.
694
695 :param digest_name: The name of the digest algorithm to use.
696 :type digest_name: :py:class:`bytes`
697
698 :return: The digest of the object
699 """
700 digest = _api.EVP_get_digestbyname(digest_name)
701 if digest == _api.NULL:
702 raise ValueError("No such digest method")
703
704 result_buffer = _api.new("char[]", _api.EVP_MAX_MD_SIZE)
705 result_length = _api.new("unsigned int[]", 1)
706 result_length[0] = len(result_buffer)
707
708 digest_result = _api.X509_digest(
709 self._x509, digest, result_buffer, result_length)
710
711 if not digest_result:
712 1/0
713
714 return ':'.join([
715 ch.encode('hex').upper() for ch
716 in _api.buffer(result_buffer, result_length[0])])
717
718
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800719 def subject_name_hash(self):
720 """
721 Return the hash of the X509 subject.
722
723 :return: The hash of the subject.
724 """
725 return _api.X509_subject_name_hash(self._x509)
726
727
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800728 def set_serial_number(self, serial):
729 """
730 Set serial number of the certificate
731
732 :param serial: The serial number
733 :type serial: :py:class:`int`
734
735 :return: None
736 """
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800737 if not isinstance(serial, (int, long)):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800738 raise TypeError("serial must be an integer")
739
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800740 hex_serial = hex(serial)[2:]
741 if not isinstance(hex_serial, bytes):
742 hex_serial = hex_serial.encode('ascii')
743
744 bignum_serial = _api.new("BIGNUM**")
745
746 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
747 # it. If bignum is still NULL after this call, then the return value is
748 # actually the result. I hope. -exarkun
749 small_serial = _api.BN_hex2bn(bignum_serial, hex_serial)
750
751 if bignum_serial[0] == _api.NULL:
752 set_result = ASN1_INTEGER_set(
753 _api.X509_get_serialNumber(self._x509), small_serial)
754 if set_result:
755 # TODO Not tested
756 _raise_current_error()
757 else:
758 asn1_serial = _api.BN_to_ASN1_INTEGER(bignum_serial[0], _api.NULL)
759 _api.BN_free(bignum_serial[0])
760 if asn1_serial == _api.NULL:
761 # TODO Not tested
762 _raise_current_error()
763 set_result = _api.X509_set_serialNumber(self._x509, asn1_serial)
764 if not set_result:
765 # TODO Not tested
766 _raise_current_error()
767
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800768
769 def get_serial_number(self):
770 """
771 Return serial number of the certificate
772
773 :return: Serial number as a Python integer
774 """
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800775 asn1_serial = _api.X509_get_serialNumber(self._x509)
776 bignum_serial = _api.ASN1_INTEGER_to_BN(asn1_serial, _api.NULL)
777 try:
778 hex_serial = _api.BN_bn2hex(bignum_serial)
779 try:
780 hexstring_serial = _api.string(hex_serial)
781 serial = int(hexstring_serial, 16)
782 return serial
783 finally:
784 _api.OPENSSL_free(hex_serial)
785 finally:
786 _api.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800787
788
789 def gmtime_adj_notAfter(self, amount):
790 """
791 Adjust the time stamp for when the certificate stops being valid
792
793 :param amount: The number of seconds by which to adjust the ending
794 validity time.
795 :type amount: :py:class:`int`
796
797 :return: None
798 """
799 if not isinstance(amount, int):
800 raise TypeError("amount must be an integer")
801
802 notAfter = _api.X509_get_notAfter(self._x509)
803 _api.X509_gmtime_adj(notAfter, amount)
804
805
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800806 def gmtime_adj_notBefore(self, amount):
807 """
808 Change the timestamp for when the certificate starts being valid to the current
809 time plus an offset.
810
811 :param amount: The number of seconds by which to adjust the starting validity
812 time.
813 :return: None
814 """
815 if not isinstance(amount, int):
816 raise TypeError("amount must be an integer")
817
818 notBefore = _api.X509_get_notBefore(self._x509)
819 _api.X509_gmtime_adj(notBefore, amount)
820
821
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800822 def has_expired(self):
823 """
824 Check whether the certificate has expired.
825
826 :return: True if the certificate has expired, false otherwise
827 """
828 now = int(time())
829 notAfter = _api.X509_get_notAfter(self._x509)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -0800830 return _api.ASN1_UTCTIME_cmp_time_t(
831 _api.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800832
833
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800834 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800835 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800836
837
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800838 def get_notBefore(self):
839 """
840 Retrieve the time stamp for when the certificate starts being valid
841
842 :return: A string giving the timestamp, in the format::
843
844 YYYYMMDDhhmmssZ
845 YYYYMMDDhhmmss+hhmm
846 YYYYMMDDhhmmss-hhmm
847
848 or None if there is no value set.
849 """
850 return self._get_boundary_time(_api.X509_get_notBefore)
851
852
853 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800854 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800855
856
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800857 def set_notBefore(self, when):
858 """
859 Set the time stamp for when the certificate starts being valid
860
861 :param when: A string giving the timestamp, in the format:
862
863 YYYYMMDDhhmmssZ
864 YYYYMMDDhhmmss+hhmm
865 YYYYMMDDhhmmss-hhmm
866 :type when: :py:class:`bytes`
867
868 :return: None
869 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800870 return self._set_boundary_time(_api.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -0800871
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800872
873 def get_notAfter(self):
874 """
875 Retrieve the time stamp for when the certificate stops being valid
876
877 :return: A string giving the timestamp, in the format::
878
879 YYYYMMDDhhmmssZ
880 YYYYMMDDhhmmss+hhmm
881 YYYYMMDDhhmmss-hhmm
882
883 or None if there is no value set.
884 """
885 return self._get_boundary_time(_api.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800886
887
888 def set_notAfter(self, when):
889 """
890 Set the time stamp for when the certificate stops being valid
891
892 :param when: A string giving the timestamp, in the format:
893
894 YYYYMMDDhhmmssZ
895 YYYYMMDDhhmmss+hhmm
896 YYYYMMDDhhmmss-hhmm
897 :type when: :py:class:`bytes`
898
899 :return: None
900 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800901 return self._set_boundary_time(_api.X509_get_notAfter, when)
902
903
904 def _get_name(self, which):
905 name = X509Name.__new__(X509Name)
906 name._name = which(self._x509)
907 if name._name == _api.NULL:
908 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800909
910 # The name is owned by the X509 structure. As long as the X509Name
911 # Python object is alive, keep the X509 Python object alive.
912 name._owner = self
913
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800914 return name
915
916
917 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800918 if not isinstance(name, X509Name):
919 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800920 set_result = which(self._x509, name._name)
921 if not set_result:
922 1/0
923
924
925 def get_issuer(self):
926 """
927 Create an X509Name object for the issuer of the certificate
928
929 :return: An X509Name object
930 """
931 return self._get_name(_api.X509_get_issuer_name)
932
933
934 def set_issuer(self, issuer):
935 """
936 Set the issuer of the certificate
937
938 :param issuer: The issuer name
939 :type issuer: :py:class:`X509Name`
940
941 :return: None
942 """
943 return self._set_name(_api.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800944
945
946 def get_subject(self):
947 """
948 Create an X509Name object for the subject of the certificate
949
950 :return: An X509Name object
951 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800952 return self._get_name(_api.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800953
954
955 def set_subject(self, subject):
956 """
957 Set the subject of the certificate
958
959 :param subject: The subject name
960 :type subject: :py:class:`X509Name`
961 :return: None
962 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800963 return self._set_name(_api.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800964
965
966 def get_extension_count(self):
967 """
968 Get the number of extensions on the certificate.
969
970 :return: The number of extensions as an integer.
971 """
972 return _api.X509_get_ext_count(self._x509)
973
974
975 def add_extensions(self, extensions):
976 """
977 Add extensions to the certificate.
978
979 :param extensions: a sequence of X509Extension objects
980 :return: None
981 """
982 for ext in extensions:
983 if not isinstance(ext, X509Extension):
984 raise ValueError("One of the elements is not an X509Extension")
985
986 add_result = _api.X509_add_ext(self._x509, ext._extension, -1)
987 if not add_result:
988 _raise_current_error()
989
990
991 def get_extension(self, index):
992 """
993 Get a specific extension of the certificate by index.
994
995 :param index: The index of the extension to retrieve.
996 :return: The X509Extension object at the specified index.
997 """
998 ext = X509Extension.__new__(X509Extension)
999 ext._extension = _api.X509_get_ext(self._x509, index)
1000 if ext._extension == _api.NULL:
1001 raise IndexError("extension index out of bounds")
1002
1003 ext._extension = _api.X509_EXTENSION_dup(ext._extension)
1004 return ext
1005
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001006X509Type = X509
1007
1008
1009
1010def load_certificate(type, buffer):
1011 """
1012 Load a certificate from a buffer
1013
1014 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1015
1016 :param buffer: The buffer the certificate is stored in
1017 :type buffer: :py:class:`bytes`
1018
1019 :return: The X509 object
1020 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001021 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001022
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001023 if type == FILETYPE_PEM:
1024 x509 = _api.PEM_read_bio_X509(bio, _api.NULL, _api.NULL, _api.NULL)
1025 elif type == FILETYPE_ASN1:
1026 x509 = _api.d2i_X509_bio(bio, _api.NULL);
1027 else:
1028 raise ValueError(
1029 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001030
1031 if x509 == _api.NULL:
1032 _raise_current_error()
1033
1034 cert = X509.__new__(X509)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001035 cert._x509 = _api.ffi.gc(x509, _api.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001036 return cert
1037
1038
1039def dump_certificate(type, cert):
1040 """
1041 Dump a certificate to a buffer
1042
1043 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1044 :param cert: The certificate to dump
1045 :return: The buffer with the dumped certificate in
1046 """
1047 bio = _api.BIO_new(_api.BIO_s_mem())
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001048 if bio == _api.NULL:
1049 1/0
1050
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001051 if type == FILETYPE_PEM:
1052 result_code = _api.PEM_write_bio_X509(bio, cert._x509)
1053 elif type == FILETYPE_ASN1:
1054 result_code = _api.i2d_X509_bio(bio, cert._x509)
1055 elif type == FILETYPE_TEXT:
1056 result_code = _api.X509_print_ex(bio, cert._x509, 0, 0)
1057 else:
1058 raise ValueError(
1059 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1060 "FILETYPE_TEXT")
1061
1062 return _bio_to_string(bio)
1063
1064
1065
1066def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1067 """
1068 Dump a private key to a buffer
1069
1070 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1071 :param pkey: The PKey to dump
1072 :param cipher: (optional) if encrypted PEM format, the cipher to
1073 use
1074 :param passphrase: (optional) if encrypted PEM format, this can be either
1075 the passphrase to use, or a callback for providing the
1076 passphrase.
1077 :return: The buffer with the dumped key in
1078 :rtype: :py:data:`str`
1079 """
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001080 bio = _api.BIO_new(_api.BIO_s_mem())
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001081 if bio == _api.NULL:
1082 1/0
1083
1084 if cipher is not None:
1085 cipher_obj = _api.EVP_get_cipherbyname(cipher)
1086 if cipher_obj == _api.NULL:
1087 raise ValueError("Invalid cipher name")
1088 else:
1089 cipher_obj = _api.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001090
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001091 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001092 if type == FILETYPE_PEM:
1093 result_code = _api.PEM_write_bio_PrivateKey(
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001094 bio, pkey._pkey, cipher_obj, _api.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001095 helper.callback, helper.callback_args)
1096 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001097 elif type == FILETYPE_ASN1:
1098 result_code = _api.i2d_PrivateKey_bio(bio, pkey._pkey)
1099 elif type == FILETYPE_TEXT:
1100 rsa = _api.EVP_PKEY_get1_RSA(pkey._pkey)
1101 result_code = _api.RSA_print(bio, rsa, 0)
1102 # TODO RSA_free(rsa)?
1103 else:
1104 raise ValueError(
1105 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1106 "FILETYPE_TEXT")
1107
1108 if result_code == 0:
1109 _raise_current_error()
1110
1111 return _bio_to_string(bio)
1112
1113
1114
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001115def _X509_REVOKED_dup(original):
1116 copy = _api.X509_REVOKED_new()
1117 if copy == _api.NULL:
1118 1/0
1119
1120 if original.serialNumber != _api.NULL:
1121 copy.serialNumber = _api.ASN1_INTEGER_dup(original.serialNumber)
1122
1123 if original.revocationDate != _api.NULL:
1124 copy.revocationDate = _api.M_ASN1_TIME_dup(original.revocationDate)
1125
1126 if original.extensions != _api.NULL:
1127 extension_stack = _api.sk_X509_EXTENSION_new_null()
1128 for i in range(_api.sk_X509_EXTENSION_num(original.extensions)):
1129 original_ext = _api.sk_X509_EXTENSION_value(original.extensions, i)
1130 copy_ext = _api.X509_EXTENSION_dup(original_ext)
1131 _api.sk_X509_EXTENSION_push(extension_stack, copy_ext)
1132 copy.extensions = extension_stack
1133
1134 copy.sequence = original.sequence
1135 return copy
1136
1137
1138
1139class Revoked(object):
1140 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1141 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1142 # OCSP_crl_reason_str. We use the latter, just like the command line
1143 # program.
1144 _crl_reasons = [
1145 "unspecified",
1146 "keyCompromise",
1147 "CACompromise",
1148 "affiliationChanged",
1149 "superseded",
1150 "cessationOfOperation",
1151 "certificateHold",
1152 # "removeFromCRL",
1153 ]
1154
1155 def __init__(self):
1156 self._revoked = _api.X509_REVOKED_new()
1157
1158
1159 def set_serial(self, hex_str):
1160 """
1161 Set the serial number of a revoked Revoked structure
1162
1163 :param hex_str: The new serial number.
1164 :type hex_str: :py:data:`str`
1165 :return: None
1166 """
1167 bignum_serial = _api.new("BIGNUM**")
1168 bn_result = _api.BN_hex2bn(bignum_serial, hex_str)
1169 if not bn_result:
1170 raise ValueError("bad hex string")
1171
1172 asn1_serial = _api.BN_to_ASN1_INTEGER(bignum_serial[0], _api.NULL)
1173 _api.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
1174
1175
1176 def get_serial(self):
1177 """
1178 Return the serial number of a Revoked structure
1179
1180 :return: The serial number as a string
1181 """
1182 bio = _api.BIO_new(_api.BIO_s_mem())
1183 if bio == _api.NULL:
1184 1/0
1185
1186 result = _api.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
1187 if result < 0:
1188 1/0
1189
1190 return _bio_to_string(bio)
1191
1192
1193 def _delete_reason(self):
1194 stack = self._revoked.extensions
1195 for i in range(_api.sk_X509_EXTENSION_num(stack)):
1196 ext = _api.sk_X509_EXTENSION_value(stack, i)
1197 if _api.OBJ_obj2nid(ext.object) == _api.NID_crl_reason:
1198 _api.sk_X509_EXTENSION_delete(stack, i)
1199 break
1200
1201
1202 def set_reason(self, reason):
1203 """
1204 Set the reason of a Revoked object.
1205
1206 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1207
1208 :param reason: The reason string.
1209 :type reason: :py:class:`str` or :py:class:`NoneType`
1210 :return: None
1211 """
1212 if reason is None:
1213 self._delete_reason()
1214 elif not isinstance(reason, bytes):
1215 raise TypeError("reason must be None or a byte string")
1216 else:
1217 reason = reason.lower().replace(' ', '')
1218 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1219
1220 new_reason_ext = _api.ASN1_ENUMERATED_new()
1221 if new_reason_ext == _api.NULL:
1222 1/0
1223
1224 set_result = _api.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1225 if set_result == _api.NULL:
1226 1/0
1227
1228 self._delete_reason()
1229 add_result = _api.X509_REVOKED_add1_ext_i2d(
1230 self._revoked, _api.NID_crl_reason, new_reason_ext, 0, 0)
1231
1232 if not add_result:
1233 1/0
1234
1235
1236 def get_reason(self):
1237 """
1238 Return the reason of a Revoked object.
1239
1240 :return: The reason as a string
1241 """
1242 extensions = self._revoked.extensions
1243 for i in range(_api.sk_X509_EXTENSION_num(extensions)):
1244 ext = _api.sk_X509_EXTENSION_value(extensions, i)
1245 if _api.OBJ_obj2nid(ext.object) == _api.NID_crl_reason:
1246 bio = _api.BIO_new(_api.BIO_s_mem())
1247 if bio == _api.NULL:
1248 1/0
1249
1250 print_result = _api.X509V3_EXT_print(bio, ext, 0, 0)
1251 if not print_result:
1252 print_result = _api.M_ASN1_OCTET_STRING_print(bio, ext.value)
1253 if print_result == 0:
1254 1/0
1255
1256 return _bio_to_string(bio)
1257
1258
1259 def all_reasons(self):
1260 """
1261 Return a list of all the supported reason strings.
1262
1263 :return: A list of reason strings.
1264 """
1265 return self._crl_reasons[:]
1266
1267
1268 def set_rev_date(self, when):
1269 """
1270 Set the revocation timestamp
1271
1272 :param when: A string giving the timestamp, in the format:
1273
1274 YYYYMMDDhhmmssZ
1275 YYYYMMDDhhmmss+hhmm
1276 YYYYMMDDhhmmss-hhmm
1277
1278 :return: None
1279 """
1280 return _set_asn1_time(self._revoked.revocationDate, when)
1281
1282
1283 def get_rev_date(self):
1284 """
1285 Retrieve the revocation date
1286
1287 :return: A string giving the timestamp, in the format:
1288
1289 YYYYMMDDhhmmssZ
1290 YYYYMMDDhhmmss+hhmm
1291 YYYYMMDDhhmmss-hhmm
1292 """
1293 return _get_asn1_time(self._revoked.revocationDate)
1294
1295
1296
1297class CRL(object):
1298 def __init__(self):
1299 """
1300 Create a new empty CRL object.
1301 """
1302 self._crl = _api.X509_CRL_new()
1303
1304
1305 def get_revoked(self):
1306 """
1307 Return revoked portion of the CRL structure (by value not reference).
1308
1309 :return: A tuple of Revoked objects.
1310 """
1311 results = []
1312 revoked_stack = self._crl.crl.revoked
1313 for i in range(_api.sk_X509_REVOKED_num(revoked_stack)):
1314 revoked = _api.sk_X509_REVOKED_value(revoked_stack, i)
1315 revoked_copy = _X509_REVOKED_dup(revoked)
1316 pyrev = Revoked.__new__(Revoked)
1317 pyrev._revoked = revoked_copy
1318 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001319 if results:
1320 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001321
1322
1323 def add_revoked(self, revoked):
1324 """
1325 Add a revoked (by value not reference) to the CRL structure
1326
1327 :param revoked: The new revoked.
1328 :type revoked: :class:`X509`
1329
1330 :return: None
1331 """
1332 copy = _X509_REVOKED_dup(revoked._revoked)
1333 if copy == _api.NULL:
1334 1/0
1335
1336 add_result = _api.X509_CRL_add0_revoked(self._crl, copy)
1337 # TODO what check on add_result?
1338
1339
1340 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1341 """
1342 export a CRL as a string
1343
1344 :param cert: Used to sign CRL.
1345 :type cert: :class:`X509`
1346
1347 :param key: Used to sign CRL.
1348 :type key: :class:`PKey`
1349
1350 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1351
1352 :param days: The number of days until the next update of this CRL.
1353 :type days: :py:data:`int`
1354
1355 :return: :py:data:`str`
1356 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001357 if not isinstance(cert, X509):
1358 raise TypeError("cert must be an X509 instance")
1359 if not isinstance(key, PKey):
1360 raise TypeError("key must be a PKey instance")
1361 if not isinstance(type, int):
1362 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001363
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001364 bio = _api.BIO_new(_api.BIO_s_mem())
1365 if bio == _api.NULL:
1366 1/0
1367
1368 # A scratch time object to give different values to different CRL fields
1369 sometime = _api.ASN1_TIME_new()
1370 if sometime == _api.NULL:
1371 1/0
1372
1373 _api.X509_gmtime_adj(sometime, 0)
1374 _api.X509_CRL_set_lastUpdate(self._crl, sometime)
1375
1376 _api.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1377 _api.X509_CRL_set_nextUpdate(self._crl, sometime)
1378
1379 _api.X509_CRL_set_issuer_name(self._crl, _api.X509_get_subject_name(cert._x509))
1380
1381 sign_result = _api.X509_CRL_sign(self._crl, key._pkey, _api.EVP_md5())
1382 if not sign_result:
1383 _raise_current_error()
1384
1385 if type == FILETYPE_PEM:
1386 ret = _api.PEM_write_bio_X509_CRL(bio, self._crl)
1387 elif type == FILETYPE_ASN1:
1388 ret = _api.i2d_X509_CRL_bio(bio, self._crl)
1389 elif type == FILETYPE_TEXT:
1390 ret = _api.X509_CRL_print(bio, self._crl)
1391 else:
1392 raise ValueError(
1393 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1394
1395 if not ret:
1396 1/0
1397
1398 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001399CRLType = CRL
1400
1401
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001402
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001403class PKCS7(object):
1404 def type_is_signed(self):
1405 """
1406 Check if this NID_pkcs7_signed object
1407
1408 :return: True if the PKCS7 is of type signed
1409 """
1410 if _api.PKCS7_type_is_signed(self._pkcs7):
1411 return True
1412 return False
1413
1414
1415 def type_is_enveloped(self):
1416 """
1417 Check if this NID_pkcs7_enveloped object
1418
1419 :returns: True if the PKCS7 is of type enveloped
1420 """
1421 if _api.PKCS7_type_is_enveloped(self._pkcs7):
1422 return True
1423 return False
1424
1425
1426 def type_is_signedAndEnveloped(self):
1427 """
1428 Check if this NID_pkcs7_signedAndEnveloped object
1429
1430 :returns: True if the PKCS7 is of type signedAndEnveloped
1431 """
1432 if _api.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
1433 return True
1434 return False
1435
1436
1437 def type_is_data(self):
1438 """
1439 Check if this NID_pkcs7_data object
1440
1441 :return: True if the PKCS7 is of type data
1442 """
1443 if _api.PKCS7_type_is_data(self._pkcs7):
1444 return True
1445 return False
1446
1447
1448 def get_type_name(self):
1449 """
1450 Returns the type name of the PKCS7 structure
1451
1452 :return: A string with the typename
1453 """
1454 nid = _api.OBJ_obj2nid(self._pkcs7.type)
1455 string_type = _api.OBJ_nid2sn(nid)
1456 return _api.string(string_type)
1457
1458PKCS7Type = PKCS7
1459
1460
1461
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001462class PKCS12(object):
1463 def __init__(self):
1464 self._pkey = None
1465 self._cert = None
1466 self._cacerts = None
1467 self._friendlyname = None
1468
1469
1470 def get_certificate(self):
1471 """
1472 Return certificate portion of the PKCS12 structure
1473
1474 :return: X509 object containing the certificate
1475 """
1476 return self._cert
1477
1478
1479 def set_certificate(self, cert):
1480 """
1481 Replace the certificate portion of the PKCS12 structure
1482
1483 :param cert: The new certificate.
1484 :type cert: :py:class:`X509` or :py:data:`None`
1485 :return: None
1486 """
1487 if not isinstance(cert, X509):
1488 raise TypeError("cert must be an X509 instance")
1489 self._cert = cert
1490
1491
1492 def get_privatekey(self):
1493 """
1494 Return private key portion of the PKCS12 structure
1495
1496 :returns: PKey object containing the private key
1497 """
1498 return self._pkey
1499
1500
1501 def set_privatekey(self, pkey):
1502 """
1503 Replace or set the certificate portion of the PKCS12 structure
1504
1505 :param pkey: The new private key.
1506 :type pkey: :py:class:`PKey`
1507 :return: None
1508 """
1509 if not isinstance(pkey, PKey):
1510 raise TypeError("pkey must be a PKey instance")
1511 self._pkey = pkey
1512
1513
1514 def get_ca_certificates(self):
1515 """
1516 Return CA certificates within of the PKCS12 object
1517
1518 :return: A newly created tuple containing the CA certificates in the chain,
1519 if any are present, or None if no CA certificates are present.
1520 """
1521 if self._cacerts is not None:
1522 return tuple(self._cacerts)
1523
1524
1525 def set_ca_certificates(self, cacerts):
1526 """
1527 Replace or set the CA certificates withing the PKCS12 object.
1528
1529 :param cacerts: The new CA certificates.
1530 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1531 :return: None
1532 """
1533 if cacerts is None:
1534 self._cacerts = None
1535 else:
1536 cacerts = list(cacerts)
1537 for cert in cacerts:
1538 if not isinstance(cert, X509):
1539 raise TypeError("iterable must only contain X509 instances")
1540 self._cacerts = cacerts
1541
1542
1543 def set_friendlyname(self, name):
1544 """
1545 Replace or set the certificate portion of the PKCS12 structure
1546
1547 :param name: The new friendly name.
1548 :type name: :py:class:`bytes`
1549 :return: None
1550 """
1551 if name is None:
1552 self._friendlyname = None
1553 elif not isinstance(name, bytes):
1554 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1555 self._friendlyname = name
1556
1557
1558 def get_friendlyname(self):
1559 """
1560 Return friendly name portion of the PKCS12 structure
1561
1562 :returns: String containing the friendlyname
1563 """
1564 return self._friendlyname
1565
1566
1567 def export(self, passphrase=None, iter=2048, maciter=1):
1568 """
1569 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1570
1571 :param passphrase: used to encrypt the PKCS12
1572 :type passphrase: :py:data:`bytes`
1573
1574 :param iter: How many times to repeat the encryption
1575 :type iter: :py:data:`int`
1576
1577 :param maciter: How many times to repeat the MAC
1578 :type maciter: :py:data:`int`
1579
1580 :return: The string containing the PKCS12
1581 """
1582 if self._cacerts is None:
1583 cacerts = _api.NULL
1584 else:
1585 cacerts = _api.sk_X509_new_null()
1586 for cert in self._cacerts:
1587 _api.sk_X509_push(cacerts, cert._x509)
1588
1589 if passphrase is None:
1590 passphrase = _api.NULL
1591
1592 friendlyname = self._friendlyname
1593 if friendlyname is None:
1594 friendlyname = _api.NULL
1595
1596 if self._pkey is None:
1597 pkey = _api.NULL
1598 else:
1599 pkey = self._pkey._pkey
1600
1601 if self._cert is None:
1602 cert = _api.NULL
1603 else:
1604 cert = self._cert._x509
1605
1606 pkcs12 = _api.PKCS12_create(
1607 passphrase, friendlyname, pkey, cert, cacerts,
1608 _api.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1609 _api.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1610 iter, maciter, 0)
1611 if pkcs12 == _api.NULL:
1612 _raise_current_error()
1613
1614 bio = _api.BIO_new(_api.BIO_s_mem())
1615 if bio == _api.NULL:
1616 1/0
1617
1618 _api.i2d_PKCS12_bio(bio, pkcs12)
1619 return _bio_to_string(bio)
1620PKCS12Type = PKCS12
1621
1622
1623
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001624class NetscapeSPKI(object):
1625 def __init__(self):
1626 self._spki = _api.NETSCAPE_SPKI_new()
1627
1628
1629 def sign(self, pkey, digest):
1630 """
1631 Sign the certificate request using the supplied key and digest
1632
1633 :param pkey: The key to sign with
1634 :param digest: The message digest to use
1635 :return: None
1636 """
1637 if pkey._only_public:
1638 raise ValueError("Key has only public part")
1639
1640 if not pkey._initialized:
1641 raise ValueError("Key is uninitialized")
1642
1643 digest_obj = _api.EVP_get_digestbyname(digest)
1644 if digest_obj == _api.NULL:
1645 raise ValueError("No such digest method")
1646
1647 sign_result = _api.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
1648 if not sign_result:
1649 1/0
1650
1651
1652 def verify(self, key):
1653 """
1654 Verifies a certificate request using the supplied public key
1655
1656 :param key: a public key
1657 :return: True if the signature is correct.
1658 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1659 problem verifying the signature.
1660 """
1661 answer = _api.NETSCAPE_SPKI_verify(self._spki, key._pkey)
1662 if answer <= 0:
1663 _raise_current_error()
1664 return True
1665
1666
1667 def b64_encode(self):
1668 """
1669 Generate a base64 encoded string from an SPKI
1670
1671 :return: The base64 encoded string
1672 """
1673 return _api.string(_api.NETSCAPE_SPKI_b64_encode(self._spki))
1674
1675
1676 def get_pubkey(self):
1677 """
1678 Get the public key of the certificate
1679
1680 :return: The public key
1681 """
1682 pkey = PKey.__new__(PKey)
1683 pkey._pkey = _api.NETSCAPE_SPKI_get_pubkey(self._spki)
1684 if pkey._pkey == _api.NULL:
1685 1/0
1686 pkey._only_public = True
1687 return pkey
1688
1689
1690 def set_pubkey(self, pkey):
1691 """
1692 Set the public key of the certificate
1693
1694 :param pkey: The public key
1695 :return: None
1696 """
1697 set_result = _api.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
1698 if not set_result:
1699 1/0
1700NetscapeSPKIType = NetscapeSPKI
1701
1702
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001703class _PassphraseHelper(object):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001704 def __init__(self, type, passphrase):
1705 if type != FILETYPE_PEM and passphrase is not None:
1706 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001707 self._passphrase = passphrase
1708 self._problems = []
1709
1710
1711 @property
1712 def callback(self):
1713 if self._passphrase is None:
1714 return _api.NULL
1715 elif isinstance(self._passphrase, bytes):
1716 return _api.NULL
1717 elif callable(self._passphrase):
1718 return _api.callback("pem_password_cb", self._read_passphrase)
1719 else:
1720 raise TypeError("Last argument must be string or callable")
1721
1722
1723 @property
1724 def callback_args(self):
1725 if self._passphrase is None:
1726 return _api.NULL
1727 elif isinstance(self._passphrase, bytes):
1728 return self._passphrase
1729 elif callable(self._passphrase):
1730 return _api.NULL
1731 else:
1732 raise TypeError("Last argument must be string or callable")
1733
1734
1735 def raise_if_problem(self):
1736 if self._problems:
1737 try:
1738 _raise_current_error()
1739 except Error:
1740 pass
1741 raise self._problems[0]
1742
1743
1744 def _read_passphrase(self, buf, size, rwflag, userdata):
1745 try:
1746 result = self._passphrase(rwflag)
1747 if not isinstance(result, bytes):
1748 raise ValueError("String expected")
1749 if len(result) > size:
1750 raise ValueError("passphrase returned by callback is too long")
1751 for i in range(len(result)):
1752 buf[i] = result[i]
1753 return len(result)
1754 except Exception as e:
1755 self._problems.append(e)
1756 return 0
1757
1758
1759
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001760def load_privatekey(type, buffer, passphrase=None):
1761 """
1762 Load a private key from a buffer
1763
1764 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1765 :param buffer: The buffer the key is stored in
1766 :param passphrase: (optional) if encrypted PEM format, this can be
1767 either the passphrase to use, or a callback for
1768 providing the passphrase.
1769
1770 :return: The PKey object
1771 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001772 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001773
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001774 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001775 if type == FILETYPE_PEM:
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001776 evp_pkey = _api.PEM_read_bio_PrivateKey(
1777 bio, _api.NULL, helper.callback, helper.callback_args)
1778 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001779 elif type == FILETYPE_ASN1:
1780 evp_pkey = _api.d2i_PrivateKey_bio(bio, _api.NULL)
1781 else:
1782 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
1783
Jean-Paul Calderone31393aa2013-02-20 13:22:21 -08001784 if evp_pkey == _api.NULL:
1785 _raise_current_error()
1786
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001787 pkey = PKey.__new__(PKey)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001788 pkey._pkey = _api.ffi.gc(evp_pkey, _api.EVP_PKEY_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001789 return pkey
1790
1791
1792
1793def dump_certificate_request(type, req):
1794 """
1795 Dump a certificate request to a buffer
1796
1797 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1798 :param req: The certificate request to dump
1799 :return: The buffer with the dumped certificate request in
1800 """
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001801 bio = _api.BIO_new(_api.BIO_s_mem())
1802 if bio == _api.NULL:
1803 1/0
1804
1805 if type == FILETYPE_PEM:
1806 result_code = _api.PEM_write_bio_X509_REQ(bio, req._req)
1807 elif type == FILETYPE_ASN1:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08001808 result_code = _api.i2d_X509_REQ_bio(bio, req._req)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001809 elif type == FILETYPE_TEXT:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08001810 result_code = _api.X509_REQ_print_ex(bio, req._req, 0, 0)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001811 else:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08001812 raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001813
1814 if result_code == 0:
1815 1/0
1816
1817 return _bio_to_string(bio)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001818
1819
1820
1821def load_certificate_request(type, buffer):
1822 """
1823 Load a certificate request from a buffer
1824
1825 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1826 :param buffer: The buffer the certificate request is stored in
1827 :return: The X509Req object
1828 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001829 bio = _new_mem_buf(buffer)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001830
1831 if type == FILETYPE_PEM:
1832 req = _api.PEM_read_bio_X509_REQ(bio, _api.NULL, _api.NULL, _api.NULL)
1833 elif type == FILETYPE_ASN1:
Jean-Paul Calderonec9a395f2013-02-20 16:59:21 -08001834 req = _api.d2i_X509_REQ_bio(bio, _api.NULL)
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001835 else:
1836 1/0
1837
1838 if req == _api.NULL:
1839 1/0
1840
1841 x509req = X509Req.__new__(X509Req)
1842 x509req._req = req
1843 return x509req
Jean-Paul Calderone8cf4f802013-02-20 16:45:02 -08001844
1845
1846
1847def sign(pkey, data, digest):
1848 """
1849 Sign data with a digest
1850
1851 :param pkey: Pkey to sign with
1852 :param data: data to be signed
1853 :param digest: message digest to use
1854 :return: signature
1855 """
1856 digest_obj = _api.EVP_get_digestbyname(digest)
1857 if digest_obj == _api.NULL:
1858 raise ValueError("No such digest method")
1859
1860 md_ctx = _api.new("EVP_MD_CTX*")
1861
1862 _api.EVP_SignInit(md_ctx, digest_obj)
1863 _api.EVP_SignUpdate(md_ctx, data, len(data))
1864
1865 signature_buffer = _api.new("unsigned char[]", 512)
1866 signature_length = _api.new("unsigned int*")
1867 signature_length[0] = len(signature_buffer)
1868 final_result = _api.EVP_SignFinal(
1869 md_ctx, signature_buffer, signature_length, pkey._pkey)
1870
1871 if final_result != 1:
1872 1/0
1873
1874 return _api.buffer(signature_buffer, signature_length[0])[:]
1875
1876
1877
1878def verify(cert, signature, data, digest):
1879 """
1880 Verify a signature
1881
1882 :param cert: signing certificate (X509 object)
1883 :param signature: signature returned by sign function
1884 :param data: data to be verified
1885 :param digest: message digest to use
1886 :return: None if the signature is correct, raise exception otherwise
1887 """
1888 digest_obj = _api.EVP_get_digestbyname(digest)
1889 if digest_obj == _api.NULL:
1890 raise ValueError("No such digest method")
1891
1892 pkey = _api.X509_get_pubkey(cert._x509)
1893 if pkey == _api.NULL:
1894 1/0
1895
1896 md_ctx = _api.new("EVP_MD_CTX*")
1897
1898 _api.EVP_VerifyInit(md_ctx, digest_obj)
1899 _api.EVP_VerifyUpdate(md_ctx, data, len(data))
1900 verify_result = _api.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey)
1901
1902 if verify_result != 1:
1903 _raise_current_error()
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001904
1905
1906
1907def load_crl(type, buffer):
1908 """
1909 Load a certificate revocation list from a buffer
1910
1911 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1912 :param buffer: The buffer the CRL is stored in
1913
1914 :return: The PKey object
1915 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001916 bio = _new_mem_buf(buffer)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001917
1918 if type == FILETYPE_PEM:
1919 crl = _api.PEM_read_bio_X509_CRL(bio, _api.NULL, _api.NULL, _api.NULL)
1920 elif type == FILETYPE_ASN1:
1921 crl = _api.d2i_X509_CRL_bio(bio, _api.NULL)
1922 else:
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001923 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
1924
1925 if crl == _api.NULL:
1926 _raise_current_error()
1927
1928 result = CRL.__new__(CRL)
1929 result._crl = crl
1930 return result
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001931
1932
1933
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001934def load_pkcs7_data(type, buffer):
1935 """
1936 Load pkcs7 data from a buffer
1937
1938 :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)
1939 :param buffer: The buffer with the pkcs7 data.
1940 :return: The PKCS7 object
1941 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001942 bio = _new_mem_buf(buffer)
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001943
1944 if type == FILETYPE_PEM:
1945 pkcs7 = _api.PEM_read_bio_PKCS7(bio, _api.NULL, _api.NULL, _api.NULL)
1946 elif type == FILETYPE_ASN1:
1947 pass
1948 else:
1949 1/0
1950 raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
1951
1952 if pkcs7 == _api.NULL:
1953 1/0
1954
1955 pypkcs7 = PKCS7.__new__(PKCS7)
1956 pypkcs7._pkcs7 = pkcs7
1957 return pypkcs7
1958
1959
1960
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001961def load_pkcs12(buffer, passphrase):
1962 """
1963 Load a PKCS12 object from a buffer
1964
1965 :param buffer: The buffer the certificate is stored in
1966 :param passphrase: (Optional) The password to decrypt the PKCS12 lump
1967 :returns: The PKCS12 object
1968 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001969 bio = _new_mem_buf(buffer)
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001970
1971 p12 = _api.d2i_PKCS12_bio(bio, _api.NULL)
1972 if p12 == _api.NULL:
1973 _raise_current_error()
1974
1975 pkey = _api.new("EVP_PKEY**")
1976 cert = _api.new("X509**")
1977 cacerts = _api.new("struct stack_st_X509**")
1978
1979 parse_result = _api.PKCS12_parse(p12, passphrase, pkey, cert, cacerts)
1980 if not parse_result:
1981 _raise_current_error()
1982
1983 # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the
1984 # queue for no particular reason. This error isn't interesting to anyone
1985 # outside this function. It's not even interesting to us. Get rid of it.
1986 try:
1987 _raise_current_error()
1988 except Error:
1989 pass
1990
1991 if pkey[0] == _api.NULL:
1992 pykey = None
1993 else:
1994 pykey = PKey.__new__(PKey)
1995 pykey._pkey = pkey[0]
1996
1997 if cert[0] == _api.NULL:
1998 pycert = None
1999 friendlyname = None
2000 else:
2001 pycert = X509.__new__(X509)
2002 pycert._x509 = cert[0]
2003
2004 friendlyname_length = _api.new("int*")
2005 friendlyname_buffer = _api.X509_alias_get0(cert[0], friendlyname_length)
2006 friendlyname = _api.buffer(friendlyname_buffer, friendlyname_length[0])[:]
2007 if friendlyname_buffer == _api.NULL:
2008 friendlyname = None
2009
2010 pycacerts = []
2011 for i in range(_api.sk_X509_num(cacerts[0])):
2012 pycacert = X509.__new__(X509)
2013 pycacert._x509 = _api.sk_X509_value(cacerts[0], i)
2014 pycacerts.append(pycacert)
2015 if not pycacerts:
2016 pycacerts = None
2017
2018 pkcs12 = PKCS12.__new__(PKCS12)
2019 pkcs12._pkey = pykey
2020 pkcs12._cert = pycert
2021 pkcs12._cacerts = pycacerts
2022 pkcs12._friendlyname = friendlyname
2023 return pkcs12