blob: aca15898a5f347f7988e18b4067ccd88d86622c9 [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 Calderone57122982013-02-21 08:47:05 -080025def _set_asn1_time(boundary, when):
26 if not isinstance(when, bytes):
27 raise TypeError("when must be a byte string")
28
29 set_result = _api.ASN1_GENERALIZEDTIME_set_string(
30 _api.cast('ASN1_GENERALIZEDTIME*', boundary), when)
31 if set_result == 0:
32 dummy = _api.ASN1_STRING_new()
33 _api.ASN1_STRING_set(dummy, when, len(when))
34 check_result = _api.ASN1_GENERALIZEDTIME_check(
35 _api.cast('ASN1_GENERALIZEDTIME*', dummy))
36 if not check_result:
37 raise ValueError("Invalid string")
38 else:
39 # TODO No tests for this case
40 raise RuntimeError("Unknown ASN1_GENERALIZEDTIME_set_string failure")
41
42
43
44def _get_asn1_time(timestamp):
45 string_timestamp = _api.cast('ASN1_STRING*', timestamp)
46 if _api.ASN1_STRING_length(string_timestamp) == 0:
47 return None
48 elif _api.ASN1_STRING_type(string_timestamp) == _api.V_ASN1_GENERALIZEDTIME:
49 return _api.string(_api.ASN1_STRING_data(string_timestamp))
50 else:
51 generalized_timestamp = _api.new("ASN1_GENERALIZEDTIME**")
52 _api.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp)
53 if generalized_timestamp[0] == _api.NULL:
54 1/0
55 else:
56 string_timestamp = _api.cast(
57 "ASN1_STRING*", generalized_timestamp[0])
58 string_data = _api.ASN1_STRING_data(string_timestamp)
59 string_result = _api.string(string_data)
60 _api.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
61 return string_result
62
63
64
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080065def _raise_current_error():
66 errors = []
67 while True:
68 error = _api.ERR_get_error()
69 if error == 0:
70 break
71 errors.append((
72 _api.string(_api.ERR_lib_error_string(error)),
73 _api.string(_api.ERR_func_error_string(error)),
74 _api.string(_api.ERR_reason_error_string(error))))
75
76 raise Error(errors)
77
78_exception_from_error_queue = _raise_current_error
79
80
81class Error(Exception):
82 pass
83
84
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080085class PKey(object):
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -080086 _only_public = False
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -080087 _initialized = True
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -080088
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -080089 def __init__(self):
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080090 pkey = _api.EVP_PKEY_new()
91 self._pkey = _api.ffi.gc(pkey, _api.EVP_PKEY_free)
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -080092 self._initialized = False
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -080093
94
95 def generate_key(self, type, bits):
96 """
97 Generate a key of a given type, with a given number of a bits
98
99 :param type: The key type (TYPE_RSA or TYPE_DSA)
100 :param bits: The number of bits
101
102 :return: None
103 """
104 if not isinstance(type, int):
105 raise TypeError("type must be an integer")
106
107 if not isinstance(bits, int):
108 raise TypeError("bits must be an integer")
109
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800110 # TODO Check error return
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800111 exponent = _api.BN_new()
112 exponent = _api.ffi.gc(exponent, _api.BN_free)
113 _api.BN_set_word(exponent, _api.RSA_F4)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800114
115 if type == TYPE_RSA:
116 if bits <= 0:
117 raise ValueError("Invalid number of bits")
118
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800119 rsa = _api.RSA_new()
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800120
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800121 result = _api.RSA_generate_key_ex(rsa, bits, exponent, _api.NULL)
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800122 if result == -1:
123 1/0
124
125 result = _api.EVP_PKEY_assign_RSA(self._pkey, rsa)
126 if not result:
127 1/0
128
129 elif type == TYPE_DSA:
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800130 dsa = _api.DSA_generate_parameters(
131 bits, _api.NULL, 0, _api.NULL, _api.NULL, _api.NULL, _api.NULL)
132 if dsa == _api.NULL:
133 1/0
134 if not _api.DSA_generate_key(dsa):
135 1/0
136 if not _api.EVP_PKEY_assign_DSA(self._pkey, dsa):
137 1/0
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800138 else:
139 raise Error("No such key type")
140
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800141 self._initialized = True
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800142
143
144 def check(self):
145 """
146 Check the consistency of an RSA private key.
147
148 :return: True if key is consistent.
149 :raise Error: if the key is inconsistent.
150 :raise TypeError: if the key is of a type which cannot be checked.
151 Only RSA keys can currently be checked.
152 """
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800153 if self._only_public:
154 raise TypeError("public key only")
155
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800156 if _api.EVP_PKEY_type(self._pkey.type) != _api.EVP_PKEY_RSA:
157 raise TypeError("key type unsupported")
158
159 rsa = _api.EVP_PKEY_get1_RSA(self._pkey)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800160 rsa = _api.ffi.gc(rsa, _api.RSA_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800161 result = _api.RSA_check_key(rsa)
162 if result:
163 return True
164 _raise_current_error()
165
166
Jean-Paul Calderonec86fcaf2013-02-20 12:38:33 -0800167 def type(self):
168 """
169 Returns the type of the key
170
171 :return: The type of the key.
172 """
173 return self._pkey.type
174
175
176 def bits(self):
177 """
178 Returns the number of bits of the key
179
180 :return: The number of bits of the key.
181 """
182 return _api.EVP_PKEY_bits(self._pkey)
183PKeyType = PKey
184
185
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800186
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800187class X509Name(object):
188 def __init__(self, name):
189 """
190 Create a new X509Name, copying the given X509Name instance.
191
192 :param name: An X509Name object to copy
193 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800194 name = _api.X509_NAME_dup(name._name)
195 self._name = _api.ffi.gc(name, _api.X509_NAME_free)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800196
197
198 def __setattr__(self, name, value):
199 if name.startswith('_'):
200 return super(X509Name, self).__setattr__(name, value)
201
202 if type(name) is not str:
203 raise TypeError("attribute name must be string, not '%.200s'" % (
204 type(value).__name__,))
205
206 nid = _api.OBJ_txt2nid(name)
207 if nid == _api.NID_undef:
208 try:
209 _raise_current_error()
210 except Error:
211 pass
212 raise AttributeError("No such attribute")
213
214 # If there's an old entry for this NID, remove it
215 for i in range(_api.X509_NAME_entry_count(self._name)):
216 ent = _api.X509_NAME_get_entry(self._name, i)
217 ent_obj = _api.X509_NAME_ENTRY_get_object(ent)
218 ent_nid = _api.OBJ_obj2nid(ent_obj)
219 if nid == ent_nid:
220 ent = _api.X509_NAME_delete_entry(self._name, i)
221 _api.X509_NAME_ENTRY_free(ent)
222 break
223
224 if isinstance(value, unicode):
225 value = value.encode('utf-8')
226
227 add_result = _api.X509_NAME_add_entry_by_NID(
228 self._name, nid, _api.MBSTRING_UTF8, value, -1, -1, 0)
229 if not add_result:
230 # TODO Untested
231 1/0
232
233
234 def __getattr__(self, name):
235 """
236 Find attribute. An X509Name object has the following attributes:
237 countryName (alias C), stateOrProvince (alias ST), locality (alias L),
238 organization (alias O), organizationalUnit (alias OU), commonName (alias
239 CN) and more...
240 """
241 nid = _api.OBJ_txt2nid(name)
242 if nid == _api.NID_undef:
243 # This is a bit weird. OBJ_txt2nid indicated failure, but it seems
244 # a lower level function, a2d_ASN1_OBJECT, also feels the need to
245 # push something onto the error queue. If we don't clean that up
246 # now, someone else will bump into it later and be quite confused.
247 # See lp#314814.
248 try:
249 _raise_current_error()
250 except Error:
251 pass
252 return super(X509Name, self).__getattr__(name)
253
254 entry_index = _api.X509_NAME_get_index_by_NID(self._name, nid, -1)
255 if entry_index == -1:
256 return None
257
258 entry = _api.X509_NAME_get_entry(self._name, entry_index)
259 data = _api.X509_NAME_ENTRY_get_data(entry)
260
261 result_buffer = _api.new("unsigned char**")
262 data_length = _api.ASN1_STRING_to_UTF8(result_buffer, data)
263 if data_length < 0:
264 1/0
265
266 result = _api.buffer(result_buffer[0], data_length)[:].decode('utf-8')
267 _api.OPENSSL_free(result_buffer[0])
268 return result
269
270
271 def __cmp__(self, other):
272 if not isinstance(other, X509Name):
273 return NotImplemented
274
275 result = _api.X509_NAME_cmp(self._name, other._name)
276 # TODO result == -2 is an error case that maybe should be checked for
277 return result
278
279
280 def __repr__(self):
281 """
282 String representation of an X509Name
283 """
284 result_buffer = _api.new("char[]", 512);
285 format_result = _api.X509_NAME_oneline(
286 self._name, result_buffer, len(result_buffer))
287
288 if format_result == _api.NULL:
289 1/0
290
291 return "<X509Name object '%s'>" % (_api.string(result_buffer),)
292
293
294 def hash(self):
295 """
296 Return the hash value of this name
297
298 :return: None
299 """
300 return _api.X509_NAME_hash(self._name)
301
302
303 def der(self):
304 """
305 Return the DER encoding of this name
306
307 :return: A :py:class:`bytes` instance giving the DER encoded form of
308 this name.
309 """
310 result_buffer = _api.new('unsigned char**')
311 encode_result = _api.i2d_X509_NAME(self._name, result_buffer)
312 if encode_result < 0:
313 1/0
314
315 string_result = _api.buffer(result_buffer[0], encode_result)[:]
316 _api.OPENSSL_free(result_buffer[0])
317 return string_result
318
319
320 def get_components(self):
321 """
322 Returns the split-up components of this name.
323
324 :return: List of tuples (name, value).
325 """
326 result = []
327 for i in range(_api.X509_NAME_entry_count(self._name)):
328 ent = _api.X509_NAME_get_entry(self._name, i)
329
330 fname = _api.X509_NAME_ENTRY_get_object(ent)
331 fval = _api.X509_NAME_ENTRY_get_data(ent)
332
333 nid = _api.OBJ_obj2nid(fname)
334 name = _api.OBJ_nid2sn(nid)
335
336 result.append((
337 _api.string(name),
338 _api.string(
339 _api.ASN1_STRING_data(fval),
340 _api.ASN1_STRING_length(fval))))
341
342 return result
343X509NameType = X509Name
344
345
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800346class X509Extension(object):
347 def __init__(self, type_name, critical, value, subject=None, issuer=None):
348 """
349 :param typename: The name of the extension to create.
350 :type typename: :py:data:`str`
351
352 :param critical: A flag indicating whether this is a critical extension.
353
354 :param value: The value of the extension.
355 :type value: :py:data:`str`
356
357 :param subject: Optional X509 cert to use as subject.
358 :type subject: :py:class:`X509`
359
360 :param issuer: Optional X509 cert to use as issuer.
361 :type issuer: :py:class:`X509`
362
363 :return: The X509Extension object
364 """
365 ctx = _api.new("X509V3_CTX*")
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800366
367 # A context is necessary for any extension which uses the r2i conversion
368 # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx.
369 # Start off by initializing most of the fields to NULL.
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800370 _api.X509V3_set_ctx(ctx, _api.NULL, _api.NULL, _api.NULL, _api.NULL, 0)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800371
372 # We have no configuration database - but perhaps we should (some
373 # extensions may require it).
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800374 _api.X509V3_set_ctx_nodb(ctx)
375
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800376 # Initialize the subject and issuer, if appropriate. ctx is a local,
377 # and as far as I can tell none of the X509V3_* APIs invoked here steal
378 # any references, so no need to mess with reference counts or duplicates.
379 if issuer is not None:
380 if not isinstance(issuer, X509):
381 raise TypeError("issuer must be an X509 instance")
382 ctx.issuer_cert = issuer._x509
383 if subject is not None:
384 if not isinstance(subject, X509):
385 raise TypeError("subject must be an X509 instance")
386 ctx.subject_cert = subject._x509
387
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800388 if critical:
389 # There are other OpenSSL APIs which would let us pass in critical
390 # separately, but they're harder to use, and since value is already
391 # a pile of crappy junk smuggling a ton of utterly important
392 # structured data, what's the point of trying to avoid nasty stuff
393 # with strings? (However, X509V3_EXT_i2d in particular seems like it
394 # would be a better API to invoke. I do not know where to get the
395 # ext_struc it desires for its last parameter, though.)
396 value = "critical," + value
397
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800398 extension = _api.X509V3_EXT_nconf(_api.NULL, ctx, type_name, value)
399 if extension == _api.NULL:
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800400 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800401 self._extension = _api.ffi.gc(extension, _api.X509_EXTENSION_free)
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800402
403
404 def __str__(self):
405 """
406 :return: a nice text representation of the extension
407 """
408 bio = _api.BIO_new(_api.BIO_s_mem())
409 if bio == _api.NULL:
410 1/0
411
412 print_result = _api.X509V3_EXT_print(bio, self._extension, 0, 0)
413 if not print_result:
414 1/0
415
416 return _bio_to_string(bio)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800417
418
419 def get_critical(self):
420 """
421 Returns the critical field of the X509Extension
422
423 :return: The critical field.
424 """
425 return _api.X509_EXTENSION_get_critical(self._extension)
426
427
428 def get_short_name(self):
429 """
430 Returns the short version of the type name of the X509Extension
431
432 :return: The short type name.
433 """
434 obj = _api.X509_EXTENSION_get_object(self._extension)
435 nid = _api.OBJ_obj2nid(obj)
436 return _api.string(_api.OBJ_nid2sn(nid))
437
438
Jean-Paul Calderoned418a9c2013-02-20 16:24:55 -0800439 def get_data(self):
440 """
441 Returns the data of the X509Extension
442
443 :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data.
444 """
445 octet_result = _api.X509_EXTENSION_get_data(self._extension)
446 string_result = _api.cast('ASN1_STRING*', octet_result)
447 char_result = _api.ASN1_STRING_data(string_result)
448 result_length = _api.ASN1_STRING_length(string_result)
449 return _api.buffer(char_result, result_length)[:]
450
451X509ExtensionType = X509Extension
452
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800453
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800454class X509Req(object):
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800455 def __init__(self):
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800456 req = _api.X509_REQ_new()
457 self._req = _api.ffi.gc(req, _api.X509_REQ_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800458
459
460 def set_pubkey(self, pkey):
461 """
462 Set the public key of the certificate request
463
464 :param pkey: The public key to use
465 :return: None
466 """
467 set_result = _api.X509_REQ_set_pubkey(self._req, pkey._pkey)
468 if not set_result:
469 1/0
470
471
472 def get_pubkey(self):
473 """
474 Get the public key from the certificate request
475
476 :return: The public key
477 """
478 pkey = PKey.__new__(PKey)
479 pkey._pkey = _api.X509_REQ_get_pubkey(self._req)
480 if pkey._pkey == _api.NULL:
481 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800482 pkey._pkey = _api.ffi.gc(pkey._pkey, _api.EVP_PKEY_free)
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800483 pkey._only_public = True
484 return pkey
485
486
487 def set_version(self, version):
488 """
489 Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate
490 request.
491
492 :param version: The version number
493 :return: None
494 """
495 set_result = _api.X509_REQ_set_version(self._req, version)
496 if not set_result:
497 _raise_current_error()
498
499
500 def get_version(self):
501 """
502 Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
503 request.
504
505 :return: an integer giving the value of the version subfield
506 """
507 return _api.X509_REQ_get_version(self._req)
508
509
510 def get_subject(self):
511 """
512 Create an X509Name object for the subject of the certificate request
513
514 :return: An X509Name object
515 """
516 name = X509Name.__new__(X509Name)
517 name._name = _api.X509_REQ_get_subject_name(self._req)
518 if name._name == _api.NULL:
519 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800520
521 # The name is owned by the X509Req structure. As long as the X509Name
522 # Python object is alive, keep the X509Req Python object alive.
523 name._owner = self
524
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800525 return name
526
527
528 def add_extensions(self, extensions):
529 """
530 Add extensions to the request.
531
532 :param extensions: a sequence of X509Extension objects
533 :return: None
534 """
535 stack = _api.sk_X509_EXTENSION_new_null()
536 if stack == _api.NULL:
537 1/0
538
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800539 stack = _api.ffi.gc(stack, _api.sk_X509_EXTENSION_free)
540
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800541 for ext in extensions:
542 if not isinstance(ext, X509Extension):
Jean-Paul Calderonec2154b72013-02-20 14:29:37 -0800543 raise ValueError("One of the elements is not an X509Extension")
Jean-Paul Calderone4328d472013-02-20 14:28:46 -0800544
545 _api.sk_X509_EXTENSION_push(stack, ext._extension)
546
547 add_result = _api.X509_REQ_add_extensions(self._req, stack)
548 if not add_result:
549 1/0
550
551
552 def sign(self, pkey, digest):
553 """
554 Sign the certificate request using the supplied key and digest
555
556 :param pkey: The key to sign with
557 :param digest: The message digest to use
558 :return: None
559 """
560 if pkey._only_public:
561 raise ValueError("Key has only public part")
562
563 if not pkey._initialized:
564 raise ValueError("Key is uninitialized")
565
566 digest_obj = _api.EVP_get_digestbyname(digest)
567 if digest_obj == _api.NULL:
568 raise ValueError("No such digest method")
569
570 sign_result = _api.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
571 if not sign_result:
572 1/0
573
574
Jean-Paul Calderone066f0572013-02-20 13:43:44 -0800575X509ReqType = X509Req
576
577
578
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800579class X509(object):
580 def __init__(self):
581 # TODO Allocation failure? And why not __new__ instead of __init__?
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800582 x509 = _api.X509_new()
583 self._x509 = _api.ffi.gc(x509, _api.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800584
585
586 def set_version(self, version):
587 """
588 Set version number of the certificate
589
590 :param version: The version number
591 :type version: :py:class:`int`
592
593 :return: None
594 """
595 if not isinstance(version, int):
596 raise TypeError("version must be an integer")
597
598 _api.X509_set_version(self._x509, version)
599
600
601 def get_version(self):
602 """
603 Return version number of the certificate
604
605 :return: Version number as a Python integer
606 """
607 return _api.X509_get_version(self._x509)
608
609
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800610 def get_pubkey(self):
611 """
612 Get the public key of the certificate
613
614 :return: The public key
615 """
616 pkey = PKey.__new__(PKey)
617 pkey._pkey = _api.X509_get_pubkey(self._x509)
618 if pkey._pkey == _api.NULL:
619 _raise_current_error()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800620 pkey._pkey = _api.ffi.gc(pkey._pkey, _api.EVP_PKEY_free)
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800621 pkey._only_public = True
622 return pkey
623
624
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800625 def set_pubkey(self, pkey):
626 """
627 Set the public key of the certificate
628
629 :param pkey: The public key
630
631 :return: None
632 """
633 if not isinstance(pkey, PKey):
634 raise TypeError("pkey must be a PKey instance")
635
636 set_result = _api.X509_set_pubkey(self._x509, pkey._pkey)
637 if not set_result:
638 _raise_current_error()
639
640
641 def sign(self, pkey, digest):
642 """
643 Sign the certificate using the supplied key and digest
644
645 :param pkey: The key to sign with
646 :param digest: The message digest to use
647 :return: None
648 """
649 if not isinstance(pkey, PKey):
650 raise TypeError("pkey must be a PKey instance")
651
Jean-Paul Calderoneedafced2013-02-19 11:48:38 -0800652 if pkey._only_public:
653 raise ValueError("Key only has public part")
654
Jean-Paul Calderone09e3bdc2013-02-19 12:15:28 -0800655 if not pkey._initialized:
656 raise ValueError("Key is uninitialized")
657
Jean-Paul Calderone3e29ccf2013-02-19 11:32:46 -0800658 evp_md = _api.EVP_get_digestbyname(digest)
659 if evp_md == _api.NULL:
660 raise ValueError("No such digest method")
661
662 sign_result = _api.X509_sign(self._x509, pkey._pkey, evp_md)
663 if not sign_result:
664 _raise_current_error()
665
666
Jean-Paul Calderonee4aa3fa2013-02-19 12:12:53 -0800667 def get_signature_algorithm(self):
668 """
669 Retrieve the signature algorithm used in the certificate
670
671 :return: A byte string giving the name of the signature algorithm used in
672 the certificate.
673 :raise ValueError: If the signature algorithm is undefined.
674 """
675 alg = self._x509.cert_info.signature.algorithm
676 nid = _api.OBJ_obj2nid(alg)
677 if nid == _api.NID_undef:
678 raise ValueError("Undefined signature algorithm")
679 return _api.string(_api.OBJ_nid2ln(nid))
680
681
Jean-Paul Calderoneb4078722013-02-19 12:01:55 -0800682 def digest(self, digest_name):
683 """
684 Return the digest of the X509 object.
685
686 :param digest_name: The name of the digest algorithm to use.
687 :type digest_name: :py:class:`bytes`
688
689 :return: The digest of the object
690 """
691 digest = _api.EVP_get_digestbyname(digest_name)
692 if digest == _api.NULL:
693 raise ValueError("No such digest method")
694
695 result_buffer = _api.new("char[]", _api.EVP_MAX_MD_SIZE)
696 result_length = _api.new("unsigned int[]", 1)
697 result_length[0] = len(result_buffer)
698
699 digest_result = _api.X509_digest(
700 self._x509, digest, result_buffer, result_length)
701
702 if not digest_result:
703 1/0
704
705 return ':'.join([
706 ch.encode('hex').upper() for ch
707 in _api.buffer(result_buffer, result_length[0])])
708
709
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800710 def subject_name_hash(self):
711 """
712 Return the hash of the X509 subject.
713
714 :return: The hash of the subject.
715 """
716 return _api.X509_subject_name_hash(self._x509)
717
718
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800719 def set_serial_number(self, serial):
720 """
721 Set serial number of the certificate
722
723 :param serial: The serial number
724 :type serial: :py:class:`int`
725
726 :return: None
727 """
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800728 if not isinstance(serial, (int, long)):
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800729 raise TypeError("serial must be an integer")
730
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800731 hex_serial = hex(serial)[2:]
732 if not isinstance(hex_serial, bytes):
733 hex_serial = hex_serial.encode('ascii')
734
735 bignum_serial = _api.new("BIGNUM**")
736
737 # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
738 # it. If bignum is still NULL after this call, then the return value is
739 # actually the result. I hope. -exarkun
740 small_serial = _api.BN_hex2bn(bignum_serial, hex_serial)
741
742 if bignum_serial[0] == _api.NULL:
743 set_result = ASN1_INTEGER_set(
744 _api.X509_get_serialNumber(self._x509), small_serial)
745 if set_result:
746 # TODO Not tested
747 _raise_current_error()
748 else:
749 asn1_serial = _api.BN_to_ASN1_INTEGER(bignum_serial[0], _api.NULL)
750 _api.BN_free(bignum_serial[0])
751 if asn1_serial == _api.NULL:
752 # TODO Not tested
753 _raise_current_error()
754 set_result = _api.X509_set_serialNumber(self._x509, asn1_serial)
755 if not set_result:
756 # TODO Not tested
757 _raise_current_error()
758
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800759
760 def get_serial_number(self):
761 """
762 Return serial number of the certificate
763
764 :return: Serial number as a Python integer
765 """
Jean-Paul Calderone78133852013-02-19 10:41:46 -0800766 asn1_serial = _api.X509_get_serialNumber(self._x509)
767 bignum_serial = _api.ASN1_INTEGER_to_BN(asn1_serial, _api.NULL)
768 try:
769 hex_serial = _api.BN_bn2hex(bignum_serial)
770 try:
771 hexstring_serial = _api.string(hex_serial)
772 serial = int(hexstring_serial, 16)
773 return serial
774 finally:
775 _api.OPENSSL_free(hex_serial)
776 finally:
777 _api.BN_free(bignum_serial)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800778
779
780 def gmtime_adj_notAfter(self, amount):
781 """
782 Adjust the time stamp for when the certificate stops being valid
783
784 :param amount: The number of seconds by which to adjust the ending
785 validity time.
786 :type amount: :py:class:`int`
787
788 :return: None
789 """
790 if not isinstance(amount, int):
791 raise TypeError("amount must be an integer")
792
793 notAfter = _api.X509_get_notAfter(self._x509)
794 _api.X509_gmtime_adj(notAfter, amount)
795
796
Jean-Paul Calderone662afe52013-02-20 08:41:11 -0800797 def gmtime_adj_notBefore(self, amount):
798 """
799 Change the timestamp for when the certificate starts being valid to the current
800 time plus an offset.
801
802 :param amount: The number of seconds by which to adjust the starting validity
803 time.
804 :return: None
805 """
806 if not isinstance(amount, int):
807 raise TypeError("amount must be an integer")
808
809 notBefore = _api.X509_get_notBefore(self._x509)
810 _api.X509_gmtime_adj(notBefore, amount)
811
812
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800813 def has_expired(self):
814 """
815 Check whether the certificate has expired.
816
817 :return: True if the certificate has expired, false otherwise
818 """
819 now = int(time())
820 notAfter = _api.X509_get_notAfter(self._x509)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -0800821 return _api.ASN1_UTCTIME_cmp_time_t(
822 _api.cast('ASN1_UTCTIME*', notAfter), now) < 0
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800823
824
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800825 def _get_boundary_time(self, which):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800826 return _get_asn1_time(which(self._x509))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800827
828
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800829 def get_notBefore(self):
830 """
831 Retrieve the time stamp for when the certificate starts being valid
832
833 :return: A string giving the timestamp, in the format::
834
835 YYYYMMDDhhmmssZ
836 YYYYMMDDhhmmss+hhmm
837 YYYYMMDDhhmmss-hhmm
838
839 or None if there is no value set.
840 """
841 return self._get_boundary_time(_api.X509_get_notBefore)
842
843
844 def _set_boundary_time(self, which, when):
Jean-Paul Calderone57122982013-02-21 08:47:05 -0800845 return _set_asn1_time(which(self._x509), when)
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800846
847
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800848 def set_notBefore(self, when):
849 """
850 Set the time stamp for when the certificate starts being valid
851
852 :param when: A string giving the timestamp, in the format:
853
854 YYYYMMDDhhmmssZ
855 YYYYMMDDhhmmss+hhmm
856 YYYYMMDDhhmmss-hhmm
857 :type when: :py:class:`bytes`
858
859 :return: None
860 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800861 return self._set_boundary_time(_api.X509_get_notBefore, when)
Jean-Paul Calderoned7d81272013-02-19 13:16:03 -0800862
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800863
864 def get_notAfter(self):
865 """
866 Retrieve the time stamp for when the certificate stops being valid
867
868 :return: A string giving the timestamp, in the format::
869
870 YYYYMMDDhhmmssZ
871 YYYYMMDDhhmmss+hhmm
872 YYYYMMDDhhmmss-hhmm
873
874 or None if there is no value set.
875 """
876 return self._get_boundary_time(_api.X509_get_notAfter)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800877
878
879 def set_notAfter(self, when):
880 """
881 Set the time stamp for when the certificate stops being valid
882
883 :param when: A string giving the timestamp, in the format:
884
885 YYYYMMDDhhmmssZ
886 YYYYMMDDhhmmss+hhmm
887 YYYYMMDDhhmmss-hhmm
888 :type when: :py:class:`bytes`
889
890 :return: None
891 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800892 return self._set_boundary_time(_api.X509_get_notAfter, when)
893
894
895 def _get_name(self, which):
896 name = X509Name.__new__(X509Name)
897 name._name = which(self._x509)
898 if name._name == _api.NULL:
899 1/0
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800900
901 # The name is owned by the X509 structure. As long as the X509Name
902 # Python object is alive, keep the X509 Python object alive.
903 name._owner = self
904
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800905 return name
906
907
908 def _set_name(self, which, name):
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800909 if not isinstance(name, X509Name):
910 raise TypeError("name must be an X509Name")
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800911 set_result = which(self._x509, name._name)
912 if not set_result:
913 1/0
914
915
916 def get_issuer(self):
917 """
918 Create an X509Name object for the issuer of the certificate
919
920 :return: An X509Name object
921 """
922 return self._get_name(_api.X509_get_issuer_name)
923
924
925 def set_issuer(self, issuer):
926 """
927 Set the issuer of the certificate
928
929 :param issuer: The issuer name
930 :type issuer: :py:class:`X509Name`
931
932 :return: None
933 """
934 return self._set_name(_api.X509_set_issuer_name, issuer)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800935
936
937 def get_subject(self):
938 """
939 Create an X509Name object for the subject of the certificate
940
941 :return: An X509Name object
942 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800943 return self._get_name(_api.X509_get_subject_name)
Jean-Paul Calderonea9de1952013-02-19 16:58:42 -0800944
945
946 def set_subject(self, subject):
947 """
948 Set the subject of the certificate
949
950 :param subject: The subject name
951 :type subject: :py:class:`X509Name`
952 :return: None
953 """
Jean-Paul Calderonec2bd4e92013-02-20 08:12:36 -0800954 return self._set_name(_api.X509_set_subject_name, subject)
Jean-Paul Calderone83d22eb2013-02-20 12:19:43 -0800955
956
957 def get_extension_count(self):
958 """
959 Get the number of extensions on the certificate.
960
961 :return: The number of extensions as an integer.
962 """
963 return _api.X509_get_ext_count(self._x509)
964
965
966 def add_extensions(self, extensions):
967 """
968 Add extensions to the certificate.
969
970 :param extensions: a sequence of X509Extension objects
971 :return: None
972 """
973 for ext in extensions:
974 if not isinstance(ext, X509Extension):
975 raise ValueError("One of the elements is not an X509Extension")
976
977 add_result = _api.X509_add_ext(self._x509, ext._extension, -1)
978 if not add_result:
979 _raise_current_error()
980
981
982 def get_extension(self, index):
983 """
984 Get a specific extension of the certificate by index.
985
986 :param index: The index of the extension to retrieve.
987 :return: The X509Extension object at the specified index.
988 """
989 ext = X509Extension.__new__(X509Extension)
990 ext._extension = _api.X509_get_ext(self._x509, index)
991 if ext._extension == _api.NULL:
992 raise IndexError("extension index out of bounds")
993
994 ext._extension = _api.X509_EXTENSION_dup(ext._extension)
995 return ext
996
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800997X509Type = X509
998
999
1000
1001def load_certificate(type, buffer):
1002 """
1003 Load a certificate from a buffer
1004
1005 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1006
1007 :param buffer: The buffer the certificate is stored in
1008 :type buffer: :py:class:`bytes`
1009
1010 :return: The X509 object
1011 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001012 bio = _new_mem_buf(buffer)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001013
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001014 if type == FILETYPE_PEM:
1015 x509 = _api.PEM_read_bio_X509(bio, _api.NULL, _api.NULL, _api.NULL)
1016 elif type == FILETYPE_ASN1:
1017 x509 = _api.d2i_X509_bio(bio, _api.NULL);
1018 else:
1019 raise ValueError(
1020 "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001021
1022 if x509 == _api.NULL:
1023 _raise_current_error()
1024
1025 cert = X509.__new__(X509)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001026 cert._x509 = _api.ffi.gc(x509, _api.X509_free)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001027 return cert
1028
1029
1030def dump_certificate(type, cert):
1031 """
1032 Dump a certificate to a buffer
1033
1034 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1035 :param cert: The certificate to dump
1036 :return: The buffer with the dumped certificate in
1037 """
1038 bio = _api.BIO_new(_api.BIO_s_mem())
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001039 if bio == _api.NULL:
1040 1/0
1041
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001042 if type == FILETYPE_PEM:
1043 result_code = _api.PEM_write_bio_X509(bio, cert._x509)
1044 elif type == FILETYPE_ASN1:
1045 result_code = _api.i2d_X509_bio(bio, cert._x509)
1046 elif type == FILETYPE_TEXT:
1047 result_code = _api.X509_print_ex(bio, cert._x509, 0, 0)
1048 else:
1049 raise ValueError(
1050 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1051 "FILETYPE_TEXT")
1052
1053 return _bio_to_string(bio)
1054
1055
1056
1057def dump_privatekey(type, pkey, cipher=None, passphrase=None):
1058 """
1059 Dump a private key to a buffer
1060
1061 :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
1062 :param pkey: The PKey to dump
1063 :param cipher: (optional) if encrypted PEM format, the cipher to
1064 use
1065 :param passphrase: (optional) if encrypted PEM format, this can be either
1066 the passphrase to use, or a callback for providing the
1067 passphrase.
1068 :return: The buffer with the dumped key in
1069 :rtype: :py:data:`str`
1070 """
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001071 bio = _api.BIO_new(_api.BIO_s_mem())
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001072 if bio == _api.NULL:
1073 1/0
1074
1075 if cipher is not None:
1076 cipher_obj = _api.EVP_get_cipherbyname(cipher)
1077 if cipher_obj == _api.NULL:
1078 raise ValueError("Invalid cipher name")
1079 else:
1080 cipher_obj = _api.NULL
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001081
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001082 helper = _PassphraseHelper(type, passphrase)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001083 if type == FILETYPE_PEM:
1084 result_code = _api.PEM_write_bio_PrivateKey(
Jean-Paul Calderone066f0572013-02-20 13:43:44 -08001085 bio, pkey._pkey, cipher_obj, _api.NULL, 0,
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001086 helper.callback, helper.callback_args)
1087 helper.raise_if_problem()
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -08001088 elif type == FILETYPE_ASN1:
1089 result_code = _api.i2d_PrivateKey_bio(bio, pkey._pkey)
1090 elif type == FILETYPE_TEXT:
1091 rsa = _api.EVP_PKEY_get1_RSA(pkey._pkey)
1092 result_code = _api.RSA_print(bio, rsa, 0)
1093 # TODO RSA_free(rsa)?
1094 else:
1095 raise ValueError(
1096 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
1097 "FILETYPE_TEXT")
1098
1099 if result_code == 0:
1100 _raise_current_error()
1101
1102 return _bio_to_string(bio)
1103
1104
1105
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001106def _X509_REVOKED_dup(original):
1107 copy = _api.X509_REVOKED_new()
1108 if copy == _api.NULL:
1109 1/0
1110
1111 if original.serialNumber != _api.NULL:
1112 copy.serialNumber = _api.ASN1_INTEGER_dup(original.serialNumber)
1113
1114 if original.revocationDate != _api.NULL:
1115 copy.revocationDate = _api.M_ASN1_TIME_dup(original.revocationDate)
1116
1117 if original.extensions != _api.NULL:
1118 extension_stack = _api.sk_X509_EXTENSION_new_null()
1119 for i in range(_api.sk_X509_EXTENSION_num(original.extensions)):
1120 original_ext = _api.sk_X509_EXTENSION_value(original.extensions, i)
1121 copy_ext = _api.X509_EXTENSION_dup(original_ext)
1122 _api.sk_X509_EXTENSION_push(extension_stack, copy_ext)
1123 copy.extensions = extension_stack
1124
1125 copy.sequence = original.sequence
1126 return copy
1127
1128
1129
1130class Revoked(object):
1131 # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_
1132 # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
1133 # OCSP_crl_reason_str. We use the latter, just like the command line
1134 # program.
1135 _crl_reasons = [
1136 "unspecified",
1137 "keyCompromise",
1138 "CACompromise",
1139 "affiliationChanged",
1140 "superseded",
1141 "cessationOfOperation",
1142 "certificateHold",
1143 # "removeFromCRL",
1144 ]
1145
1146 def __init__(self):
1147 self._revoked = _api.X509_REVOKED_new()
1148
1149
1150 def set_serial(self, hex_str):
1151 """
1152 Set the serial number of a revoked Revoked structure
1153
1154 :param hex_str: The new serial number.
1155 :type hex_str: :py:data:`str`
1156 :return: None
1157 """
1158 bignum_serial = _api.new("BIGNUM**")
1159 bn_result = _api.BN_hex2bn(bignum_serial, hex_str)
1160 if not bn_result:
1161 raise ValueError("bad hex string")
1162
1163 asn1_serial = _api.BN_to_ASN1_INTEGER(bignum_serial[0], _api.NULL)
1164 _api.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
1165
1166
1167 def get_serial(self):
1168 """
1169 Return the serial number of a Revoked structure
1170
1171 :return: The serial number as a string
1172 """
1173 bio = _api.BIO_new(_api.BIO_s_mem())
1174 if bio == _api.NULL:
1175 1/0
1176
1177 result = _api.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber)
1178 if result < 0:
1179 1/0
1180
1181 return _bio_to_string(bio)
1182
1183
1184 def _delete_reason(self):
1185 stack = self._revoked.extensions
1186 for i in range(_api.sk_X509_EXTENSION_num(stack)):
1187 ext = _api.sk_X509_EXTENSION_value(stack, i)
1188 if _api.OBJ_obj2nid(ext.object) == _api.NID_crl_reason:
1189 _api.sk_X509_EXTENSION_delete(stack, i)
1190 break
1191
1192
1193 def set_reason(self, reason):
1194 """
1195 Set the reason of a Revoked object.
1196
1197 If :py:data:`reason` is :py:data:`None`, delete the reason instead.
1198
1199 :param reason: The reason string.
1200 :type reason: :py:class:`str` or :py:class:`NoneType`
1201 :return: None
1202 """
1203 if reason is None:
1204 self._delete_reason()
1205 elif not isinstance(reason, bytes):
1206 raise TypeError("reason must be None or a byte string")
1207 else:
1208 reason = reason.lower().replace(' ', '')
1209 reason_code = [r.lower() for r in self._crl_reasons].index(reason)
1210
1211 new_reason_ext = _api.ASN1_ENUMERATED_new()
1212 if new_reason_ext == _api.NULL:
1213 1/0
1214
1215 set_result = _api.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
1216 if set_result == _api.NULL:
1217 1/0
1218
1219 self._delete_reason()
1220 add_result = _api.X509_REVOKED_add1_ext_i2d(
1221 self._revoked, _api.NID_crl_reason, new_reason_ext, 0, 0)
1222
1223 if not add_result:
1224 1/0
1225
1226
1227 def get_reason(self):
1228 """
1229 Return the reason of a Revoked object.
1230
1231 :return: The reason as a string
1232 """
1233 extensions = self._revoked.extensions
1234 for i in range(_api.sk_X509_EXTENSION_num(extensions)):
1235 ext = _api.sk_X509_EXTENSION_value(extensions, i)
1236 if _api.OBJ_obj2nid(ext.object) == _api.NID_crl_reason:
1237 bio = _api.BIO_new(_api.BIO_s_mem())
1238 if bio == _api.NULL:
1239 1/0
1240
1241 print_result = _api.X509V3_EXT_print(bio, ext, 0, 0)
1242 if not print_result:
1243 print_result = _api.M_ASN1_OCTET_STRING_print(bio, ext.value)
1244 if print_result == 0:
1245 1/0
1246
1247 return _bio_to_string(bio)
1248
1249
1250 def all_reasons(self):
1251 """
1252 Return a list of all the supported reason strings.
1253
1254 :return: A list of reason strings.
1255 """
1256 return self._crl_reasons[:]
1257
1258
1259 def set_rev_date(self, when):
1260 """
1261 Set the revocation timestamp
1262
1263 :param when: A string giving the timestamp, in the format:
1264
1265 YYYYMMDDhhmmssZ
1266 YYYYMMDDhhmmss+hhmm
1267 YYYYMMDDhhmmss-hhmm
1268
1269 :return: None
1270 """
1271 return _set_asn1_time(self._revoked.revocationDate, when)
1272
1273
1274 def get_rev_date(self):
1275 """
1276 Retrieve the revocation date
1277
1278 :return: A string giving the timestamp, in the format:
1279
1280 YYYYMMDDhhmmssZ
1281 YYYYMMDDhhmmss+hhmm
1282 YYYYMMDDhhmmss-hhmm
1283 """
1284 return _get_asn1_time(self._revoked.revocationDate)
1285
1286
1287
1288class CRL(object):
1289 def __init__(self):
1290 """
1291 Create a new empty CRL object.
1292 """
1293 self._crl = _api.X509_CRL_new()
1294
1295
1296 def get_revoked(self):
1297 """
1298 Return revoked portion of the CRL structure (by value not reference).
1299
1300 :return: A tuple of Revoked objects.
1301 """
1302 results = []
1303 revoked_stack = self._crl.crl.revoked
1304 for i in range(_api.sk_X509_REVOKED_num(revoked_stack)):
1305 revoked = _api.sk_X509_REVOKED_value(revoked_stack, i)
1306 revoked_copy = _X509_REVOKED_dup(revoked)
1307 pyrev = Revoked.__new__(Revoked)
1308 pyrev._revoked = revoked_copy
1309 results.append(pyrev)
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001310 if results:
1311 return tuple(results)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001312
1313
1314 def add_revoked(self, revoked):
1315 """
1316 Add a revoked (by value not reference) to the CRL structure
1317
1318 :param revoked: The new revoked.
1319 :type revoked: :class:`X509`
1320
1321 :return: None
1322 """
1323 copy = _X509_REVOKED_dup(revoked._revoked)
1324 if copy == _api.NULL:
1325 1/0
1326
1327 add_result = _api.X509_CRL_add0_revoked(self._crl, copy)
1328 # TODO what check on add_result?
1329
1330
1331 def export(self, cert, key, type=FILETYPE_PEM, days=100):
1332 """
1333 export a CRL as a string
1334
1335 :param cert: Used to sign CRL.
1336 :type cert: :class:`X509`
1337
1338 :param key: Used to sign CRL.
1339 :type key: :class:`PKey`
1340
1341 :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`.
1342
1343 :param days: The number of days until the next update of this CRL.
1344 :type days: :py:data:`int`
1345
1346 :return: :py:data:`str`
1347 """
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001348 if not isinstance(cert, X509):
1349 raise TypeError("cert must be an X509 instance")
1350 if not isinstance(key, PKey):
1351 raise TypeError("key must be a PKey instance")
1352 if not isinstance(type, int):
1353 raise TypeError("type must be an integer")
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001354
Jean-Paul Calderone85b74eb2013-02-21 09:15:01 -08001355 bio = _api.BIO_new(_api.BIO_s_mem())
1356 if bio == _api.NULL:
1357 1/0
1358
1359 # A scratch time object to give different values to different CRL fields
1360 sometime = _api.ASN1_TIME_new()
1361 if sometime == _api.NULL:
1362 1/0
1363
1364 _api.X509_gmtime_adj(sometime, 0)
1365 _api.X509_CRL_set_lastUpdate(self._crl, sometime)
1366
1367 _api.X509_gmtime_adj(sometime, days * 24 * 60 * 60)
1368 _api.X509_CRL_set_nextUpdate(self._crl, sometime)
1369
1370 _api.X509_CRL_set_issuer_name(self._crl, _api.X509_get_subject_name(cert._x509))
1371
1372 sign_result = _api.X509_CRL_sign(self._crl, key._pkey, _api.EVP_md5())
1373 if not sign_result:
1374 _raise_current_error()
1375
1376 if type == FILETYPE_PEM:
1377 ret = _api.PEM_write_bio_X509_CRL(bio, self._crl)
1378 elif type == FILETYPE_ASN1:
1379 ret = _api.i2d_X509_CRL_bio(bio, self._crl)
1380 elif type == FILETYPE_TEXT:
1381 ret = _api.X509_CRL_print(bio, self._crl)
1382 else:
1383 raise ValueError(
1384 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT")
1385
1386 if not ret:
1387 1/0
1388
1389 return _bio_to_string(bio)
Jean-Paul Calderone57122982013-02-21 08:47:05 -08001390CRLType = CRL
1391
1392
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001393
Jean-Paul Calderone4e8be1c2013-02-21 18:31:12 -08001394class PKCS7(object):
1395 def type_is_signed(self):
1396 """
1397 Check if this NID_pkcs7_signed object
1398
1399 :return: True if the PKCS7 is of type signed
1400 """
1401 if _api.PKCS7_type_is_signed(self._pkcs7):
1402 return True
1403 return False
1404
1405
1406 def type_is_enveloped(self):
1407 """
1408 Check if this NID_pkcs7_enveloped object
1409
1410 :returns: True if the PKCS7 is of type enveloped
1411 """
1412 if _api.PKCS7_type_is_enveloped(self._pkcs7):
1413 return True
1414 return False
1415
1416
1417 def type_is_signedAndEnveloped(self):
1418 """
1419 Check if this NID_pkcs7_signedAndEnveloped object
1420
1421 :returns: True if the PKCS7 is of type signedAndEnveloped
1422 """
1423 if _api.PKCS7_type_is_signedAndEnveloped(self._pkcs7):
1424 return True
1425 return False
1426
1427
1428 def type_is_data(self):
1429 """
1430 Check if this NID_pkcs7_data object
1431
1432 :return: True if the PKCS7 is of type data
1433 """
1434 if _api.PKCS7_type_is_data(self._pkcs7):
1435 return True
1436 return False
1437
1438
1439 def get_type_name(self):
1440 """
1441 Returns the type name of the PKCS7 structure
1442
1443 :return: A string with the typename
1444 """
1445 nid = _api.OBJ_obj2nid(self._pkcs7.type)
1446 string_type = _api.OBJ_nid2sn(nid)
1447 return _api.string(string_type)
1448
1449PKCS7Type = PKCS7
1450
1451
1452
Jean-Paul Calderonee5912ce2013-02-21 10:49:35 -08001453class PKCS12(object):
1454 def __init__(self):
1455 self._pkey = None
1456 self._cert = None
1457 self._cacerts = None
1458 self._friendlyname = None
1459
1460
1461 def get_certificate(self):
1462 """
1463 Return certificate portion of the PKCS12 structure
1464
1465 :return: X509 object containing the certificate
1466 """
1467 return self._cert
1468
1469
1470 def set_certificate(self, cert):
1471 """
1472 Replace the certificate portion of the PKCS12 structure
1473
1474 :param cert: The new certificate.
1475 :type cert: :py:class:`X509` or :py:data:`None`
1476 :return: None
1477 """
1478 if not isinstance(cert, X509):
1479 raise TypeError("cert must be an X509 instance")
1480 self._cert = cert
1481
1482
1483 def get_privatekey(self):
1484 """
1485 Return private key portion of the PKCS12 structure
1486
1487 :returns: PKey object containing the private key
1488 """
1489 return self._pkey
1490
1491
1492 def set_privatekey(self, pkey):
1493 """
1494 Replace or set the certificate portion of the PKCS12 structure
1495
1496 :param pkey: The new private key.
1497 :type pkey: :py:class:`PKey`
1498 :return: None
1499 """
1500 if not isinstance(pkey, PKey):
1501 raise TypeError("pkey must be a PKey instance")
1502 self._pkey = pkey
1503
1504
1505 def get_ca_certificates(self):
1506 """
1507 Return CA certificates within of the PKCS12 object
1508
1509 :return: A newly created tuple containing the CA certificates in the chain,
1510 if any are present, or None if no CA certificates are present.
1511 """
1512 if self._cacerts is not None:
1513 return tuple(self._cacerts)
1514
1515
1516 def set_ca_certificates(self, cacerts):
1517 """
1518 Replace or set the CA certificates withing the PKCS12 object.
1519
1520 :param cacerts: The new CA certificates.
1521 :type cacerts: :py:data:`None` or an iterable of :py:class:`X509`
1522 :return: None
1523 """
1524 if cacerts is None:
1525 self._cacerts = None
1526 else:
1527 cacerts = list(cacerts)
1528 for cert in cacerts:
1529 if not isinstance(cert, X509):
1530 raise TypeError("iterable must only contain X509 instances")
1531 self._cacerts = cacerts
1532
1533
1534 def set_friendlyname(self, name):
1535 """
1536 Replace or set the certificate portion of the PKCS12 structure
1537
1538 :param name: The new friendly name.
1539 :type name: :py:class:`bytes`
1540 :return: None
1541 """
1542 if name is None:
1543 self._friendlyname = None
1544 elif not isinstance(name, bytes):
1545 raise TypeError("name must be a byte string or None (not %r)" % (name,))
1546 self._friendlyname = name
1547
1548
1549 def get_friendlyname(self):
1550 """
1551 Return friendly name portion of the PKCS12 structure
1552
1553 :returns: String containing the friendlyname
1554 """
1555 return self._friendlyname
1556
1557
1558 def export(self, passphrase=None, iter=2048, maciter=1):
1559 """
1560 Dump a PKCS12 object as a string. See also "man PKCS12_create".
1561
1562 :param passphrase: used to encrypt the PKCS12
1563 :type passphrase: :py:data:`bytes`
1564
1565 :param iter: How many times to repeat the encryption
1566 :type iter: :py:data:`int`
1567
1568 :param maciter: How many times to repeat the MAC
1569 :type maciter: :py:data:`int`
1570
1571 :return: The string containing the PKCS12
1572 """
1573 if self._cacerts is None:
1574 cacerts = _api.NULL
1575 else:
1576 cacerts = _api.sk_X509_new_null()
1577 for cert in self._cacerts:
1578 _api.sk_X509_push(cacerts, cert._x509)
1579
1580 if passphrase is None:
1581 passphrase = _api.NULL
1582
1583 friendlyname = self._friendlyname
1584 if friendlyname is None:
1585 friendlyname = _api.NULL
1586
1587 if self._pkey is None:
1588 pkey = _api.NULL
1589 else:
1590 pkey = self._pkey._pkey
1591
1592 if self._cert is None:
1593 cert = _api.NULL
1594 else:
1595 cert = self._cert._x509
1596
1597 pkcs12 = _api.PKCS12_create(
1598 passphrase, friendlyname, pkey, cert, cacerts,
1599 _api.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1600 _api.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
1601 iter, maciter, 0)
1602 if pkcs12 == _api.NULL:
1603 _raise_current_error()
1604
1605 bio = _api.BIO_new(_api.BIO_s_mem())
1606 if bio == _api.NULL:
1607 1/0
1608
1609 _api.i2d_PKCS12_bio(bio, pkcs12)
1610 return _bio_to_string(bio)
1611PKCS12Type = PKCS12
1612
1613
1614
Jean-Paul Calderone3b89f472013-02-21 09:32:25 -08001615class NetscapeSPKI(object):
1616 def __init__(self):
1617 self._spki = _api.NETSCAPE_SPKI_new()
1618
1619
1620 def sign(self, pkey, digest):
1621 """
1622 Sign the certificate request using the supplied key and digest
1623
1624 :param pkey: The key to sign with
1625 :param digest: The message digest to use
1626 :return: None
1627 """
1628 if pkey._only_public:
1629 raise ValueError("Key has only public part")
1630
1631 if not pkey._initialized:
1632 raise ValueError("Key is uninitialized")
1633
1634 digest_obj = _api.EVP_get_digestbyname(digest)
1635 if digest_obj == _api.NULL:
1636 raise ValueError("No such digest method")
1637
1638 sign_result = _api.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj)
1639 if not sign_result:
1640 1/0
1641
1642
1643 def verify(self, key):
1644 """
1645 Verifies a certificate request using the supplied public key
1646
1647 :param key: a public key
1648 :return: True if the signature is correct.
1649 :raise OpenSSL.crypto.Error: If the signature is invalid or there is a
1650 problem verifying the signature.
1651 """
1652 answer = _api.NETSCAPE_SPKI_verify(self._spki, key._pkey)
1653 if answer <= 0:
1654 _raise_current_error()
1655 return True
1656
1657
1658 def b64_encode(self):
1659 """
1660 Generate a base64 encoded string from an SPKI
1661
1662 :return: The base64 encoded string
1663 """
1664 return _api.string(_api.NETSCAPE_SPKI_b64_encode(self._spki))
1665
1666
1667 def get_pubkey(self):
1668 """
1669 Get the public key of the certificate
1670
1671 :return: The public key
1672 """
1673 pkey = PKey.__new__(PKey)
1674 pkey._pkey = _api.NETSCAPE_SPKI_get_pubkey(self._spki)
1675 if pkey._pkey == _api.NULL:
1676 1/0
1677 pkey._only_public = True
1678 return pkey
1679
1680
1681 def set_pubkey(self, pkey):
1682 """
1683 Set the public key of the certificate
1684
1685 :param pkey: The public key
1686 :return: None
1687 """
1688 set_result = _api.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey)
1689 if not set_result:
1690 1/0
1691NetscapeSPKIType = NetscapeSPKI
1692
1693
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001694class _PassphraseHelper(object):
Jean-Paul Calderone23478b32013-02-20 13:31:38 -08001695 def __init__(self, type, passphrase):
1696 if type != FILETYPE_PEM and passphrase is not None:
1697 raise ValueError("only FILETYPE_PEM key format supports encryption")
Jean-Paul Calderonee41f05c2013-02-20 13:28:16 -08001698 self._passphrase = passphrase
1699 self._problems = []
1700
1701
1702 @property
1703 def callback(self):
1704 if self._passphrase is None:
1705 return _api.NULL
1706 elif isinstance(self._passphrase, bytes):
1707 return _api.NULL
1708 elif callable(self._passphrase):
1709 return _api.callback("pem_password_cb", self._read_passphrase)
1710 else:
1711 raise TypeError("Last argument must be string or callable")
1712
1713
1714 @property
1715 def callback_args(self):
1716 if self._passphrase is None:
1717 return _api.NULL
1718 elif isinstance(self._passphrase, bytes):
1719 return self._passphrase
1720 elif callable(self._passphrase):
1721 return _api.NULL
1722 else:
1723 raise TypeError("Last argument must be string or callable")
1724
1725
1726 def raise_if_problem(self):
1727 if self._problems:
1728 try:
1729 _raise_current_error()
1730 except Error:
1731 pass
1732 raise self._problems[0]
1733
1734
1735 def _read_passphrase(self, buf, size, rwflag, userdata):
1736 try:
1737 result = self._passphrase(rwflag)
1738 if not isinstance(result, bytes):
1739 raise ValueError("String expected")
1740 if len(result) > size:
1741 raise ValueError("passphrase returned by callback is too long")
1742 for i in range(len(result)):
1743 buf[i] = result[i]
1744 return len(result)
1745 except Exception as e:
1746 self._problems.append(e)
1747 return 0
1748
1749
1750
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -08001751def _new_mem_buf(buffer):
1752 bio = _api.BIO_new_mem_buf(buffer, len(buffer))
1753 if bio == _api.NULL:
1754 1/0
1755 bio = _api.ffi.gc(bio, _api.BIO_free)
1756 return bio
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