2 Implements interface to openssl X509 and X509Store structures,
3 I.e allows to load, analyze and verify certificates.
5 X509Store objects are also used to verify other signed documets,
6 such as CMS, OCSP and timestamps.
11 from ctypes import c_void_p, c_long, c_ulong, c_int, POINTER, c_char_p, Structure, cast
12 from ctypescrypto.bio import Membio
13 from ctypescrypto.pkey import PKey
14 from ctypescrypto.oid import Oid
15 from ctypescrypto.exception import LibCryptoError
16 from ctypescrypto import libcrypto, pyver, chartype, inttype, bintype
17 from datetime import datetime
22 from datetime import timedelta, tzinfo
25 """tzinfo object for UTC.
26 If no pytz is available, we would use it.
28 def utcoffset(self, dt):
39 __all__ = ['X509', 'X509Error', 'X509Name', 'X509Store', 'StackOfX509']
41 if hasattr(libcrypto,"X509_get_version"):
43 # If it is OpenSSL 1.1 or above, use accessor functions
44 _X509_get_version = libcrypto.X509_get_version
45 _X509_get_version.restype = c_long
46 _X509_get_version.argtypes = (c_void_p,)
48 _X509_get_notBefore=libcrypto.X509_getm_notBefore
49 _X509_get_notBefore.restype = c_void_p
50 _X509_get_notBefore.argtypes = (c_void_p,)
52 _X509_get_notAfter=libcrypto.X509_getm_notAfter
53 _X509_get_notAfter.restype = c_void_p
54 _X509_get_notAfter.argtypes = (c_void_p,)
56 # Otherwise declare X509 structure internals and define deep poke
58 class _validity(Structure):
59 """ ctypes representation of X509_VAL structure
60 needed to access certificate validity period, because openssl
61 doesn't provide fuctions for it - only macros
63 _fields_ = [('notBefore', c_void_p), ('notAfter', c_void_p)]
65 class _cinf(Structure):
66 """ ctypes representtion of X509_CINF structure
67 neede to access certificate data, which are accessable only
70 _fields_ = [('version', c_void_p),
71 ('serialNumber', c_void_p),
72 ('sign_alg', c_void_p),
74 ('validity', POINTER(_validity)),
75 ('subject', c_void_p),
77 ('issuerUID', c_void_p),
78 ('subjectUID', c_void_p),
79 ('extensions', c_void_p),
82 class _x509(Structure):
84 ctypes represntation of X509 structure needed
85 to access certificate data which are accesable only via
88 _fields_ = [('cert_info', POINTER(_cinf)),
89 ('sig_alg', c_void_p),
90 ('signature', c_void_p),
91 # There are a lot of parsed extension fields there
93 _px509 = POINTER(_x509)
94 def _X509_get_version(ptr):
95 asn1int = cast(ptr, _px509)[0].cert_info[0].version
96 return libcrypto.ASN1_INTEGER_get(asn1int)
98 def _X509_get_notBefore(ptr):
99 # (x)->cert_info->validity->notBefore
100 return cast(ptr, _px509)[0].cert_info[0].validity[0].notBefore
101 def _X509_get_notAfter(ptr):
102 return cast(ptr, _px509)[0].cert_info[0].validity[0].notAfter
104 if hasattr(libcrypto,'sk_num'):
105 sk_num = libcrypto.sk_num
106 sk_set = libcrypto.sk_set
107 sk_value = libcrypto.sk_value
108 sk_delete = libcrypto.sk_delete
109 sk_new_null = libcrypto.sk_new_null
110 sk_pop_free = libcrypto.sk_pop_free
111 sk_push = libcrypto.sk_push
113 sk_num = libcrypto.OPENSSL_sk_num
114 sk_set = libcrypto.OPENSSL_sk_set
115 sk_value = libcrypto.OPENSSL_sk_value
116 sk_delete = libcrypto.OPENSSL_sk_delete
117 sk_new_null = libcrypto.OPENSSL_sk_new_null
118 sk_pop_free = libcrypto.OPENSSL_sk_pop_free
119 sk_push = libcrypto.OPENSSL_sk_push
120 class X509Error(LibCryptoError):
122 Exception, generated when some openssl function fail
123 during X509 operation
128 class X509Name(object):
130 Class which represents X.509 distinguished name - typically
131 a certificate subject name or an issuer name.
133 Now used only to represent information, extracted from the
134 certificate. Potentially can be also used to build DN when creating
135 certificate signing request
137 # XN_FLAG_SEP_COMMA_PLUS & ASN1_STRFLG_UTF8_CONVERT
140 def __init__(self, ptr=None, copy=False):
142 Creates a X509Name object
143 @param ptr - pointer to X509_NAME C structure (as returned by some
145 @param copy - indicates that this structure have to be freed upon
150 self.need_free = copy
151 self.writable = False
153 self.ptr = libcrypto.X509_NAME_new()
154 self.need_free = True
162 libcrypto.X509_NAME_free(self.ptr)
165 Produces an ascii representation of the name, escaping all
166 symbols > 0x80. Probably it is not what you want, unless
167 your native language is English
170 libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0,
171 self.PRINT_FLAG | self.ESC_MSB)
172 return bio.__bytes__()
174 def __unicode__(self):
176 Produces unicode representation of the name.
179 libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0, self.PRINT_FLAG)
180 return bio.__unicode__()
184 __str__ = __unicode__
189 return number of components in the name
191 return libcrypto.X509_NAME_entry_count(self.ptr)
192 def __cmp__(self, other):
196 return libcrypto.X509_NAME_cmp(self.ptr, other.ptr)
197 def __eq__(self, other):
198 return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) == 0
199 def __gt__(self, other):
200 return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) > 0
201 def __lt__(self, other):
202 return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) < 0
204 def __getitem__(self, key):
205 if isinstance(key, Oid):
206 # Return first matching field
207 idx = libcrypto.X509_NAME_get_index_by_NID(self.ptr, key.nid, -1)
209 raise KeyError("Key not found " + str(Oid))
210 entry = libcrypto.X509_NAME_get_entry(self.ptr, idx)
211 value = libcrypto.X509_NAME_ENTRY_get_data(entry)
213 libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG)
215 elif isinstance(key, inttype):
216 # Return OID, string tuple
217 entry = libcrypto.X509_NAME_get_entry(self.ptr, key)
219 raise IndexError("name entry index out of range")
220 oid = Oid.fromobj(libcrypto.X509_NAME_ENTRY_get_object(entry))
221 value = libcrypto.X509_NAME_ENTRY_get_data(entry)
223 libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG)
224 return (oid, chartype(bio))
226 raise TypeError("X509 NAME can be indexed by Oids or integers only")
228 def __setitem__(self, key, val):
229 if not self.writable:
230 raise ValueError("Attempt to modify constant X509 object")
232 raise NotImplementedError
233 def __delitem__(self, key):
234 if not self.writable:
235 raise ValueError("Attempt to modify constant X509 object")
237 raise NotImplementedError
239 return libcrypto.X509_NAME_hash(self.ptr)
241 class _x509_ext(Structure):
242 """ Represens C structure X509_EXTENSION """
243 _fields_ = [("object", c_void_p),
248 class X509_EXT(object):
249 """ Python object which represents a certificate extension """
250 def __init__(self, ptr, copy=False):
251 """ Initializes from the pointer to X509_EXTENSION.
252 If copy is True, creates a copy, otherwise just
256 self.ptr = libcrypto.X509_EXTENSION_dup(ptr)
258 self.ptr = cast(ptr, POINTER(_x509_ext))
260 libcrypto.X509_EXTENSION_free(self.ptr)
263 libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0)
265 def __unicode__(self):
267 libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0)
272 __str__ = __unicode__
275 "Returns OID of the extension"
276 return Oid.fromobj(self.ptr[0].object)
279 "Returns True if extensin have critical flag set"
280 return self.ptr[0].critical > 0
282 class _X509extlist(object):
284 Represents list of certificate extensions. Really it keeps
285 reference to certificate object
287 def __init__(self, cert):
289 Initialize from X509 object
295 Returns number of extensions
297 return libcrypto.X509_get_ext_count(self.cert.cert)
299 def __getitem__(self, item):
301 Returns extension by index, creating a copy
303 ext_ptr = libcrypto.X509_get_ext(self.cert.cert, item)
306 return X509_EXT(ext_ptr, True)
309 Return list of extensions with given Oid
311 if not isinstance(oid, Oid):
312 raise TypeError("Need crytypescrypto.oid.Oid as argument")
317 index = libcrypto.X509_get_ext_by_NID(self.cert.cert, oid.nid,
319 if index >= end or index < 0:
321 found.append(self[index])
324 def find_critical(self, crit=True):
326 Return list of critical extensions (or list of non-cricital, if
327 optional second argument is False
337 index = libcrypto.X509_get_ext_by_critical(self.cert.cert, flag,
339 if index >= end or index < 0:
341 found.append(self[index])
344 def _X509__asn1date_to_datetime(asn1date):
346 Converts openssl ASN1_TIME object to python datetime.datetime
349 libcrypto.ASN1_TIME_print(bio.bio, asn1date)
350 pydate = datetime.strptime(str(bio), "%b %d %H:%M:%S %Y %Z")
351 return pydate.replace(tzinfo=utc)
355 Represents X.509 certificate.
357 def __init__(self, data=None, ptr=None, format="PEM"):
359 Initializes certificate
360 @param data - serialized certificate in PEM or DER format.
361 @param ptr - pointer to X509, returned by some openssl function.
362 mutually exclusive with data
363 @param format - specifies data format. "PEM" or "DER", default PEM
367 raise TypeError("Cannot use data and ptr simultaneously")
370 raise TypeError("data argument is required")
374 self.cert = libcrypto.PEM_read_bio_X509(bio.bio, None, None,
377 self.cert = libcrypto.d2i_X509_bio(bio.bio, None)
378 if self.cert is None:
379 raise X509Error("error reading certificate")
380 self.extensions = _X509extlist(self)
383 Frees certificate object
385 libcrypto.X509_free(self.cert)
387 """ Returns der string of the certificate """
389 if libcrypto.i2d_X509_bio(bio.bio, self.cert) == 0:
390 raise X509Error("error serializing certificate")
395 """ Returns valid call to the constructor """
396 return "X509(data=" + repr(self.pem()) + ",format='PEM')"
399 """EVP PKEy object of certificate public key"""
400 return PKey(ptr=libcrypto.X509_get_pubkey(self.cert, False))
402 """ Returns PEM represntation of the certificate """
404 if libcrypto.PEM_write_bio_X509(bio.bio, self.cert) == 0:
405 raise X509Error("error serializing certificate")
407 def verify(self, store=None, chain=None, key=None):
409 Verify self. Supports verification on both X509 store object
410 or just public issuer key
411 @param store X509Store object.
412 @param chain - list of X509 objects to add into verification
413 context.These objects are untrusted, but can be used to
414 build certificate chain up to trusted object in the store
415 @param key - PKey object with open key to validate signature
417 parameters store and key are mutually exclusive. If neither
418 is specified, attempts to verify self as self-signed certificate
420 if store is not None and key is not None:
421 raise X509Error("key and store cannot be specified simultaneously")
422 if store is not None:
423 ctx = libcrypto.X509_STORE_CTX_new()
425 raise X509Error("Error allocating X509_STORE_CTX")
426 if chain is not None and len(chain) > 0:
427 chain_ptr = StackOfX509(chain).ptr
430 if libcrypto.X509_STORE_CTX_init(ctx, store.store, self.cert,
432 raise X509Error("Error allocating X509_STORE_CTX")
433 res = libcrypto.X509_verify_cert(ctx)
434 libcrypto.X509_STORE_CTX_free(ctx)
438 if self.issuer != self.subject:
439 # Not a self-signed certificate
442 res = libcrypto.X509_verify(self.cert, key.key)
444 raise X509Error("X509_verify failed")
449 """ X509Name for certificate subject name """
450 return X509Name(libcrypto.X509_get_subject_name(self.cert))
453 """ X509Name for certificate issuer name """
454 return X509Name(libcrypto.X509_get_issuer_name(self.cert))
457 """ Serial number of certificate as integer """
458 asnint = libcrypto.X509_get_serialNumber(self.cert)
460 libcrypto.i2a_ASN1_INTEGER(bio.bio, asnint)
461 return int(str(bio), 16)
465 certificate version as integer. Really certificate stores 0 for
466 version 1 and 2 for version 3, but we return 1 and 3
468 return _X509_get_version(self.cert) + 1
471 """ Certificate validity period start date """
472 asn1 = _X509_get_notBefore(self.cert)
473 return __asn1date_to_datetime(asn1)
476 """ Certificate validity period end date """
477 asn1 = _X509_get_notAfter(self.cert)
478 return __asn1date_to_datetime(asn1)
480 """ Returns True if certificate is CA certificate """
481 return libcrypto.X509_check_ca(self.cert) > 0
483 class X509Store(object):
485 Represents trusted certificate store. Can be used to lookup CA
486 certificates to verify
488 @param file - file with several certificates and crls
490 @param dir - hashed directory with certificates and crls
491 @param default - if true, default verify location (directory)
495 def __init__(self, file=None, dir=None, default=False):
497 Creates X509 store and installs lookup method. Optionally initializes
498 by certificates from given file or directory.
501 # Todo - set verification flags
503 self.store = libcrypto.X509_STORE_new()
504 if self.store is None:
505 raise X509Error("allocating store")
506 lookup = libcrypto.X509_STORE_add_lookup(self.store,
507 libcrypto.X509_LOOKUP_file())
509 raise X509Error("error installing file lookup method")
514 fn = file.encode(sys.getfilesystemencoding())
515 if not libcrypto.X509_LOOKUP_ctrl(lookup, 1, fn, 1, None) > 0:
516 raise X509Error("error loading trusted certs from file "+file)
517 lookup = libcrypto.X509_STORE_add_lookup(self.store,
518 libcrypto.X509_LOOKUP_hash_dir())
520 raise X509Error("error installing hashed lookup method")
525 dr = dir.encode(sys.getfilesystemencoding())
526 if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, dr, 1, None) > 0:
527 raise X509Error("error adding hashed trusted certs dir "+dir)
529 if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, None, 3, None) > 0:
530 raise X509Error("error adding default trusted certs dir ")
531 def add_cert(self, cert):
533 Explicitely adds certificate to set of trusted in the store
534 @param cert - X509 object to add
536 if not isinstance(cert, X509):
537 raise TypeError("cert should be X509")
538 libcrypto.X509_STORE_add_cert(self.store, cert.cert)
539 def add_callback(self, callback):
541 Installs callback function, which would receive detailed information
542 about verified ceritificates
544 raise NotImplementedError
545 def setflags(self, flags):
547 Set certificate verification flags.
548 @param flags - integer bit mask. See OpenSSL X509_V_FLAG_* constants
550 libcrypto.X509_STORE_set_flags(self.store, flags)
551 def setpurpose(self, purpose):
553 Sets certificate purpose which verified certificate should match
554 @param purpose - number from 1 to 9 or standard strind defined
556 possible strings - sslcient,sslserver, nssslserver, smimesign,i
557 smimeencrypt, crlsign, any, ocsphelper
559 if isinstance(purpose, str):
560 purp_no = libcrypto.X509_PURPOSE_get_by_sname(purpose)
562 raise X509Error("Invalid certificate purpose '%s'" % purpose)
563 elif isinstance(purpose, int):
565 if libcrypto.X509_STORE_set_purpose(self.store, purp_no) <= 0:
566 raise X509Error("cannot set purpose")
567 def setdepth(self, depth):
569 Sets the verification depth i.e. max length of certificate chain
572 libcrypto.X509_STORE_set_depth(self.store, depth)
573 def settime(self, time):
575 Set point in time used to check validity of certificates for
576 Time can be either python datetime object or number of seconds
579 if isinstance(time, datetime) or isinstance(time,
581 seconds = int(time.strftime("%s"))
582 elif isinstance(time, int):
585 raise TypeError("datetime.date, datetime.datetime or integer " +
586 "is required as time argument")
587 raise NotImplementedError
588 class StackOfX509(object):
590 Implements OpenSSL STACK_OF(X509) object.
591 It looks much like python container types
593 def __init__(self, certs=None, ptr=None, disposable=True):
596 @param certs - list of X509 objects. If specified, read-write
597 stack is created and populated by these certificates
598 @param ptr - pointer to OpenSSL STACK_OF(X509) as returned by
600 @param disposable - if True, stack created from object, returned
601 by function is copy, and can be modified and need to be
602 freeid. If false, it is just pointer into another
603 structure i.e. CMS_ContentInfo
605 self.need_free = False
607 self.need_free = True
608 self.ptr = sk_new_null()
609 if certs is not None:
612 elif certs is not None:
613 raise ValueError("cannot handle certs an ptr simultaneously")
615 self.need_free = disposable
618 return sk_num(self.ptr)
619 def __getitem__(self, index):
620 if index < 0 or index >= len(self):
622 p = sk_value(self.ptr, index)
623 return X509(ptr=libcrypto.X509_dup(p))
624 def __setitem__(self, index, value):
625 if not self.need_free:
626 raise ValueError("Stack is read-only")
627 if index < 0 or index >= len(self):
629 if not isinstance(value, X509):
630 raise TypeError('StackOfX509 can contain only X509 objects')
631 p = sk_value(self.ptr, index)
632 sk_set(self.ptr, index, libcrypto.X509_dup(value.cert))
633 libcrypto.X509_free(p)
634 def __delitem__(self, index):
635 if not self.need_free:
636 raise ValueError("Stack is read-only")
637 if index < 0 or index >= len(self):
639 p = sk_delete(self.ptr, index)
640 libcrypto.X509_free(p)
643 sk_pop_free(self.ptr, libcrypto.X509_free)
644 def append(self, value):
645 """ Adds certificate to stack """
646 if not self.need_free:
647 raise ValueError("Stack is read-only")
648 if not isinstance(value, X509):
649 raise TypeError('StackOfX509 can contain only X509 objects')
650 sk_push(self.ptr, libcrypto.X509_dup(value.cert))
652 libcrypto.d2i_X509_bio.argtypes = (c_void_p,POINTER(c_void_p))
653 libcrypto.X509_free.argtypes = (c_void_p,)
654 libcrypto.X509_dup.restype = c_void_p
655 libcrypto.X509_dup.argtypes = (c_void_p, )
656 libcrypto.i2a_ASN1_INTEGER.argtypes = (c_void_p, c_void_p)
657 libcrypto.ASN1_STRING_print_ex.argtypes = (c_void_p, c_void_p, c_long)
658 libcrypto.PEM_read_bio_X509.restype = c_void_p
659 libcrypto.PEM_read_bio_X509.argtypes = (c_void_p, POINTER(c_void_p),
661 libcrypto.PEM_write_bio_X509.restype = c_int
662 libcrypto.PEM_write_bio_X509.argtypes = (c_void_p, c_void_p)
663 libcrypto.ASN1_TIME_print.argtypes = (c_void_p, c_void_p)
664 libcrypto.ASN1_INTEGER_get.argtypes = (c_void_p, )
665 libcrypto.ASN1_INTEGER_get.restype = c_long
666 libcrypto.X509_check_ca.argtypes = (c_void_p, )
667 libcrypto.X509_get_serialNumber.argtypes = (c_void_p, )
668 libcrypto.X509_get_serialNumber.restype = c_void_p
669 libcrypto.X509_get_subject_name.argtypes = (c_void_p, )
670 libcrypto.X509_get_subject_name.restype = c_void_p
671 libcrypto.X509_get_issuer_name.argtypes = (c_void_p, )
672 libcrypto.X509_get_issuer_name.restype = c_void_p
673 libcrypto.X509_NAME_ENTRY_get_object.restype = c_void_p
674 libcrypto.X509_NAME_ENTRY_get_object.argtypes = (c_void_p, )
675 libcrypto.X509_NAME_ENTRY_get_data.restype = c_void_p
676 libcrypto.X509_NAME_ENTRY_get_data.argtypes = (c_void_p, )
677 libcrypto.OBJ_obj2nid.argtypes = (c_void_p, )
678 libcrypto.X509_NAME_get_entry.restype = c_void_p
679 libcrypto.X509_NAME_get_entry.argtypes = (c_void_p, c_int)
680 libcrypto.X509_STORE_new.restype = c_void_p
681 libcrypto.X509_STORE_add_lookup.restype = c_void_p
682 libcrypto.X509_STORE_add_lookup.argtypes = (c_void_p, c_void_p)
683 libcrypto.X509_STORE_add_cert.argtypes = (c_void_p, c_void_p)
684 libcrypto.X509_STORE_CTX_new.restype = c_void_p
685 libcrypto.X509_STORE_CTX_free.argtypes = (c_void_p,)
686 libcrypto.X509_STORE_CTX_init.argtypes = (c_void_p, c_void_p, c_void_p,
688 libcrypto.X509_STORE_set_depth.argtypes = (c_void_p, c_int)
689 libcrypto.X509_STORE_set_flags.argtypes = (c_void_p, c_ulong)
690 libcrypto.X509_STORE_set_purpose.argtypes = (c_void_p, c_int)
691 libcrypto.X509_LOOKUP_file.restype = c_void_p
692 libcrypto.X509_LOOKUP_hash_dir.restype = c_void_p
693 libcrypto.X509_LOOKUP_ctrl.restype = c_int
694 libcrypto.X509_LOOKUP_ctrl.argtypes = (c_void_p, c_int, c_char_p, c_long,
696 libcrypto.X509_EXTENSION_free.argtypes = (c_void_p, )
697 libcrypto.X509_EXTENSION_dup.argtypes = (c_void_p, )
698 libcrypto.X509_EXTENSION_dup.restype = POINTER(_x509_ext)
699 libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long,
701 libcrypto.X509_get_ext.restype = c_void_p
702 libcrypto.X509_get_ext.argtypes = (c_void_p, c_int)
703 libcrypto.X509_get_ext_by_critical.argtypes = (c_void_p, c_int, c_int)
704 libcrypto.X509_get_ext_by_NID.argtypes = (c_void_p, c_int, c_int)
705 libcrypto.X509_get_ext_count.argtypes = (c_void_p, )
706 libcrypto.X509_get_pubkey.restype = c_void_p
707 libcrypto.X509_get_pubkey.argtypes = (c_void_p, )
708 libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long,
710 libcrypto.X509_LOOKUP_file.restype = c_void_p
711 libcrypto.X509_LOOKUP_hash_dir.restype = c_void_p
712 libcrypto.X509_NAME_cmp.argtypes = (c_void_p, c_void_p)
713 libcrypto.X509_NAME_entry_count.argtypes = (c_void_p,)
714 libcrypto.X509_NAME_free.argtypes = (c_void_p,)
715 libcrypto.X509_NAME_new.restype = c_void_p
716 libcrypto.X509_NAME_print_ex.argtypes = (c_void_p, c_void_p, c_int, c_ulong)
717 libcrypto.X509_PURPOSE_get_by_sname.argtypes=(c_char_p,)
718 libcrypto.X509_verify.argtypes = (c_void_p, c_void_p)
719 libcrypto.X509_verify_cert.argtypes = (c_void_p,)
720 sk_num.restype = c_int
721 sk_num.argtypes= (c_void_p,)
722 sk_set.argtypes = (c_void_p, c_int, c_void_p)
723 sk_set.restype = c_void_p
724 sk_value.argtypes = (c_void_p, c_int)
725 sk_value.restype = c_void_p
726 sk_delete.argtypes = (c_void_p, c_int)
727 sk_delete.restype = c_void_p
728 sk_new_null.restype = c_void_p
729 sk_pop_free.argtypes = (c_void_p, c_void_p)
730 sk_push.argtypes = (c_void_p, c_void_p)
731 libcrypto.X509_NAME_hash.restype = c_long
732 libcrypto.X509_NAME_hash.argtypes = (c_void_p, )
733 libcrypto.X509_NAME_get_index_by_NID.argtypes = (c_void_p, c_int, c_int)