X-Git-Url: https://wagner.pp.ru/gitweb/?a=blobdiff_plain;f=ctypescrypto%2Fx509.py;h=ed47cf5e1ba6989a2f93fc9009f22069936d920e;hb=1c45d3f211f72ce19c9e92be868c15afe5e6ec62;hp=159956d72f457dc3640a95db75007092f8408bb0;hpb=954b6dc9e3312f8d8b49f20f8466e6d2a8342f35;p=oss%2Fctypescrypto.git diff --git a/ctypescrypto/x509.py b/ctypescrypto/x509.py index 159956d..ed47cf5 100644 --- a/ctypescrypto/x509.py +++ b/ctypescrypto/x509.py @@ -1,5 +1,5 @@ """ -Implements interface to openssl X509 and X509Store structures, +Implements interface to openssl X509 and X509Store structures, I.e allows to load, analyze and verify certificates. X509Store objects are also used to verify other signed documets, @@ -8,23 +8,23 @@ such as CMS, OCSP and timestamps. -from ctypes import c_void_p,create_string_buffer,c_long,c_int,POINTER,c_char_p,Structure,cast +from ctypes import c_void_p, c_long, c_ulong, c_int, POINTER, c_char_p, Structure, cast from ctypescrypto.bio import Membio from ctypescrypto.pkey import PKey from ctypescrypto.oid import Oid from ctypescrypto.exception import LibCryptoError from ctypescrypto import libcrypto from datetime import datetime + try: from pytz import utc except ImportError: - from datetime import timedelta,tzinfo - ZERO=timedelta(0) + from datetime import timedelta, tzinfo + ZERO = timedelta(0) class UTC(tzinfo): - """tzinfo object for UTC. + """tzinfo object for UTC. If no pytz is available, we would use it. """ - def utcoffset(self, dt): return ZERO @@ -34,46 +34,72 @@ except ImportError: def dst(self, dt): return ZERO - utc=UTC() + utc = UTC() -__all__ = ['X509','X509Error','X509Name','X509Store','StackOfX509'] +__all__ = ['X509', 'X509Error', 'X509Name', 'X509Store', 'StackOfX509'] -class _validity(Structure): - """ ctypes representation of X509_VAL structure - needed to access certificate validity period, because openssl - doesn't provide fuctions for it - only macros - """ - _fields_ = [('notBefore',c_void_p),('notAfter',c_void_p)] +if hasattr(libcrypto,"X509_get_version"): + +# If it is OpenSSL 1.1 or above, use accessor functions + _X509_get_version = libcrypto.X509_get_version + _X509_get_version.restype = c_long + _X509_get_version.argtypes = (c_void_p,) -class _cinf(Structure): - """ ctypes representtion of X509_CINF structure - neede to access certificate data, which are accessable only - via macros - """ - _fields_ = [('version',c_void_p), - ('serialNumber',c_void_p), - ('sign_alg',c_void_p), - ('issuer',c_void_p), - ('validity',POINTER(_validity)), - ('subject',c_void_p), - ('pubkey',c_void_p), - ('issuerUID',c_void_p), - ('subjectUID',c_void_p), - ('extensions',c_void_p), - ] - -class _x509(Structure): - """ - ctypes represntation of X509 structure needed - to access certificate data which are accesable only via - macros, not functions - """ - _fields_ = [('cert_info',POINTER(_cinf)), - ('sig_alg',c_void_p), - ('signature',c_void_p), - # There are a lot of parsed extension fields there + _X509_get_notBefore=libcrypto.X509_get_notBefore + _X509_get_notBefore.restype = c_void_p + _X509_get_notBefore.argtypes = (c_void_p,) + + _X509_get_notAfter=libcrypto.X509_get_notAfter + _X509_get_notAfter.restype = c_void_p + _X509_get_notAfter.argtypes = (c_void_p,) +else: + # Otherwise declare X509 structure internals and define deep poke + # functions + class _validity(Structure): + """ ctypes representation of X509_VAL structure + needed to access certificate validity period, because openssl + doesn't provide fuctions for it - only macros + """ + _fields_ = [('notBefore', c_void_p), ('notAfter', c_void_p)] + + class _cinf(Structure): + """ ctypes representtion of X509_CINF structure + neede to access certificate data, which are accessable only + via macros + """ + _fields_ = [('version', c_void_p), + ('serialNumber', c_void_p), + ('sign_alg', c_void_p), + ('issuer', c_void_p), + ('validity', POINTER(_validity)), + ('subject', c_void_p), + ('pubkey', c_void_p), + ('issuerUID', c_void_p), + ('subjectUID', c_void_p), + ('extensions', c_void_p), ] -_px509 = POINTER(_x509) + + class _x509(Structure): + """ + ctypes represntation of X509 structure needed + to access certificate data which are accesable only via + macros, not functions + """ + _fields_ = [('cert_info', POINTER(_cinf)), + ('sig_alg', c_void_p), + ('signature', c_void_p), + # There are a lot of parsed extension fields there + ] + _px509 = POINTER(_x509) + def _X509_get_version(ptr): + asn1int = cast(ptr, _px509)[0].cert_info[0].version + return libcrypto.ASN1_INTEGER_get(asn1int) + + def _X509_get_notBefore(ptr): + # (x)->cert_info->validity->notBefore + return cast(ptr, _px509)[0].cert_info[0].validity[0].notBefore + def _X509_get_notAfter(ptr): + return cast(ptr, _px509)[0].cert_info[0].validity[0].notAfter class X509Error(LibCryptoError): """ @@ -85,7 +111,7 @@ class X509Error(LibCryptoError): class X509Name(object): """ - Class which represents X.509 distinguished name - typically + Class which represents X.509 distinguished name - typically a certificate subject name or an issuer name. Now used only to represent information, extracted from the @@ -93,22 +119,25 @@ class X509Name(object): certificate signing request """ # XN_FLAG_SEP_COMMA_PLUS & ASN1_STRFLG_UTF8_CONVERT - PRINT_FLAG=0x10010 - ESC_MSB=4 - def __init__(self,ptr=None,copy=False): + PRINT_FLAG = 0x10010 + ESC_MSB = 4 + def __init__(self, ptr=None, copy=False): """ Creates a X509Name object - @param ptr - pointer to X509_NAME C structure (as returned by some OpenSSL functions - @param copy - indicates that this structure have to be freed upon object destruction + @param ptr - pointer to X509_NAME C structure (as returned by some + OpenSSL functions + @param copy - indicates that this structure have to be freed upon + object destruction """ if ptr is not None: - self.ptr=ptr - self.need_free=copy - self.writable=False + self.ptr = ptr + self.need_free = copy + self.writable = False else: - self.ptr=libcrypto.X509_NAME_new() - self.need_free=True - self.writable=True + self.ptr = libcrypto.X509_NAME_new() + self.need_free = True + self.writable = True + def __del__(self): """ Frees if neccessary @@ -117,62 +146,65 @@ class X509Name(object): libcrypto.X509_NAME_free(self.ptr) def __str__(self): """ - Produces an ascii representation of the name, escaping all symbols > 0x80 - Probably it is not what you want, unless your native language is English + Produces an ascii representation of the name, escaping all + symbols > 0x80. Probably it is not what you want, unless + your native language is English """ - b=Membio() - libcrypto.X509_NAME_print_ex(b.bio,self.ptr,0,self.PRINT_FLAG | self.ESC_MSB) - return str(b) + bio = Membio() + libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0, + self.PRINT_FLAG | self.ESC_MSB) + return str(bio) + def __unicode__(self): """ - Produces unicode representation of the name. + Produces unicode representation of the name. """ - b=Membio() - libcrypto.X509_NAME_print_ex(b.bio,self.ptr,0,self.PRINT_FLAG) - return unicode(b) + bio = Membio() + libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0, self.PRINT_FLAG) + return unicode(bio) def __len__(self): """ return number of components in the name """ return libcrypto.X509_NAME_entry_count(self.ptr) - def __cmp__(self,other): + def __cmp__(self, other): """ Compares X509 names """ - return libcrypto.X509_NAME_cmp(self.ptr,other.ptr) - def __eq__(self,other): - return libcrypto.X509_NAME_cmp(self.ptr,other.ptr)==0 + return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) + def __eq__(self, other): + return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) == 0 - def __getitem__(self,key): - if isinstance(key,Oid): + def __getitem__(self, key): + if isinstance(key, Oid): # Return first matching field - idx=libcrypto.X509_NAME_get_index_by_NID(self.ptr,key.nid,-1) - if idx<0: - raise KeyError("Key not found "+str(Oid)) - entry=libcrypto.X509_NAME_get_entry(self.ptr,idx) - s=libcrypto.X509_NAME_ENTRY_get_data(entry) - b=Membio() - libcrypto.ASN1_STRING_print_ex(b.bio,s,self.PRINT_FLAG) - return unicode(b) - elif isinstance(key,(int,long)): + idx = libcrypto.X509_NAME_get_index_by_NID(self.ptr, key.nid, -1) + if idx < 0: + raise KeyError("Key not found " + str(Oid)) + entry = libcrypto.X509_NAME_get_entry(self.ptr, idx) + value = libcrypto.X509_NAME_ENTRY_get_data(entry) + bio = Membio() + libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG) + return unicode(bio) + elif isinstance(key, (int, long)): # Return OID, string tuple - entry=libcrypto.X509_NAME_get_entry(self.ptr,key) + entry = libcrypto.X509_NAME_get_entry(self.ptr, key) if entry is None: raise IndexError("name entry index out of range") - oid=Oid.fromobj(libcrypto.X509_NAME_ENTRY_get_object(entry)) - s=libcrypto.X509_NAME_ENTRY_get_data(entry) - b=Membio() - libcrypto.ASN1_STRING_print_ex(b.bio,s,self.PRINT_FLAG) - return (oid,unicode(b)) + oid = Oid.fromobj(libcrypto.X509_NAME_ENTRY_get_object(entry)) + value = libcrypto.X509_NAME_ENTRY_get_data(entry) + bio = Membio() + libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG) + return (oid, unicode(bio)) else: raise TypeError("X509 NAME can be indexed by Oids or integers only") - def __setitem__(self,key,val): + def __setitem__(self, key, val): if not self.writable: raise ValueError("Attempt to modify constant X509 object") else: raise NotImplementedError - def __delitem__(self,key): + def __delitem__(self, key): if not self.writable: raise ValueError("Attempt to modify constant X509 object") else: @@ -182,111 +214,140 @@ class X509Name(object): class _x509_ext(Structure): """ Represens C structure X509_EXTENSION """ - _fields_=[("object",c_void_p), - ("critical",c_int), - ("value",c_void_p)] + _fields_ = [("object", c_void_p), + ("critical", c_int), + ("value", c_void_p) + ] class X509_EXT(object): """ Python object which represents a certificate extension """ - def __init__(self,ptr,copy=False): + def __init__(self, ptr, copy=False): """ Initializes from the pointer to X509_EXTENSION. If copy is True, creates a copy, otherwise just stores pointer. """ if copy: - self.ptr=libcrypto.X509_EXTENSION_dup(ptr) + self.ptr = libcrypto.X509_EXTENSION_dup(ptr) else: - self.ptr=cast(ptr,POINTER(_x509_ext)) + self.ptr = cast(ptr, POINTER(_x509_ext)) def __del__(self): libcrypto.X509_EXTENSION_free(self.ptr) def __str__(self): - b=Membio() - libcrypto.X509V3_EXT_print(b.bio,self.ptr,0x20010,0) - return str(b) + bio = Membio() + libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0) + return str(bio) def __unicode__(self): - b=Membio() - libcrypto.X509V3_EXT_print(b.bio,self.ptr,0x20010,0) - return unicode(b) + bio = Membio() + libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0) + return unicode(bio) @property def oid(self): + "Returns OID of the extension" return Oid.fromobj(self.ptr[0].object) @property - def critical(self): - return self.ptr[0].critical >0 -class _X509extlist(object): + def critical(self): + "Returns True if extensin have critical flag set" + return self.ptr[0].critical > 0 + +class _X509extlist(object): """ - Represents list of certificate extensions + Represents list of certificate extensions. Really it keeps + reference to certificate object """ - def __init__(self,cert): - self.cert=cert + def __init__(self, cert): + """ + Initialize from X509 object + """ + self.cert = cert + def __len__(self): + """ + Returns number of extensions + """ return libcrypto.X509_get_ext_count(self.cert.cert) - def __getitem__(self,item): - p=libcrypto.X509_get_ext(self.cert.cert,item) - if p is None: + + def __getitem__(self, item): + """ + Returns extension by index, creating a copy + """ + ext_ptr = libcrypto.X509_get_ext(self.cert.cert, item) + if ext_ptr is None: raise IndexError - return X509_EXT(p,True) - def find(self,oid): + return X509_EXT(ext_ptr, True) + def find(self, oid): """ Return list of extensions with given Oid """ - if not isinstance(oid,Oid): + if not isinstance(oid, Oid): raise TypeError("Need crytypescrypto.oid.Oid as argument") - found=[] - l=-1 - end=len(self) + found = [] + index = -1 + end = len(self) while True: - l=libcrypto.X509_get_ext_by_NID(self.cert.cert,oid.nid,l) - if l>=end or l<0: - break - found.append(self[l]) + index = libcrypto.X509_get_ext_by_NID(self.cert.cert, oid.nid, + index) + if index >= end or index < 0: + break + found.append(self[index]) return found - def find_critical(self,crit=True): + + def find_critical(self, crit=True): """ Return list of critical extensions (or list of non-cricital, if optional second argument is False """ if crit: - flag=1 + flag = 1 else: - flag=0 - found=[] - end=len(self) - l=-1 + flag = 0 + found = [] + end = len(self) + index = -1 while True: - l=libcrypto.X509_get_ext_by_critical(self.cert.cert,flag,l) - if l>=end or l<0: - break - found.append(self[l]) - return found + index = libcrypto.X509_get_ext_by_critical(self.cert.cert, flag, + index) + if index >= end or index < 0: + break + found.append(self[index]) + return found + +def _X509__asn1date_to_datetime(asn1date): + """ + Converts openssl ASN1_TIME object to python datetime.datetime + """ + bio = Membio() + libcrypto.ASN1_TIME_print(bio.bio, asn1date) + pydate = datetime.strptime(str(bio), "%b %d %H:%M:%S %Y %Z") + return pydate.replace(tzinfo=utc) class X509(object): """ - Represents X.509 certificate. + Represents X.509 certificate. """ - def __init__(self,data=None,ptr=None,format="PEM"): + def __init__(self, data=None, ptr=None, format="PEM"): """ Initializes certificate @param data - serialized certificate in PEM or DER format. - @param ptr - pointer to X509, returned by some openssl function. + @param ptr - pointer to X509, returned by some openssl function. mutually exclusive with data @param format - specifies data format. "PEM" or "DER", default PEM """ if ptr is not None: - if data is not None: + if data is not None: raise TypeError("Cannot use data and ptr simultaneously") self.cert = ptr elif data is None: raise TypeError("data argument is required") else: - b=Membio(data) + bio = Membio(data) if format == "PEM": - self.cert=libcrypto.PEM_read_bio_X509(b.bio,None,None,None) + self.cert = libcrypto.PEM_read_bio_X509(bio.bio, None, None, + None) else: - self.cert=libcrypto.d2i_X509_bio(b.bio,None) + self.cert = libcrypto.d2i_X509_bio(bio.bio, None) if self.cert is None: raise X509Error("error reading certificate") - self.extensions=_X509extlist(self) + self.extensions = _X509extlist(self) def __del__(self): """ Frees certificate object @@ -294,62 +355,63 @@ class X509(object): libcrypto.X509_free(self.cert) def __str__(self): """ Returns der string of the certificate """ - b=Membio() - if libcrypto.i2d_X509_bio(b.bio,self.cert)==0: + bio = Membio() + if libcrypto.i2d_X509_bio(bio.bio, self.cert) == 0: raise X509Error("error serializing certificate") - return str(b) + return str(bio) def __repr__(self): """ Returns valid call to the constructor """ - return "X509(data="+repr(str(self))+",format='DER')" + return "X509(data=" + repr(str(self)) + ",format='DER')" @property def pubkey(self): """EVP PKEy object of certificate public key""" - return PKey(ptr=libcrypto.X509_get_pubkey(self.cert,False)) + return PKey(ptr=libcrypto.X509_get_pubkey(self.cert, False)) def pem(self): """ Returns PEM represntation of the certificate """ - b=Membio() - if libcrypto.PEM_write_bio_X509(b.bio,self.cert)==0: + bio = Membio() + if libcrypto.PEM_write_bio_X509(bio.bio, self.cert) == 0: raise X509Error("error serializing certificate") - return str(b) - def verify(self,store=None,chain=[],key=None): - """ - Verify self. Supports verification on both X509 store object + return str(bio) + def verify(self, store=None, chain=None, key=None): + """ + Verify self. Supports verification on both X509 store object or just public issuer key @param store X509Store object. @param chain - list of X509 objects to add into verification context.These objects are untrusted, but can be used to build certificate chain up to trusted object in the store @param key - PKey object with open key to validate signature - - parameters store and key are mutually exclusive. If neither + + parameters store and key are mutually exclusive. If neither is specified, attempts to verify self as self-signed certificate """ if store is not None and key is not None: raise X509Error("key and store cannot be specified simultaneously") if store is not None: - ctx=libcrypto.X509_STORE_CTX_new() + ctx = libcrypto.X509_STORE_CTX_new() if ctx is None: raise X509Error("Error allocating X509_STORE_CTX") - if chain is not None and len(chain)>0: - ch=StackOfX509(chain) + if chain is not None and len(chain) > 0: + chain_ptr = StackOfX509(chain).ptr else: - ch=None - if libcrypto.X509_STORE_CTX_init(ctx,store.store,self.cert,ch) < 0: + chain_ptr = None + if libcrypto.X509_STORE_CTX_init(ctx, store.store, self.cert, + chain_ptr) < 0: raise X509Error("Error allocating X509_STORE_CTX") - res= libcrypto.X509_verify_cert(ctx) + res = libcrypto.X509_verify_cert(ctx) libcrypto.X509_STORE_CTX_free(ctx) - return res>0 + return res > 0 else: if key is None: if self.issuer != self.subject: # Not a self-signed certificate return False key = self.pubkey - res = libcrypto.X509_verify(self.cert,key.key) + res = libcrypto.X509_verify(self.cert, key.key) if res < 0: raise X509Error("X509_verify failed") - return res>0 - + return res > 0 + @property def subject(self): """ X509Name for certificate subject name """ @@ -361,134 +423,134 @@ class X509(object): @property def serial(self): """ Serial number of certificate as integer """ - asnint=libcrypto.X509_get_serialNumber(self.cert) - b=Membio() - libcrypto.i2a_ASN1_INTEGER(b.bio,asnint) - return int(str(b),16) - @property + asnint = libcrypto.X509_get_serialNumber(self.cert) + bio = Membio() + libcrypto.i2a_ASN1_INTEGER(bio.bio, asnint) + return int(str(bio), 16) + @property def version(self): - """ certificate version as integer. Really certificate stores 0 for - version 1 and 2 for version 3, but we return 1 and 3 """ - asn1int=cast(self.cert,_px509)[0].cert_info[0].version - return libcrypto.ASN1_INTEGER_get(asn1int)+1 + """ + certificate version as integer. Really certificate stores 0 for + version 1 and 2 for version 3, but we return 1 and 3 + """ + return _X509_get_version(self.cert) + 1 @property def startDate(self): """ Certificate validity period start date """ - # Need deep poke into certificate structure (x)->cert_info->validity->notBefore - global utc - asn1date=cast(self.cert,_px509)[0].cert_info[0].validity[0].notBefore - b=Membio() - libcrypto.ASN1_TIME_print(b.bio,asn1date) - return datetime.strptime(str(b),"%b %d %H:%M:%S %Y %Z").replace(tzinfo=utc) + asn1 = _X509_get_notBefore(self.cert) + return __asn1date_to_datetime(asn1) @property def endDate(self): """ Certificate validity period end date """ - # Need deep poke into certificate structure (x)->cert_info->validity->notAfter - global utc - asn1date=cast(self.cert,_px509)[0].cert_info[0].validity[0].notAfter - b=Membio() - libcrypto.ASN1_TIME_print(b.bio,asn1date) - return datetime.strptime(str(b),"%b %d %H:%M:%S %Y %Z").replace(tzinfo=utc) + asn1 = _X509_get_notAfter(self.cert) + return __asn1date_to_datetime(asn1) def check_ca(self): """ Returns True if certificate is CA certificate """ - return libcrypto.X509_check_ca(self.cert)>0 + return libcrypto.X509_check_ca(self.cert) > 0 + class X509Store(object): """ - Represents trusted certificate store. Can be used to lookup CA - certificates to verify + Represents trusted certificate store. Can be used to lookup CA + certificates to verify - @param file - file with several certificates and crls - to load into store - @param dir - hashed directory with certificates and crls - @param default - if true, default verify location (directory) - is installed + @param file - file with several certificates and crls + to load into store + @param dir - hashed directory with certificates and crls + @param default - if true, default verify location (directory) + is installed """ - def __init__(self,file=None,dir=None,default=False): + def __init__(self, file=None, dir=None, default=False): """ - Creates X509 store and installs lookup method. Optionally initializes + Creates X509 store and installs lookup method. Optionally initializes by certificates from given file or directory. """ # # Todo - set verification flags - # - self.store=libcrypto.X509_STORE_new() + # + self.store = libcrypto.X509_STORE_new() if self.store is None: raise X509Error("allocating store") - lookup=libcrypto.X509_STORE_add_lookup(self.store,libcrypto.X509_LOOKUP_file()) + lookup = libcrypto.X509_STORE_add_lookup(self.store, + libcrypto.X509_LOOKUP_file()) if lookup is None: raise X509Error("error installing file lookup method") - if (file is not None): - if not libcrypto.X509_LOOKUP_ctrl(lookup,1,file,1,None)>0: + if file is not None: + if not libcrypto.X509_LOOKUP_ctrl(lookup, 1, file, 1, None) > 0: raise X509Error("error loading trusted certs from file "+file) - lookup=libcrypto.X509_STORE_add_lookup(self.store,libcrypto.X509_LOOKUP_hash_dir()) + lookup = libcrypto.X509_STORE_add_lookup(self.store, + libcrypto.X509_LOOKUP_hash_dir()) if lookup is None: raise X509Error("error installing hashed lookup method") if dir is not None: - if not libcrypto.X509_LOOKUP_ctrl(lookup,2,dir,1,None)>0: + if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, dir, 1, None) > 0: raise X509Error("error adding hashed trusted certs dir "+dir) if default: - if not libcrypto.X509_LOOKUP_ctrl(lookup,2,None,3,None)>0: + if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, None, 3, None) > 0: raise X509Error("error adding default trusted certs dir ") - def add_cert(self,cert): + def add_cert(self, cert): """ Explicitely adds certificate to set of trusted in the store @param cert - X509 object to add """ - if not isinstance(cert,X509): + if not isinstance(cert, X509): raise TypeError("cert should be X509") - libcrypto.X509_STORE_add_cert(self.store,cert.cert) - def add_callback(self,callback): + libcrypto.X509_STORE_add_cert(self.store, cert.cert) + def add_callback(self, callback): """ Installs callback function, which would receive detailed information about verified ceritificates """ raise NotImplementedError - def setflags(self,flags): + def setflags(self, flags): """ Set certificate verification flags. @param flags - integer bit mask. See OpenSSL X509_V_FLAG_* constants """ - libcrypto.X509_STORE_set_flags(self.store,flags) - def setpurpose(self,purpose): + libcrypto.X509_STORE_set_flags(self.store, flags) + def setpurpose(self, purpose): """ Sets certificate purpose which verified certificate should match - @param purpose - number from 1 to 9 or standard strind defined in Openssl - possible strings - sslcient,sslserver, nssslserver, smimesign,smimeencrypt, crlsign, any,ocsphelper - """ - if isinstance(purpose,str): - purp_no=X509_PURPOSE_get_by_sname(purpose) - if purp_no <=0: - raise X509Error("Invalid certificate purpose '"+purpose+"'") - elif isinstance(purpose,int): + @param purpose - number from 1 to 9 or standard strind defined + in Openssl + possible strings - sslcient,sslserver, nssslserver, smimesign,i + smimeencrypt, crlsign, any, ocsphelper + """ + if isinstance(purpose, str): + purp_no = libcrypto.X509_PURPOSE_get_by_sname(purpose) + if purp_no <= 0: + raise X509Error("Invalid certificate purpose '%s'" % purpose) + elif isinstance(purpose, int): purp_no = purpose - if libcrypto.X509_STORE_set_purpose(self.store,purp_no)<=0: + if libcrypto.X509_STORE_set_purpose(self.store, purp_no) <= 0: raise X509Error("cannot set purpose") - def setdepth(self,depth): + def setdepth(self, depth): """ Sets the verification depth i.e. max length of certificate chain which is acceptable """ - libcrypto.X509_STORE_set_depth(self.store,depth) + libcrypto.X509_STORE_set_depth(self.store, depth) def settime(self, time): """ Set point in time used to check validity of certificates for Time can be either python datetime object or number of seconds sinse epoch """ - if isinstance(time,datetime.datetime) or isinstance(time,datetime.date): - d=int(time.strftime("%s")) - elif isinstance(time,int): - pass + if isinstance(time, datetime) or isinstance(time, + datetime.date): + seconds = int(time.strftime("%s")) + elif isinstance(time, int): + seconds = time else: - raise TypeError("datetime.date, datetime.datetime or integer is required as time argument") + raise TypeError("datetime.date, datetime.datetime or integer " + + "is required as time argument") raise NotImplementedError class StackOfX509(object): """ Implements OpenSSL STACK_OF(X509) object. It looks much like python container types """ - def __init__(self,certs=None,ptr=None,disposable=True): + def __init__(self, certs=None, ptr=None, disposable=True): """ Create stack @param certs - list of X509 objects. If specified, read-write @@ -500,85 +562,132 @@ class StackOfX509(object): freeid. If false, it is just pointer into another structure i.e. CMS_ContentInfo """ + self.need_free = False if ptr is None: self.need_free = True - self.ptr=libcrypto.sk_new_null() + self.ptr = libcrypto.sk_new_null() if certs is not None: for crt in certs: self.append(crt) elif certs is not None: - raise ValueError("cannot handle certs an ptr simultaneously") + raise ValueError("cannot handle certs an ptr simultaneously") else: self.need_free = disposable - self.ptr=ptr + self.ptr = ptr def __len__(self): return libcrypto.sk_num(self.ptr) - def __getitem__(self,index): - if index <0 or index>=len(self): + def __getitem__(self, index): + if index < 0 or index >= len(self): raise IndexError - p=libcrypto.sk_value(self.ptr,index) + p = libcrypto.sk_value(self.ptr, index) return X509(ptr=libcrypto.X509_dup(p)) - def __setitem__(self,index,value): + def __setitem__(self, index, value): if not self.need_free: raise ValueError("Stack is read-only") - if index <0 or index>=len(self): + if index < 0 or index >= len(self): raise IndexError - if not isinstance(value,X509): - raise TypeError('StackOfX508 can contain only X509 objects') - p=libcrypto.sk_value(self.ptr,index) - libcrypto.sk_set(self.ptr,index,libcrypto.X509_dup(value.cert)) + if not isinstance(value, X509): + raise TypeError('StackOfX509 can contain only X509 objects') + p = libcrypto.sk_value(self.ptr, index) + libcrypto.sk_set(self.ptr, index, libcrypto.X509_dup(value.cert)) libcrypto.X509_free(p) - def __delitem__(self,index): + def __delitem__(self, index): if not self.need_free: raise ValueError("Stack is read-only") - if index <0 or index>=len(self): + if index < 0 or index >= len(self): raise IndexError - p=libcrypto.sk_delete(self.ptr,index) + p = libcrypto.sk_delete(self.ptr, index) libcrypto.X509_free(p) def __del__(self): if self.need_free: - libcrypto.sk_pop_free(self.ptr,libcrypto.X509_free) - def append(self,value): + libcrypto.sk_pop_free(self.ptr, libcrypto.X509_free) + def append(self, value): + """ Adds certificate to stack """ if not self.need_free: raise ValueError("Stack is read-only") - if not isinstance(value,X509): - raise TypeError('StackOfX508 can contain only X509 objects') - libcrypto.sk_push(self.ptr,libcrypto.X509_dup(value.cert)) -libcrypto.i2a_ASN1_INTEGER.argtypes=(c_void_p,c_void_p) -libcrypto.ASN1_STRING_print_ex.argtypes=(c_void_p,c_void_p,c_long) -libcrypto.PEM_read_bio_X509.restype=c_void_p -libcrypto.PEM_read_bio_X509.argtypes=(c_void_p,POINTER(c_void_p),c_void_p,c_void_p) -libcrypto.PEM_write_bio_X509.restype=c_int -libcrypto.PEM_write_bio_X509.argtypes=(c_void_p,c_void_p) -libcrypto.ASN1_TIME_print.argtypes=(c_void_p,c_void_p) -libcrypto.ASN1_INTEGER_get.argtypes=(c_void_p,) -libcrypto.ASN1_INTEGER_get.restype=c_long -libcrypto.X509_get_serialNumber.argtypes=(c_void_p,) -libcrypto.X509_get_serialNumber.restype=c_void_p -libcrypto.X509_NAME_ENTRY_get_object.restype=c_void_p -libcrypto.X509_NAME_ENTRY_get_object.argtypes=(c_void_p,) -libcrypto.OBJ_obj2nid.argtypes=(c_void_p,) -libcrypto.X509_NAME_get_entry.restype=c_void_p -libcrypto.X509_NAME_get_entry.argtypes=(c_void_p,c_int) -libcrypto.X509_STORE_new.restype=c_void_p -libcrypto.X509_STORE_add_lookup.restype=c_void_p -libcrypto.X509_STORE_add_lookup.argtypes=(c_void_p,c_void_p) -libcrypto.X509_LOOKUP_file.restype=c_void_p -libcrypto.X509_LOOKUP_hash_dir.restype=c_void_p -libcrypto.X509_LOOKUP_ctrl.restype=c_int -libcrypto.X509_LOOKUP_ctrl.argtypes=(c_void_p,c_int,c_char_p,c_long,POINTER(c_char_p)) -libcrypto.X509_EXTENSION_dup.argtypes=(c_void_p,) -libcrypto.X509_EXTENSION_dup.restype=POINTER(_x509_ext) -libcrypto.X509V3_EXT_print.argtypes=(c_void_p,POINTER(_x509_ext),c_long,c_int) -libcrypto.X509_get_ext.restype=c_void_p -libcrypto.X509_get_ext.argtypes=(c_void_p,c_int) -libcrypto.X509V3_EXT_print.argtypes=(c_void_p,POINTER(_x509_ext),c_long,c_int) -libcrypto.sk_set.argtypes=(c_void_p,c_int,c_void_p) -libcrypto.sk_set.restype=c_void_p -libcrypto.sk_value.argtypes=(c_void_p,c_int) -libcrypto.sk_value.restype=c_void_p -libcrypto.X509_dup.restype=c_void_p -libcrypto.sk_new_null.restype=c_void_p -libcrypto.X509_dup.argtypes=(c_void_p,) -libcrypto.X509_NAME_hash.restype=c_long -libcrypto.X509_NAME_hash.argtypes=(c_void_p,) + if not isinstance(value, X509): + raise TypeError('StackOfX509 can contain only X509 objects') + libcrypto.sk_push(self.ptr, libcrypto.X509_dup(value.cert)) + +libcrypto.d2i_X509_bio.argtypes = (c_void_p,POINTER(c_void_p)) +libcrypto.X509_free.argtypes = (c_void_p,) +libcrypto.X509_dup.restype = c_void_p +libcrypto.X509_dup.argtypes = (c_void_p, ) +libcrypto.i2a_ASN1_INTEGER.argtypes = (c_void_p, c_void_p) +libcrypto.ASN1_STRING_print_ex.argtypes = (c_void_p, c_void_p, c_long) +libcrypto.PEM_read_bio_X509.restype = c_void_p +libcrypto.PEM_read_bio_X509.argtypes = (c_void_p, POINTER(c_void_p), + c_void_p, c_void_p) +libcrypto.PEM_write_bio_X509.restype = c_int +libcrypto.PEM_write_bio_X509.argtypes = (c_void_p, c_void_p) +libcrypto.ASN1_TIME_print.argtypes = (c_void_p, c_void_p) +libcrypto.ASN1_INTEGER_get.argtypes = (c_void_p, ) +libcrypto.ASN1_INTEGER_get.restype = c_long +libcrypto.X509_check_ca.argtypes = (c_void_p, ) +libcrypto.X509_get_serialNumber.argtypes = (c_void_p, ) +libcrypto.X509_get_serialNumber.restype = c_void_p +libcrypto.X509_get_subject_name.argtypes = (c_void_p, ) +libcrypto.X509_get_subject_name.restype = c_void_p +libcrypto.X509_get_issuer_name.argtypes = (c_void_p, ) +libcrypto.X509_get_issuer_name.restype = c_void_p +libcrypto.X509_NAME_ENTRY_get_object.restype = c_void_p +libcrypto.X509_NAME_ENTRY_get_object.argtypes = (c_void_p, ) +libcrypto.X509_NAME_ENTRY_get_data.restype = c_void_p +libcrypto.X509_NAME_ENTRY_get_data.argtypes = (c_void_p, ) +libcrypto.OBJ_obj2nid.argtypes = (c_void_p, ) +libcrypto.X509_NAME_get_entry.restype = c_void_p +libcrypto.X509_NAME_get_entry.argtypes = (c_void_p, c_int) +libcrypto.X509_STORE_new.restype = c_void_p +libcrypto.X509_STORE_add_lookup.restype = c_void_p +libcrypto.X509_STORE_add_lookup.argtypes = (c_void_p, c_void_p) +libcrypto.X509_STORE_add_cert.argtypes = (c_void_p, c_void_p) +libcrypto.X509_STORE_CTX_new.restype = c_void_p +libcrypto.X509_STORE_CTX_free.argtypes = (c_void_p,) +libcrypto.X509_STORE_CTX_init.argtypes = (c_void_p, c_void_p, c_void_p, + c_void_p) +libcrypto.X509_STORE_set_depth.argtypes = (c_void_p, c_int) +libcrypto.X509_STORE_set_flags.argtypes = (c_void_p, c_ulong) +libcrypto.X509_STORE_set_purpose.argtypes = (c_void_p, c_int) +libcrypto.X509_LOOKUP_file.restype = c_void_p +libcrypto.X509_LOOKUP_hash_dir.restype = c_void_p +libcrypto.X509_LOOKUP_ctrl.restype = c_int +libcrypto.X509_LOOKUP_ctrl.argtypes = (c_void_p, c_int, c_char_p, c_long, + POINTER(c_char_p)) +libcrypto.X509_EXTENSION_free.argtypes = (c_void_p, ) +libcrypto.X509_EXTENSION_dup.argtypes = (c_void_p, ) +libcrypto.X509_EXTENSION_dup.restype = POINTER(_x509_ext) +libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long, + c_int) +libcrypto.X509_get_ext.restype = c_void_p +libcrypto.X509_get_ext.argtypes = (c_void_p, c_int) +libcrypto.X509_get_ext_by_critical.argtypes = (c_void_p, c_int, c_int) +libcrypto.X509_get_ext_by_NID.argtypes = (c_void_p, c_int, c_int) +libcrypto.X509_get_ext_count.argtypes = (c_void_p, ) +libcrypto.X509_get_pubkey.restype = c_void_p +libcrypto.X509_get_pubkey.argtypes = (c_void_p, ) +libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long, + c_int) +libcrypto.X509_LOOKUP_file.restype = c_void_p +libcrypto.X509_LOOKUP_hash_dir.restype = c_void_p +libcrypto.X509_NAME_cmp.argtypes = (c_void_p, c_void_p) +libcrypto.X509_NAME_entry_count.argtypes = (c_void_p,) +libcrypto.X509_NAME_free.argtypes = (c_void_p,) +libcrypto.X509_NAME_new.restype = c_void_p +libcrypto.X509_NAME_print_ex.argtypes = (c_void_p, c_void_p, c_int, c_ulong) +libcrypto.X509_PURPOSE_get_by_sname.argtypes=(c_char_p,) +libcrypto.X509_verify.argtypes = (c_void_p, c_void_p) +libcrypto.X509_verify_cert.argtypes = (c_void_p,) +libcrypto.sk_num.restype = c_int +libcrypto.sk_num.argtypes= (c_void_p,) +libcrypto.sk_set.argtypes = (c_void_p, c_int, c_void_p) +libcrypto.sk_set.restype = c_void_p +libcrypto.sk_value.argtypes = (c_void_p, c_int) +libcrypto.sk_value.restype = c_void_p +libcrypto.sk_delete.argtypes = (c_void_p, c_int) +libcrypto.sk_delete.restype = c_void_p +libcrypto.sk_new_null.restype = c_void_p +libcrypto.sk_pop_free.argtypes = (c_void_p, c_void_p) +libcrypto.sk_push.argtypes = (c_void_p, c_void_p) +libcrypto.X509_NAME_hash.restype = c_long +libcrypto.X509_NAME_hash.argtypes = (c_void_p, ) +libcrypto.X509_NAME_get_index_by_NID.argtypes = (c_void_p, c_int, c_int)