pysmime.core

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