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
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_get_notBefore
49 _X509_get_notBefore.restype = c_void_p
50 _X509_get_notBefore.argtypes = (c_void_p,)
52 _X509_get_notAfter=libcrypto.X509_get_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 class X509Error(LibCryptoError):
106 Exception, generated when some openssl function fail
107 during X509 operation
112 class X509Name(object):
114 Class which represents X.509 distinguished name - typically
115 a certificate subject name or an issuer name.
117 Now used only to represent information, extracted from the
118 certificate. Potentially can be also used to build DN when creating
119 certificate signing request
121 # XN_FLAG_SEP_COMMA_PLUS & ASN1_STRFLG_UTF8_CONVERT
124 def __init__(self, ptr=None, copy=False):
126 Creates a X509Name object
127 @param ptr - pointer to X509_NAME C structure (as returned by some
129 @param copy - indicates that this structure have to be freed upon
134 self.need_free = copy
135 self.writable = False
137 self.ptr = libcrypto.X509_NAME_new()
138 self.need_free = True
146 libcrypto.X509_NAME_free(self.ptr)
149 Produces an ascii representation of the name, escaping all
150 symbols > 0x80. Probably it is not what you want, unless
151 your native language is English
154 libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0,
155 self.PRINT_FLAG | self.ESC_MSB)
158 def __unicode__(self):
160 Produces unicode representation of the name.
163 libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0, self.PRINT_FLAG)
167 return number of components in the name
169 return libcrypto.X509_NAME_entry_count(self.ptr)
170 def __cmp__(self, other):
174 return libcrypto.X509_NAME_cmp(self.ptr, other.ptr)
175 def __eq__(self, other):
176 return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) == 0
178 def __getitem__(self, key):
179 if isinstance(key, Oid):
180 # Return first matching field
181 idx = libcrypto.X509_NAME_get_index_by_NID(self.ptr, key.nid, -1)
183 raise KeyError("Key not found " + str(Oid))
184 entry = libcrypto.X509_NAME_get_entry(self.ptr, idx)
185 value = libcrypto.X509_NAME_ENTRY_get_data(entry)
187 libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG)
189 elif isinstance(key, (int, long)):
190 # Return OID, string tuple
191 entry = libcrypto.X509_NAME_get_entry(self.ptr, key)
193 raise IndexError("name entry index out of range")
194 oid = Oid.fromobj(libcrypto.X509_NAME_ENTRY_get_object(entry))
195 value = libcrypto.X509_NAME_ENTRY_get_data(entry)
197 libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG)
198 return (oid, unicode(bio))
200 raise TypeError("X509 NAME can be indexed by Oids or integers only")
202 def __setitem__(self, key, val):
203 if not self.writable:
204 raise ValueError("Attempt to modify constant X509 object")
206 raise NotImplementedError
207 def __delitem__(self, key):
208 if not self.writable:
209 raise ValueError("Attempt to modify constant X509 object")
211 raise NotImplementedError
213 return libcrypto.X509_NAME_hash(self.ptr)
215 class _x509_ext(Structure):
216 """ Represens C structure X509_EXTENSION """
217 _fields_ = [("object", c_void_p),
222 class X509_EXT(object):
223 """ Python object which represents a certificate extension """
224 def __init__(self, ptr, copy=False):
225 """ Initializes from the pointer to X509_EXTENSION.
226 If copy is True, creates a copy, otherwise just
230 self.ptr = libcrypto.X509_EXTENSION_dup(ptr)
232 self.ptr = cast(ptr, POINTER(_x509_ext))
234 libcrypto.X509_EXTENSION_free(self.ptr)
237 libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0)
239 def __unicode__(self):
241 libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0)
245 "Returns OID of the extension"
246 return Oid.fromobj(self.ptr[0].object)
249 "Returns True if extensin have critical flag set"
250 return self.ptr[0].critical > 0
252 class _X509extlist(object):
254 Represents list of certificate extensions. Really it keeps
255 reference to certificate object
257 def __init__(self, cert):
259 Initialize from X509 object
265 Returns number of extensions
267 return libcrypto.X509_get_ext_count(self.cert.cert)
269 def __getitem__(self, item):
271 Returns extension by index, creating a copy
273 ext_ptr = libcrypto.X509_get_ext(self.cert.cert, item)
276 return X509_EXT(ext_ptr, True)
279 Return list of extensions with given Oid
281 if not isinstance(oid, Oid):
282 raise TypeError("Need crytypescrypto.oid.Oid as argument")
287 index = libcrypto.X509_get_ext_by_NID(self.cert.cert, oid.nid,
289 if index >= end or index < 0:
291 found.append(self[index])
294 def find_critical(self, crit=True):
296 Return list of critical extensions (or list of non-cricital, if
297 optional second argument is False
307 index = libcrypto.X509_get_ext_by_critical(self.cert.cert, flag,
309 if index >= end or index < 0:
311 found.append(self[index])
314 def _X509__asn1date_to_datetime(asn1date):
316 Converts openssl ASN1_TIME object to python datetime.datetime
319 libcrypto.ASN1_TIME_print(bio.bio, asn1date)
320 pydate = datetime.strptime(str(bio), "%b %d %H:%M:%S %Y %Z")
321 return pydate.replace(tzinfo=utc)
325 Represents X.509 certificate.
327 def __init__(self, data=None, ptr=None, format="PEM"):
329 Initializes certificate
330 @param data - serialized certificate in PEM or DER format.
331 @param ptr - pointer to X509, returned by some openssl function.
332 mutually exclusive with data
333 @param format - specifies data format. "PEM" or "DER", default PEM
337 raise TypeError("Cannot use data and ptr simultaneously")
340 raise TypeError("data argument is required")
344 self.cert = libcrypto.PEM_read_bio_X509(bio.bio, None, None,
347 self.cert = libcrypto.d2i_X509_bio(bio.bio, None)
348 if self.cert is None:
349 raise X509Error("error reading certificate")
350 self.extensions = _X509extlist(self)
353 Frees certificate object
355 libcrypto.X509_free(self.cert)
357 """ Returns der string of the certificate """
359 if libcrypto.i2d_X509_bio(bio.bio, self.cert) == 0:
360 raise X509Error("error serializing certificate")
363 """ Returns valid call to the constructor """
364 return "X509(data=" + repr(str(self)) + ",format='DER')"
367 """EVP PKEy object of certificate public key"""
368 return PKey(ptr=libcrypto.X509_get_pubkey(self.cert, False))
370 """ Returns PEM represntation of the certificate """
372 if libcrypto.PEM_write_bio_X509(bio.bio, self.cert) == 0:
373 raise X509Error("error serializing certificate")
375 def verify(self, store=None, chain=None, key=None):
377 Verify self. Supports verification on both X509 store object
378 or just public issuer key
379 @param store X509Store object.
380 @param chain - list of X509 objects to add into verification
381 context.These objects are untrusted, but can be used to
382 build certificate chain up to trusted object in the store
383 @param key - PKey object with open key to validate signature
385 parameters store and key are mutually exclusive. If neither
386 is specified, attempts to verify self as self-signed certificate
388 if store is not None and key is not None:
389 raise X509Error("key and store cannot be specified simultaneously")
390 if store is not None:
391 ctx = libcrypto.X509_STORE_CTX_new()
393 raise X509Error("Error allocating X509_STORE_CTX")
394 if chain is not None and len(chain) > 0:
395 chain_ptr = StackOfX509(chain).ptr
398 if libcrypto.X509_STORE_CTX_init(ctx, store.store, self.cert,
400 raise X509Error("Error allocating X509_STORE_CTX")
401 res = libcrypto.X509_verify_cert(ctx)
402 libcrypto.X509_STORE_CTX_free(ctx)
406 if self.issuer != self.subject:
407 # Not a self-signed certificate
410 res = libcrypto.X509_verify(self.cert, key.key)
412 raise X509Error("X509_verify failed")
417 """ X509Name for certificate subject name """
418 return X509Name(libcrypto.X509_get_subject_name(self.cert))
421 """ X509Name for certificate issuer name """
422 return X509Name(libcrypto.X509_get_issuer_name(self.cert))
425 """ Serial number of certificate as integer """
426 asnint = libcrypto.X509_get_serialNumber(self.cert)
428 libcrypto.i2a_ASN1_INTEGER(bio.bio, asnint)
429 return int(str(bio), 16)
433 certificate version as integer. Really certificate stores 0 for
434 version 1 and 2 for version 3, but we return 1 and 3
436 return _X509_get_version(self.cert) + 1
439 """ Certificate validity period start date """
440 asn1 = _X509_get_notBefore(self.cert)
441 return __asn1date_to_datetime(asn1)
444 """ Certificate validity period end date """
445 asn1 = _X509_get_notAfter(self.cert)
446 return __asn1date_to_datetime(asn1)
448 """ Returns True if certificate is CA certificate """
449 return libcrypto.X509_check_ca(self.cert) > 0
451 class X509Store(object):
453 Represents trusted certificate store. Can be used to lookup CA
454 certificates to verify
456 @param file - file with several certificates and crls
458 @param dir - hashed directory with certificates and crls
459 @param default - if true, default verify location (directory)
463 def __init__(self, file=None, dir=None, default=False):
465 Creates X509 store and installs lookup method. Optionally initializes
466 by certificates from given file or directory.
469 # Todo - set verification flags
471 self.store = libcrypto.X509_STORE_new()
472 if self.store is None:
473 raise X509Error("allocating store")
474 lookup = libcrypto.X509_STORE_add_lookup(self.store,
475 libcrypto.X509_LOOKUP_file())
477 raise X509Error("error installing file lookup method")
479 if not libcrypto.X509_LOOKUP_ctrl(lookup, 1, file, 1, None) > 0:
480 raise X509Error("error loading trusted certs from file "+file)
481 lookup = libcrypto.X509_STORE_add_lookup(self.store,
482 libcrypto.X509_LOOKUP_hash_dir())
484 raise X509Error("error installing hashed lookup method")
486 if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, dir, 1, None) > 0:
487 raise X509Error("error adding hashed trusted certs dir "+dir)
489 if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, None, 3, None) > 0:
490 raise X509Error("error adding default trusted certs dir ")
491 def add_cert(self, cert):
493 Explicitely adds certificate to set of trusted in the store
494 @param cert - X509 object to add
496 if not isinstance(cert, X509):
497 raise TypeError("cert should be X509")
498 libcrypto.X509_STORE_add_cert(self.store, cert.cert)
499 def add_callback(self, callback):
501 Installs callback function, which would receive detailed information
502 about verified ceritificates
504 raise NotImplementedError
505 def setflags(self, flags):
507 Set certificate verification flags.
508 @param flags - integer bit mask. See OpenSSL X509_V_FLAG_* constants
510 libcrypto.X509_STORE_set_flags(self.store, flags)
511 def setpurpose(self, purpose):
513 Sets certificate purpose which verified certificate should match
514 @param purpose - number from 1 to 9 or standard strind defined
516 possible strings - sslcient,sslserver, nssslserver, smimesign,i
517 smimeencrypt, crlsign, any, ocsphelper
519 if isinstance(purpose, str):
520 purp_no = libcrypto.X509_PURPOSE_get_by_sname(purpose)
522 raise X509Error("Invalid certificate purpose '%s'" % purpose)
523 elif isinstance(purpose, int):
525 if libcrypto.X509_STORE_set_purpose(self.store, purp_no) <= 0:
526 raise X509Error("cannot set purpose")
527 def setdepth(self, depth):
529 Sets the verification depth i.e. max length of certificate chain
532 libcrypto.X509_STORE_set_depth(self.store, depth)
533 def settime(self, time):
535 Set point in time used to check validity of certificates for
536 Time can be either python datetime object or number of seconds
539 if isinstance(time, datetime) or isinstance(time,
541 seconds = int(time.strftime("%s"))
542 elif isinstance(time, int):
545 raise TypeError("datetime.date, datetime.datetime or integer " +
546 "is required as time argument")
547 raise NotImplementedError
548 class StackOfX509(object):
550 Implements OpenSSL STACK_OF(X509) object.
551 It looks much like python container types
553 def __init__(self, certs=None, ptr=None, disposable=True):
556 @param certs - list of X509 objects. If specified, read-write
557 stack is created and populated by these certificates
558 @param ptr - pointer to OpenSSL STACK_OF(X509) as returned by
560 @param disposable - if True, stack created from object, returned
561 by function is copy, and can be modified and need to be
562 freeid. If false, it is just pointer into another
563 structure i.e. CMS_ContentInfo
565 self.need_free = False
567 self.need_free = True
568 self.ptr = libcrypto.sk_new_null()
569 if certs is not None:
572 elif certs is not None:
573 raise ValueError("cannot handle certs an ptr simultaneously")
575 self.need_free = disposable
578 return libcrypto.sk_num(self.ptr)
579 def __getitem__(self, index):
580 if index < 0 or index >= len(self):
582 p = libcrypto.sk_value(self.ptr, index)
583 return X509(ptr=libcrypto.X509_dup(p))
584 def __setitem__(self, index, value):
585 if not self.need_free:
586 raise ValueError("Stack is read-only")
587 if index < 0 or index >= len(self):
589 if not isinstance(value, X509):
590 raise TypeError('StackOfX509 can contain only X509 objects')
591 p = libcrypto.sk_value(self.ptr, index)
592 libcrypto.sk_set(self.ptr, index, libcrypto.X509_dup(value.cert))
593 libcrypto.X509_free(p)
594 def __delitem__(self, index):
595 if not self.need_free:
596 raise ValueError("Stack is read-only")
597 if index < 0 or index >= len(self):
599 p = libcrypto.sk_delete(self.ptr, index)
600 libcrypto.X509_free(p)
603 libcrypto.sk_pop_free(self.ptr, libcrypto.X509_free)
604 def append(self, value):
605 """ Adds certificate to stack """
606 if not self.need_free:
607 raise ValueError("Stack is read-only")
608 if not isinstance(value, X509):
609 raise TypeError('StackOfX509 can contain only X509 objects')
610 libcrypto.sk_push(self.ptr, libcrypto.X509_dup(value.cert))
612 libcrypto.d2i_X509_bio.argtypes = (c_void_p,POINTER(c_void_p))
613 libcrypto.X509_free.argtypes = (c_void_p,)
614 libcrypto.X509_dup.restype = c_void_p
615 libcrypto.X509_dup.argtypes = (c_void_p, )
616 libcrypto.i2a_ASN1_INTEGER.argtypes = (c_void_p, c_void_p)
617 libcrypto.ASN1_STRING_print_ex.argtypes = (c_void_p, c_void_p, c_long)
618 libcrypto.PEM_read_bio_X509.restype = c_void_p
619 libcrypto.PEM_read_bio_X509.argtypes = (c_void_p, POINTER(c_void_p),
621 libcrypto.PEM_write_bio_X509.restype = c_int
622 libcrypto.PEM_write_bio_X509.argtypes = (c_void_p, c_void_p)
623 libcrypto.ASN1_TIME_print.argtypes = (c_void_p, c_void_p)
624 libcrypto.ASN1_INTEGER_get.argtypes = (c_void_p, )
625 libcrypto.ASN1_INTEGER_get.restype = c_long
626 libcrypto.X509_check_ca.argtypes = (c_void_p, )
627 libcrypto.X509_get_serialNumber.argtypes = (c_void_p, )
628 libcrypto.X509_get_serialNumber.restype = c_void_p
629 libcrypto.X509_get_subject_name.argtypes = (c_void_p, )
630 libcrypto.X509_get_subject_name.restype = c_void_p
631 libcrypto.X509_get_issuer_name.argtypes = (c_void_p, )
632 libcrypto.X509_get_issuer_name.restype = c_void_p
633 libcrypto.X509_NAME_ENTRY_get_object.restype = c_void_p
634 libcrypto.X509_NAME_ENTRY_get_object.argtypes = (c_void_p, )
635 libcrypto.X509_NAME_ENTRY_get_data.restype = c_void_p
636 libcrypto.X509_NAME_ENTRY_get_data.argtypes = (c_void_p, )
637 libcrypto.OBJ_obj2nid.argtypes = (c_void_p, )
638 libcrypto.X509_NAME_get_entry.restype = c_void_p
639 libcrypto.X509_NAME_get_entry.argtypes = (c_void_p, c_int)
640 libcrypto.X509_STORE_new.restype = c_void_p
641 libcrypto.X509_STORE_add_lookup.restype = c_void_p
642 libcrypto.X509_STORE_add_lookup.argtypes = (c_void_p, c_void_p)
643 libcrypto.X509_STORE_add_cert.argtypes = (c_void_p, c_void_p)
644 libcrypto.X509_STORE_CTX_new.restype = c_void_p
645 libcrypto.X509_STORE_CTX_free.argtypes = (c_void_p,)
646 libcrypto.X509_STORE_CTX_init.argtypes = (c_void_p, c_void_p, c_void_p,
648 libcrypto.X509_STORE_set_depth.argtypes = (c_void_p, c_int)
649 libcrypto.X509_STORE_set_flags.argtypes = (c_void_p, c_ulong)
650 libcrypto.X509_STORE_set_purpose.argtypes = (c_void_p, c_int)
651 libcrypto.X509_LOOKUP_file.restype = c_void_p
652 libcrypto.X509_LOOKUP_hash_dir.restype = c_void_p
653 libcrypto.X509_LOOKUP_ctrl.restype = c_int
654 libcrypto.X509_LOOKUP_ctrl.argtypes = (c_void_p, c_int, c_char_p, c_long,
656 libcrypto.X509_EXTENSION_free.argtypes = (c_void_p, )
657 libcrypto.X509_EXTENSION_dup.argtypes = (c_void_p, )
658 libcrypto.X509_EXTENSION_dup.restype = POINTER(_x509_ext)
659 libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long,
661 libcrypto.X509_get_ext.restype = c_void_p
662 libcrypto.X509_get_ext.argtypes = (c_void_p, c_int)
663 libcrypto.X509_get_ext_by_critical.argtypes = (c_void_p, c_int, c_int)
664 libcrypto.X509_get_ext_by_NID.argtypes = (c_void_p, c_int, c_int)
665 libcrypto.X509_get_ext_count.argtypes = (c_void_p, )
666 libcrypto.X509_get_pubkey.restype = c_void_p
667 libcrypto.X509_get_pubkey.argtypes = (c_void_p, )
668 libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long,
670 libcrypto.X509_LOOKUP_file.restype = c_void_p
671 libcrypto.X509_LOOKUP_hash_dir.restype = c_void_p
672 libcrypto.X509_NAME_cmp.argtypes = (c_void_p, c_void_p)
673 libcrypto.X509_NAME_entry_count.argtypes = (c_void_p,)
674 libcrypto.X509_NAME_free.argtypes = (c_void_p,)
675 libcrypto.X509_NAME_new.restype = c_void_p
676 libcrypto.X509_NAME_print_ex.argtypes = (c_void_p, c_void_p, c_int, c_ulong)
677 libcrypto.X509_PURPOSE_get_by_sname.argtypes=(c_char_p,)
678 libcrypto.X509_verify.argtypes = (c_void_p, c_void_p)
679 libcrypto.X509_verify_cert.argtypes = (c_void_p,)
680 libcrypto.sk_num.restupe = c_int
681 libcrypto.sk_num.argtypes= (c_void_p,)
682 libcrypto.sk_set.argtypes = (c_void_p, c_int, c_void_p)
683 libcrypto.sk_set.restype = c_void_p
684 libcrypto.sk_value.argtypes = (c_void_p, c_int)
685 libcrypto.sk_value.restype = c_void_p
686 libcrypto.sk_delete.argtypes = (c_void_p, c_int)
687 libcrypto.sk_delete.restype = c_void_p
688 libcrypto.sk_new_null.restype = c_void_p
689 libcrypto.sk_pop_free.argtypes = (c_void_p, c_void_p)
690 libcrypto.sk_push.argtypes = (c_void_p, c_void_p)
691 libcrypto.X509_NAME_hash.restype = c_long
692 libcrypto.X509_NAME_hash.argtypes = (c_void_p, )
693 libcrypto.X509_NAME_get_index_by_NID.argtypes = (c_void_p, c_int, c_int)