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_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
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 class _validity(Structure):
42 """ ctypes representation of X509_VAL structure
43 needed to access certificate validity period, because openssl
44 doesn't provide fuctions for it - only macros
46 _fields_ = [('notBefore', c_void_p), ('notAfter', c_void_p)]
48 class _cinf(Structure):
49 """ ctypes representtion of X509_CINF structure
50 neede to access certificate data, which are accessable only
53 _fields_ = [('version', c_void_p),
54 ('serialNumber', c_void_p),
55 ('sign_alg', c_void_p),
57 ('validity', POINTER(_validity)),
58 ('subject', c_void_p),
60 ('issuerUID', c_void_p),
61 ('subjectUID', c_void_p),
62 ('extensions', c_void_p),
65 class _x509(Structure):
67 ctypes represntation of X509 structure needed
68 to access certificate data which are accesable only via
71 _fields_ = [('cert_info', POINTER(_cinf)),
72 ('sig_alg', c_void_p),
73 ('signature', c_void_p),
74 # There are a lot of parsed extension fields there
76 _px509 = POINTER(_x509)
78 class X509Error(LibCryptoError):
80 Exception, generated when some openssl function fail
86 class X509Name(object):
88 Class which represents X.509 distinguished name - typically
89 a certificate subject name or an issuer name.
91 Now used only to represent information, extracted from the
92 certificate. Potentially can be also used to build DN when creating
93 certificate signing request
95 # XN_FLAG_SEP_COMMA_PLUS & ASN1_STRFLG_UTF8_CONVERT
98 def __init__(self, ptr=None, copy=False):
100 Creates a X509Name object
101 @param ptr - pointer to X509_NAME C structure (as returned by some
103 @param copy - indicates that this structure have to be freed upon
108 self.need_free = copy
109 self.writable = False
111 self.ptr = libcrypto.X509_NAME_new()
112 self.need_free = True
120 libcrypto.X509_NAME_free(self.ptr)
123 Produces an ascii representation of the name, escaping all
124 symbols > 0x80. Probably it is not what you want, unless
125 your native language is English
128 libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0,
129 self.PRINT_FLAG | self.ESC_MSB)
132 def __unicode__(self):
134 Produces unicode representation of the name.
137 libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0, self.PRINT_FLAG)
141 return number of components in the name
143 return libcrypto.X509_NAME_entry_count(self.ptr)
144 def __cmp__(self, other):
148 return libcrypto.X509_NAME_cmp(self.ptr, other.ptr)
149 def __eq__(self, other):
150 return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) == 0
152 def __getitem__(self, key):
153 if isinstance(key, Oid):
154 # Return first matching field
155 idx = libcrypto.X509_NAME_get_index_by_NID(self.ptr, key.nid, -1)
157 raise KeyError("Key not found " + str(Oid))
158 entry = libcrypto.X509_NAME_get_entry(self.ptr, idx)
159 value = libcrypto.X509_NAME_ENTRY_get_data(entry)
161 libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG)
163 elif isinstance(key, (int, long)):
164 # Return OID, string tuple
165 entry = libcrypto.X509_NAME_get_entry(self.ptr, key)
167 raise IndexError("name entry index out of range")
168 oid = Oid.fromobj(libcrypto.X509_NAME_ENTRY_get_object(entry))
169 value = libcrypto.X509_NAME_ENTRY_get_data(entry)
171 libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG)
172 return (oid, unicode(bio))
174 raise TypeError("X509 NAME can be indexed by Oids or integers only")
176 def __setitem__(self, key, val):
177 if not self.writable:
178 raise ValueError("Attempt to modify constant X509 object")
180 raise NotImplementedError
181 def __delitem__(self, key):
182 if not self.writable:
183 raise ValueError("Attempt to modify constant X509 object")
185 raise NotImplementedError
187 return libcrypto.X509_NAME_hash(self.ptr)
189 class _x509_ext(Structure):
190 """ Represens C structure X509_EXTENSION """
191 _fields_ = [("object", c_void_p),
196 class X509_EXT(object):
197 """ Python object which represents a certificate extension """
198 def __init__(self, ptr, copy=False):
199 """ Initializes from the pointer to X509_EXTENSION.
200 If copy is True, creates a copy, otherwise just
204 self.ptr = libcrypto.X509_EXTENSION_dup(ptr)
206 self.ptr = cast(ptr, POINTER(_x509_ext))
208 libcrypto.X509_EXTENSION_free(self.ptr)
211 libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0)
213 def __unicode__(self):
215 libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0)
219 "Returns OID of the extension"
220 return Oid.fromobj(self.ptr[0].object)
223 "Returns True if extensin have critical flag set"
224 return self.ptr[0].critical > 0
226 class _X509extlist(object):
228 Represents list of certificate extensions. Really it keeps
229 reference to certificate object
231 def __init__(self, cert):
233 Initialize from X509 object
239 Returns number of extensions
241 return libcrypto.X509_get_ext_count(self.cert.cert)
243 def __getitem__(self, item):
245 Returns extension by index, creating a copy
247 ext_ptr = libcrypto.X509_get_ext(self.cert.cert, item)
250 return X509_EXT(ext_ptr, True)
253 Return list of extensions with given Oid
255 if not isinstance(oid, Oid):
256 raise TypeError("Need crytypescrypto.oid.Oid as argument")
261 index = libcrypto.X509_get_ext_by_NID(self.cert.cert, oid.nid,
263 if index >= end or index < 0:
265 found.append(self[index])
268 def find_critical(self, crit=True):
270 Return list of critical extensions (or list of non-cricital, if
271 optional second argument is False
281 index = libcrypto.X509_get_ext_by_critical(self.cert.cert, flag,
283 if index >= end or index < 0:
285 found.append(self[index])
288 def _X509__asn1date_to_datetime(asn1date):
290 Converts openssl ASN1_TIME object to python datetime.datetime
293 libcrypto.ASN1_TIME_print(bio.bio, asn1date)
294 pydate = datetime.strptime(str(bio), "%b %d %H:%M:%S %Y %Z")
295 return pydate.replace(tzinfo=utc)
299 Represents X.509 certificate.
301 def __init__(self, data=None, ptr=None, format="PEM"):
303 Initializes certificate
304 @param data - serialized certificate in PEM or DER format.
305 @param ptr - pointer to X509, returned by some openssl function.
306 mutually exclusive with data
307 @param format - specifies data format. "PEM" or "DER", default PEM
311 raise TypeError("Cannot use data and ptr simultaneously")
314 raise TypeError("data argument is required")
318 self.cert = libcrypto.PEM_read_bio_X509(bio.bio, None, None,
321 self.cert = libcrypto.d2i_X509_bio(bio.bio, None)
322 if self.cert is None:
323 raise X509Error("error reading certificate")
324 self.extensions = _X509extlist(self)
327 Frees certificate object
329 libcrypto.X509_free(self.cert)
331 """ Returns der string of the certificate """
333 if libcrypto.i2d_X509_bio(bio.bio, self.cert) == 0:
334 raise X509Error("error serializing certificate")
337 """ Returns valid call to the constructor """
338 return "X509(data=" + repr(str(self)) + ",format='DER')"
341 """EVP PKEy object of certificate public key"""
342 return PKey(ptr=libcrypto.X509_get_pubkey(self.cert, False))
344 """ Returns PEM represntation of the certificate """
346 if libcrypto.PEM_write_bio_X509(bio.bio, self.cert) == 0:
347 raise X509Error("error serializing certificate")
349 def verify(self, store=None, chain=None, key=None):
351 Verify self. Supports verification on both X509 store object
352 or just public issuer key
353 @param store X509Store object.
354 @param chain - list of X509 objects to add into verification
355 context.These objects are untrusted, but can be used to
356 build certificate chain up to trusted object in the store
357 @param key - PKey object with open key to validate signature
359 parameters store and key are mutually exclusive. If neither
360 is specified, attempts to verify self as self-signed certificate
362 if store is not None and key is not None:
363 raise X509Error("key and store cannot be specified simultaneously")
364 if store is not None:
365 ctx = libcrypto.X509_STORE_CTX_new()
367 raise X509Error("Error allocating X509_STORE_CTX")
368 if chain is not None and len(chain) > 0:
369 chain_ptr = StackOfX509(chain).ptr
372 if libcrypto.X509_STORE_CTX_init(ctx, store.store, self.cert,
374 raise X509Error("Error allocating X509_STORE_CTX")
375 res = libcrypto.X509_verify_cert(ctx)
376 libcrypto.X509_STORE_CTX_free(ctx)
380 if self.issuer != self.subject:
381 # Not a self-signed certificate
384 res = libcrypto.X509_verify(self.cert, key.key)
386 raise X509Error("X509_verify failed")
391 """ X509Name for certificate subject name """
392 return X509Name(libcrypto.X509_get_subject_name(self.cert))
395 """ X509Name for certificate issuer name """
396 return X509Name(libcrypto.X509_get_issuer_name(self.cert))
399 """ Serial number of certificate as integer """
400 asnint = libcrypto.X509_get_serialNumber(self.cert)
402 libcrypto.i2a_ASN1_INTEGER(bio.bio, asnint)
403 return int(str(bio), 16)
407 certificate version as integer. Really certificate stores 0 for
408 version 1 and 2 for version 3, but we return 1 and 3
410 asn1int = cast(self.cert, _px509)[0].cert_info[0].version
411 return libcrypto.ASN1_INTEGER_get(asn1int) + 1
414 """ Certificate validity period start date """
415 # Need deep poke into certificate structure
416 # (x)->cert_info->validity->notBefore
417 asn1 = cast(self.cert, _px509)[0].cert_info[0].validity[0].notBefore
418 return __asn1date_to_datetime(asn1)
421 """ Certificate validity period end date """
422 # Need deep poke into certificate structure
423 # (x)->cert_info->validity->notAfter
424 asn1 = cast(self.cert, _px509)[0].cert_info[0].validity[0].notAfter
425 return __asn1date_to_datetime(asn1)
427 """ Returns True if certificate is CA certificate """
428 return libcrypto.X509_check_ca(self.cert) > 0
430 class X509Store(object):
432 Represents trusted certificate store. Can be used to lookup CA
433 certificates to verify
435 @param file - file with several certificates and crls
437 @param dir - hashed directory with certificates and crls
438 @param default - if true, default verify location (directory)
442 def __init__(self, file=None, dir=None, default=False):
444 Creates X509 store and installs lookup method. Optionally initializes
445 by certificates from given file or directory.
448 # Todo - set verification flags
450 self.store = libcrypto.X509_STORE_new()
451 if self.store is None:
452 raise X509Error("allocating store")
453 lookup = libcrypto.X509_STORE_add_lookup(self.store,
454 libcrypto.X509_LOOKUP_file())
456 raise X509Error("error installing file lookup method")
458 if not libcrypto.X509_LOOKUP_ctrl(lookup, 1, file, 1, None) > 0:
459 raise X509Error("error loading trusted certs from file "+file)
460 lookup = libcrypto.X509_STORE_add_lookup(self.store,
461 libcrypto.X509_LOOKUP_hash_dir())
463 raise X509Error("error installing hashed lookup method")
465 if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, dir, 1, None) > 0:
466 raise X509Error("error adding hashed trusted certs dir "+dir)
468 if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, None, 3, None) > 0:
469 raise X509Error("error adding default trusted certs dir ")
470 def add_cert(self, cert):
472 Explicitely adds certificate to set of trusted in the store
473 @param cert - X509 object to add
475 if not isinstance(cert, X509):
476 raise TypeError("cert should be X509")
477 libcrypto.X509_STORE_add_cert(self.store, cert.cert)
478 def add_callback(self, callback):
480 Installs callback function, which would receive detailed information
481 about verified ceritificates
483 raise NotImplementedError
484 def setflags(self, flags):
486 Set certificate verification flags.
487 @param flags - integer bit mask. See OpenSSL X509_V_FLAG_* constants
489 libcrypto.X509_STORE_set_flags(self.store, flags)
490 def setpurpose(self, purpose):
492 Sets certificate purpose which verified certificate should match
493 @param purpose - number from 1 to 9 or standard strind defined
495 possible strings - sslcient,sslserver, nssslserver, smimesign,i
496 smimeencrypt, crlsign, any, ocsphelper
498 if isinstance(purpose, str):
499 purp_no = libcrypto.X509_PURPOSE_get_by_sname(purpose)
501 raise X509Error("Invalid certificate purpose '%s'" % purpose)
502 elif isinstance(purpose, int):
504 if libcrypto.X509_STORE_set_purpose(self.store, purp_no) <= 0:
505 raise X509Error("cannot set purpose")
506 def setdepth(self, depth):
508 Sets the verification depth i.e. max length of certificate chain
511 libcrypto.X509_STORE_set_depth(self.store, depth)
512 def settime(self, time):
514 Set point in time used to check validity of certificates for
515 Time can be either python datetime object or number of seconds
518 if isinstance(time, datetime) or isinstance(time,
520 seconds = int(time.strftime("%s"))
521 elif isinstance(time, int):
524 raise TypeError("datetime.date, datetime.datetime or integer " +
525 "is required as time argument")
526 raise NotImplementedError
527 class StackOfX509(object):
529 Implements OpenSSL STACK_OF(X509) object.
530 It looks much like python container types
532 def __init__(self, certs=None, ptr=None, disposable=True):
535 @param certs - list of X509 objects. If specified, read-write
536 stack is created and populated by these certificates
537 @param ptr - pointer to OpenSSL STACK_OF(X509) as returned by
539 @param disposable - if True, stack created from object, returned
540 by function is copy, and can be modified and need to be
541 freeid. If false, it is just pointer into another
542 structure i.e. CMS_ContentInfo
545 self.need_free = True
546 self.ptr = libcrypto.sk_new_null()
547 if certs is not None:
550 elif certs is not None:
551 raise ValueError("cannot handle certs an ptr simultaneously")
553 self.need_free = disposable
556 return libcrypto.sk_num(self.ptr)
557 def __getitem__(self, index):
558 if index < 0 or index >= len(self):
560 p = libcrypto.sk_value(self.ptr, index)
561 return X509(ptr=libcrypto.X509_dup(p))
562 def __setitem__(self, index, value):
563 if not self.need_free:
564 raise ValueError("Stack is read-only")
565 if index < 0 or index >= len(self):
567 if not isinstance(value, X509):
568 raise TypeError('StackOfX508 can contain only X509 objects')
569 p = libcrypto.sk_value(self.ptr, index)
570 libcrypto.sk_set(self.ptr, index, libcrypto.X509_dup(value.cert))
571 libcrypto.X509_free(p)
572 def __delitem__(self, index):
573 if not self.need_free:
574 raise ValueError("Stack is read-only")
575 if index < 0 or index >= len(self):
577 p = libcrypto.sk_delete(self.ptr, index)
578 libcrypto.X509_free(p)
581 libcrypto.sk_pop_free(self.ptr, libcrypto.X509_free)
582 def append(self, value):
583 """ Adds certificate to stack """
584 if not self.need_free:
585 raise ValueError("Stack is read-only")
586 if not isinstance(value, X509):
587 raise TypeError('StackOfX508 can contain only X509 objects')
588 libcrypto.sk_push(self.ptr, libcrypto.X509_dup(value.cert))
590 libcrypto.i2a_ASN1_INTEGER.argtypes = (c_void_p, c_void_p)
591 libcrypto.ASN1_STRING_print_ex.argtypes = (c_void_p, c_void_p, c_long)
592 libcrypto.PEM_read_bio_X509.restype = c_void_p
593 libcrypto.PEM_read_bio_X509.argtypes = (c_void_p, POINTER(c_void_p),
595 libcrypto.PEM_write_bio_X509.restype = c_int
596 libcrypto.PEM_write_bio_X509.argtypes = (c_void_p, c_void_p)
597 libcrypto.ASN1_TIME_print.argtypes = (c_void_p, c_void_p)
598 libcrypto.ASN1_INTEGER_get.argtypes = (c_void_p, )
599 libcrypto.ASN1_INTEGER_get.restype = c_long
600 libcrypto.X509_get_serialNumber.argtypes = (c_void_p, )
601 libcrypto.X509_get_serialNumber.restype = c_void_p
602 libcrypto.X509_NAME_ENTRY_get_object.restype = c_void_p
603 libcrypto.X509_NAME_ENTRY_get_object.argtypes = (c_void_p, )
604 libcrypto.OBJ_obj2nid.argtypes = (c_void_p, )
605 libcrypto.X509_NAME_get_entry.restype = c_void_p
606 libcrypto.X509_NAME_get_entry.argtypes = (c_void_p, c_int)
607 libcrypto.X509_STORE_new.restype = c_void_p
608 libcrypto.X509_STORE_add_lookup.restype = c_void_p
609 libcrypto.X509_STORE_add_lookup.argtypes = (c_void_p, c_void_p)
610 libcrypto.X509_LOOKUP_file.restype = c_void_p
611 libcrypto.X509_LOOKUP_hash_dir.restype = c_void_p
612 libcrypto.X509_LOOKUP_ctrl.restype = c_int
613 libcrypto.X509_LOOKUP_ctrl.argtypes = (c_void_p, c_int, c_char_p, c_long,
615 libcrypto.X509_EXTENSION_dup.argtypes = (c_void_p, )
616 libcrypto.X509_EXTENSION_dup.restype = POINTER(_x509_ext)
617 libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long,
619 libcrypto.X509_get_ext.restype = c_void_p
620 libcrypto.X509_get_ext.argtypes = (c_void_p, c_int)
621 libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long,
623 libcrypto.sk_set.argtypes = (c_void_p, c_int, c_void_p)
624 libcrypto.sk_set.restype = c_void_p
625 libcrypto.sk_value.argtypes = (c_void_p, c_int)
626 libcrypto.sk_value.restype = c_void_p
627 libcrypto.X509_dup.restype = c_void_p
628 libcrypto.sk_new_null.restype = c_void_p
629 libcrypto.X509_dup.argtypes = (c_void_p, )
630 libcrypto.X509_NAME_hash.restype = c_long
631 libcrypto.X509_NAME_hash.argtypes = (c_void_p, )