From: Victor Wagner Date: Thu, 30 Oct 2014 20:26:14 +0000 (+0300) Subject: Merge branch 'master' of https://github.com/vbwagner/ctypescrypto X-Git-Url: https://www.wagner.pp.ru/gitweb/?a=commitdiff_plain;h=95a6e6a2c24635d189b408b3cc4dc739e1fe7641;hp=297667fab5c13886d82b037e74c84459f52e829c;p=oss%2Fctypescrypto.git Merge branch 'master' of https://github.com/vbwagner/ctypescrypto --- diff --git a/README.md b/README.md index 27991db..87c85d9 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ data structures and groups of functions. Digest calculation ------------------ -Module *ctypescrypto.digest* contain *new()* function which produces -objects simular to python *hashlib* module objects. +Module **ctypescrypto.digest** contain **new()** function which produces +objects simular to python **hashlib** module objects. On the systems where hashlib is linked with libcrypto dynamically, hashlib even able to make use of digest types, provided by loadable @@ -30,6 +30,9 @@ engines. This module would utilize same copy of libcrypto library as other ctypescrypto modules, so it would work with engine-provided digests. +Additionally there is **DigestType** class which may be needed to +construct CMS SignedData objects or add signatures to them. + Symmetric ciphers ----------------- @@ -39,22 +42,30 @@ This object provides methods *update* and *finish* which allows to encrypt/decrypt data. All ciphers, supported by your version of OpenSSL and its loadable engines are supported. +Additionally the **CipherType** class instances may be used directly to +pass to other functions such as CMS EnvelopedData or EncryptedData +**create** + Public key operations --------------------- -Module *ctypescrypto.pkey* provides *PKey* object, which represents +Module **ctypescrypto.pkey** provides **PKey** object, which represents public/private key pair or just public key. With this object you can sign data, derive shared key and verify signatures. This is quite low-level object, which can be used to implement some non-standard protocols and operations. +Additional module **ctypescrypto.ec** allows to create **PKey** objects +with elliptic curve keys from just raw secret key as byte buffer or +python big integer. + X509 certificates ----------------- -Module *ctypescrypto.x509* contains objects *X509* which represents +Module **ctypescrypto.x509** contains objects **X509** which represents certificate (and can be constructed from string, contained PEM -or DER certificate) and object *X509Store* which is a store of trusted +or DER certificate) and object **X509Store** which is a store of trusted CA certificates which can be used to high-level signature verifications (i.e. in PKCS7/CMS messages). @@ -65,13 +76,43 @@ position of field. Unicode in the names is supported. There is no support for certificate validity time yet. +**StackOfX509** implements collection of certificates, neccessary for +some operations with CMS and certificate verification. + +CMS documents +------------- + +There is basic constructor function **CMS()**, which parses PEM or der +representation of cryptographic message and generates appropriate +object. There are **SignedData**, **EnvelopedData** and +**EncryptedData** clases. Each class has static method **create** +allowing to create this subtype of message from raw data and appropriate +keys and certificates. + +**SignedData** has **verify()** method. **EnvelopedData** and +**EncryptedData** - **decrypt** method. + +Unfortunatly, **SignedAndEnvelopedData** seems to be unsupported in +libcrypto as of version 1.0.1 of OpenSSL. + +PBKDF2 +------ + +Provices interface to password based keyderivation function +Interface slightly differs from the **hashlib.pbkdf2_hmac** function, +which have appeared in Python 2.7.8 but functionality is just same, +although OpenSSL implementation might be faster. + + + + OID database ------------ OpenSSL conteins internal object identifiers (OID) database. Each OID have apart from dotted-decimal representation long name, short name and -numeric identifer. Module *ctypescrypto.oid* provides interface to the -database. *Oid* objects store numeric identifier internally and can +numeric identifer. Module **ctypescrypto.oid** provides interface to the +database. **Oid** objects store numeric identifier internally and can return both long and short name and dotted-decimal representation. BIO library @@ -87,7 +128,7 @@ string or unicode object. Exceptions ---------- -Exceptions, used in the *ctypescrypto* to report problems are tied +Exceptions, used in the **ctypescrypto** to report problems are tied closely with OpenSSL error-reporting functions, so if such an exception occurs, as much as possibly information from inside libcrypto would be available in the Python @@ -95,8 +136,12 @@ available in the Python Engine support -------------- -There is just one function *ctypescrypt.engine.set_default*. which loads +There is just one function **ctypescrypt.engine.set_default**. which loads specified engine by id and makes it default for all algorithms, supported by it. It is enough for me to use Russian national -cryptographic algoritms, provided by *gost* engine. +cryptographic algoritms, provided by **gost** engine. + +Test Suite +---------- +Test suite is fairly incomplete. Contributions are welcome. diff --git a/ctypescrypto/cms.py b/ctypescrypto/cms.py index 9defb1d..2c03898 100644 --- a/ctypescrypto/cms.py +++ b/ctypescrypto/cms.py @@ -10,8 +10,9 @@ create it from raw data and neccessary certificates. """ - +from ctypes import c_int, c_void_p, c_char_p, c_int from ctypescrypto.exception import LibCryptoError +from ctypescrypto import libcrypto from ctypescrypto.bio import Membio from ctypescrypto.oid import Oid @@ -62,30 +63,60 @@ def CMS(data,format="PEM"): return SignedData(ptr) elif typeoid.shortname()=="pkcs7-envelopedData": return EnvelopedData(ptr) + elif typeoid.shortname()=="pkcs7-encryptedData": + return EncryptedData(ptr) else: raise NotImplementedError("cannot handle "+typeoid.shortname()) +class CMSBase: + """ + Common ancessor for all CMS types. + Implements serializatio/deserialization + """ + def __init__(self,ptr=None): + self.ptr=ptr + def __str__(self): + """ + Serialize in DER format + """ + b=Membio() + if not libcrypto.i2d_CMS_bio(b.bio,self.ptr): + raise CMSError("writing CMS to PEM") + return str(b) + def pem(self): + """ + Serialize in PEM format + """ + b=Membio() + if not libcrypto.PEM_write_bio_CMS(b.bio,self.ptr): + raise CMSError("writing CMS to PEM") + return str(b) + -class SignedData: - def __init__(self,ptr=None): - self.ptr=ptr +class SignedData(CMSBase): @staticmethod - def create(data,cert,pkey,flags=Flags.BINARY): + def create(data,cert,pkey,flags=Flags.BINARY,certs=[]): """ Creates SignedData message by signing data with pkey and certificate. @param data - data to sign @param pkey - pkey object with private key to sign + @param flags - OReed combination of Flags constants + @param certs - list of X509 objects to include into CMS """ if not pkey.cansign: raise ValueError("Specified keypair has no private part") if cert.pubkey!=pkey: raise ValueError("Certificate doesn't match public key") b=Membio(data) - ptr=libcrypto.CMS_sign(cert.cert,pkey.ptr,None,b.bio,flags) + if certs is not None and len(certs)>0: + certstack=StackOfX509(certs) + else: + certstack=None + ptr=libcrypto.CMS_sign(cert.cert,pkey.ptr,certstack,b.bio,flags) if ptr is None: raise CMSError("signing message") return SignedData(ptr) @@ -94,10 +125,11 @@ class SignedData: Adds another signer to already signed message @param cert - signer's certificate @param pkey - signer's private key - @param data - data to sign (if detached) - @param md - message digest to use as DigestType object (if None - default for key - would be used) - @param flags - flags + @param md - message digest to use as DigestType object + (if None - default for key would be used) + @param data - data to sign (if detached and + Flags.REUSE_DIGEST is not specified) + @param flags - ORed combination of Flags consants """ if not pkey.cansign: raise ValueError("Specified keypair has no private part") @@ -116,49 +148,53 @@ class SignedData: res= libcrypto.CMS_final(self.ptr,biodata,None,flags) if res<=0: raise CMSError - def verify(self,store,flags,data=None): + def verify(self,store,flags,data=None,certs=[]): + """ + Verifies signature under CMS message using trusted cert store + + @param store - X509Store object with trusted certs + @param flags - OR-ed combination of flag consants + @param data - message data, if messge has detached signature + param certs - list of certificates to use during verification + If Flags.NOINTERN is specified, these are only + sertificates to search for signing certificates + @returns True if signature valid, False otherwise + """ bio=None if data!=None: b=Membio(data) bio=b.bio - res=libcrypto.CMS_verify(self.ptr,store.store,bio,None,flags) + if certs is not None and len(certs)>0: + certstack=StackOfX509(certs) + else: + certstack=None + res=libcrypto.CMS_verify(self.ptr,certstack,store.store,bio,None,flags) return res>0 - def __str__(self): - """ - Serialize in DER format - """ - b=Membio() - if not libcrypto.i2d_CMS_bio(b.bio,self.ptr): - raise CMSError("writing CMS to PEM") - return str(b) - - def pem(self): - """ - Serialize in PEM format - """ - b=Membio() - if not libcrypto.PEM_write_bio_CMS(b.bio,self.ptr): - raise CMSError("writing CMS to PEM") - return str(b) - @property def signers(self,store=None): """ Return list of signer's certificates """ - raise NotImplementedError + p=libcrypto.CMS_get0_signers(self.ptr) + if p is None: + raise CMSError + return StackOfX509(ptr=p,disposable=False) @property def data(self): """ - Returns signed data if present + Returns signed data if present in the message """ - raise NotImplementedError + b=Membio() + if not libcrypto.CMS_verify(self.ptr,None,None,None,b.bio,Flags.NO_VERIFY): + raise CMSError("extract data") + return str(b) def addcert(self,cert): """ Adds a certificate (probably intermediate CA) to the SignedData structure """ - raise NotImplementedError + if libcrypto.CMS_add1_cert(self.ptr,cert.cert)<=0: + raise CMSError("adding cert") def addcrl(self,crl): """ Adds a CRL to the signed data structure @@ -169,7 +205,10 @@ class SignedData: """ List of the certificates contained in the structure """ - raise NotImplementedError + p=CMS_get1_certs(self.ptr) + if p is None: + raise CMSError("getting certs") + return StackOfX509(ptr=p,disposable=True) @property def crls(self): """ @@ -177,29 +216,7 @@ class SignedData: """ raise NotImplementedError -class EnvelopedData: - def __init__(self,ptr): - """ - Initializes an object. For internal use - """ - self.ptr=ptr - def __str__(self): - """ - Serialize in DER format - """ - b=Membio() - if not libcrypto.i2d_CMS_bio(b.bio,self.ptr): - raise CMSError("writing CMS to PEM") - return str(b) - - def pem(self): - """ - Serialize in PEM format - """ - b=Membio() - if not libcrypto.PEM_write_bio_CMS(b.bio,self.ptr): - raise CMSError("writing CMS to PEM") - return str(b) +class EnvelopedData(CMSBase): @staticmethod def create(recipients,data,cipher,flags=0): """ @@ -209,8 +226,12 @@ class EnvelopedData: @param cipher - CipherType object @param flags - flag """ - # Need to be able to handle OPENSSL stacks - raise NotImplementedError + recp=StackOfX509(recipients) + b=Membio(data) + p=libcrypto.CMS_encrypt(recp.ptr,b.bio,cipher.cipher_type,flags) + if p is None: + raise CMSError("encrypt EnvelopedData") + return EnvelopedData(p) def decrypt(self,pkey,cert,flags=0): """ Decrypts message @@ -229,3 +250,36 @@ class EnvelopedData: if res<=0: raise CMSError("decrypting CMS") return str(b) + +class EncryptedData(CMSBase): + @staticmethod + def create(data,cipher,key,flags=0): + """ + Creates an EncryptedData message. + @param data data to encrypt + @param cipher cipher.CipherType object represening required + cipher type + @param key - byte array used as simmetic key + @param flags - OR-ed combination of Flags constant + """ + b=Membio(data) + ptr=libcrypto.CMS_EncryptedData_encrypt(b.bio,cipher.cipher_type,key,len(key),flags) + if ptr is None: + raise CMSError("encrypt data") + return EncryptedData(ptr) + def decrypt(self,key,flags=0): + """ + Decrypts encrypted data message + @param key - symmetic key to decrypt + @param flags - OR-ed combination of Flags constant + """ + b=Membio() + if libcrypto.CMS_EncryptedData_decrypt(self.ptr,key,len(key),None, + b.bio,flags)<=0: + raise CMSError("decrypt data") + return str(b) + + + +libcrypto.CMS_verify.restype=c_int +libcrypto.CMS_verify.argtypes=(c_void_p,c_void_p,c_void_p,c_void_p,c_void_p,c_int) diff --git a/ctypescrypto/x509.py b/ctypescrypto/x509.py index 5d5d448..5c2a50d 100644 --- a/ctypescrypto/x509.py +++ b/ctypescrypto/x509.py @@ -1,10 +1,9 @@ -from ctypes import c_void_p,create_string_buffer,c_long,c_int +from ctypes import c_void_p,create_string_buffer,c_long,c_int,POINTER,c_char_p from ctypescrypto.bio import Membio from ctypescrypto.pkey import PKey from ctypescrypto.oid import Oid from ctypescrypto.exception import LibCryptoError from ctypescrypto import libcrypto - class X509Error(LibCryptoError): """ Exception, generated when some openssl function fail @@ -163,13 +162,17 @@ class X509: def pubkey(self): """EVP PKEy object of certificate public key""" return PKey(ptr=libcrypto.X509_get_pubkey(self.cert,False)) - def verify(self,store=None,key=None): + def verify(self,store=None,chain=[],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 - parameters are mutually exclusive. If neither is specified, attempts to verify + parameters stora and key are mutually exclusive. If neither is specified, attempts to verify + itself as self-signed certificate """ if store is not None and key is not None: @@ -178,7 +181,11 @@ class X509: ctx=libcrypto.X509_STORE_CTX_new() if ctx is None: raise X509Error("Error allocating X509_STORE_CTX") - if libcrypto.X509_STORE_CTX_init(ctx,store.ptr,self.cert,None) < 0: + if chain is not None and len(chain)>0: + ch=StackOfX509(chain) + else: + ch=None + if libcrypto.X509_STORE_CTX_init(ctx,store.store,self.cert,ch) < 0: raise X509Error("Error allocating X509_STORE_CTX") res= libcrypto.X509_verify_cert(ctx) libcrypto.X509_STORE_CTX_free(ctx) @@ -243,21 +250,22 @@ class X509Store: # Todo - set verification flags # 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()) if lookup is None: raise X509Error("error installing file lookup method") if (file is not None): - if not libcrypto.X509_LOOKUP_loadfile(lookup,file,1): + 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()) if lookup is None: raise X509Error("error installing hashed lookup method") if dir is not None: - if not libcrypto.X509_LOOKUP_add_dir(lookup,dir,1): + 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.add_dir(lookup,None,3): + 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): """ @@ -293,6 +301,75 @@ class X509Store: purp_no = purpose if libcrypto.X509_STORE_set_purpose(self.store,purp_no)<=0: raise X509Error("cannot set purpose") + def setdepth(self,depth): + libcrypto.X509_STORE_set_depth(self.store,depth) + def settime(self, time): + """ + Set point in time used to check validity of certificates for + """ + if isinstance(time,datetime.datetime) or isinstance(time,datetime.date): + d=int(time.strftime("%s")) + elif isinstance(time,int): + pass + else: + raise TypeError("datetime.date, datetime.datetime or integer is required as time argument") + raise NotImplementedError +class StackOfX509: + """ + Implements OpenSSL STACK_OF(X509) object. + It looks much like python container types + """ + def __init__(self,certs=None,ptr=None,disposable=True): + """ + Create stack + @param certs - list of X509 objects. If specified, read-write + stack is created and populated by these certificates + @param ptr - pointer to OpenSSL STACK_OF(X509) as returned by + some functions + @param disposable - if True, stack created from object, returned + by function is copy, and can be modified and need to be + freeid. If false, it is just pointer into another + structure i.e. CMS_ContentInfo + """ + if ptr is None: + self.need_free = True + self.ptr=libcrypt.sk_new_null() + if certs is not None: + for crt in certs: + self.append(crt) + elif not certs is None: + raise ValueError("cannot handle certs an ptr simultaneously") + else: + self.need_free = disposable + self.ptr=ptr + def __len__(self): + return libcrypto.sk_num(self.ptr) + def __getitem__(self,index): + if index <0 or index>=len(self): + raise IndexError + p=libcrypto.sk_value(self.ptr,index) + return X509(ptr=libcrypto.X509_dup(p)) + def __putitem__(self,index,value): + if not self.need_free: + raise ValueError("Stack is read-only") + if index <0 or index>=len(self): + raise IndexError + p=libcrypto.sk_set(self.ptr,index,libcrypto.X509_dup(value.cert)) + libcrypto.X509_free(p) + def __delitem__(self,index): + if not self.need_free: + raise ValueError("Stack is read-only") + if index <0 or index>=len(self): + raise IndexError + 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): + if not self.need_free: + raise ValueError("Stack is read-only") + 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.X509_get_serialNumber.argtypes=(c_void_p,) @@ -302,3 +379,10 @@ 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)) diff --git a/tests/testx509.py b/tests/testx509.py index 65b4a7a..2a09e78 100644 --- a/tests/testx509.py +++ b/tests/testx509.py @@ -3,6 +3,7 @@ from ctypescrypto.x509 import X509,X509Store from ctypescrypto.oid import Oid +from tempfile import NamedTemporaryFile import unittest @@ -67,6 +68,47 @@ WSaUuftL/+yFk729xDoYkOZhFwUSUM5SbEZ0JpufWFjDi3Qwj3ZOTXliHC3e4C71 iFTXJP8/Au8kfezlA4b+eS81zWq2BFvNlBQsgf04S88oew0CuBBgtjUIIw7XZkS0 3QIDAQAB -----END PUBLIC KEY----- +""" + digicert_cert="""digicert.crt +-----BEGIN CERTIFICATE----- +MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/ +PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC +7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw +PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6 +4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo +LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U +pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy +BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH +AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH +AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o +dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0 +AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1 +AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp +AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl +AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo +AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg +AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg +AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB +gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy +dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy +dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw +gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB +c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 +LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE +FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI +Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU +nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/ +roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU +xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+ +BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu +zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA +-----END CERTIFICATE----- """ def test_readpubkey(self): c=X509(self.cert1) @@ -104,7 +146,31 @@ iFTXJP8/Au8kfezlA4b+eS81zWq2BFvNlBQsgf04S88oew0CuBBgtjUIIw7XZkS0 pk2=c.pubkey self.assertFalse(c.verify(key=pk2)) self.assertTrue(c.verify(key=pubkey)) + def test_default_filestore(self): + store=X509Store(default=True) + c1=X509(self.cert1) + # Cert signed by our CA shouldn't be successfully verified + # by default CA store + self.assertFalse(c1.verify(store)) + # but cert, downloaded from some commercial CA - should. + c2=X509(self.digicert_cert) + self.assertTrue(c2.verify(store)) def test_verify_by_filestore(self): + trusted=NamedTemporaryFile() + trusted.write(self.ca_cert) + trusted.flush() + goodcert=X509(self.cert1) + badcert=X509(self.cert1[0:-30]+"GG"+self.cert1[-28:]) + gitcert=X509(self.digicert_cert) + store=X509Store(file=trusted.name) + # We should successfuly verify certificate signed by our CA cert + self.assertTrue(goodcert.verify(store)) + # We should reject corrupted certificate + self.assertFalse(badcert.verify(store)) + # And if we specify explicitely certificate file, certificate, + # signed by some commercial CA should be rejected too + self.assertFalse(gitcert.verify(store)) + trusted.close() pass def test_verify_by_dirstore(self): pass