Package pysmime :: Module core
[hide private]
[frames] | no frames]

Source Code for Module pysmime.core

  1  # pysmime/core.py 
  2  # Lorenzo Gaggini <lg@libersoft.it> 
  3  # Libersoft <tech@libersoft.it> 
  4  # http://www.libersoft.it 
  5  # License: http://www.gnu.org/licenses/gpl.txt 
  6   
  7  """ 
  8  Core functions to verify, sign, encrypt and decrypt SMIME data, build just on 
  9  top of M2Crypto library wrapper to OpenSSL. 
 10  """ 
 11   
 12  import os 
 13  import base64 
 14  import logging 
 15   
 16  from M2Crypto import SMIME, X509, Rand, m2 
 17  from util import BIO_from_buffer, set_keyring, set_certificate 
 18   
 19   
20 -class BadPKCS7Type(BaseException):
21 """ 22 Exception raised if requested PKCS#7 type is not valid. 23 Ammitted values are PEM and DER. 24 """ 25 pass
26 27
28 -class CertStoreNotAvailable(BaseException):
29 """ 30 Exception raised if the reference certstore for verification is not 31 available. 32 """ 33 pass
34 35
36 -class MissingSignerCertificate(BaseException):
37 """ 38 Exception raised if the input PKCS#7 is not a signed PKCS#7. 39 """ 40 pass
41 42
43 -def encrypt(input_bio, cert, keyring_source, cypher):
44 """ 45 Encrypts the input data with the public key in the certificate from keyring 46 source with selected cypher. 47 48 @type input_bio: M2Crypto.BIO 49 @param input_bio: input data to encrypt. 50 @type cert: filepath or M2Crypto.BIO or M2Crypto.X509.X509 51 @param cert: the recipient certificate reference from filepath, could be 52 from file, from memory or from pkcs11 smartcard, based on 53 keyring_soruce input parameter. 54 @type keyring_source: str 55 @keyword keyring_source: the type of the source for input certificate, used 56 to recall the appropriate method for encrypter settings. Ammitted 57 values are: file, memory, pkcs11. 58 @type cypher: str 59 @keyword cypher: the cypher to use for encryption of the data, run 60 "openssl enc -help" for supported cyphers, you have to choose a public 61 key cypher from availables. 62 @rtype: M2Crypto.SMIME.PKCS7 63 @return: the PKCS#7 encrypted data in PEM format. 64 """ 65 encrypter = SMIME.SMIME() 66 x509 = set_certificate(cert, keyring_source) 67 sk = X509.X509_Stack() 68 sk.push(x509) 69 encrypter.set_x509_stack(sk) 70 encrypter.set_cipher(SMIME.Cipher(cypher)) 71 Rand.load_file('randpool.dat', -1) 72 try: 73 p7 = encrypter.encrypt(input_bio) 74 except SMIME.SMIME_Error, e: 75 logging.error('smime error: ' + str(e)) 76 raise 77 except SMIME.PKCS7_Error, e: 78 logging.error('pkcs7 error: ' + str(e)) 79 raise 80 Rand.save_file('randpool.dat') 81 return p7
82 83
84 -def decrypt(input_bio, private_key, cert, keyring_source, type):
85 """ 86 Decrypts the input data with the private key and the certificate from 87 keyring source. 88 89 @type input_bio: M2Crypto.BIO 90 @param input_bio: input data to sign. 91 @type private_key: filepath or M2Crypto.BIO or M2Crypto.EVP.PKey 92 @param private_key: recipient private key reference, could be from file, 93 from memory or from pkcs11 smartcard, based on keyring_soruce input 94 parameter. 95 @type cert: filepath or M2Crypto.BIO or M2Crypto.X509.X509 96 @param cert: recipient certificate, could be from filepath, from memory or 97 from pkcs11 smartcard, based on keyring_soruce input parameter. 98 @type keyring_source: str 99 @keyword keyring_source: the type of the source for input certificate, used 100 to recall the appropriate method for decrypter settings. Ammitted 101 values are: file, memory, pkcs11. 102 @type type: str 103 @keyword type: specifies the type of input PKCS#7 data: PEM or DER 104 @rtype: str 105 @return: the decrypted data in plain form. 106 @raise BadPKCS7Type: The requested PKCS#7 type is not valid. Ammitted 107 values are PEM and DER. 108 """ 109 decrypter = SMIME.SMIME() 110 set_keyring(decrypter, private_key, cert, keyring_source) 111 try: 112 if type == 'PEM': 113 p7, data_bio = SMIME.smime_load_pkcs7_bio(input_bio) 114 elif type == 'DER': 115 p7 = SMIME.PKCS7(m2.pkcs7_read_bio_der(input_bio._ptr()), 1) 116 else: 117 logging.error('pkcs7 type error: unknown type') 118 raise BadPKCS7Type('unknown type: ' + type + 119 '; possible values: PEM, DER') 120 except SMIME.SMIME_Error, e: 121 logging.error('load pkcs7 error: ' + str(e)) 122 pass 123 try: 124 decrypted_data = decrypter.decrypt(p7) 125 except SMIME.SMIME_Error, e: 126 logging.error('smime error: ' + str(e)) 127 raise 128 except SMIME.PKCS7_Error, e: 129 logging.error('pkcs7 error: ' + str(e)) 130 raise 131 return decrypted_data.replace('\r', '')
132 133
134 -def sign(input_bio, private_key, cert, keyring_source, type):
135 """ 136 Signs the input data with the private key and the certificate from keyring 137 source. 138 139 @type input_bio: M2Crypto.BIO 140 @param input_bio: input data to sign. 141 @type private_key: filepath or M2Crypto.BIO or M2Crypto.EVP.PKey 142 @param private_key: sender private key reference, could be from file, 143 from memory or from pkcs11 smartcard, based on keyring_soruce input 144 parameter. 145 @type cert: filepath or M2Crypto.BIO or M2Crypto.X509.X509 146 @param cert: sender certificate, could be from filepath, from memory or 147 from pkcs11 smartcard, based on keyring_soruce input parameter. 148 @type keyring_source: str 149 @keyword keyring_source: the type of the source for input certificate, used 150 to recall the appropriate method for signer settings. Ammitted 151 values are: file, memory, pkcs11. 152 @type type: str 153 @keyword type: specifies the type of output PKCS#7 data: PEM or DER 154 @rtype: M2Crypto.SMIME.PKCS7 155 @return: the PKCS#7 signed data in PEM or DER format. 156 """ 157 signer = SMIME.SMIME() 158 set_keyring(signer, private_key, cert, keyring_source) 159 Rand.load_file('randpool.dat', -1) 160 try: 161 if type == 'PEM': 162 p7 = signer.sign(input_bio, flags=SMIME.PKCS7_DETACHED) 163 elif type == 'DER': 164 p7 = signer.sign(input_bio) 165 else: 166 logging.error('pkcs7 type error: unknown type') 167 raise BadPKCS7Type('unknown type: ' + type + 168 '; possible values: PEM, DER') 169 except SMIME.SMIME_Error, e: 170 logging.error('smime error: ' + str(e)) 171 raise 172 except SMIME.PKCS7_Error, e: 173 logging.error('pkcs7 error: ' + str(e)) 174 raise 175 Rand.save_file('randpool.dat') 176 return p7
177 178
179 -def verify(input_bio, certstore_path, AUTO_SIGNED_CERT, type):
180 """ 181 Retrieves X.509 certificate from input data and verifies signed message 182 using as certificate store input certstore, inspired by: 183 U{http://code.activestate.com/recipes/285211/}. 184 185 @type input_bio: M2Crypto.BIO 186 @param input_bio: input data to verify 187 @type certstore_path: filepath 188 @param certstore_path: path to the file of the trusted certificates, 189 for example /etc/ssl/certs/ca-certificats.crt. 190 @type type: str 191 @keyword type: specifies the type of input PKCS#7 data: PEM or DER 192 @type AUTO_SIGNED_CERT: boolean 193 @keyword AUTOSIGNED_CERT: to accept or not auto signed certificates as 194 valid for verification. 195 @rtype: list or None 196 @return: a list of verified certificates retrieved from the original data 197 if verification success, else None. 198 @raise CertStoreNotAvailable: the reference certstore for verification is 199 not available. 200 @raise MissingSignerCertificate: the input PKCS#7 is not a signed PKCS#7. 201 """ 202 signer = SMIME.SMIME() 203 cert_store = X509.X509_Store() 204 if not os.access(certstore_path, os.R_OK): 205 logging.error('certstore not available for verify') 206 raise CertStoreNotAvailable('certstore not available %' % 207 (certstore_path)) 208 cert_store.load_info(certstore_path) 209 signer.set_x509_store(cert_store) 210 data_bio = None 211 try: 212 if type == 'PEM': 213 p7, data_bio = SMIME.smime_load_pkcs7_bio(input_bio) 214 elif type == 'DER': 215 p7 = SMIME.PKCS7(m2.pkcs7_read_bio_der(input_bio._ptr()), 1) 216 else: 217 logging.error('pkcs7 type error: unknown type') 218 raise BadPKCS7Type('unknown type: ' + type + 219 '; possible values: PEM, DER') 220 except SMIME.SMIME_Error, e: 221 logging.error('load pkcs7 error: ' + str(e)) 222 raise 223 if data_bio is not None: 224 data = data_bio.read() 225 data_bio = BIO_from_buffer(data) 226 sk3 = p7.get0_signers(X509.X509_Stack()) 227 if len(sk3) == 0: 228 logging.error('missing certificate') 229 raise MissingSignerCertificate('missing certificate') 230 signer_certs = [] 231 for cert in sk3: 232 signer_certs.append( 233 "-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n" 234 % base64.encodestring(cert.as_der())) 235 signer.set_x509_stack(sk3) 236 v = None 237 try: 238 if AUTO_SIGNED_CERT: 239 v = signer.verify(p7, data_bio, flags=SMIME.PKCS7_NOVERIFY) 240 else: 241 v = signer.verify(p7, data_bio) 242 except SMIME.SMIME_Error, e: 243 logging.error('smime error: ' + str(e)) 244 raise 245 except SMIME.PKCS7_Error, e: 246 logging.error('pkcs7 error: ' + str(e)) 247 raise 248 if data_bio is not None and data != v and v is not None: 249 return 250 return signer_certs
251