scapy/crypto/cert.py
author Arnaud Ebalard <arno@natisbad.org>
Mon Nov 02 22:09:11 2009 +0100 (2009-11-02)
changeset 1153 42db888aaf7b
child 1199 3f7190d02f83
permissions -rwxr-xr-x
Added cert.py: X.509 Cert and Key module based on OpenSSL binary
     1 ## This file is part of Scapy
     2 ## See http://www.secdev.org/projects/scapy for more informations
     3 ## Copyright (C) Arnaud Ebalard <arno@natisbad.org>
     4 ## This program is published under a GPLv2 license
     5 
     6 import os, sys, math, socket, struct, sha, hmac, string, time
     7 import random, popen2, tempfile
     8 from scapy.utils import strxor
     9 try:
    10     HAS_HASHLIB=True
    11     import hashlib
    12 except:
    13     HAS_HASHLIB=False
    14 
    15 from Crypto.PublicKey import *
    16 from Crypto.Cipher import *
    17 from Crypto.Hash import *
    18 
    19 # Maximum allowed size in bytes for a certificate file, to avoid
    20 # loading huge file when importing a cert
    21 MAX_KEY_SIZE=50*1024
    22 MAX_CERT_SIZE=50*1024
    23 MAX_CRL_SIZE=10*1024*1024   # some are that big
    24 
    25 #####################################################################
    26 # Some helpers
    27 #####################################################################
    28 
    29 def warning(m):
    30     print "WARNING: %s" % m
    31 
    32 def randstring(l):
    33     """
    34     Returns a random string of length l (l >= 0)
    35     """
    36     tmp = map(lambda x: struct.pack("B", random.randrange(0, 256, 1)), [""]*l)
    37     return "".join(tmp)
    38 
    39 def zerofree_randstring(l):
    40     """
    41     Returns a random string of length l (l >= 0) without zero in it. 
    42     """
    43     tmp = map(lambda x: struct.pack("B", random.randrange(1, 256, 1)), [""]*l)
    44     return "".join(tmp)
    45 
    46 def strand(s1, s2):
    47     """
    48     Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2
    49     must be of same length.
    50     """
    51     return "".join(map(lambda x,y:chr(ord(x)&ord(y)), s1, s2))
    52 
    53 # OS2IP function defined in RFC 3447 for octet string to integer conversion
    54 def pkcs_os2ip(x):
    55     """
    56     Accepts a byte string as input parameter and return the associated long
    57     value:
    58 
    59     Input : x        octet string to be converted
    60 
    61     Output: x        corresponding nonnegative integer
    62 
    63     Reverse function is pkcs_i2osp()
    64     """
    65     return RSA.number.bytes_to_long(x) 
    66 
    67 # IP2OS function defined in RFC 3447 for octet string to integer conversion
    68 def pkcs_i2osp(x,xLen):
    69     """
    70     Converts a long (the first parameter) to the associated byte string
    71     representation of length l (second parameter). Basically, the length
    72     parameters allow the function to perform the associated padding.
    73 
    74     Input : x        nonnegative integer to be converted
    75             xLen     intended length of the resulting octet string
    76 
    77     Output: x        corresponding nonnegative integer
    78 
    79     Reverse function is pkcs_os2ip().
    80     """
    81     z = RSA.number.long_to_bytes(x)
    82     padlen = max(0, xLen-len(z))
    83     return '\x00'*padlen + z
    84 
    85 # for every hash function a tuple is provided, giving access to 
    86 # - hash output length in byte
    87 # - associated hash function that take data to be hashed as parameter
    88 #   XXX I do not provide update() at the moment.
    89 # - DER encoding of the leading bits of digestInfo (the hash value
    90 #   will be concatenated to create the complete digestInfo).
    91 # 
    92 # Notes:
    93 # - MD4 asn.1 value should be verified. Also, as stated in 
    94 #   PKCS#1 v2.1, MD4 should not be used.
    95 # - hashlib is available from http://code.krypto.org/python/hashlib/
    96 # - 'tls' one is the concatenation of both md5 and sha1 hashes used
    97 #   by SSL/TLS when signing/verifying things
    98 _hashFuncParams = {
    99     "md2"    : (16, 
   100                 lambda x: MD2.new(x).digest(), 
   101                 '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10'),
   102     "md4"    : (16, 
   103                 lambda x: MD4.new(x).digest(), 
   104                 '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04\x05\x00\x04\x10'), # is that right ?
   105     "md5"    : (16, 
   106                 lambda x: MD5.new(x).digest(), 
   107                 '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
   108     "sha1"   : (20,
   109                 lambda x: SHA.new(x).digest(), 
   110                 '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
   111     "tls"    : (36,
   112                 lambda x: MD5.new(x).digest() + SHA.new(x).digest(),
   113                 '') }
   114 
   115 if HAS_HASHLIB:
   116     _hashFuncParams["sha224"] = (28, 
   117                 lambda x: hashlib.sha224(x).digest(),
   118                 '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c')
   119     _hashFuncParams["sha256"] = (32, 
   120                 lambda x: hashlib.sha256(x).digest(), 
   121                 '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')
   122     _hashFuncParams["sha384"] = (48, 
   123                 lambda x: hashlib.sha384(x).digest(),
   124                '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30')
   125     _hashFuncParams["sha512"] = (64, 
   126                lambda x: hashlib.sha512(x).digest(),
   127                '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40')
   128 else:
   129     warning("hashlib support is not available. Consider installing it")
   130     warning("if you need sha224, sha256, sha384 and sha512 algs.")
   131     
   132 def pkcs_mgf1(mgfSeed, maskLen, h):
   133     """
   134     Implements generic MGF1 Mask Generation function as described in
   135     Appendix B.2.1 of RFC 3447. The hash function is passed by name.
   136     valid values are 'md2', 'md4', 'md5', 'sha1', 'tls, 'sha256',
   137     'sha384' and 'sha512'. Returns None on error.
   138 
   139     Input:
   140        mgfSeed: seed from which mask is generated, an octet string
   141        maskLen: intended length in octets of the mask, at most 2^32 * hLen
   142                 hLen (see below)
   143        h      : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
   144                 'sha256', 'sha384'). hLen denotes the length in octets of
   145                 the hash function output.
   146 
   147     Output:
   148        an octet string of length maskLen
   149     """
   150 
   151     # steps are those of Appendix B.2.1
   152     if not _hashFuncParams.has_key(h):
   153         warning("pkcs_mgf1: invalid hash (%s) provided")
   154         return None
   155     hLen = _hashFuncParams[h][0]
   156     hFunc = _hashFuncParams[h][1]
   157     if maskLen > 2**32 * hLen:                               # 1)
   158         warning("pkcs_mgf1: maskLen > 2**32 * hLen")         
   159         return None
   160     T = ""                                                   # 2)
   161     maxCounter = math.ceil(float(maskLen) / float(hLen))     # 3)
   162     counter = 0
   163     while counter < maxCounter:
   164         C = pkcs_i2osp(counter, 4)
   165         T += hFunc(mgfSeed + C)
   166         counter += 1
   167     return T[:maskLen]
   168 
   169 
   170 def pkcs_emsa_pss_encode(M, emBits, h, mgf, sLen): 
   171     """
   172     Implements EMSA-PSS-ENCODE() function described in Sect. 9.1.1 of RFC 3447
   173 
   174     Input:
   175        M     : message to be encoded, an octet string
   176        emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM),
   177                where EM is the encoded message, output of the function.
   178        h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
   179                'sha256', 'sha384'). hLen denotes the length in octets of
   180                the hash function output. 
   181        mgf   : the mask generation function f : seed, maskLen -> mask
   182        sLen  : intended length in octets of the salt
   183 
   184     Output:
   185        encoded message, an octet string of length emLen = ceil(emBits/8)
   186 
   187     On error, None is returned.
   188     """
   189 
   190     # 1) is not done
   191     hLen = _hashFuncParams[h][0]                             # 2)
   192     hFunc = _hashFuncParams[h][1]
   193     mHash = hFunc(M)
   194     emLen = int(math.ceil(emBits/8.))
   195     if emLen < hLen + sLen + 2:                              # 3)
   196         warning("encoding error (emLen < hLen + sLen + 2)")
   197         return None
   198     salt = randstring(sLen)                                  # 4)
   199     MPrime = '\x00'*8 + mHash + salt                         # 5)
   200     H = hFunc(MPrime)                                        # 6)
   201     PS = '\x00'*(emLen - sLen - hLen - 2)                    # 7)
   202     DB = PS + '\x01' + salt                                  # 8)
   203     dbMask = mgf(H, emLen - hLen - 1)                        # 9)
   204     maskedDB = strxor(DB, dbMask)                            # 10)
   205     l = (8*emLen - emBits)/8                                 # 11)
   206     rem = 8*emLen - emBits - 8*l # additionnal bits
   207     andMask = l*'\x00'
   208     if rem:
   209         j = chr(reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem))))
   210         andMask += j
   211         l += 1
   212     maskedDB = strand(maskedDB[:l], andMask) + maskedDB[l:]
   213     EM = maskedDB + H + '\xbc'                               # 12)
   214     return EM                                                # 13)
   215 
   216 
   217 def pkcs_emsa_pss_verify(M, EM, emBits, h, mgf, sLen):
   218     """
   219     Implements EMSA-PSS-VERIFY() function described in Sect. 9.1.2 of RFC 3447
   220 
   221     Input:
   222        M     : message to be encoded, an octet string
   223        EM    : encoded message, an octet string of length emLen = ceil(emBits/8)
   224        emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM)
   225        h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
   226                'sha256', 'sha384'). hLen denotes the length in octets of
   227                the hash function output.
   228        mgf   : the mask generation function f : seed, maskLen -> mask
   229        sLen  : intended length in octets of the salt
   230 
   231     Output:
   232        True if the verification is ok, False otherwise.
   233     """
   234     
   235     # 1) is not done
   236     hLen = _hashFuncParams[h][0]                             # 2)
   237     hFunc = _hashFuncParams[h][1]
   238     mHash = hFunc(M)
   239     emLen = int(math.ceil(emBits/8.))                        # 3)
   240     if emLen < hLen + sLen + 2:
   241         return False
   242     if EM[-1] != '\xbc':                                     # 4)
   243         return False
   244     l = emLen - hLen - 1                                     # 5)
   245     maskedDB = EM[:l]
   246     H = EM[l:l+hLen]
   247     l = (8*emLen - emBits)/8                                 # 6)
   248     rem = 8*emLen - emBits - 8*l # additionnal bits
   249     andMask = l*'\xff'
   250     if rem:
   251         val = reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem)))
   252         j = chr(~val & 0xff)
   253         andMask += j
   254         l += 1
   255     if strand(maskedDB[:l], andMask) != '\x00'*l:
   256         return False
   257     dbMask = mgf(H, emLen - hLen - 1)                        # 7)
   258     DB = strxor(maskedDB, dbMask)                            # 8)
   259     l = (8*emLen - emBits)/8                                 # 9)
   260     rem = 8*emLen - emBits - 8*l # additionnal bits
   261     andMask = l*'\x00'
   262     if rem:
   263         j = chr(reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem))))
   264         andMask += j
   265         l += 1
   266     DB = strand(DB[:l], andMask) + DB[l:]
   267     l = emLen - hLen - sLen - 1                              # 10)
   268     if DB[:l] != '\x00'*(l-1) + '\x01':
   269         return False
   270     salt = DB[-sLen:]                                        # 11)
   271     MPrime = '\x00'*8 + mHash + salt                         # 12)
   272     HPrime = hFunc(MPrime)                                   # 13)
   273     return H == HPrime                                       # 14)
   274 
   275 
   276 def pkcs_emsa_pkcs1_v1_5_encode(M, emLen, h): # section 9.2 of RFC 3447
   277     """
   278     Implements EMSA-PKCS1-V1_5-ENCODE() function described in Sect.
   279     9.2 of RFC 3447.
   280 
   281     Input:
   282        M    : message to be encode, an octet string
   283        emLen: intended length in octets of the encoded message, at least
   284               tLen + 11, where tLen is the octet length of the DER encoding
   285               T of a certain value computed during the encoding operation.
   286        h    : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
   287               'sha256', 'sha384'). hLen denotes the length in octets of
   288               the hash function output.
   289 
   290     Output:
   291        encoded message, an octet string of length emLen
   292 
   293     On error, None is returned.
   294     """
   295     hLen = _hashFuncParams[h][0]                             # 1)
   296     hFunc = _hashFuncParams[h][1]
   297     H = hFunc(M)
   298     hLeadingDigestInfo = _hashFuncParams[h][2]               # 2)
   299     T = hLeadingDigestInfo + H
   300     tLen = len(T)
   301     if emLen < tLen + 11:                                    # 3)
   302         warning("pkcs_emsa_pkcs1_v1_5_encode: intended encoded message length too short")
   303         return None
   304     PS = '\xff'*(emLen - tLen - 3)                           # 4)
   305     EM = '\x00' + '\x01' + PS + '\x00' + T                   # 5)
   306     return EM                                                # 6)
   307 
   308 
   309 # XXX should add other pgf1 instance in a better fashion.
   310 
   311 def create_ca_file(anchor_list, filename):
   312     """
   313     Concatenate all the certificates (PEM format for the export) in
   314     'anchor_list' and write the result to file 'filename'. On success
   315     'filename' is returned, None otherwise.
   316 
   317     If you are used to OpenSSL tools, this function builds a CAfile
   318     that can be used for certificate and CRL check.
   319 
   320     Also see create_temporary_ca_file().
   321     """
   322     try:
   323         f = open(filename, "w")
   324         for a in anchor_list:
   325             s = a.output(fmt="PEM")
   326             f.write(s)
   327         f.close()
   328     except:
   329         return None
   330     return filename
   331 
   332 def create_temporary_ca_file(anchor_list):
   333     """
   334     Concatenate all the certificates (PEM format for the export) in
   335     'anchor_list' and write the result to file to a temporary file
   336     using mkstemp() from tempfile module. On success 'filename' is
   337     returned, None otherwise.
   338 
   339     If you are used to OpenSSL tools, this function builds a CAfile
   340     that can be used for certificate and CRL check.
   341 
   342     Also see create_temporary_ca_file().
   343     """
   344     try:
   345         f, fname = tempfile.mkstemp()
   346         for a in anchor_list:
   347             s = a.output(fmt="PEM")
   348             l = os.write(f, s)
   349         os.close(f)
   350     except:
   351         return None
   352     return fname
   353 
   354 def create_temporary_ca_path(anchor_list, folder):
   355     """
   356     Create a CA path folder as defined in OpenSSL terminology, by
   357     storing all certificates in 'anchor_list' list in PEM format
   358     under provided 'folder' and then creating the associated links
   359     using the hash as usually done by c_rehash.
   360 
   361     Note that you can also include CRL in 'anchor_list'. In that
   362     case, they will also be stored under 'folder' and associated
   363     links will be created.
   364 
   365     In folder, the files are created with names of the form
   366     0...ZZ.pem. If you provide an empty list, folder will be created
   367     if it does not already exist, but that's all.
   368 
   369     The number of certificates written to folder is returned on
   370     success, None on error.
   371     """
   372     # We should probably avoid writing duplicate anchors and also
   373     # check if they are all certs.
   374     try:
   375         if not os.path.isdir(folder):
   376             os.makedirs(folder)
   377     except:
   378         return None
   379     
   380     l = len(anchor_list)
   381     if l == 0:
   382         return None
   383     fmtstr = "%%0%sd.pem" % math.ceil(math.log(l, 10))
   384     i = 0
   385     try:
   386         for a in anchor_list:
   387             fname = os.path.join(folder, fmtstr % i)
   388             f = open(fname, "w")
   389             s = a.output(fmt="PEM")
   390             f.write(s)
   391             f.close()
   392             i += 1
   393     except:
   394         return None
   395 
   396     r,w=popen2.popen2("c_rehash %s" % folder)
   397     r.close(); w.close()
   398 
   399     return l
   400 
   401 
   402 #####################################################################
   403 # Public Key Cryptography related stuff
   404 #####################################################################
   405 
   406 class OSSLHelper:
   407     def _apply_ossl_cmd(self, osslcmd, rawdata):
   408 	r,w=popen2.popen2(osslcmd)
   409 	w.write(rawdata)
   410 	w.close()
   411 	res = r.read()
   412 	r.close()
   413 	return res
   414 
   415 class _EncryptAndVerify:
   416     ### Below are encryption methods
   417 
   418     def _rsaep(self, m):
   419         """
   420         Internal method providing raw RSA encryption, i.e. simple modular
   421         exponentiation of the given message representative 'm', a long
   422         between 0 and n-1.
   423 
   424         This is the encryption primitive RSAEP described in PKCS#1 v2.1,
   425         i.e. RFC 3447 Sect. 5.1.1.
   426 
   427         Input:
   428            m: message representative, a long between 0 and n-1, where
   429               n is the key modulus.
   430 
   431         Output:
   432            ciphertext representative, a long between 0 and n-1
   433 
   434         Not intended to be used directly. Please, see encrypt() method.
   435         """
   436 
   437         n = self.modulus
   438         if type(m) is int:
   439             m = long(m)
   440         if type(m) is not long or m > n-1:
   441             warning("Key._rsaep() expects a long between 0 and n-1")
   442             return None
   443 
   444         return self.key.encrypt(m, "")[0]
   445 
   446 
   447     def _rsaes_pkcs1_v1_5_encrypt(self, M):
   448         """
   449         Implements RSAES-PKCS1-V1_5-ENCRYPT() function described in section
   450         7.2.1 of RFC 3447.
   451 
   452         Input:
   453            M: message to be encrypted, an octet string of length mLen, where
   454               mLen <= k - 11 (k denotes the length in octets of the key modulus)
   455 
   456         Output:
   457            ciphertext, an octet string of length k
   458 
   459         On error, None is returned.
   460         """
   461 
   462         # 1) Length checking
   463         mLen = len(M)
   464         k = self.modulusLen / 8
   465         if mLen > k - 11:
   466             warning("Key._rsaes_pkcs1_v1_5_encrypt(): message too "
   467                     "long (%d > %d - 11)" % (mLen, k))
   468             return None
   469 
   470         # 2) EME-PKCS1-v1_5 encoding
   471         PS = zerofree_randstring(k - mLen - 3)      # 2.a)
   472         EM = '\x00' + '\x02' + PS + '\x00' + M      # 2.b)
   473 
   474         # 3) RSA encryption
   475         m = pkcs_os2ip(EM)                          # 3.a)
   476         c = self._rsaep(m)                          # 3.b)
   477         C = pkcs_i2osp(c, k)                        # 3.c)
   478 
   479         return C                                    # 4)
   480 
   481 
   482     def _rsaes_oaep_encrypt(self, M, h=None, mgf=None, L=None):
   483         """
   484         Internal method providing RSAES-OAEP-ENCRYPT as defined in Sect.
   485         7.1.1 of RFC 3447. Not intended to be used directly. Please, see
   486         encrypt() method for type "OAEP".
   487 
   488 
   489         Input:
   490            M  : message to be encrypted, an octet string of length mLen
   491                 where mLen <= k - 2*hLen - 2 (k denotes the length in octets
   492                 of the RSA modulus and hLen the length in octets of the hash
   493                 function output)
   494            h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
   495                 'sha256', 'sha384'). hLen denotes the length in octets of
   496                 the hash function output. 'sha1' is used by default if not
   497                 provided.
   498            mgf: the mask generation function f : seed, maskLen -> mask
   499            L  : optional label to be associated with the message; the default
   500                 value for L, if not provided is the empty string
   501 
   502         Output:
   503            ciphertext, an octet string of length k
   504 
   505         On error, None is returned.
   506         """
   507         # The steps below are the one described in Sect. 7.1.1 of RFC 3447.
   508         # 1) Length Checking
   509                                                     # 1.a) is not done
   510         mLen = len(M)
   511         if h is None:
   512             h = "sha1"
   513         if not _hashFuncParams.has_key(h):
   514             warning("Key._rsaes_oaep_encrypt(): unknown hash function %s.", h)
   515             return None
   516         hLen = _hashFuncParams[h][0]
   517         hFun = _hashFuncParams[h][1]
   518         k = self.modulusLen / 8
   519         if mLen > k - 2*hLen - 2:                   # 1.b)
   520             warning("Key._rsaes_oaep_encrypt(): message too long.")
   521             return None
   522         
   523         # 2) EME-OAEP encoding
   524         if L is None:                               # 2.a)
   525             L = ""
   526         lHash = hFun(L)
   527         PS = '\x00'*(k - mLen - 2*hLen - 2)         # 2.b)
   528         DB = lHash + PS + '\x01' + M                # 2.c)
   529         seed = randstring(hLen)                     # 2.d)
   530         if mgf is None:                             # 2.e)
   531             mgf = lambda x,y: pkcs_mgf1(x,y,h)
   532         dbMask = mgf(seed, k - hLen - 1)
   533         maskedDB = strxor(DB, dbMask)               # 2.f)
   534         seedMask = mgf(maskedDB, hLen)              # 2.g)
   535         maskedSeed = strxor(seed, seedMask)         # 2.h)
   536         EM = '\x00' + maskedSeed + maskedDB         # 2.i)
   537 
   538         # 3) RSA Encryption
   539         m = pkcs_os2ip(EM)                          # 3.a)
   540         c = self._rsaep(m)                          # 3.b)
   541         C = pkcs_i2osp(c, k)                        # 3.c)
   542 
   543         return C                                    # 4)
   544 
   545 
   546     def encrypt(self, m, t=None, h=None, mgf=None, L=None):
   547         """
   548         Encrypt message 'm' using 't' encryption scheme where 't' can be:
   549 
   550         - None: the message 'm' is directly applied the RSAEP encryption
   551                 primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
   552                 Sect 5.1.1. Simply put, the message undergo a modular
   553                 exponentiation using the public key. Additionnal method
   554                 parameters are just ignored.
   555 
   556         - 'pkcs': the message 'm' is applied RSAES-PKCS1-V1_5-ENCRYPT encryption
   557                 scheme as described in section 7.2.1 of RFC 3447. In that
   558                 context, other parameters ('h', 'mgf', 'l') are not used.
   559 
   560         - 'oaep': the message 'm' is applied the RSAES-OAEP-ENCRYPT encryption
   561                 scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
   562                 7.1.1. In that context,
   563 
   564                 o 'h' parameter provides the name of the hash method to use.
   565                   Possible values are "md2", "md4", "md5", "sha1", "tls",
   566                   "sha224", "sha256", "sha384" and "sha512". if none is provided,
   567                   sha1 is used.
   568 
   569                 o 'mgf' is the mask generation function. By default, mgf
   570                   is derived from the provided hash function using the
   571                   generic MGF1 (see pkcs_mgf1() for details).
   572 
   573                 o 'L' is the optional label to be associated with the
   574                   message. If not provided, the default value is used, i.e
   575                   the empty string. No check is done on the input limitation
   576                   of the hash function regarding the size of 'L' (for
   577                   instance, 2^61 - 1 for SHA-1). You have been warned.
   578         """
   579 
   580         if t is None: # Raw encryption
   581             m = pkcs_os2ip(m)
   582             c = self._rsaep(m)
   583             return pkcs_i2osp(c, self.modulusLen/8)
   584         
   585         elif t == "pkcs":
   586             return self._rsaes_pkcs1_v1_5_encrypt(m)
   587         
   588         elif t == "oaep":
   589             return self._rsaes_oaep_encrypt(m, h, mgf, L)
   590 
   591         else:
   592             warning("Key.encrypt(): Unknown encryption type (%s) provided" % t)
   593             return None
   594 
   595     ### Below are verification related methods
   596 
   597     def _rsavp1(self, s):
   598         """
   599         Internal method providing raw RSA verification, i.e. simple modular
   600         exponentiation of the given signature representative 'c', an integer
   601         between 0 and n-1.
   602 
   603         This is the signature verification primitive RSAVP1 described in
   604         PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.2.
   605 
   606         Input:
   607           s: signature representative, an integer between 0 and n-1,
   608              where n is the key modulus.
   609 
   610         Output:
   611            message representative, an integer between 0 and n-1
   612 
   613         Not intended to be used directly. Please, see verify() method.
   614         """
   615         return self._rsaep(s)
   616 
   617     def _rsassa_pss_verify(self, M, S, h=None, mgf=None, sLen=None):
   618         """
   619         Implements RSASSA-PSS-VERIFY() function described in Sect 8.1.2
   620         of RFC 3447
   621 
   622         Input:
   623            M: message whose signature is to be verified
   624            S: signature to be verified, an octet string of length k, where k
   625               is the length in octets of the RSA modulus n.
   626 
   627         Output:
   628            True is the signature is valid. False otherwise.
   629         """
   630 
   631         # Set default parameters if not provided
   632         if h is None: # By default, sha1
   633             h = "sha1"
   634         if not _hashFuncParams.has_key(h):
   635             warning("Key._rsassa_pss_verify(): unknown hash function "
   636                     "provided (%s)" % h)
   637             return False
   638         if mgf is None: # use mgf1 with underlying hash function
   639             mgf = lambda x,y: pkcs_mgf1(x, y, h)
   640         if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
   641             hLen = _hashFuncParams[h][0]
   642             sLen = hLen
   643 
   644         # 1) Length checking
   645         modBits = self.modulusLen
   646         k = modBits / 8
   647         if len(S) != k:
   648             return False
   649 
   650         # 2) RSA verification
   651         s = pkcs_os2ip(S)                           # 2.a)
   652         m = self._rsavp1(s)                         # 2.b)
   653         emLen = math.ceil((modBits - 1) / 8.)       # 2.c)
   654         EM = pkcs_i2osp(m, emLen) 
   655 
   656         # 3) EMSA-PSS verification
   657         Result = pkcs_emsa_pss_verify(M, EM, modBits - 1, h, mgf, sLen)
   658 
   659         return Result                               # 4)
   660 
   661 
   662     def _rsassa_pkcs1_v1_5_verify(self, M, S, h):
   663         """
   664         Implements RSASSA-PKCS1-v1_5-VERIFY() function as described in
   665         Sect. 8.2.2 of RFC 3447.
   666 
   667         Input:
   668            M: message whose signature is to be verified, an octet string
   669            S: signature to be verified, an octet string of length k, where
   670               k is the length in octets of the RSA modulus n
   671            h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
   672                 'sha256', 'sha384').
   673            
   674         Output:
   675            True if the signature is valid. False otherwise.
   676         """
   677 
   678         # 1) Length checking
   679         k = self.modulusLen / 8
   680         if len(S) != k:
   681             warning("invalid signature (len(S) != k)")
   682             return False
   683 
   684         # 2) RSA verification
   685         s = pkcs_os2ip(S)                           # 2.a)
   686         m = self._rsavp1(s)                         # 2.b)
   687         EM = pkcs_i2osp(m, k)                       # 2.c)
   688 
   689         # 3) EMSA-PKCS1-v1_5 encoding
   690         EMPrime = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
   691         if EMPrime is None:
   692             warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.")
   693             return False
   694 
   695         # 4) Comparison
   696         return EM == EMPrime
   697 
   698 
   699     def verify(self, M, S, t=None, h=None, mgf=None, sLen=None):
   700         """
   701         Verify alleged signature 'S' is indeed the signature of message 'M' using
   702         't' signature scheme where 't' can be:
   703 
   704         - None: the alleged signature 'S' is directly applied the RSAVP1 signature
   705                 primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
   706                 5.2.1. Simply put, the provided signature is applied a moular
   707                 exponentiation using the public key. Then, a comparison of the
   708                 result is done against 'M'. On match, True is returned.
   709                 Additionnal method parameters are just ignored.
   710 
   711         - 'pkcs': the alleged signature 'S' and message 'M' are applied
   712                 RSASSA-PKCS1-v1_5-VERIFY signature verification scheme as
   713                 described in Sect. 8.2.2 of RFC 3447. In that context,
   714                 the hash function name is passed using 'h'. Possible values are
   715                 "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384"
   716                 and "sha512". If none is provided, sha1 is used. Other additionnal
   717                 parameters are ignored.
   718 
   719         - 'pss': the alleged signature 'S' and message 'M' are applied
   720                 RSASSA-PSS-VERIFY signature scheme as described in Sect. 8.1.2.
   721                 of RFC 3447. In that context,
   722 
   723                 o 'h' parameter provides the name of the hash method to use.
   724                    Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224",
   725                    "sha256", "sha384" and "sha512". if none is provided, sha1
   726                    is used. 
   727 
   728                 o 'mgf' is the mask generation function. By default, mgf
   729                    is derived from the provided hash function using the
   730                    generic MGF1 (see pkcs_mgf1() for details).
   731 
   732                 o 'sLen' is the length in octet of the salt. You can overload the
   733                   default value (the octet length of the hash value for provided
   734                   algorithm) by providing another one with that parameter.
   735         """
   736         if t is None: # RSAVP1
   737             S = pkcs_os2ip(S)
   738             n = self.modulus
   739             if S > n-1:
   740                 warning("Signature to be verified is too long for key modulus")
   741                 return False
   742             m = self._rsavp1(S)
   743             if m is None:
   744                 return False
   745             l = int(math.ceil(math.log(m, 2) / 8.)) # Hack
   746             m = pkcs_i2osp(m, l)
   747             return M == m
   748 
   749         elif t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY
   750             if h is None:
   751                 h = "sha1"
   752             return self._rsassa_pkcs1_v1_5_verify(M, S, h)
   753 
   754         elif t == "pss": # RSASSA-PSS-VERIFY
   755             return self._rsassa_pss_verify(M, S, h, mgf, sLen)
   756 
   757         else:
   758             warning("Key.verify(): Unknown signature type (%s) provided" % t)
   759             return None
   760     
   761 class _DecryptAndSignMethods(OSSLHelper):
   762     ### Below are decryption related methods. Encryption ones are inherited
   763     ### from PubKey
   764 
   765     def _rsadp(self, c):
   766         """
   767         Internal method providing raw RSA decryption, i.e. simple modular
   768         exponentiation of the given ciphertext representative 'c', a long
   769         between 0 and n-1.
   770 
   771         This is the decryption primitive RSADP described in PKCS#1 v2.1,
   772         i.e. RFC 3447 Sect. 5.1.2.
   773 
   774         Input:
   775            c: ciphertest representative, a long between 0 and n-1, where
   776               n is the key modulus.
   777 
   778         Output:
   779            ciphertext representative, a long between 0 and n-1
   780 
   781         Not intended to be used directly. Please, see encrypt() method.
   782         """
   783 
   784         n = self.modulus
   785         if type(c) is int:
   786             c = long(c)        
   787         if type(c) is not long or c > n-1:
   788             warning("Key._rsaep() expects a long between 0 and n-1")
   789             return None
   790 
   791         return self.key.decrypt(c)    
   792 
   793 
   794     def _rsaes_pkcs1_v1_5_decrypt(self, C):
   795         """
   796         Implements RSAES-PKCS1-V1_5-DECRYPT() function described in section
   797         7.2.2 of RFC 3447.
   798 
   799         Input:
   800            C: ciphertext to be decrypted, an octet string of length k, where
   801               k is the length in octets of the RSA modulus n.
   802 
   803         Output:
   804            an octet string of length k at most k - 11
   805 
   806         on error, None is returned.
   807         """
   808         
   809         # 1) Length checking
   810         cLen = len(C)
   811         k = self.modulusLen / 8
   812         if cLen != k or k < 11:
   813             warning("Key._rsaes_pkcs1_v1_5_decrypt() decryption error "
   814                     "(cLen != k or k < 11)")
   815             return None
   816 
   817         # 2) RSA decryption
   818         c = pkcs_os2ip(C)                           # 2.a)
   819         m = self._rsadp(c)                          # 2.b)
   820         EM = pkcs_i2osp(m, k)                       # 2.c)
   821 
   822         # 3) EME-PKCS1-v1_5 decoding
   823 
   824         # I am aware of the note at the end of 7.2.2 regarding error
   825         # conditions reporting but the one provided below are for _local_
   826         # debugging purposes. --arno
   827         
   828         if EM[0] != '\x00':
   829             warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
   830                     "(first byte is not 0x00)")
   831             return None
   832 
   833         if EM[1] != '\x02':
   834             warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
   835                     "(second byte is not 0x02)")
   836             return None
   837 
   838         tmp = EM[2:].split('\x00', 1)
   839         if len(tmp) != 2:
   840             warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
   841                     "(no 0x00 to separate PS from M)")
   842             return None
   843 
   844         PS, M = tmp
   845         if len(PS) < 8:
   846             warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
   847                     "(PS is less than 8 byte long)")
   848             return None
   849 
   850         return M                                    # 4)
   851 
   852 
   853     def _rsaes_oaep_decrypt(self, C, h=None, mgf=None, L=None):
   854         """
   855         Internal method providing RSAES-OAEP-DECRYPT as defined in Sect.
   856         7.1.2 of RFC 3447. Not intended to be used directly. Please, see
   857         encrypt() method for type "OAEP".
   858 
   859 
   860         Input:
   861            C  : ciphertext to be decrypted, an octet string of length k, where
   862                 k = 2*hLen + 2 (k denotes the length in octets of the RSA modulus
   863                 and hLen the length in octets of the hash function output)
   864            h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
   865                 'sha256', 'sha384'). 'sha1' is used if none is provided.
   866            mgf: the mask generation function f : seed, maskLen -> mask
   867            L  : optional label whose association with the message is to be
   868                 verified; the default value for L, if not provided is the empty
   869                 string.
   870 
   871         Output:
   872            message, an octet string of length k mLen, where mLen <= k - 2*hLen - 2
   873 
   874         On error, None is returned.
   875         """
   876         # The steps below are the one described in Sect. 7.1.2 of RFC 3447.
   877 
   878         # 1) Length Checking
   879                                                     # 1.a) is not done
   880         if h is None:
   881             h = "sha1"
   882         if not _hashFuncParams.has_key(h):
   883             warning("Key._rsaes_oaep_decrypt(): unknown hash function %s.", h)
   884             return None
   885         hLen = _hashFuncParams[h][0]
   886         hFun = _hashFuncParams[h][1]
   887         k = self.modulusLen / 8
   888         cLen = len(C)
   889         if cLen != k:                               # 1.b)
   890             warning("Key._rsaes_oaep_decrypt(): decryption error. "
   891                     "(cLen != k)")
   892             return None
   893         if k < 2*hLen + 2:
   894             warning("Key._rsaes_oaep_decrypt(): decryption error. "
   895                     "(k < 2*hLen + 2)")
   896             return None
   897 
   898         # 2) RSA decryption
   899         c = pkcs_os2ip(C)                           # 2.a)
   900         m = self._rsadp(c)                          # 2.b)
   901         EM = pkcs_i2osp(m, k)                       # 2.c)
   902 
   903         # 3) EME-OAEP decoding
   904         if L is None:                               # 3.a)
   905             L = ""
   906         lHash = hFun(L)
   907         Y = EM[:1]                                  # 3.b)
   908         if Y != '\x00':
   909             warning("Key._rsaes_oaep_decrypt(): decryption error. "
   910                     "(Y is not zero)")
   911             return None
   912         maskedSeed = EM[1:1+hLen]
   913         maskedDB = EM[1+hLen:]
   914         if mgf is None:
   915             mgf = lambda x,y: pkcs_mgf1(x, y, h)
   916         seedMask = mgf(maskedDB, hLen)              # 3.c)
   917         seed = strxor(maskedSeed, seedMask)         # 3.d)
   918         dbMask = mgf(seed, k - hLen - 1)            # 3.e)
   919         DB = strxor(maskedDB, dbMask)               # 3.f)
   920 
   921         # I am aware of the note at the end of 7.1.2 regarding error
   922         # conditions reporting but the one provided below are for _local_
   923         # debugging purposes. --arno
   924 
   925         lHashPrime = DB[:hLen]                      # 3.g)
   926         tmp = DB[hLen:].split('\x01', 1)
   927         if len(tmp) != 2:
   928             warning("Key._rsaes_oaep_decrypt(): decryption error. "
   929                     "(0x01 separator not found)")
   930             return None
   931         PS, M = tmp
   932         if PS != '\x00'*len(PS):
   933             warning("Key._rsaes_oaep_decrypt(): decryption error. "
   934                     "(invalid padding string)")
   935             return None
   936         if lHash != lHashPrime:
   937             warning("Key._rsaes_oaep_decrypt(): decryption error. "
   938                     "(invalid hash)")
   939             return None            
   940         return M                                    # 4)
   941 
   942 
   943     def decrypt(self, C, t=None, h=None, mgf=None, L=None):
   944         """
   945         Decrypt ciphertext 'C' using 't' decryption scheme where 't' can be:
   946 
   947         - None: the ciphertext 'C' is directly applied the RSADP decryption
   948                 primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
   949                 Sect 5.1.2. Simply, put the message undergo a modular
   950                 exponentiation using the private key. Additionnal method
   951                 parameters are just ignored.
   952 
   953         - 'pkcs': the ciphertext 'C' is applied RSAES-PKCS1-V1_5-DECRYPT
   954                 decryption scheme as described in section 7.2.2 of RFC 3447.
   955                 In that context, other parameters ('h', 'mgf', 'l') are not
   956                 used.
   957 
   958         - 'oaep': the ciphertext 'C' is applied the RSAES-OAEP-DECRYPT decryption
   959                 scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
   960                 7.1.2. In that context,
   961 
   962                 o 'h' parameter provides the name of the hash method to use.
   963                   Possible values are "md2", "md4", "md5", "sha1", "tls",
   964                   "sha224", "sha256", "sha384" and "sha512". if none is provided,
   965                   sha1 is used by default.
   966 
   967                 o 'mgf' is the mask generation function. By default, mgf
   968                   is derived from the provided hash function using the
   969                   generic MGF1 (see pkcs_mgf1() for details).
   970 
   971                 o 'L' is the optional label to be associated with the
   972                   message. If not provided, the default value is used, i.e
   973                   the empty string. No check is done on the input limitation
   974                   of the hash function regarding the size of 'L' (for
   975                   instance, 2^61 - 1 for SHA-1). You have been warned.        
   976         """
   977         if t is None:
   978             C = pkcs_os2ip(C)
   979             c = self._rsadp(C)
   980             l = int(math.ceil(math.log(c, 2) / 8.)) # Hack
   981             return pkcs_i2osp(c, l)
   982 
   983         elif t == "pkcs":
   984             return self._rsaes_pkcs1_v1_5_decrypt(C)
   985 
   986         elif t == "oaep":
   987             return self._rsaes_oaep_decrypt(C, h, mgf, L)
   988 
   989         else:
   990             warning("Key.decrypt(): Unknown decryption type (%s) provided" % t)
   991             return None
   992 
   993     ### Below are signature related methods. Verification ones are inherited from
   994     ### PubKey
   995 
   996     def _rsasp1(self, m):
   997         """
   998         Internal method providing raw RSA signature, i.e. simple modular
   999         exponentiation of the given message representative 'm', an integer
  1000         between 0 and n-1.
  1001 
  1002         This is the signature primitive RSASP1 described in PKCS#1 v2.1,
  1003         i.e. RFC 3447 Sect. 5.2.1.
  1004 
  1005         Input:
  1006            m: message representative, an integer between 0 and n-1, where
  1007               n is the key modulus.
  1008 
  1009         Output:
  1010            signature representative, an integer between 0 and n-1
  1011 
  1012         Not intended to be used directly. Please, see sign() method.
  1013         """
  1014         return self._rsadp(m)
  1015 
  1016 
  1017     def _rsassa_pss_sign(self, M, h=None, mgf=None, sLen=None):
  1018         """
  1019         Implements RSASSA-PSS-SIGN() function described in Sect. 8.1.1 of
  1020         RFC 3447.
  1021 
  1022         Input:
  1023            M: message to be signed, an octet string
  1024 
  1025         Output:
  1026            signature, an octet string of length k, where k is the length in
  1027            octets of the RSA modulus n.
  1028 
  1029         On error, None is returned.
  1030         """
  1031 
  1032         # Set default parameters if not provided
  1033         if h is None: # By default, sha1
  1034             h = "sha1"
  1035         if not _hashFuncParams.has_key(h):
  1036             warning("Key._rsassa_pss_sign(): unknown hash function "
  1037                     "provided (%s)" % h)
  1038             return None
  1039         if mgf is None: # use mgf1 with underlying hash function
  1040             mgf = lambda x,y: pkcs_mgf1(x, y, h)
  1041         if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
  1042             hLen = _hashFuncParams[h][0]
  1043             sLen = hLen
  1044 
  1045         # 1) EMSA-PSS encoding
  1046         modBits = self.modulusLen
  1047         k = modBits / 8
  1048         EM = pkcs_emsa_pss_encode(M, modBits - 1, h, mgf, sLen)
  1049         if EM is None:
  1050             warning("Key._rsassa_pss_sign(): unable to encode")
  1051             return None
  1052 
  1053         # 2) RSA signature
  1054         m = pkcs_os2ip(EM)                          # 2.a)
  1055         s = self._rsasp1(m)                         # 2.b)
  1056         S = pkcs_i2osp(s, k)                        # 2.c)
  1057 
  1058         return S                                    # 3)
  1059 
  1060 
  1061     def _rsassa_pkcs1_v1_5_sign(self, M, h):
  1062         """
  1063         Implements RSASSA-PKCS1-v1_5-SIGN() function as described in
  1064         Sect. 8.2.1 of RFC 3447.
  1065 
  1066         Input:
  1067            M: message to be signed, an octet string
  1068            h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls'
  1069                 'sha256', 'sha384').
  1070            
  1071         Output:
  1072            the signature, an octet string.
  1073         """
  1074         
  1075         # 1) EMSA-PKCS1-v1_5 encoding
  1076         k = self.modulusLen / 8
  1077         EM = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
  1078         if EM is None:
  1079             warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode")
  1080             return None
  1081 
  1082         # 2) RSA signature
  1083         m = pkcs_os2ip(EM)                          # 2.a)
  1084         s = self._rsasp1(m)                         # 2.b)
  1085         S = pkcs_i2osp(s, k)                        # 2.c)
  1086 
  1087         return S                                    # 3)
  1088 
  1089 
  1090     def sign(self, M, t=None, h=None, mgf=None, sLen=None):
  1091         """
  1092         Sign message 'M' using 't' signature scheme where 't' can be:
  1093 
  1094         - None: the message 'M' is directly applied the RSASP1 signature
  1095                 primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
  1096                 5.2.1. Simply put, the message undergo a modular exponentiation
  1097                 using the private key. Additionnal method parameters are just
  1098                 ignored.
  1099 
  1100         - 'pkcs': the message 'M' is applied RSASSA-PKCS1-v1_5-SIGN signature
  1101                 scheme as described in Sect. 8.2.1 of RFC 3447. In that context,
  1102                 the hash function name is passed using 'h'. Possible values are
  1103                 "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384"
  1104                 and "sha512". If none is provided, sha1 is used. Other additionnal 
  1105                 parameters are ignored.
  1106 
  1107         - 'pss' : the message 'M' is applied RSASSA-PSS-SIGN signature scheme as
  1108                 described in Sect. 8.1.1. of RFC 3447. In that context,
  1109 
  1110                 o 'h' parameter provides the name of the hash method to use.
  1111                    Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224",
  1112                    "sha256", "sha384" and "sha512". if none is provided, sha1
  1113                    is used. 
  1114 
  1115                 o 'mgf' is the mask generation function. By default, mgf
  1116                    is derived from the provided hash function using the
  1117                    generic MGF1 (see pkcs_mgf1() for details).
  1118 
  1119                 o 'sLen' is the length in octet of the salt. You can overload the
  1120                   default value (the octet length of the hash value for provided
  1121                   algorithm) by providing another one with that parameter.
  1122         """
  1123 
  1124         if t is None: # RSASP1
  1125             M = pkcs_os2ip(M)
  1126             n = self.modulus
  1127             if M > n-1:
  1128                 warning("Message to be signed is too long for key modulus")
  1129                 return None
  1130             s = self._rsasp1(M)
  1131             if s is None:
  1132                 return None
  1133             return pkcs_i2osp(s, self.modulusLen/8)
  1134         
  1135         elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN
  1136             if h is None:
  1137                 h = "sha1"
  1138             return self._rsassa_pkcs1_v1_5_sign(M, h)
  1139         
  1140         elif t == "pss": # RSASSA-PSS-SIGN
  1141             return self._rsassa_pss_sign(M, h, mgf, sLen)
  1142 
  1143         else:
  1144             warning("Key.sign(): Unknown signature type (%s) provided" % t)
  1145             return None
  1146 
  1147 
  1148 
  1149 
  1150 class PubKey(OSSLHelper, _EncryptAndVerify):
  1151     # Below are the fields we recognize in the -text output of openssl
  1152     # and from which we extract information. We expect them in that
  1153     # order. Number of spaces does matter.
  1154     possible_fields = [ "Modulus (",
  1155                         "Exponent:" ]
  1156     possible_fields_count = len(possible_fields)
  1157     
  1158     def __init__(self, keypath):
  1159         error_msg = "Unable to import key."
  1160 
  1161         # XXX Temporary hack to use PubKey inside Cert
  1162         if type(keypath) is tuple:
  1163             e, m, mLen = keypath
  1164             self.modulus = m
  1165             self.modulusLen = mLen
  1166             self.pubExp = e
  1167             return
  1168 
  1169         fields_dict = {}
  1170         for k in self.possible_fields:
  1171             fields_dict[k] = None
  1172 
  1173         self.keypath = None
  1174         rawkey = None
  1175 
  1176         if (not '\x00' in keypath) and os.path.isfile(keypath): # file
  1177             self.keypath = keypath
  1178             key_size = os.path.getsize(keypath)
  1179             if key_size > MAX_KEY_SIZE:
  1180                 raise Exception(error_msg)
  1181             try:
  1182                 f = open(keypath)
  1183                 rawkey = f.read()
  1184                 f.close()
  1185             except:
  1186     		raise Exception(error_msg)     
  1187         else:
  1188             rawkey = keypath
  1189 
  1190 	if rawkey is None:
  1191 	    raise Exception(error_msg)
  1192 
  1193 	self.rawkey = rawkey
  1194 
  1195         # Let's try to get file format : PEM or DER.
  1196         fmtstr = 'openssl rsa -text -pubin -inform %s -noout '
  1197         convertstr = 'openssl rsa -pubin -inform %s -outform %s 2>/dev/null'
  1198         key_header = "-----BEGIN PUBLIC KEY-----"
  1199         key_footer = "-----END PUBLIC KEY-----"
  1200         l = rawkey.split(key_header, 1)
  1201         if len(l) == 2: # looks like PEM
  1202             tmp = l[1]
  1203             l = tmp.split(key_footer, 1)
  1204             if len(l) == 2:
  1205                 tmp = l[0]
  1206                 rawkey = "%s%s%s\n" % (key_header, tmp, key_footer)
  1207             else:
  1208                 raise Exception(error_msg)
  1209             r,w,e = popen2.popen3(fmtstr % "PEM")
  1210             w.write(rawkey)
  1211             w.close()
  1212             textkey = r.read()
  1213             r.close()
  1214             res = e.read()
  1215             e.close()
  1216             if res == '':
  1217                 self.format = "PEM"
  1218                 self.pemkey = rawkey
  1219                 self.textkey = textkey
  1220                 cmd = convertstr % ("PEM", "DER")
  1221                 self.derkey = self._apply_ossl_cmd(cmd, rawkey)
  1222             else:
  1223                 raise Exception(error_msg)
  1224         else: # not PEM, try DER
  1225             r,w,e = popen2.popen3(fmtstr % "DER")            
  1226             w.write(rawkey)
  1227             w.close()
  1228             textkey = r.read()
  1229             r.close()
  1230             res = e.read()
  1231 	    if res == '':
  1232 		self.format = "DER"
  1233                 self.derkey = rawkey
  1234                 self.textkey = textkey
  1235                 cmd = convertstr % ("DER", "PEM")
  1236                 self.pemkey = self._apply_ossl_cmd(cmd, rawkey)
  1237                 cmd = convertstr % ("DER", "DER")
  1238                 self.derkey = self._apply_ossl_cmd(cmd, rawkey)                
  1239 	    else:
  1240                 try: # Perhaps it is a cert
  1241                     c = Cert(keypath)
  1242                 except:
  1243                     raise Exception(error_msg)
  1244                 # TODO:
  1245                 # Reconstruct a key (der and pem) and provide:
  1246                 # self.format
  1247                 # self.derkey
  1248                 # self.pemkey
  1249                 # self.textkey
  1250                 # self.keypath
  1251 
  1252         self.osslcmdbase = 'openssl rsa -pubin -inform %s ' % self.format
  1253 
  1254         self.keypath = keypath
  1255 
  1256         # Parse the -text output of openssl to make things available
  1257         l = self.textkey.split('\n', 1)
  1258         if len(l) != 2:
  1259             raise Exception(error_msg)
  1260         cur, tmp = l
  1261         i = 0
  1262         k = self.possible_fields[i] # Modulus (
  1263         cur = cur[len(k):] + '\n'
  1264         while k:
  1265             l = tmp.split('\n', 1)
  1266             if len(l) != 2: # Over
  1267                 fields_dict[k] = cur
  1268                 break
  1269             l, tmp = l
  1270 
  1271             newkey = 0
  1272             # skip fields we have already seen, this is the purpose of 'i'
  1273             for j in range(i, self.possible_fields_count):
  1274                 f = self.possible_fields[j]
  1275                 if l.startswith(f):
  1276                     fields_dict[k] = cur
  1277                     cur = l[len(f):] + '\n'
  1278                     k = f
  1279                     newkey = 1
  1280                     i = j+1
  1281                     break
  1282             if newkey == 1:
  1283                 continue
  1284             cur += l + '\n'
  1285 
  1286         # modulus and modulus length
  1287         v = fields_dict["Modulus ("]
  1288         self.modulusLen = None
  1289         if v:
  1290             v, rem = v.split(' bit):', 1)
  1291             self.modulusLen = int(v)
  1292             rem = rem.replace('\n','').replace(' ','').replace(':','')
  1293             self.modulus = long(rem, 16)
  1294         if self.modulus is None:
  1295             raise Exception(error_msg)
  1296         
  1297         # public exponent
  1298         v = fields_dict["Exponent:"]
  1299         self.pubExp = None
  1300         if v:
  1301             self.pubExp = long(v.split('(', 1)[0])
  1302         if self.pubExp is None:
  1303             raise Exception(error_msg)
  1304 
  1305         self.key = RSA.construct((self.modulus, self.pubExp, ))
  1306 
  1307     def __str__(self):
  1308         return self.derkey
  1309 
  1310 
  1311 class Key(OSSLHelper, _DecryptAndSignMethods, _EncryptAndVerify):
  1312     # Below are the fields we recognize in the -text output of openssl
  1313     # and from which we extract information. We expect them in that
  1314     # order. Number of spaces does matter.
  1315     possible_fields = [ "Private-Key: (",
  1316                         "modulus:",
  1317                         "publicExponent:",
  1318                         "privateExponent:",
  1319                         "prime1:",
  1320                         "prime2:",
  1321                         "exponent1:",
  1322                         "exponent2:",
  1323                         "coefficient:" ]
  1324     possible_fields_count = len(possible_fields)
  1325     
  1326     def __init__(self, keypath):
  1327         error_msg = "Unable to import key."
  1328 
  1329         fields_dict = {}
  1330         for k in self.possible_fields:
  1331             fields_dict[k] = None
  1332 
  1333         self.keypath = None
  1334         rawkey = None
  1335 
  1336         if (not '\x00' in keypath) and os.path.isfile(keypath):
  1337             self.keypath = keypath
  1338             key_size = os.path.getsize(keypath)
  1339             if key_size > MAX_KEY_SIZE:
  1340                 raise Exception(error_msg)
  1341             try:
  1342                 f = open(keypath)
  1343                 rawkey = f.read()
  1344                 f.close()
  1345             except:
  1346     		raise Exception(error_msg)     
  1347         else:
  1348             rawkey = keypath
  1349 
  1350 	if rawkey is None:
  1351 	    raise Exception(error_msg)
  1352 
  1353 	self.rawkey = rawkey
  1354 
  1355         # Let's try to get file format : PEM or DER.
  1356         fmtstr = 'openssl rsa -text -inform %s -noout '
  1357         convertstr = 'openssl rsa -inform %s -outform %s 2>/dev/null'
  1358         key_header = "-----BEGIN RSA PRIVATE KEY-----"
  1359         key_footer = "-----END RSA PRIVATE KEY-----"
  1360         l = rawkey.split(key_header, 1)
  1361         if len(l) == 2: # looks like PEM
  1362             tmp = l[1]
  1363             l = tmp.split(key_footer, 1)
  1364             if len(l) == 2:
  1365                 tmp = l[0]
  1366                 rawkey = "%s%s%s\n" % (key_header, tmp, key_footer)
  1367             else:
  1368                 raise Exception(error_msg)
  1369             r,w,e = popen2.popen3(fmtstr % "PEM")
  1370             w.write(rawkey)
  1371             w.close()
  1372             textkey = r.read()
  1373             r.close()
  1374             res = e.read()
  1375             e.close()
  1376             if res == '':
  1377                 self.format = "PEM"
  1378                 self.pemkey = rawkey
  1379                 self.textkey = textkey
  1380                 cmd = convertstr % ("PEM", "DER")
  1381                 self.derkey = self._apply_ossl_cmd(cmd, rawkey)
  1382             else:
  1383                 raise Exception(error_msg)
  1384         else: # not PEM, try DER
  1385             r,w,e = popen2.popen3(fmtstr % "DER")            
  1386             w.write(rawkey)
  1387             w.close()
  1388             textkey = r.read()
  1389             r.close()
  1390             res = e.read()
  1391 	    if res == '':
  1392 		self.format = "DER"
  1393                 self.derkey = rawkey
  1394                 self.textkey = textkey
  1395                 cmd = convertstr % ("DER", "PEM")
  1396                 self.pemkey = self._apply_ossl_cmd(cmd, rawkey)
  1397                 cmd = convertstr % ("DER", "DER")
  1398                 self.derkey = self._apply_ossl_cmd(cmd, rawkey)
  1399 	    else:
  1400 		raise Exception(error_msg)     
  1401 
  1402         self.osslcmdbase = 'openssl rsa -inform %s ' % self.format
  1403 
  1404         r,w,e = popen2.popen3('openssl asn1parse -inform DER ')
  1405         w.write(self.derkey)
  1406         w.close()
  1407         self.asn1parsekey = r.read()
  1408         r.close()
  1409         res = e.read()
  1410         e.close()
  1411         if res != '':
  1412             raise Exception(error_msg)
  1413 
  1414         self.keypath = keypath
  1415 
  1416         # Parse the -text output of openssl to make things available
  1417         l = self.textkey.split('\n', 1)
  1418         if len(l) != 2:
  1419             raise Exception(error_msg)
  1420         cur, tmp = l
  1421         i = 0
  1422         k = self.possible_fields[i] # Private-Key: (
  1423         cur = cur[len(k):] + '\n'
  1424         while k:
  1425             l = tmp.split('\n', 1)
  1426             if len(l) != 2: # Over
  1427                 fields_dict[k] = cur
  1428                 break
  1429             l, tmp = l
  1430 
  1431             newkey = 0
  1432             # skip fields we have already seen, this is the purpose of 'i'
  1433             for j in range(i, self.possible_fields_count):
  1434                 f = self.possible_fields[j]
  1435                 if l.startswith(f):
  1436                     fields_dict[k] = cur
  1437                     cur = l[len(f):] + '\n'
  1438                     k = f
  1439                     newkey = 1
  1440                     i = j+1
  1441                     break
  1442             if newkey == 1:
  1443                 continue
  1444             cur += l + '\n'
  1445 
  1446         # modulus length
  1447         v = fields_dict["Private-Key: ("]
  1448         self.modulusLen = None
  1449         if v:
  1450             self.modulusLen = int(v.split(' bit', 1)[0])
  1451         if self.modulusLen is None:
  1452             raise Exception(error_msg)
  1453         
  1454         # public exponent
  1455         v = fields_dict["publicExponent:"]
  1456         self.pubExp = None
  1457         if v:
  1458             self.pubExp = long(v.split('(', 1)[0])
  1459         if self.pubExp is None:
  1460             raise Exception(error_msg)
  1461 
  1462         tmp = {}
  1463         for k in ["modulus:", "privateExponent:", "prime1:", "prime2:",
  1464                   "exponent1:", "exponent2:", "coefficient:"]:
  1465             v = fields_dict[k]
  1466             if v:
  1467                 s = v.replace('\n', '').replace(' ', '').replace(':', '')
  1468                 tmp[k] = long(s, 16)
  1469             else:
  1470                 raise Exception(error_msg)
  1471 
  1472         self.modulus     = tmp["modulus:"]
  1473         self.privExp     = tmp["privateExponent:"]
  1474         self.prime1      = tmp["prime1:"]
  1475         self.prime2      = tmp["prime2:"] 
  1476         self.exponent1   = tmp["exponent1:"]
  1477         self.exponent2   = tmp["exponent2:"]
  1478         self.coefficient = tmp["coefficient:"]
  1479 
  1480         self.key = RSA.construct((self.modulus, self.pubExp, self.privExp))
  1481 
  1482     def __str__(self):
  1483         return self.derkey
  1484 
  1485 
  1486 # We inherit from PubKey to get access to all encryption and verification
  1487 # methods. To have that working, we simply need Cert to provide 
  1488 # modulusLen and key attribute.
  1489 # XXX Yes, it is a hack.
  1490 class Cert(OSSLHelper, _EncryptAndVerify):
  1491     # Below are the fields we recognize in the -text output of openssl
  1492     # and from which we extract information. We expect them in that
  1493     # order. Number of spaces does matter.
  1494     possible_fields = [ "        Version:",
  1495                         "        Serial Number:",
  1496                         "        Signature Algorithm:",
  1497                         "        Issuer:",
  1498                         "            Not Before:",
  1499                         "            Not After :",
  1500                         "        Subject:",
  1501                         "            Public Key Algorithm:",
  1502                         "                Modulus (",
  1503                         "                Exponent:",
  1504                         "            X509v3 Subject Key Identifier:",
  1505                         "            X509v3 Authority Key Identifier:",
  1506                         "                keyid:",
  1507                         "                DirName:",
  1508                         "                serial:",
  1509                         "            X509v3 Basic Constraints:",
  1510                         "            X509v3 Key Usage:",
  1511                         "            X509v3 Extended Key Usage:",
  1512                         "            X509v3 CRL Distribution Points:",
  1513                         "            Authority Information Access:",
  1514                         "    Signature Algorithm:" ]
  1515     possible_fields_count = len(possible_fields)
  1516     
  1517     def __init__(self, certpath):
  1518         error_msg = "Unable to import certificate."
  1519 
  1520         fields_dict = {}
  1521         for k in self.possible_fields:
  1522             fields_dict[k] = None
  1523 
  1524         self.certpath = None
  1525         rawcert = None
  1526 
  1527         if (not '\x00' in certpath) and os.path.isfile(certpath): # file
  1528             self.certpath = certpath
  1529             cert_size = os.path.getsize(certpath)
  1530             if cert_size > MAX_CERT_SIZE:
  1531                 raise Exception(error_msg)
  1532             try:
  1533                 f = open(certpath)
  1534                 rawcert = f.read()
  1535                 f.close()
  1536             except:
  1537     		raise Exception(error_msg)     
  1538         else:
  1539             rawcert = certpath
  1540             
  1541 	if rawcert is None:
  1542 	    raise Exception(error_msg)
  1543 
  1544 	self.rawcert = rawcert
  1545 
  1546         # Let's try to get file format : PEM or DER.
  1547         fmtstr = 'openssl x509 -text -inform %s -noout '
  1548         convertstr = 'openssl x509 -inform %s -outform %s '
  1549         cert_header = "-----BEGIN CERTIFICATE-----"
  1550         cert_footer = "-----END CERTIFICATE-----"
  1551         l = rawcert.split(cert_header, 1)
  1552         if len(l) == 2: # looks like PEM
  1553             tmp = l[1]
  1554             l = tmp.split(cert_footer, 1)
  1555             if len(l) == 2:
  1556                 tmp = l[0]
  1557                 rawcert = "%s%s%s\n" % (cert_header, tmp, cert_footer)
  1558             else:
  1559                 raise Exception(error_msg)
  1560             r,w,e = popen2.popen3(fmtstr % "PEM")
  1561             w.write(rawcert)
  1562             w.close()
  1563             textcert = r.read()
  1564             r.close()
  1565             res = e.read()
  1566             e.close()
  1567             if res == '':
  1568                 self.format = "PEM"
  1569                 self.pemcert = rawcert
  1570                 self.textcert = textcert
  1571                 cmd = convertstr % ("PEM", "DER")
  1572                 self.dercert = self._apply_ossl_cmd(cmd, rawcert)
  1573             else:
  1574                 raise Exception(error_msg)
  1575         else: # not PEM, try DER
  1576             r,w,e = popen2.popen3(fmtstr % "DER")            
  1577             w.write(rawcert)
  1578             w.close()
  1579             textcert = r.read()
  1580             r.close()
  1581             res = e.read()
  1582 	    if res == '':
  1583 		self.format = "DER"
  1584                 self.dercert = rawcert
  1585                 self.textcert = textcert
  1586                 cmd = convertstr % ("DER", "PEM")
  1587                 self.pemcert = self._apply_ossl_cmd(cmd, rawcert)
  1588                 cmd = convertstr % ("DER", "DER")                
  1589                 self.dercert = self._apply_ossl_cmd(cmd, rawcert)
  1590 	    else:
  1591 		raise Exception(error_msg)
  1592 
  1593         self.osslcmdbase = 'openssl x509 -inform %s ' % self.format
  1594                                                   
  1595         r,w,e = popen2.popen3('openssl asn1parse -inform DER ')
  1596         w.write(self.dercert)
  1597         w.close()
  1598         self.asn1parsecert = r.read()
  1599         r.close()
  1600         res = e.read()
  1601         e.close()
  1602         if res != '':
  1603             raise Exception(error_msg)
  1604         
  1605         # Grab _raw_ X509v3 Authority Key Identifier, if any.
  1606         tmp = self.asn1parsecert.split(":X509v3 Authority Key Identifier", 1)
  1607         self.authorityKeyID = None
  1608         if len(tmp) == 2:
  1609             tmp = tmp[1]
  1610             tmp = tmp.split("[HEX DUMP]:", 1)[1]
  1611             self.authorityKeyID=tmp.split('\n',1)[0]
  1612 
  1613         # Grab _raw_ X509v3 Subject Key Identifier, if any.
  1614         tmp = self.asn1parsecert.split(":X509v3 Subject Key Identifier", 1)
  1615         self.subjectKeyID = None
  1616         if len(tmp) == 2:
  1617             tmp = tmp[1]
  1618             tmp = tmp.split("[HEX DUMP]:", 1)[1]
  1619             self.subjectKeyID=tmp.split('\n',1)[0]            
  1620 
  1621         # Get tbsCertificate using the worst hack. output of asn1parse
  1622         # looks like that:
  1623         #
  1624         # 0:d=0  hl=4 l=1298 cons: SEQUENCE          
  1625         # 4:d=1  hl=4 l=1018 cons: SEQUENCE          
  1626         # ...
  1627         #
  1628         l1,l2 = self.asn1parsecert.split('\n', 2)[:2]
  1629         hl1 = int(l1.split("hl=",1)[1].split("l=",1)[0])
  1630         rem = l2.split("hl=",1)[1]
  1631         hl2, rem = rem.split("l=",1)
  1632         hl2 = int(hl2)
  1633         l = int(rem.split("cons",1)[0])
  1634         self.tbsCertificate = self.dercert[hl1:hl1+hl2+l]
  1635 
  1636         # Parse the -text output of openssl to make things available
  1637         tmp = self.textcert.split('\n', 2)[2]
  1638         l = tmp.split('\n', 1)
  1639         if len(l) != 2:
  1640             raise Exception(error_msg)
  1641         cur, tmp = l
  1642         i = 0
  1643         k = self.possible_fields[i] # Version:
  1644         cur = cur[len(k):] + '\n'
  1645         while k:
  1646             l = tmp.split('\n', 1)
  1647             if len(l) != 2: # Over
  1648                 fields_dict[k] = cur
  1649                 break
  1650             l, tmp = l
  1651 
  1652             newkey = 0
  1653             # skip fields we have already seen, this is the purpose of 'i'
  1654             for j in range(i, self.possible_fields_count):
  1655                 f = self.possible_fields[j]
  1656                 if l.startswith(f):
  1657                     fields_dict[k] = cur
  1658                     cur = l[len(f):] + '\n'
  1659                     k = f
  1660                     newkey = 1
  1661                     i = j+1
  1662                     break
  1663             if newkey == 1:
  1664                 continue
  1665             cur += l + '\n'
  1666 
  1667         # version
  1668         v = fields_dict["        Version:"]
  1669         self.version = None
  1670         if v:
  1671             self.version = int(v[1:2])
  1672         if self.version is None:
  1673             raise Exception(error_msg)
  1674 
  1675         # serial number
  1676         v = fields_dict["        Serial Number:"]
  1677         self.serial = None
  1678         if v:
  1679             v = v.replace('\n', '').strip()
  1680             if "0x" in v:
  1681                 v = v.split("0x", 1)[1].split(')', 1)[0]
  1682             v = v.replace(':', '').upper()
  1683             if len(v) % 2:
  1684                 v = '0' + v
  1685             self.serial = v
  1686         if self.serial is None:
  1687             raise Exception(error_msg)
  1688 
  1689         # Signature Algorithm        
  1690         v = fields_dict["        Signature Algorithm:"]
  1691         self.sigAlg = None
  1692         if v:
  1693             v = v.split('\n',1)[0]
  1694             v = v.strip()
  1695             self.sigAlg = v
  1696         if self.sigAlg is None:
  1697             raise Exception(error_msg)
  1698         
  1699         # issuer
  1700         v = fields_dict["        Issuer:"]
  1701         self.issuer = None
  1702         if v:
  1703             v = v.split('\n',1)[0]
  1704             v = v.strip()
  1705             self.issuer = v
  1706         if self.issuer is None:
  1707             raise Exception(error_msg)
  1708 
  1709         # not before
  1710         v = fields_dict["            Not Before:"]
  1711         self.notBefore_str = None
  1712         if v:
  1713             v = v.split('\n',1)[0]
  1714             v = v.strip()
  1715             self.notBefore_str = v
  1716         if self.notBefore_str is None:
  1717             raise Exception(error_msg)
  1718         self.notBefore = time.strptime(self.notBefore_str,
  1719                                        "%b %d %H:%M:%S %Y %Z")
  1720         self.notBefore_str_simple = time.strftime("%x", self.notBefore)
  1721         
  1722         # not after
  1723         v = fields_dict["            Not After :"]
  1724         self.notAfter_str = None
  1725         if v:
  1726             v = v.split('\n',1)[0]
  1727             v = v.strip()
  1728             self.notAfter_str = v
  1729         if self.notAfter_str is None:
  1730             raise Exception(error_msg)
  1731         self.notAfter = time.strptime(self.notAfter_str,
  1732                                       "%b %d %H:%M:%S %Y %Z")
  1733         self.notAfter_str_simple = time.strftime("%x", self.notAfter)
  1734         
  1735         # subject
  1736         v = fields_dict["        Subject:"]
  1737         self.subject = None
  1738         if v:
  1739             v = v.split('\n',1)[0]
  1740             v = v.strip()
  1741             self.subject = v
  1742         if self.subject is None:
  1743             raise Exception(error_msg)
  1744         
  1745         # Public Key Algorithm
  1746         v = fields_dict["            Public Key Algorithm:"]
  1747         self.pubKeyAlg = None
  1748         if v:
  1749             v = v.split('\n',1)[0]
  1750             v = v.strip()
  1751             self.pubKeyAlg = v
  1752         if self.pubKeyAlg is None:
  1753             raise Exception(error_msg)
  1754         
  1755         # Modulus
  1756         v = fields_dict["                Modulus ("]
  1757         self.modulus = None
  1758         if v:
  1759             v,t = v.split(' bit):',1)
  1760             self.modulusLen = int(v)
  1761             t = t.replace(' ', '').replace('\n', ''). replace(':', '')
  1762             self.modulus_hexdump = t
  1763             self.modulus = long(t, 16)
  1764         if self.modulus is None:
  1765             raise Exception(error_msg)
  1766 
  1767         # Exponent
  1768         v = fields_dict["                Exponent:"]
  1769         self.exponent = None
  1770         if v:
  1771             v = v.split('(',1)[0]
  1772             self.exponent = long(v)
  1773         if self.exponent is None:
  1774             raise Exception(error_msg)
  1775 
  1776         # Public Key instance
  1777         self.key = RSA.construct((self.modulus, self.exponent, ))
  1778         
  1779         # Subject Key Identifier
  1780 
  1781         # Authority Key Identifier: keyid, dirname and serial
  1782         self.authorityKeyID_keyid   = None
  1783         self.authorityKeyID_dirname = None
  1784         self.authorityKeyID_serial  = None
  1785         if self.authorityKeyID: # (hex version already done using asn1parse)
  1786             v = fields_dict["                keyid:"]
  1787             if v:
  1788                 v = v.split('\n',1)[0]
  1789                 v = v.strip().replace(':', '')
  1790                 self.authorityKeyID_keyid = v
  1791             v = fields_dict["                DirName:"]
  1792             if v:
  1793                 v = v.split('\n',1)[0]
  1794                 self.authorityKeyID_dirname = v
  1795             v = fields_dict["                serial:"]
  1796             if v:
  1797                 v = v.split('\n',1)[0]
  1798                 v = v.strip().replace(':', '')
  1799                 self.authorityKeyID_serial = v                
  1800 
  1801         # Basic constraints
  1802         self.basicConstraintsCritical = False
  1803         self.basicConstraints=None
  1804         v = fields_dict["            X509v3 Basic Constraints:"]
  1805         if v:
  1806             self.basicConstraints = {}
  1807             v,t = v.split('\n',2)[:2]
  1808             if "critical" in v:
  1809                 self.basicConstraintsCritical = True
  1810             if "CA:" in t:
  1811                 self.basicConstraints["CA"] = t.split('CA:')[1][:4] == "TRUE"
  1812             if "pathlen:" in t:
  1813                 self.basicConstraints["pathlen"] = int(t.split('pathlen:')[1])
  1814 
  1815         # X509v3 Key Usage
  1816         self.keyUsage = []
  1817         v = fields_dict["            X509v3 Key Usage:"]
  1818         if v:	
  1819             # man 5 x509v3_config
  1820             ku_mapping = {"Digital Signature": "digitalSignature",
  1821                           "Non Repudiation": "nonRepudiation",
  1822                           "Key Encipherment": "keyEncipherment",
  1823                           "Data Encipherment": "dataEncipherment",
  1824                           "Key Agreement": "keyAgreement",
  1825                           "Certificate Sign": "keyCertSign",
  1826                           "CRL Sign": "cRLSign",
  1827                           "Encipher Only": "encipherOnly",
  1828                           "Decipher Only": "decipherOnly"}
  1829             v = v.split('\n',2)[1]
  1830             l = map(lambda x: x.strip(), v.split(','))
  1831             while l:
  1832                 c = l.pop()
  1833                 if ku_mapping.has_key(c):
  1834                     self.keyUsage.append(ku_mapping[c])
  1835                 else:
  1836                     self.keyUsage.append(c) # Add it anyway
  1837                     print "Found unknown X509v3 Key Usage: '%s'" % c
  1838                     print "Report it to arno (at) natisbad.org for addition"
  1839 
  1840         # X509v3 Extended Key Usage
  1841         self.extKeyUsage = []
  1842         v = fields_dict["            X509v3 Extended Key Usage:"]
  1843         if v:	
  1844             # man 5 x509v3_config:
  1845             eku_mapping = {"TLS Web Server Authentication": "serverAuth",
  1846                            "TLS Web Client Authentication": "clientAuth",
  1847                            "Code Signing": "codeSigning",
  1848                            "E-mail Protection": "emailProtection",
  1849                            "Time Stamping": "timeStamping",
  1850                            "Microsoft Individual Code Signing": "msCodeInd",
  1851                            "Microsoft Commercial Code Signing": "msCodeCom",
  1852                            "Microsoft Trust List Signing": "msCTLSign",
  1853                            "Microsoft Encrypted File System": "msEFS",
  1854                            "Microsoft Server Gated Crypto": "msSGC",
  1855                            "Netscape Server Gated Crypto": "nsSGC",
  1856                            "IPSec End System": "iPsecEndSystem",
  1857                            "IPSec Tunnel": "iPsecTunnel",
  1858                            "IPSec User": "iPsecUser"}
  1859             v = v.split('\n',2)[1]
  1860             l = map(lambda x: x.strip(), v.split(','))
  1861             while l:
  1862                 c = l.pop()
  1863                 if eku_mapping.has_key(c):
  1864                     self.extKeyUsage.append(eku_mapping[c])
  1865                 else:
  1866                     self.extKeyUsage.append(c) # Add it anyway
  1867                     print "Found unknown X509v3 Extended Key Usage: '%s'" % c
  1868                     print "Report it to arno (at) natisbad.org for addition"
  1869 
  1870         # CRL Distribution points
  1871         self.cRLDistributionPoints = []
  1872         v = fields_dict["            X509v3 CRL Distribution Points:"]
  1873         if v:
  1874             v = v.split("\n\n", 1)[0]
  1875             v = v.split("URI:")[1:]
  1876             self.CRLDistributionPoints = map(lambda x: x.strip(), v)
  1877             
  1878         # Authority Information Access: list of tuples ("method", "location")
  1879         self.authorityInfoAccess = []
  1880         v = fields_dict["            Authority Information Access:"]
  1881         if v:
  1882             v = v.split("\n\n", 1)[0]
  1883             v = v.split("\n")[1:]
  1884             for e in v:
  1885                 method, location = map(lambda x: x.strip(), e.split(" - ", 1))
  1886                 self.authorityInfoAccess.append((method, location))
  1887 
  1888         # signature field
  1889         v = fields_dict["    Signature Algorithm:" ]
  1890         self.sig = None
  1891         if v:
  1892             v = v.split('\n',1)[1]
  1893             v = v.replace(' ', '').replace('\n', '')
  1894             self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':')))
  1895             self.sigLen = len(self.sig)
  1896         if self.sig is None:
  1897             raise Exception(error_msg)
  1898 
  1899     def isIssuerCert(self, other):
  1900         """
  1901         True if 'other' issued 'self', i.e.:
  1902           - self.issuer == other.subject
  1903           - self is signed by other
  1904         """
  1905         # XXX should be done on raw values, instead of their textual repr
  1906         if self.issuer != other.subject:
  1907             return False
  1908 
  1909         # Sanity check regarding modulus length and the
  1910         # signature length
  1911         keyLen = (other.modulusLen + 7)/8
  1912         if keyLen != self.sigLen:
  1913             return False
  1914 
  1915         unenc = other.encrypt(self.sig) # public key encryption, i.e. decrypt
  1916 
  1917         # XXX Check block type (00 or 01 and type of padding)
  1918         unenc = unenc[1:]
  1919         if not '\x00' in unenc:
  1920             return False
  1921         pos = unenc.index('\x00')
  1922         unenc = unenc[pos+1:]
  1923 
  1924         found = None
  1925         for k in _hashFuncParams.keys():
  1926             if self.sigAlg.startswith(k):
  1927                 found = k
  1928                 break
  1929         if not found:
  1930             return False
  1931         hlen, hfunc, digestInfo =  _hashFuncParams[k]
  1932         
  1933         if len(unenc) != (hlen+len(digestInfo)):
  1934             return False
  1935 
  1936         if not unenc.startswith(digestInfo):
  1937             return False
  1938 
  1939         h = unenc[-hlen:]
  1940         myh = hfunc(self.tbsCertificate)
  1941 
  1942         return h == myh
  1943 
  1944     def chain(self, certlist):
  1945         """
  1946         Construct the chain of certificates leading from 'self' to the
  1947         self signed root using the certificates in 'certlist'. If the
  1948         list does not provide all the required certs to go to the root
  1949         the function returns a incomplete chain starting with the
  1950         certificate. This fact can be tested by tchecking if the last
  1951         certificate of the returned chain is self signed (if c is the
  1952         result, c[-1].isSelfSigned())
  1953         """
  1954         d = {}
  1955         for c in certlist:
  1956             # XXX we should check if we have duplicate
  1957             d[c.subject] = c
  1958         res = [self]
  1959         cur = self
  1960         while not cur.isSelfSigned():
  1961             if d.has_key(cur.issuer):
  1962                 possible_issuer = d[cur.issuer]
  1963                 if cur.isIssuerCert(possible_issuer):
  1964                     res.append(possible_issuer)
  1965                     cur = possible_issuer
  1966                 else:
  1967                     break
  1968         return res
  1969 
  1970     def remainingDays(self, now=None):
  1971         """
  1972         Based on the value of notBefore field, returns the number of
  1973         days the certificate will still be valid. The date used for the
  1974         comparison is the current and local date, as returned by 
  1975         time.localtime(), except if 'now' argument is provided another
  1976         one. 'now' argument can be given as either a time tuple or a string
  1977         representing the date. Accepted format for the string version
  1978         are:
  1979         
  1980          - '%b %d %H:%M:%S %Y %Z' e.g. 'Jan 30 07:38:59 2008 GMT'
  1981          - '%m/%d/%y' e.g. '01/30/08' (less precise)
  1982 
  1983         If the certificate is no more valid at the date considered, then,
  1984         a negative value is returned representing the number of days
  1985         since it has expired.
  1986         
  1987         The number of days is returned as a float to deal with the unlikely
  1988         case of certificates that are still just valid.
  1989         """
  1990         if now is None:
  1991             now = time.localtime()
  1992         elif type(now) is str:
  1993             try:
  1994                 if '/' in now:
  1995                     now = time.strptime(now, '%m/%d/%y')
  1996                 else:
  1997                     now = time.strptime(now, '%b %d %H:%M:%S %Y %Z')
  1998             except:
  1999                 warning("Bad time string provided '%s'. Using current time" % now)
  2000                 now = time.localtime()
  2001 
  2002         now = time.mktime(now)
  2003         nft = time.mktime(self.notAfter)
  2004         diff = (nft - now)/(24.*3600)
  2005         return diff
  2006 
  2007 
  2008     # return SHA-1 hash of cert embedded public key
  2009     # !! At the moment, the trailing 0 is in the hashed string if any
  2010     def keyHash(self):
  2011 	m = self.modulus_hexdump
  2012         res = []
  2013         i = 0
  2014         l = len(m)
  2015         while i<l: # get a string version of modulus
  2016             res.append(struct.pack("B", int(m[i:i+2], 16)))
  2017             i += 2
  2018         return sha.new("".join(res)).digest()    
  2019 
  2020     def output(self, fmt="DER"):
  2021         if fmt == "DER":
  2022             return self.dercert
  2023         elif fmt == "PEM":
  2024             return self.pemcert
  2025         elif fmt == "TXT":
  2026             return self.textcert
  2027 
  2028     def export(self, filename, fmt="DER"):
  2029         """
  2030         Export certificate in 'fmt' format (PEM, DER or TXT) to file 'filename'
  2031         """
  2032         f = open(filename, "wb")
  2033         f.write(self.output(fmt))
  2034         f.close()
  2035 
  2036     def isSelfSigned(self):
  2037         """
  2038         Return True if the certificate is self signed:
  2039           - issuer and subject are the same
  2040           - the signature of the certificate is valid.
  2041         """
  2042         if self.issuer == self.subject:
  2043             return self.isIssuerCert(self)
  2044         return False
  2045 
  2046     # Print main informations stored in certificate
  2047     def show(self):
  2048         print "Serial: %s" % self.serial
  2049         print "Issuer: " + self.issuer
  2050         print "Subject: " + self.subject
  2051         print "Validity: %s to %s" % (self.notBefore_str_simple,
  2052                                       self.notAfter_str_simple)
  2053 
  2054     def __repr__(self):
  2055         return "[X.509 Cert. Subject:%s, Issuer:%s]" % (self.subject, self.issuer)
  2056 
  2057     def __str__(self):
  2058         return self.dercert
  2059 
  2060     def verifychain(self, anchors, untrusted=None):
  2061         """
  2062         Perform verification of certificate chains for that certificate. The
  2063         behavior of verifychain method is mapped (and also based) on openssl
  2064         verify userland tool (man 1 verify).
  2065         A list of anchors is required. untrusted parameter can be provided 
  2066         a list of untrusted certificates that can be used to reconstruct the
  2067         chain.
  2068 
  2069         If you have a lot of certificates to verify against the same
  2070         list of anchor, consider constructing this list as a cafile
  2071         and use .verifychain_from_cafile() instead.
  2072         """
  2073         cafile = create_temporary_ca_file(anchors)
  2074         if not cafile:
  2075             return False
  2076         untrusted_file = None
  2077         if untrusted:
  2078             untrusted_file = create_temporary_ca_file(untrusted) # hack
  2079             if not untrusted_file:
  2080                 os.unlink(cafile)
  2081                 return False
  2082         res = self.verifychain_from_cafile(cafile, 
  2083                                            untrusted_file=untrusted_file)
  2084         os.unlink(cafile)
  2085         if untrusted_file:
  2086             os.unlink(untrusted_file)
  2087         return res
  2088 
  2089     def verifychain_from_cafile(self, cafile, untrusted_file=None):
  2090         """
  2091         Does the same job as .verifychain() but using the list of anchors
  2092         from the cafile. This is useful (because more efficient) if
  2093         you have a lot of certificates to verify do it that way: it
  2094         avoids the creation of a cafile from anchors at each call.
  2095 
  2096         As for .verifychain(), a list of untrusted certificates can be
  2097         passed (as a file, this time)
  2098         """
  2099         u = ""
  2100         if untrusted_file:
  2101             u = "-untrusted %s" % untrusted_file
  2102         try:
  2103             cmd = "openssl verify -CAfile %s %s " % (cafile, u)
  2104             pemcert = self.output(fmt="PEM")
  2105             cmdres = self._apply_ossl_cmd(cmd, pemcert)
  2106         except:
  2107             return False
  2108         return cmdres.endswith("\nOK\n") or cmdres.endswith(": OK\n")
  2109 
  2110     def verifychain_from_capath(self, capath, untrusted_file=None):
  2111         """
  2112         Does the same job as .verifychain_from_cafile() but using the list
  2113         of anchors in capath directory. The directory should contain
  2114         certificates files in PEM format with associated links as
  2115         created using c_rehash utility (man c_rehash).
  2116 
  2117         As for .verifychain_from_cafile(), a list of untrusted certificates
  2118         can be passed as a file (concatenation of the certificates in
  2119         PEM format)
  2120         """
  2121         u = ""
  2122         if untrusted_file:
  2123             u = "-untrusted %s" % untrusted_file
  2124         try:
  2125             cmd = "openssl verify -CApath %s %s " % (capath, u)
  2126             pemcert = self.output(fmt="PEM")
  2127             cmdres = self._apply_ossl_cmd(cmd, pemcert)
  2128         except:
  2129             return False
  2130         return cmdres.endswith("\nOK\n") or cmdres.endswith(": OK\n")
  2131 
  2132     def is_revoked(self, crl_list):
  2133         """
  2134         Given a list of trusted CRL (their signature has already been
  2135         verified with trusted anchors), this function returns True if
  2136         the certificate is marked as revoked by one of those CRL.
  2137 
  2138         Note that if the Certificate was on hold in a previous CRL and
  2139         is now valid again in a new CRL and bot are in the list, it
  2140         will be considered revoked: this is because _all_ CRLs are 
  2141         checked (not only the freshest) and revocation status is not
  2142         handled.
  2143 
  2144         Also note that the check on the issuer is performed on the
  2145         Authority Key Identifier if available in _both_ the CRL and the
  2146         Cert. Otherwise, the issuers are simply compared.
  2147         """
  2148         for c in crl_list:
  2149             if (self.authorityKeyID is not None and 
  2150                 c.authorityKeyID is not None and
  2151                 self.authorityKeyID == c.authorityKeyID):
  2152                 return self.serial in map(lambda x: x[0], c.revoked_cert_serials)
  2153             elif (self.issuer == c.issuer):
  2154                 return self.serial in map(lambda x: x[0], c.revoked_cert_serials)
  2155         return False
  2156 
  2157 def print_chain(l):
  2158     llen = len(l) - 1
  2159     if llen < 0:
  2160         return ""
  2161     c = l[llen]
  2162     llen -= 1
  2163     s = "_ "
  2164     if not c.isSelfSigned():
  2165         s = "_ ... [Missing Root]\n"
  2166     else:
  2167         s += "%s [Self Signed]\n" % c.subject
  2168     i = 1
  2169     while (llen != -1):
  2170         c = l[llen]
  2171         s += "%s\_ %s" % (" "*i, c.subject)
  2172         if llen != 0:
  2173             s += "\n"
  2174         i += 2
  2175         llen -= 1
  2176     print s
  2177 
  2178 # import popen2
  2179 # a=popen2.Popen3("openssl crl -text -inform DER -noout ", capturestderr=True)
  2180 # a.tochild.write(open("samples/klasa1.crl").read())
  2181 # a.tochild.close()
  2182 # a.poll()
  2183 
  2184 class CRL(OSSLHelper):
  2185     # Below are the fields we recognize in the -text output of openssl
  2186     # and from which we extract information. We expect them in that
  2187     # order. Number of spaces does matter.
  2188     possible_fields = [ "        Version",
  2189                         "        Signature Algorithm:",
  2190                         "        Issuer:",
  2191                         "        Last Update:",
  2192                         "        Next Update:",
  2193                         "        CRL extensions:",
  2194                         "            X509v3 Issuer Alternative Name:",
  2195                         "            X509v3 Authority Key Identifier:", 
  2196                         "                keyid:",
  2197                         "                DirName:",
  2198                         "                serial:",
  2199                         "            X509v3 CRL Number:", 
  2200                         "Revoked Certificates:",
  2201                         "No Revoked Certificates.",
  2202                         "    Signature Algorithm:" ]
  2203     possible_fields_count = len(possible_fields)
  2204 
  2205     def __init__(self, crlpath):
  2206         error_msg = "Unable to import CRL."
  2207 
  2208         fields_dict = {}
  2209         for k in self.possible_fields:
  2210             fields_dict[k] = None
  2211 
  2212         self.crlpath = None
  2213         rawcrl = None
  2214 
  2215         if (not '\x00' in crlpath) and os.path.isfile(crlpath):
  2216             self.crlpath = crlpath
  2217             cert_size = os.path.getsize(crlpath)
  2218             if cert_size > MAX_CRL_SIZE:
  2219                 raise Exception(error_msg)
  2220             try:
  2221                 f = open(crlpath)
  2222                 rawcrl = f.read()
  2223                 f.close()
  2224             except:
  2225     		raise Exception(error_msg)     
  2226         else:
  2227             rawcrl = crlpath
  2228 
  2229 	if rawcrl is None:
  2230 	    raise Exception(error_msg)
  2231 
  2232 	self.rawcrl = rawcrl
  2233 
  2234         # Let's try to get file format : PEM or DER.
  2235         fmtstr = 'openssl crl -text -inform %s -noout '
  2236         convertstr = 'openssl crl -inform %s -outform %s '
  2237         crl_header = "-----BEGIN X509 CRL-----"
  2238         crl_footer = "-----END X509 CRL-----"
  2239         l = rawcrl.split(crl_header, 1)
  2240         if len(l) == 2: # looks like PEM
  2241             tmp = l[1]
  2242             l = tmp.split(crl_footer, 1)
  2243             if len(l) == 2:
  2244                 tmp = l[0]
  2245                 rawcrl = "%s%s%s\n" % (crl_header, tmp, crl_footer)
  2246             else:
  2247                 raise Exception(error_msg)
  2248             r,w,e = popen2.popen3(fmtstr % "PEM")
  2249             w.write(rawcrl)
  2250             w.close()
  2251             textcrl = r.read()
  2252             r.close()
  2253             res = e.read()
  2254             e.close()
  2255             if res == '':
  2256                 self.format = "PEM"
  2257                 self.pemcrl = rawcrl
  2258                 self.textcrl = textcrl
  2259                 cmd = convertstr % ("PEM", "DER")
  2260                 self.dercrl = self._apply_ossl_cmd(cmd, rawcrl)
  2261             else:
  2262                 raise Exception(error_msg)
  2263         else: # not PEM, try DER
  2264             r,w,e = popen2.popen3(fmtstr % "DER")            
  2265             w.write(rawcrl)
  2266             w.close()
  2267             textcrl = r.read()
  2268             r.close()
  2269             res = e.read()
  2270 	    if res == '':
  2271 		self.format = "DER"
  2272                 self.dercrl = rawcrl
  2273                 self.textcrl = textcrl
  2274                 cmd = convertstr % ("DER", "PEM")
  2275                 self.pemcrl = self._apply_ossl_cmd(cmd, rawcrl)
  2276                 cmd = convertstr % ("DER", "DER")
  2277                 self.dercrl = self._apply_ossl_cmd(cmd, rawcrl)
  2278 	    else:
  2279 		raise Exception(error_msg)
  2280 
  2281         self.osslcmdbase = 'openssl crl -inform %s ' % self.format
  2282 
  2283         r,w,e = popen2.popen3('openssl asn1parse -inform DER ')
  2284         w.write(self.dercrl)
  2285         w.close()
  2286         self.asn1parsecrl = r.read()
  2287         r.close()
  2288         res = e.read()
  2289         e.close()
  2290         if res != '':
  2291             raise Exception(error_msg)
  2292 
  2293         # Grab _raw_ X509v3 Authority Key Identifier, if any.
  2294         tmp = self.asn1parsecrl.split(":X509v3 Authority Key Identifier", 1)
  2295         self.authorityKeyID = None
  2296         if len(tmp) == 2:
  2297             tmp = tmp[1]
  2298             tmp = tmp.split("[HEX DUMP]:", 1)[1]
  2299             self.authorityKeyID=tmp.split('\n',1)[0]
  2300 
  2301         # Parse the -text output of openssl to make things available
  2302         tmp = self.textcrl.split('\n', 1)[1]
  2303         l = tmp.split('\n', 1)
  2304         if len(l) != 2:
  2305             raise Exception(error_msg)
  2306         cur, tmp = l
  2307         i = 0
  2308         k = self.possible_fields[i] # Version
  2309         cur = cur[len(k):] + '\n'
  2310         while k:
  2311             l = tmp.split('\n', 1)
  2312             if len(l) != 2: # Over
  2313                 fields_dict[k] = cur
  2314                 break
  2315             l, tmp = l
  2316 
  2317             newkey = 0
  2318             # skip fields we have already seen, this is the purpose of 'i'
  2319             for j in range(i, self.possible_fields_count):
  2320                 f = self.possible_fields[j]
  2321                 if l.startswith(f):
  2322                     fields_dict[k] = cur
  2323                     cur = l[len(f):] + '\n'
  2324                     k = f
  2325                     newkey = 1
  2326                     i = j+1
  2327                     break
  2328             if newkey == 1:
  2329                 continue
  2330             cur += l + '\n'
  2331 
  2332         # version
  2333         v = fields_dict["        Version"]
  2334         self.version = None
  2335         if v:
  2336             self.version = int(v[1:2])
  2337         if self.version is None:
  2338             raise Exception(error_msg)
  2339 
  2340         # signature algorithm
  2341         v = fields_dict["        Signature Algorithm:"]
  2342         self.sigAlg = None
  2343         if v:
  2344             v = v.split('\n',1)[0]
  2345             v = v.strip()
  2346             self.sigAlg = v
  2347         if self.sigAlg is None:
  2348             raise Exception(error_msg)
  2349 
  2350         # issuer
  2351         v = fields_dict["        Issuer:"]
  2352         self.issuer = None
  2353         if v:
  2354             v = v.split('\n',1)[0]
  2355             v = v.strip()
  2356             self.issuer = v
  2357         if self.issuer is None:
  2358             raise Exception(error_msg)
  2359 
  2360         # last update
  2361         v = fields_dict["        Last Update:"]
  2362         self.lastUpdate_str = None
  2363         if v:
  2364             v = v.split('\n',1)[0]
  2365             v = v.strip()
  2366             self.lastUpdate_str = v
  2367         if self.lastUpdate_str is None:
  2368             raise Exception(error_msg)
  2369         self.lastUpdate = time.strptime(self.lastUpdate_str,
  2370                                        "%b %d %H:%M:%S %Y %Z")
  2371         self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate)
  2372 
  2373         # next update
  2374         v = fields_dict["        Next Update:"]
  2375         self.nextUpdate_str = None
  2376         if v:
  2377             v = v.split('\n',1)[0]
  2378             v = v.strip()
  2379             self.nextUpdate_str = v
  2380         if self.nextUpdate_str is None:
  2381             raise Exception(error_msg)
  2382         self.nextUpdate = time.strptime(self.nextUpdate_str,
  2383                                        "%b %d %H:%M:%S %Y %Z")
  2384         self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate)
  2385         
  2386         # XXX Do something for Issuer Alternative Name
  2387 
  2388         # Authority Key Identifier: keyid, dirname and serial
  2389         self.authorityKeyID_keyid   = None
  2390         self.authorityKeyID_dirname = None
  2391         self.authorityKeyID_serial  = None
  2392         if self.authorityKeyID: # (hex version already done using asn1parse)
  2393             v = fields_dict["                keyid:"]
  2394             if v:
  2395                 v = v.split('\n',1)[0]
  2396                 v = v.strip().replace(':', '')
  2397                 self.authorityKeyID_keyid = v
  2398             v = fields_dict["                DirName:"]
  2399             if v:
  2400                 v = v.split('\n',1)[0]
  2401                 self.authorityKeyID_dirname = v
  2402             v = fields_dict["                serial:"]
  2403             if v:
  2404                 v = v.split('\n',1)[0]
  2405                 v = v.strip().replace(':', '')
  2406                 self.authorityKeyID_serial = v
  2407 
  2408         # number
  2409         v = fields_dict["            X509v3 CRL Number:"]
  2410         self.number = None
  2411         if v:
  2412             v = v.split('\n',2)[1]
  2413             v = v.strip()
  2414             self.number = int(v)
  2415 
  2416         # Get the list of serial numbers of revoked certificates
  2417         self.revoked_cert_serials = []
  2418         v = fields_dict["Revoked Certificates:"]
  2419         t = fields_dict["No Revoked Certificates."]
  2420         if (t is None and v is not None):
  2421             v = v.split("Serial Number: ")[1:]
  2422             for r in v:
  2423                 s,d = r.split('\n', 1)
  2424                 s = s.split('\n', 1)[0]
  2425                 d = d.split("Revocation Date:", 1)[1]
  2426                 d = time.strptime(d.strip(), "%b %d %H:%M:%S %Y %Z")
  2427                 self.revoked_cert_serials.append((s,d))
  2428 
  2429         # signature field
  2430         v = fields_dict["    Signature Algorithm:" ]
  2431         self.sig = None
  2432         if v:
  2433             v = v.split('\n',1)[1]
  2434             v = v.replace(' ', '').replace('\n', '')
  2435             self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':')))
  2436             self.sigLen = len(self.sig)
  2437         if self.sig is None:
  2438             raise Exception(error_msg)
  2439 
  2440     def __str__(self):
  2441         return self.dercrl
  2442         
  2443     # Print main informations stored in CRL
  2444     def show(self):
  2445         print "Version: %d" % self.version
  2446         print "sigAlg: " + self.sigAlg
  2447         print "Issuer: " + self.issuer
  2448         print "lastUpdate: %s" % self.lastUpdate_str_simple
  2449         print "nextUpdate: %s" % self.nextUpdate_str_simple
  2450 
  2451     def verify(self, anchors):
  2452         """
  2453         Return True if the CRL is signed by one of the provided
  2454         anchors. False on error (invalid signature, missing anchorand, ...)
  2455         """
  2456         cafile = create_temporary_ca_file(anchors)
  2457         if cafile is None:
  2458             return False
  2459         try:
  2460             cmd = self.osslcmdbase + '-noout -CAfile %s 2>&1' % cafile
  2461             cmdres = self._apply_ossl_cmd(cmd, self.rawcrl)
  2462         except:
  2463             os.unlink(cafile)
  2464             return False
  2465         os.unlink(cafile)
  2466         return "verify OK" in cmdres
  2467 
  2468 
  2469     
  2470