diff --git a/.travis/install.sh b/.travis/install.sh
index 447603ba7826c738b4d2d77f45297d0ed13b8578..ab33fd6062ee4bd8c80887158aa4ab67060b0223 100644
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -3,7 +3,7 @@ if [ -z $TRAVIS_SUDO ] && [ "$TRAVIS_OS_NAME" = "osx" ]
 then 
   PIP_INSTALL_FLAGS="--user"
 fi
-$TRAVIS_SUDO pip install $PIP_INSTALL_FLAGS pycrypto mock
+$TRAVIS_SUDO pip install $PIP_INSTALL_FLAGS pycrypto ecdsa mock
 
 # Install pcap & dnet
 if [ ! -z $SCAPY_USE_PCAPDNET ]
diff --git a/scapy/all.py b/scapy/all.py
index d39eb322253b6b9049dbe19c9686ac8c1045b94c..15dd3ca43adf01673140a43fc75d3bcd46e58b35 100644
--- a/scapy/all.py
+++ b/scapy/all.py
@@ -43,7 +43,5 @@ from asn1.asn1 import *
 from asn1.ber import *
 from asn1.mib import *
 
-from crypto import *
-
 from pipetool import *
 from scapypipes import *
diff --git a/scapy/asn1/asn1.py b/scapy/asn1/asn1.py
index 624a5e32434de8a6cba2cbea8778267370b0392e..6823d45ba0e406d7685e8962cb714a75c10d2a46 100644
--- a/scapy/asn1/asn1.py
+++ b/scapy/asn1/asn1.py
@@ -257,7 +257,8 @@ class ASN1_BOOLEAN(ASN1_INTEGER):
 class ASN1_BIT_STRING(ASN1_Object):
     """
     /!\ ASN1_BIT_STRING values are bit strings like "011101".
-    /!\ A zero-bit padded readable string is provided nonetheless.
+    /!\ A zero-bit padded readable string is provided nonetheless,
+    /!\ which is also output when __str__ is called.
     """
     tag = ASN1_Class_UNIVERSAL.BIT_STRING
     def __init__(self, val, readable=False):
@@ -282,6 +283,8 @@ class ASN1_BIT_STRING(ASN1_Object):
             if len(s) > 20:
                 s = s[:10] + "..." + s[-10:]
             return "<%s[%r] (%d unused bit%s)>" % (self.__dict__.get("name", self.__class__.__name__), s, self.unused_bits, "s" if self.unused_bits>1 else "")
+    def __str__(self):
+        return self.val_readable
 
 class ASN1_STRING(ASN1_Object):
     tag = ASN1_Class_UNIVERSAL.STRING
diff --git a/scapy/asn1/mib.py b/scapy/asn1/mib.py
index a77bbe84f57c1f20789ec22e4c9d19b6a3a409aa..5506c0f99f479292a800ae67467ba4887ce5e184 100644
--- a/scapy/asn1/mib.py
+++ b/scapy/asn1/mib.py
@@ -450,6 +450,16 @@ x962Signature_oids = {
 
 ####### elliptic curves #######
 
+ansiX962Curve_oids = {
+        "prime192v1"                : "1.2.840.10045.3.1.1",
+        "prime192v2"                : "1.2.840.10045.3.1.2",
+        "prime192v3"                : "1.2.840.10045.3.1.3",
+        "prime239v1"                : "1.2.840.10045.3.1.4",
+        "prime239v2"                : "1.2.840.10045.3.1.5",
+        "prime239v3"                : "1.2.840.10045.3.1.6",
+        "prime256v1"                : "1.2.840.10045.3.1.7"
+        }
+
 certicomCurve_oids = {
         "ansit163k1"                : "1.3.132.0.1",
         "ansit163r1"                : "1.3.132.0.2",
@@ -558,6 +568,7 @@ x509_oids_sets = [
                  evPolicy_oids,
                  x962KeyType_oids,
                  x962Signature_oids,
+                 ansiX962Curve_oids,
                  certicomCurve_oids
                  ]
 
@@ -567,3 +578,28 @@ for oids_set in x509_oids_sets:
     x509_oids.update(oids_set)
 
 conf.mib = MIBDict(_name="MIB", **x509_oids)
+
+
+#########################
+## Hash mapping helper ##
+#########################
+
+# This dict enables static access to string references to the hash functions
+# of some algorithms from pkcs1_oids and x962Signature_oids.
+
+hash_by_oid = {
+        "1.2.840.113549.1.1.2"  : "md2",
+        "1.2.840.113549.1.1.3"  : "md4",
+        "1.2.840.113549.1.1.4"  : "md5",
+        "1.2.840.113549.1.1.5"  : "sha1",
+        "1.2.840.113549.1.1.11" : "sha256",
+        "1.2.840.113549.1.1.12" : "sha384",
+        "1.2.840.113549.1.1.13" : "sha512",
+        "1.2.840.113549.1.1.14" : "sha224",
+        "1.2.840.10045.4.1"     : "sha1",
+        "1.2.840.10045.4.3.1"   : "sha224",
+        "1.2.840.10045.4.3.2"   : "sha256",
+        "1.2.840.10045.4.3.3"   : "sha384",
+        "1.2.840.10045.4.3.4"   : "sha512"
+        }
+
diff --git a/scapy/crypto/cert.py b/scapy/crypto/cert.py
deleted file mode 100644
index c89656a58c5ffe9e1d1ca5ddaca1a3ed6c522d92..0000000000000000000000000000000000000000
--- a/scapy/crypto/cert.py
+++ /dev/null
@@ -1,2485 +0,0 @@
-## This file is part of Scapy
-## See http://www.secdev.org/projects/scapy for more informations
-## Copyright (C) Arnaud Ebalard <arno@natisbad.org>
-## This program is published under a GPLv2 license
-
-"""
-Cryptographic certificates.
-"""
-
-import os, sys, math, socket, struct, hmac, string, time, random, tempfile
-from subprocess import Popen, PIPE
-from scapy.utils import strxor
-try:
-    HAS_HASHLIB=True
-    import hashlib
-except:
-    HAS_HASHLIB=False
-
-from Crypto.PublicKey import *
-from Crypto.Cipher import *
-from Crypto.Hash import *
-from Crypto.Util import number
-
-# Maximum allowed size in bytes for a certificate file, to avoid
-# loading huge file when importing a cert
-MAX_KEY_SIZE=50*1024
-MAX_CERT_SIZE=50*1024
-MAX_CRL_SIZE=10*1024*1024   # some are that big
-
-#####################################################################
-# Some helpers
-#####################################################################
-
-def popen3(cmd):
-    p = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE,
-              close_fds=True)
-    return p.stdout, p.stdin, p.stderr
-
-def warning(m):
-    print "WARNING: %s" % m
-
-def randstring(l):
-    """
-    Returns a random string of length l (l >= 0)
-    """
-    tmp = map(lambda x: struct.pack("B", random.randrange(0, 256, 1)), [""]*l)
-    return "".join(tmp)
-
-def zerofree_randstring(l):
-    """
-    Returns a random string of length l (l >= 0) without zero in it. 
-    """
-    tmp = map(lambda x: struct.pack("B", random.randrange(1, 256, 1)), [""]*l)
-    return "".join(tmp)
-
-def strand(s1, s2):
-    """
-    Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2
-    must be of same length.
-    """
-    return "".join(map(lambda x,y:chr(ord(x)&ord(y)), s1, s2))
-
-# OS2IP function defined in RFC 3447 for octet string to integer conversion
-def pkcs_os2ip(x):
-    """
-    Accepts a byte string as input parameter and return the associated long
-    value:
-
-    Input : x        octet string to be converted
-
-    Output: x        corresponding nonnegative integer
-
-    Reverse function is pkcs_i2osp()
-    """
-    return number.bytes_to_long(x) 
-
-# IP2OS function defined in RFC 3447 for octet string to integer conversion
-def pkcs_i2osp(x,xLen):
-    """
-    Converts a long (the first parameter) to the associated byte string
-    representation of length l (second parameter). Basically, the length
-    parameters allow the function to perform the associated padding.
-
-    Input : x        nonnegative integer to be converted
-            xLen     intended length of the resulting octet string
-
-    Output: x        corresponding nonnegative integer
-
-    Reverse function is pkcs_os2ip().
-    """
-    z = number.long_to_bytes(x)
-    padlen = max(0, xLen-len(z))
-    return '\x00'*padlen + z
-
-# for every hash function a tuple is provided, giving access to 
-# - hash output length in byte
-# - associated hash function that take data to be hashed as parameter
-#   XXX I do not provide update() at the moment.
-# - DER encoding of the leading bits of digestInfo (the hash value
-#   will be concatenated to create the complete digestInfo).
-# 
-# Notes:
-# - MD4 asn.1 value should be verified. Also, as stated in 
-#   PKCS#1 v2.1, MD4 should not be used.
-# - hashlib is available from http://code.krypto.org/python/hashlib/
-# - 'tls' one is the concatenation of both md5 and sha1 hashes used
-#   by SSL/TLS when signing/verifying things
-_hashFuncParams = {
-    "md2"    : (16, 
-                lambda x: MD2.new(x).digest(), 
-                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10'),
-    "md4"    : (16, 
-                lambda x: MD4.new(x).digest(), 
-                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04\x05\x00\x04\x10'), # is that right ?
-    "md5"    : (16, 
-                lambda x: MD5.new(x).digest(), 
-                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
-    "sha1"   : (20,
-                lambda x: SHA.new(x).digest(), 
-                '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
-    "tls"    : (36,
-                lambda x: MD5.new(x).digest() + SHA.new(x).digest(),
-                '') }
-
-if HAS_HASHLIB:
-    _hashFuncParams["sha224"] = (28, 
-                lambda x: hashlib.sha224(x).digest(),
-                '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c')
-    _hashFuncParams["sha256"] = (32, 
-                lambda x: hashlib.sha256(x).digest(), 
-                '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')
-    _hashFuncParams["sha384"] = (48, 
-                lambda x: hashlib.sha384(x).digest(),
-               '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30')
-    _hashFuncParams["sha512"] = (64, 
-               lambda x: hashlib.sha512(x).digest(),
-               '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40')
-else:
-    warning("hashlib support is not available. Consider installing it")
-    warning("if you need sha224, sha256, sha384 and sha512 algs.")
-    
-def pkcs_mgf1(mgfSeed, maskLen, h):
-    """
-    Implements generic MGF1 Mask Generation function as described in
-    Appendix B.2.1 of RFC 3447. The hash function is passed by name.
-    valid values are 'md2', 'md4', 'md5', 'sha1', 'tls, 'sha256',
-    'sha384' and 'sha512'. Returns None on error.
-
-    Input:
-       mgfSeed: seed from which mask is generated, an octet string
-       maskLen: intended length in octets of the mask, at most 2^32 * hLen
-                hLen (see below)
-       h      : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-                'sha256', 'sha384'). hLen denotes the length in octets of
-                the hash function output.
-
-    Output:
-       an octet string of length maskLen
-    """
-
-    # steps are those of Appendix B.2.1
-    if h not in _hashFuncParams:
-        warning("pkcs_mgf1: invalid hash (%s) provided")
-        return None
-    hLen = _hashFuncParams[h][0]
-    hFunc = _hashFuncParams[h][1]
-    if maskLen > 2**32 * hLen:                               # 1)
-        warning("pkcs_mgf1: maskLen > 2**32 * hLen")         
-        return None
-    T = ""                                                   # 2)
-    maxCounter = math.ceil(float(maskLen) / float(hLen))     # 3)
-    counter = 0
-    while counter < maxCounter:
-        C = pkcs_i2osp(counter, 4)
-        T += hFunc(mgfSeed + C)
-        counter += 1
-    return T[:maskLen]
-
-
-def pkcs_emsa_pss_encode(M, emBits, h, mgf, sLen): 
-    """
-    Implements EMSA-PSS-ENCODE() function described in Sect. 9.1.1 of RFC 3447
-
-    Input:
-       M     : message to be encoded, an octet string
-       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM),
-               where EM is the encoded message, output of the function.
-       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-               'sha256', 'sha384'). hLen denotes the length in octets of
-               the hash function output. 
-       mgf   : the mask generation function f : seed, maskLen -> mask
-       sLen  : intended length in octets of the salt
-
-    Output:
-       encoded message, an octet string of length emLen = ceil(emBits/8)
-
-    On error, None is returned.
-    """
-
-    # 1) is not done
-    hLen = _hashFuncParams[h][0]                             # 2)
-    hFunc = _hashFuncParams[h][1]
-    mHash = hFunc(M)
-    emLen = int(math.ceil(emBits/8.))
-    if emLen < hLen + sLen + 2:                              # 3)
-        warning("encoding error (emLen < hLen + sLen + 2)")
-        return None
-    salt = randstring(sLen)                                  # 4)
-    MPrime = '\x00'*8 + mHash + salt                         # 5)
-    H = hFunc(MPrime)                                        # 6)
-    PS = '\x00'*(emLen - sLen - hLen - 2)                    # 7)
-    DB = PS + '\x01' + salt                                  # 8)
-    dbMask = mgf(H, emLen - hLen - 1)                        # 9)
-    maskedDB = strxor(DB, dbMask)                            # 10)
-    l = (8*emLen - emBits)/8                                 # 11)
-    rem = 8*emLen - emBits - 8*l # additionnal bits
-    andMask = l*'\x00'
-    if rem:
-        j = chr(sum(1<<x for x in xrange(8 - rem)))
-        andMask += j
-        l += 1
-    maskedDB = strand(maskedDB[:l], andMask) + maskedDB[l:]
-    EM = maskedDB + H + '\xbc'                               # 12)
-    return EM                                                # 13)
-
-
-def pkcs_emsa_pss_verify(M, EM, emBits, h, mgf, sLen):
-    """
-    Implements EMSA-PSS-VERIFY() function described in Sect. 9.1.2 of RFC 3447
-
-    Input:
-       M     : message to be encoded, an octet string
-       EM    : encoded message, an octet string of length emLen = ceil(emBits/8)
-       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM)
-       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-               'sha256', 'sha384'). hLen denotes the length in octets of
-               the hash function output.
-       mgf   : the mask generation function f : seed, maskLen -> mask
-       sLen  : intended length in octets of the salt
-
-    Output:
-       True if the verification is ok, False otherwise.
-    """
-    
-    # 1) is not done
-    hLen = _hashFuncParams[h][0]                             # 2)
-    hFunc = _hashFuncParams[h][1]
-    mHash = hFunc(M)
-    emLen = int(math.ceil(emBits/8.))                        # 3)
-    if emLen < hLen + sLen + 2:
-        return False
-    if EM[-1] != '\xbc':                                     # 4)
-        return False
-    l = emLen - hLen - 1                                     # 5)
-    maskedDB = EM[:l]
-    H = EM[l:l+hLen]
-    l = (8*emLen - emBits)/8                                 # 6)
-    rem = 8*emLen - emBits - 8*l # additionnal bits
-    andMask = l*'\xff'
-    if rem:
-        j = chr(~sum(1 << x for x in xrange(8 - rem)) & 0xff)
-        andMask += j
-        l += 1
-    if strand(maskedDB[:l], andMask) != '\x00'*l:
-        return False
-    dbMask = mgf(H, emLen - hLen - 1)                        # 7)
-    DB = strxor(maskedDB, dbMask)                            # 8)
-    l = (8*emLen - emBits)/8                                 # 9)
-    rem = 8*emLen - emBits - 8*l # additionnal bits
-    andMask = l*'\x00'
-    if rem:
-        j = chr(sum(1 << x for x in xrange(8 - rem)))
-        andMask += j
-        l += 1
-    DB = strand(DB[:l], andMask) + DB[l:]
-    l = emLen - hLen - sLen - 1                              # 10)
-    if DB[:l] != '\x00'*(l-1) + '\x01':
-        return False
-    salt = DB[-sLen:]                                        # 11)
-    MPrime = '\x00'*8 + mHash + salt                         # 12)
-    HPrime = hFunc(MPrime)                                   # 13)
-    return H == HPrime                                       # 14)
-
-
-def pkcs_emsa_pkcs1_v1_5_encode(M, emLen, h): # section 9.2 of RFC 3447
-    """
-    Implements EMSA-PKCS1-V1_5-ENCODE() function described in Sect.
-    9.2 of RFC 3447.
-
-    Input:
-       M    : message to be encode, an octet string
-       emLen: intended length in octets of the encoded message, at least
-              tLen + 11, where tLen is the octet length of the DER encoding
-              T of a certain value computed during the encoding operation.
-       h    : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-              'sha256', 'sha384'). hLen denotes the length in octets of
-              the hash function output.
-
-    Output:
-       encoded message, an octet string of length emLen
-
-    On error, None is returned.
-    """
-    hLen = _hashFuncParams[h][0]                             # 1)
-    hFunc = _hashFuncParams[h][1]
-    H = hFunc(M)
-    hLeadingDigestInfo = _hashFuncParams[h][2]               # 2)
-    T = hLeadingDigestInfo + H
-    tLen = len(T)
-    if emLen < tLen + 11:                                    # 3)
-        warning("pkcs_emsa_pkcs1_v1_5_encode: intended encoded message length too short")
-        return None
-    PS = '\xff'*(emLen - tLen - 3)                           # 4)
-    EM = '\x00' + '\x01' + PS + '\x00' + T                   # 5)
-    return EM                                                # 6)
-
-
-# XXX should add other pgf1 instance in a better fashion.
-
-def create_ca_file(anchor_list, filename):
-    """
-    Concatenate all the certificates (PEM format for the export) in
-    'anchor_list' and write the result to file 'filename'. On success
-    'filename' is returned, None otherwise.
-
-    If you are used to OpenSSL tools, this function builds a CAfile
-    that can be used for certificate and CRL check.
-
-    Also see create_temporary_ca_file().
-    """
-    try:
-        f = open(filename, "w")
-        for a in anchor_list:
-            s = a.output(fmt="PEM")
-            f.write(s)
-        f.close()
-    except:
-        return None
-    return filename
-
-def create_temporary_ca_file(anchor_list):
-    """
-    Concatenate all the certificates (PEM format for the export) in
-    'anchor_list' and write the result to file to a temporary file
-    using mkstemp() from tempfile module. On success 'filename' is
-    returned, None otherwise.
-
-    If you are used to OpenSSL tools, this function builds a CAfile
-    that can be used for certificate and CRL check.
-
-    Also see create_temporary_ca_file().
-    """
-    try:
-        f, fname = tempfile.mkstemp()
-        for a in anchor_list:
-            s = a.output(fmt="PEM")
-            l = os.write(f, s)
-        os.close(f)
-    except:
-        return None
-    return fname
-
-def create_temporary_ca_path(anchor_list, folder):
-    """
-    Create a CA path folder as defined in OpenSSL terminology, by
-    storing all certificates in 'anchor_list' list in PEM format
-    under provided 'folder' and then creating the associated links
-    using the hash as usually done by c_rehash.
-
-    Note that you can also include CRL in 'anchor_list'. In that
-    case, they will also be stored under 'folder' and associated
-    links will be created.
-
-    In folder, the files are created with names of the form
-    0...ZZ.pem. If you provide an empty list, folder will be created
-    if it does not already exist, but that's all.
-
-    The number of certificates written to folder is returned on
-    success, None on error.
-    """
-    # We should probably avoid writing duplicate anchors and also
-    # check if they are all certs.
-    try:
-        if not os.path.isdir(folder):
-            os.makedirs(folder)
-    except:
-        return None
-    
-    l = len(anchor_list)
-    if l == 0:
-        return None
-    fmtstr = "%%0%sd.pem" % math.ceil(math.log(l, 10))
-    i = 0
-    try:
-        for a in anchor_list:
-            fname = os.path.join(folder, fmtstr % i)
-            f = open(fname, "w")
-            s = a.output(fmt="PEM")
-            f.write(s)
-            f.close()
-            i += 1
-    except:
-        return None
-
-    r,w,e=popen3(["c_rehash", folder])
-    r.close(); w.close(); e.close()
-
-    return l
-
-
-#####################################################################
-# Public Key Cryptography related stuff
-#####################################################################
-
-class OSSLHelper:
-    def _apply_ossl_cmd(self, osslcmd, rawdata):
-        r,w,e=popen3(osslcmd)
-        w.write(rawdata)
-        w.close()
-        res = r.read()
-        r.close()
-        e.close()
-        return res
-
-class _EncryptAndVerify:
-    ### Below are encryption methods
-
-    def _rsaep(self, m):
-        """
-        Internal method providing raw RSA encryption, i.e. simple modular
-        exponentiation of the given message representative 'm', a long
-        between 0 and n-1.
-
-        This is the encryption primitive RSAEP described in PKCS#1 v2.1,
-        i.e. RFC 3447 Sect. 5.1.1.
-
-        Input:
-           m: message representative, a long between 0 and n-1, where
-              n is the key modulus.
-
-        Output:
-           ciphertext representative, a long between 0 and n-1
-
-        Not intended to be used directly. Please, see encrypt() method.
-        """
-
-        n = self.modulus
-        if type(m) is int:
-            m = long(m)
-        if type(m) is not long or m > n-1:
-            warning("Key._rsaep() expects a long between 0 and n-1")
-            return None
-
-        return self.key.encrypt(m, "")[0]
-
-
-    def _rsaes_pkcs1_v1_5_encrypt(self, M):
-        """
-        Implements RSAES-PKCS1-V1_5-ENCRYPT() function described in section
-        7.2.1 of RFC 3447.
-
-        Input:
-           M: message to be encrypted, an octet string of length mLen, where
-              mLen <= k - 11 (k denotes the length in octets of the key modulus)
-
-        Output:
-           ciphertext, an octet string of length k
-
-        On error, None is returned.
-        """
-
-        # 1) Length checking
-        mLen = len(M)
-        k = self.modulusLen / 8
-        if mLen > k - 11:
-            warning("Key._rsaes_pkcs1_v1_5_encrypt(): message too "
-                    "long (%d > %d - 11)" % (mLen, k))
-            return None
-
-        # 2) EME-PKCS1-v1_5 encoding
-        PS = zerofree_randstring(k - mLen - 3)      # 2.a)
-        EM = '\x00' + '\x02' + PS + '\x00' + M      # 2.b)
-
-        # 3) RSA encryption
-        m = pkcs_os2ip(EM)                          # 3.a)
-        c = self._rsaep(m)                          # 3.b)
-        C = pkcs_i2osp(c, k)                        # 3.c)
-
-        return C                                    # 4)
-
-
-    def _rsaes_oaep_encrypt(self, M, h=None, mgf=None, L=None):
-        """
-        Internal method providing RSAES-OAEP-ENCRYPT as defined in Sect.
-        7.1.1 of RFC 3447. Not intended to be used directly. Please, see
-        encrypt() method for type "OAEP".
-
-
-        Input:
-           M  : message to be encrypted, an octet string of length mLen
-                where mLen <= k - 2*hLen - 2 (k denotes the length in octets
-                of the RSA modulus and hLen the length in octets of the hash
-                function output)
-           h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-                'sha256', 'sha384'). hLen denotes the length in octets of
-                the hash function output. 'sha1' is used by default if not
-                provided.
-           mgf: the mask generation function f : seed, maskLen -> mask
-           L  : optional label to be associated with the message; the default
-                value for L, if not provided is the empty string
-
-        Output:
-           ciphertext, an octet string of length k
-
-        On error, None is returned.
-        """
-        # The steps below are the one described in Sect. 7.1.1 of RFC 3447.
-        # 1) Length Checking
-                                                    # 1.a) is not done
-        mLen = len(M)
-        if h is None:
-            h = "sha1"
-        if h not in _hashFuncParams:
-            warning("Key._rsaes_oaep_encrypt(): unknown hash function %s.", h)
-            return None
-        hLen = _hashFuncParams[h][0]
-        hFun = _hashFuncParams[h][1]
-        k = self.modulusLen / 8
-        if mLen > k - 2*hLen - 2:                   # 1.b)
-            warning("Key._rsaes_oaep_encrypt(): message too long.")
-            return None
-        
-        # 2) EME-OAEP encoding
-        if L is None:                               # 2.a)
-            L = ""
-        lHash = hFun(L)
-        PS = '\x00'*(k - mLen - 2*hLen - 2)         # 2.b)
-        DB = lHash + PS + '\x01' + M                # 2.c)
-        seed = randstring(hLen)                     # 2.d)
-        if mgf is None:                             # 2.e)
-            mgf = lambda x,y: pkcs_mgf1(x,y,h)
-        dbMask = mgf(seed, k - hLen - 1)
-        maskedDB = strxor(DB, dbMask)               # 2.f)
-        seedMask = mgf(maskedDB, hLen)              # 2.g)
-        maskedSeed = strxor(seed, seedMask)         # 2.h)
-        EM = '\x00' + maskedSeed + maskedDB         # 2.i)
-
-        # 3) RSA Encryption
-        m = pkcs_os2ip(EM)                          # 3.a)
-        c = self._rsaep(m)                          # 3.b)
-        C = pkcs_i2osp(c, k)                        # 3.c)
-
-        return C                                    # 4)
-
-
-    def encrypt(self, m, t=None, h=None, mgf=None, L=None):
-        """
-        Encrypt message 'm' using 't' encryption scheme where 't' can be:
-
-        - None: the message 'm' is directly applied the RSAEP encryption
-                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
-                Sect 5.1.1. Simply put, the message undergo a modular
-                exponentiation using the public key. Additionnal method
-                parameters are just ignored.
-
-        - 'pkcs': the message 'm' is applied RSAES-PKCS1-V1_5-ENCRYPT encryption
-                scheme as described in section 7.2.1 of RFC 3447. In that
-                context, other parameters ('h', 'mgf', 'l') are not used.
-
-        - 'oaep': the message 'm' is applied the RSAES-OAEP-ENCRYPT encryption
-                scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
-                7.1.1. In that context,
-
-                o 'h' parameter provides the name of the hash method to use.
-                  Possible values are "md2", "md4", "md5", "sha1", "tls",
-                  "sha224", "sha256", "sha384" and "sha512". if none is provided,
-                  sha1 is used.
-
-                o 'mgf' is the mask generation function. By default, mgf
-                  is derived from the provided hash function using the
-                  generic MGF1 (see pkcs_mgf1() for details).
-
-                o 'L' is the optional label to be associated with the
-                  message. If not provided, the default value is used, i.e
-                  the empty string. No check is done on the input limitation
-                  of the hash function regarding the size of 'L' (for
-                  instance, 2^61 - 1 for SHA-1). You have been warned.
-        """
-
-        if t is None: # Raw encryption
-            m = pkcs_os2ip(m)
-            c = self._rsaep(m)
-            return pkcs_i2osp(c, self.modulusLen/8)
-        
-        elif t == "pkcs":
-            return self._rsaes_pkcs1_v1_5_encrypt(m)
-        
-        elif t == "oaep":
-            return self._rsaes_oaep_encrypt(m, h, mgf, L)
-
-        else:
-            warning("Key.encrypt(): Unknown encryption type (%s) provided" % t)
-            return None
-
-    ### Below are verification related methods
-
-    def _rsavp1(self, s):
-        """
-        Internal method providing raw RSA verification, i.e. simple modular
-        exponentiation of the given signature representative 'c', an integer
-        between 0 and n-1.
-
-        This is the signature verification primitive RSAVP1 described in
-        PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.2.
-
-        Input:
-          s: signature representative, an integer between 0 and n-1,
-             where n is the key modulus.
-
-        Output:
-           message representative, an integer between 0 and n-1
-
-        Not intended to be used directly. Please, see verify() method.
-        """
-        return self._rsaep(s)
-
-    def _rsassa_pss_verify(self, M, S, h=None, mgf=None, sLen=None):
-        """
-        Implements RSASSA-PSS-VERIFY() function described in Sect 8.1.2
-        of RFC 3447
-
-        Input:
-           M: message whose signature is to be verified
-           S: signature to be verified, an octet string of length k, where k
-              is the length in octets of the RSA modulus n.
-
-        Output:
-           True is the signature is valid. False otherwise.
-        """
-
-        # Set default parameters if not provided
-        if h is None: # By default, sha1
-            h = "sha1"
-        if h not in _hashFuncParams:
-            warning("Key._rsassa_pss_verify(): unknown hash function "
-                    "provided (%s)" % h)
-            return False
-        if mgf is None: # use mgf1 with underlying hash function
-            mgf = lambda x,y: pkcs_mgf1(x, y, h)
-        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
-            hLen = _hashFuncParams[h][0]
-            sLen = hLen
-
-        # 1) Length checking
-        modBits = self.modulusLen
-        k = modBits / 8
-        if len(S) != k:
-            return False
-
-        # 2) RSA verification
-        s = pkcs_os2ip(S)                           # 2.a)
-        m = self._rsavp1(s)                         # 2.b)
-        emLen = math.ceil((modBits - 1) / 8.)       # 2.c)
-        EM = pkcs_i2osp(m, emLen) 
-
-        # 3) EMSA-PSS verification
-        Result = pkcs_emsa_pss_verify(M, EM, modBits - 1, h, mgf, sLen)
-
-        return Result                               # 4)
-
-
-    def _rsassa_pkcs1_v1_5_verify(self, M, S, h):
-        """
-        Implements RSASSA-PKCS1-v1_5-VERIFY() function as described in
-        Sect. 8.2.2 of RFC 3447.
-
-        Input:
-           M: message whose signature is to be verified, an octet string
-           S: signature to be verified, an octet string of length k, where
-              k is the length in octets of the RSA modulus n
-           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-                'sha256', 'sha384').
-           
-        Output:
-           True if the signature is valid. False otherwise.
-        """
-
-        # 1) Length checking
-        k = self.modulusLen / 8
-        if len(S) != k:
-            warning("invalid signature (len(S) != k)")
-            return False
-
-        # 2) RSA verification
-        s = pkcs_os2ip(S)                           # 2.a)
-        m = self._rsavp1(s)                         # 2.b)
-        EM = pkcs_i2osp(m, k)                       # 2.c)
-
-        # 3) EMSA-PKCS1-v1_5 encoding
-        EMPrime = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
-        if EMPrime is None:
-            warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.")
-            return False
-
-        # 4) Comparison
-        return EM == EMPrime
-
-
-    def verify(self, M, S, t=None, h=None, mgf=None, sLen=None):
-        """
-        Verify alleged signature 'S' is indeed the signature of message 'M' using
-        't' signature scheme where 't' can be:
-
-        - None: the alleged signature 'S' is directly applied the RSAVP1 signature
-                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
-                5.2.1. Simply put, the provided signature is applied a moular
-                exponentiation using the public key. Then, a comparison of the
-                result is done against 'M'. On match, True is returned.
-                Additionnal method parameters are just ignored.
-
-        - 'pkcs': the alleged signature 'S' and message 'M' are applied
-                RSASSA-PKCS1-v1_5-VERIFY signature verification scheme as
-                described in Sect. 8.2.2 of RFC 3447. In that context,
-                the hash function name is passed using 'h'. Possible values are
-                "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384"
-                and "sha512". If none is provided, sha1 is used. Other additionnal
-                parameters are ignored.
-
-        - 'pss': the alleged signature 'S' and message 'M' are applied
-                RSASSA-PSS-VERIFY signature scheme as described in Sect. 8.1.2.
-                of RFC 3447. In that context,
-
-                o 'h' parameter provides the name of the hash method to use.
-                   Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224",
-                   "sha256", "sha384" and "sha512". if none is provided, sha1
-                   is used. 
-
-                o 'mgf' is the mask generation function. By default, mgf
-                   is derived from the provided hash function using the
-                   generic MGF1 (see pkcs_mgf1() for details).
-
-                o 'sLen' is the length in octet of the salt. You can overload the
-                  default value (the octet length of the hash value for provided
-                  algorithm) by providing another one with that parameter.
-        """
-        if t is None: # RSAVP1
-            S = pkcs_os2ip(S)
-            n = self.modulus
-            if S > n-1:
-                warning("Signature to be verified is too long for key modulus")
-                return False
-            m = self._rsavp1(S)
-            if m is None:
-                return False
-            l = int(math.ceil(math.log(m, 2) / 8.)) # Hack
-            m = pkcs_i2osp(m, l)
-            return M == m
-
-        elif t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY
-            if h is None:
-                h = "sha1"
-            return self._rsassa_pkcs1_v1_5_verify(M, S, h)
-
-        elif t == "pss": # RSASSA-PSS-VERIFY
-            return self._rsassa_pss_verify(M, S, h, mgf, sLen)
-
-        else:
-            warning("Key.verify(): Unknown signature type (%s) provided" % t)
-            return None
-    
-class _DecryptAndSignMethods(OSSLHelper):
-    ### Below are decryption related methods. Encryption ones are inherited
-    ### from PubKey
-
-    def _rsadp(self, c):
-        """
-        Internal method providing raw RSA decryption, i.e. simple modular
-        exponentiation of the given ciphertext representative 'c', a long
-        between 0 and n-1.
-
-        This is the decryption primitive RSADP described in PKCS#1 v2.1,
-        i.e. RFC 3447 Sect. 5.1.2.
-
-        Input:
-           c: ciphertest representative, a long between 0 and n-1, where
-              n is the key modulus.
-
-        Output:
-           ciphertext representative, a long between 0 and n-1
-
-        Not intended to be used directly. Please, see encrypt() method.
-        """
-
-        n = self.modulus
-        if type(c) is int:
-            c = long(c)        
-        if type(c) is not long or c > n-1:
-            warning("Key._rsaep() expects a long between 0 and n-1")
-            return None
-
-        return self.key.decrypt(c)    
-
-
-    def _rsaes_pkcs1_v1_5_decrypt(self, C):
-        """
-        Implements RSAES-PKCS1-V1_5-DECRYPT() function described in section
-        7.2.2 of RFC 3447.
-
-        Input:
-           C: ciphertext to be decrypted, an octet string of length k, where
-              k is the length in octets of the RSA modulus n.
-
-        Output:
-           an octet string of length k at most k - 11
-
-        on error, None is returned.
-        """
-        
-        # 1) Length checking
-        cLen = len(C)
-        k = self.modulusLen / 8
-        if cLen != k or k < 11:
-            warning("Key._rsaes_pkcs1_v1_5_decrypt() decryption error "
-                    "(cLen != k or k < 11)")
-            return None
-
-        # 2) RSA decryption
-        c = pkcs_os2ip(C)                           # 2.a)
-        m = self._rsadp(c)                          # 2.b)
-        EM = pkcs_i2osp(m, k)                       # 2.c)
-
-        # 3) EME-PKCS1-v1_5 decoding
-
-        # I am aware of the note at the end of 7.2.2 regarding error
-        # conditions reporting but the one provided below are for _local_
-        # debugging purposes. --arno
-        
-        if EM[0] != '\x00':
-            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
-                    "(first byte is not 0x00)")
-            return None
-
-        if EM[1] != '\x02':
-            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
-                    "(second byte is not 0x02)")
-            return None
-
-        tmp = EM[2:].split('\x00', 1)
-        if len(tmp) != 2:
-            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
-                    "(no 0x00 to separate PS from M)")
-            return None
-
-        PS, M = tmp
-        if len(PS) < 8:
-            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
-                    "(PS is less than 8 byte long)")
-            return None
-
-        return M                                    # 4)
-
-
-    def _rsaes_oaep_decrypt(self, C, h=None, mgf=None, L=None):
-        """
-        Internal method providing RSAES-OAEP-DECRYPT as defined in Sect.
-        7.1.2 of RFC 3447. Not intended to be used directly. Please, see
-        encrypt() method for type "OAEP".
-
-
-        Input:
-           C  : ciphertext to be decrypted, an octet string of length k, where
-                k = 2*hLen + 2 (k denotes the length in octets of the RSA modulus
-                and hLen the length in octets of the hash function output)
-           h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-                'sha256', 'sha384'). 'sha1' is used if none is provided.
-           mgf: the mask generation function f : seed, maskLen -> mask
-           L  : optional label whose association with the message is to be
-                verified; the default value for L, if not provided is the empty
-                string.
-
-        Output:
-           message, an octet string of length k mLen, where mLen <= k - 2*hLen - 2
-
-        On error, None is returned.
-        """
-        # The steps below are the one described in Sect. 7.1.2 of RFC 3447.
-
-        # 1) Length Checking
-                                                    # 1.a) is not done
-        if h is None:
-            h = "sha1"
-        if h not in _hashFuncParams:
-            warning("Key._rsaes_oaep_decrypt(): unknown hash function %s.", h)
-            return None
-        hLen = _hashFuncParams[h][0]
-        hFun = _hashFuncParams[h][1]
-        k = self.modulusLen / 8
-        cLen = len(C)
-        if cLen != k:                               # 1.b)
-            warning("Key._rsaes_oaep_decrypt(): decryption error. "
-                    "(cLen != k)")
-            return None
-        if k < 2*hLen + 2:
-            warning("Key._rsaes_oaep_decrypt(): decryption error. "
-                    "(k < 2*hLen + 2)")
-            return None
-
-        # 2) RSA decryption
-        c = pkcs_os2ip(C)                           # 2.a)
-        m = self._rsadp(c)                          # 2.b)
-        EM = pkcs_i2osp(m, k)                       # 2.c)
-
-        # 3) EME-OAEP decoding
-        if L is None:                               # 3.a)
-            L = ""
-        lHash = hFun(L)
-        Y = EM[:1]                                  # 3.b)
-        if Y != '\x00':
-            warning("Key._rsaes_oaep_decrypt(): decryption error. "
-                    "(Y is not zero)")
-            return None
-        maskedSeed = EM[1:1+hLen]
-        maskedDB = EM[1+hLen:]
-        if mgf is None:
-            mgf = lambda x,y: pkcs_mgf1(x, y, h)
-        seedMask = mgf(maskedDB, hLen)              # 3.c)
-        seed = strxor(maskedSeed, seedMask)         # 3.d)
-        dbMask = mgf(seed, k - hLen - 1)            # 3.e)
-        DB = strxor(maskedDB, dbMask)               # 3.f)
-
-        # I am aware of the note at the end of 7.1.2 regarding error
-        # conditions reporting but the one provided below are for _local_
-        # debugging purposes. --arno
-
-        lHashPrime = DB[:hLen]                      # 3.g)
-        tmp = DB[hLen:].split('\x01', 1)
-        if len(tmp) != 2:
-            warning("Key._rsaes_oaep_decrypt(): decryption error. "
-                    "(0x01 separator not found)")
-            return None
-        PS, M = tmp
-        if PS != '\x00'*len(PS):
-            warning("Key._rsaes_oaep_decrypt(): decryption error. "
-                    "(invalid padding string)")
-            return None
-        if lHash != lHashPrime:
-            warning("Key._rsaes_oaep_decrypt(): decryption error. "
-                    "(invalid hash)")
-            return None            
-        return M                                    # 4)
-
-
-    def decrypt(self, C, t=None, h=None, mgf=None, L=None):
-        """
-        Decrypt ciphertext 'C' using 't' decryption scheme where 't' can be:
-
-        - None: the ciphertext 'C' is directly applied the RSADP decryption
-                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
-                Sect 5.1.2. Simply, put the message undergo a modular
-                exponentiation using the private key. Additionnal method
-                parameters are just ignored.
-
-        - 'pkcs': the ciphertext 'C' is applied RSAES-PKCS1-V1_5-DECRYPT
-                decryption scheme as described in section 7.2.2 of RFC 3447.
-                In that context, other parameters ('h', 'mgf', 'l') are not
-                used.
-
-        - 'oaep': the ciphertext 'C' is applied the RSAES-OAEP-DECRYPT decryption
-                scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
-                7.1.2. In that context,
-
-                o 'h' parameter provides the name of the hash method to use.
-                  Possible values are "md2", "md4", "md5", "sha1", "tls",
-                  "sha224", "sha256", "sha384" and "sha512". if none is provided,
-                  sha1 is used by default.
-
-                o 'mgf' is the mask generation function. By default, mgf
-                  is derived from the provided hash function using the
-                  generic MGF1 (see pkcs_mgf1() for details).
-
-                o 'L' is the optional label to be associated with the
-                  message. If not provided, the default value is used, i.e
-                  the empty string. No check is done on the input limitation
-                  of the hash function regarding the size of 'L' (for
-                  instance, 2^61 - 1 for SHA-1). You have been warned.        
-        """
-        if t is None:
-            C = pkcs_os2ip(C)
-            c = self._rsadp(C)
-            l = int(math.ceil(math.log(c, 2) / 8.)) # Hack
-            return pkcs_i2osp(c, l)
-
-        elif t == "pkcs":
-            return self._rsaes_pkcs1_v1_5_decrypt(C)
-
-        elif t == "oaep":
-            return self._rsaes_oaep_decrypt(C, h, mgf, L)
-
-        else:
-            warning("Key.decrypt(): Unknown decryption type (%s) provided" % t)
-            return None
-
-    ### Below are signature related methods. Verification ones are inherited from
-    ### PubKey
-
-    def _rsasp1(self, m):
-        """
-        Internal method providing raw RSA signature, i.e. simple modular
-        exponentiation of the given message representative 'm', an integer
-        between 0 and n-1.
-
-        This is the signature primitive RSASP1 described in PKCS#1 v2.1,
-        i.e. RFC 3447 Sect. 5.2.1.
-
-        Input:
-           m: message representative, an integer between 0 and n-1, where
-              n is the key modulus.
-
-        Output:
-           signature representative, an integer between 0 and n-1
-
-        Not intended to be used directly. Please, see sign() method.
-        """
-        return self._rsadp(m)
-
-
-    def _rsassa_pss_sign(self, M, h=None, mgf=None, sLen=None):
-        """
-        Implements RSASSA-PSS-SIGN() function described in Sect. 8.1.1 of
-        RFC 3447.
-
-        Input:
-           M: message to be signed, an octet string
-
-        Output:
-           signature, an octet string of length k, where k is the length in
-           octets of the RSA modulus n.
-
-        On error, None is returned.
-        """
-
-        # Set default parameters if not provided
-        if h is None: # By default, sha1
-            h = "sha1"
-        if h not in _hashFuncParams:
-            warning("Key._rsassa_pss_sign(): unknown hash function "
-                    "provided (%s)" % h)
-            return None
-        if mgf is None: # use mgf1 with underlying hash function
-            mgf = lambda x,y: pkcs_mgf1(x, y, h)
-        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
-            hLen = _hashFuncParams[h][0]
-            sLen = hLen
-
-        # 1) EMSA-PSS encoding
-        modBits = self.modulusLen
-        k = modBits / 8
-        EM = pkcs_emsa_pss_encode(M, modBits - 1, h, mgf, sLen)
-        if EM is None:
-            warning("Key._rsassa_pss_sign(): unable to encode")
-            return None
-
-        # 2) RSA signature
-        m = pkcs_os2ip(EM)                          # 2.a)
-        s = self._rsasp1(m)                         # 2.b)
-        S = pkcs_i2osp(s, k)                        # 2.c)
-
-        return S                                    # 3)
-
-
-    def _rsassa_pkcs1_v1_5_sign(self, M, h):
-        """
-        Implements RSASSA-PKCS1-v1_5-SIGN() function as described in
-        Sect. 8.2.1 of RFC 3447.
-
-        Input:
-           M: message to be signed, an octet string
-           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls'
-                'sha256', 'sha384').
-           
-        Output:
-           the signature, an octet string.
-        """
-        
-        # 1) EMSA-PKCS1-v1_5 encoding
-        k = self.modulusLen / 8
-        EM = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
-        if EM is None:
-            warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode")
-            return None
-
-        # 2) RSA signature
-        m = pkcs_os2ip(EM)                          # 2.a)
-        s = self._rsasp1(m)                         # 2.b)
-        S = pkcs_i2osp(s, k)                        # 2.c)
-
-        return S                                    # 3)
-
-
-    def sign(self, M, t=None, h=None, mgf=None, sLen=None):
-        """
-        Sign message 'M' using 't' signature scheme where 't' can be:
-
-        - None: the message 'M' is directly applied the RSASP1 signature
-                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
-                5.2.1. Simply put, the message undergo a modular exponentiation
-                using the private key. Additionnal method parameters are just
-                ignored.
-
-        - 'pkcs': the message 'M' is applied RSASSA-PKCS1-v1_5-SIGN signature
-                scheme as described in Sect. 8.2.1 of RFC 3447. In that context,
-                the hash function name is passed using 'h'. Possible values are
-                "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384"
-                and "sha512". If none is provided, sha1 is used. Other additionnal 
-                parameters are ignored.
-
-        - 'pss' : the message 'M' is applied RSASSA-PSS-SIGN signature scheme as
-                described in Sect. 8.1.1. of RFC 3447. In that context,
-
-                o 'h' parameter provides the name of the hash method to use.
-                   Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224",
-                   "sha256", "sha384" and "sha512". if none is provided, sha1
-                   is used. 
-
-                o 'mgf' is the mask generation function. By default, mgf
-                   is derived from the provided hash function using the
-                   generic MGF1 (see pkcs_mgf1() for details).
-
-                o 'sLen' is the length in octet of the salt. You can overload the
-                  default value (the octet length of the hash value for provided
-                  algorithm) by providing another one with that parameter.
-        """
-
-        if t is None: # RSASP1
-            M = pkcs_os2ip(M)
-            n = self.modulus
-            if M > n-1:
-                warning("Message to be signed is too long for key modulus")
-                return None
-            s = self._rsasp1(M)
-            if s is None:
-                return None
-            return pkcs_i2osp(s, self.modulusLen/8)
-        
-        elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN
-            if h is None:
-                h = "sha1"
-            return self._rsassa_pkcs1_v1_5_sign(M, h)
-        
-        elif t == "pss": # RSASSA-PSS-SIGN
-            return self._rsassa_pss_sign(M, h, mgf, sLen)
-
-        else:
-            warning("Key.sign(): Unknown signature type (%s) provided" % t)
-            return None
-
-
-def openssl_parse_RSA(fmt="PEM"):
-    return popen3(['openssl', 'rsa', '-text', '-pubin', '-inform', fmt, '-noout'])
-def openssl_convert_RSA(infmt="PEM", outfmt="DER"):
-    return ['openssl', 'rsa', '-pubin', '-inform', infmt, '-outform', outfmt]
-
-class PubKey(OSSLHelper, _EncryptAndVerify):
-    # Below are the fields we recognize in the -text output of openssl
-    # and from which we extract information. We expect them in that
-    # order. Number of spaces does matter.
-    possible_fields = [ "Modulus (",
-                        "Exponent:" ]
-    possible_fields_count = len(possible_fields)
-    
-    def __init__(self, keypath):
-        error_msg = "Unable to import key."
-
-        # XXX Temporary hack to use PubKey inside Cert
-        if type(keypath) is tuple:
-            e, m, mLen = keypath
-            self.modulus = m
-            self.modulusLen = mLen
-            self.pubExp = e
-            return
-
-        fields_dict = {}
-        for k in self.possible_fields:
-            fields_dict[k] = None
-
-        self.keypath = None
-        rawkey = None
-
-        if (not '\x00' in keypath) and os.path.isfile(keypath): # file
-            self.keypath = keypath
-            key_size = os.path.getsize(keypath)
-            if key_size > MAX_KEY_SIZE:
-                raise Exception(error_msg)
-            try:
-                f = open(keypath)
-                rawkey = f.read()
-                f.close()
-            except:
-                raise Exception(error_msg)     
-        else:
-            rawkey = keypath
-
-        if rawkey is None:
-            raise Exception(error_msg)
-
-        self.rawkey = rawkey
-
-        key_header = "-----BEGIN PUBLIC KEY-----"
-        key_footer = "-----END PUBLIC KEY-----"
-        l = rawkey.split(key_header, 1)
-        if len(l) == 2: # looks like PEM
-            tmp = l[1]
-            l = tmp.split(key_footer, 1)
-            if len(l) == 2:
-                tmp = l[0]
-                rawkey = "%s%s%s\n" % (key_header, tmp, key_footer)
-            else:
-                raise Exception(error_msg)
-            r,w,e = openssl_parse_RSA("PEM")
-            w.write(rawkey)
-            w.close()
-            textkey = r.read()
-            r.close()
-            res = e.read()
-            e.close()
-            if res == '':
-                self.format = "PEM"
-                self.pemkey = rawkey
-                self.textkey = textkey
-                cmd = openssl_convert_RSA_cmd("PEM", "DER")
-                self.derkey = self._apply_ossl_cmd(cmd, rawkey)
-            else:
-                raise Exception(error_msg)
-        else: # not PEM, try DER
-            r,w,e = openssl_parse_RSA("DER")
-            w.write(rawkey)
-            w.close()
-            textkey = r.read()
-            r.close()
-            res = e.read()
-            if res == '':
-                self.format = "DER"
-                self.derkey = rawkey
-                self.textkey = textkey
-                cmd = openssl_convert_RSA_cmd("DER", "PEM")
-                self.pemkey = self._apply_ossl_cmd(cmd, rawkey)
-                cmd = openssl_convert_RSA_cmd("DER", "DER")
-                self.derkey = self._apply_ossl_cmd(cmd, rawkey)                
-            else:
-                try: # Perhaps it is a cert
-                    c = Cert(keypath)
-                except:
-                    raise Exception(error_msg)
-                # TODO:
-                # Reconstruct a key (der and pem) and provide:
-                # self.format
-                # self.derkey
-                # self.pemkey
-                # self.textkey
-                # self.keypath
-
-        self.osslcmdbase = ['openssl', 'rsa', '-pubin', '-inform',  self.format]
-
-        self.keypath = keypath
-
-        # Parse the -text output of openssl to make things available
-        l = self.textkey.split('\n', 1)
-        if len(l) != 2:
-            raise Exception(error_msg)
-        cur, tmp = l
-        i = 0
-        k = self.possible_fields[i] # Modulus (
-        cur = cur[len(k):] + '\n'
-        while k:
-            l = tmp.split('\n', 1)
-            if len(l) != 2: # Over
-                fields_dict[k] = cur
-                break
-            l, tmp = l
-
-            newkey = 0
-            # skip fields we have already seen, this is the purpose of 'i'
-            for j in xrange(i, self.possible_fields_count):
-                f = self.possible_fields[j]
-                if l.startswith(f):
-                    fields_dict[k] = cur
-                    cur = l[len(f):] + '\n'
-                    k = f
-                    newkey = 1
-                    i = j + 1
-                    break
-            if newkey == 1:
-                continue
-            cur += l + '\n'
-
-        # modulus and modulus length
-        v = fields_dict["Modulus ("]
-        self.modulusLen = None
-        if v:
-            v, rem = v.split(' bit):', 1)
-            self.modulusLen = int(v)
-            rem = rem.replace('\n','').replace(' ','').replace(':','')
-            self.modulus = long(rem, 16)
-        if self.modulus is None:
-            raise Exception(error_msg)
-        
-        # public exponent
-        v = fields_dict["Exponent:"]
-        self.pubExp = None
-        if v:
-            self.pubExp = long(v.split('(', 1)[0])
-        if self.pubExp is None:
-            raise Exception(error_msg)
-
-        self.key = RSA.construct((self.modulus, self.pubExp, ))
-
-    def __str__(self):
-        return self.derkey
-
-
-class Key(OSSLHelper, _DecryptAndSignMethods, _EncryptAndVerify):
-    # Below are the fields we recognize in the -text output of openssl
-    # and from which we extract information. We expect them in that
-    # order. Number of spaces does matter.
-    possible_fields = [ "Private-Key: (",
-                        "modulus:",
-                        "publicExponent:",
-                        "privateExponent:",
-                        "prime1:",
-                        "prime2:",
-                        "exponent1:",
-                        "exponent2:",
-                        "coefficient:" ]
-    possible_fields_count = len(possible_fields)
-    
-    def __init__(self, keypath):
-        error_msg = "Unable to import key."
-
-        fields_dict = {}
-        for k in self.possible_fields:
-            fields_dict[k] = None
-
-        self.keypath = None
-        rawkey = None
-
-        if (not '\x00' in keypath) and os.path.isfile(keypath):
-            self.keypath = keypath
-            key_size = os.path.getsize(keypath)
-            if key_size > MAX_KEY_SIZE:
-                raise Exception(error_msg)
-            try:
-                f = open(keypath)
-                rawkey = f.read()
-                f.close()
-            except:
-                raise Exception(error_msg)     
-        else:
-            rawkey = keypath
-
-        if rawkey is None:
-            raise Exception(error_msg)
-
-        self.rawkey = rawkey
-
-        # Let's try to get file format : PEM or DER.
-        fmtstr = 'openssl rsa -text -inform %s -noout'
-        convertstr = 'openssl rsa -inform %s -outform %s'
-        key_header = "-----BEGIN RSA PRIVATE KEY-----"
-        key_footer = "-----END RSA PRIVATE KEY-----"
-        l = rawkey.split(key_header, 1)
-        if len(l) == 2: # looks like PEM
-            tmp = l[1]
-            l = tmp.split(key_footer, 1)
-            if len(l) == 2:
-                tmp = l[0]
-                rawkey = "%s%s%s\n" % (key_header, tmp, key_footer)
-            else:
-                raise Exception(error_msg)
-            r,w,e = popen3((fmtstr % "PEM").split(" "))
-            w.write(rawkey)
-            w.close()
-            textkey = r.read()
-            r.close()
-            res = e.read()
-            e.close()
-            if res == '':
-                self.format = "PEM"
-                self.pemkey = rawkey
-                self.textkey = textkey
-                cmd = (convertstr % ("PEM", "DER")).split(" ")
-                self.derkey = self._apply_ossl_cmd(cmd, rawkey)
-            else:
-                raise Exception(error_msg)
-        else: # not PEM, try DER
-            r,w,e = popen3((fmtstr % "DER").split(" "))
-            w.write(rawkey)
-            w.close()
-            textkey = r.read()
-            r.close()
-            res = e.read()
-            if res == '':
-                self.format = "DER"
-                self.derkey = rawkey
-                self.textkey = textkey
-                cmd = (convertstr % ("DER", "PEM")).split(" ")
-                self.pemkey = self._apply_ossl_cmd(cmd, rawkey)
-                cmd = (convertstr % ("DER", "DER")).split(" ")
-                self.derkey = self._apply_ossl_cmd(cmd, rawkey)
-            else:
-                raise Exception(error_msg)     
-
-        self.osslcmdbase = ['openssl', 'rsa', '-inform', self.format]
-
-        r,w,e = popen3(["openssl", "asn1parse", "-inform", "DER"])
-        w.write(self.derkey)
-        w.close()
-        self.asn1parsekey = r.read()
-        r.close()
-        res = e.read()
-        e.close()
-        if res != '':
-            raise Exception(error_msg)
-
-        self.keypath = keypath
-
-        # Parse the -text output of openssl to make things available
-        l = self.textkey.split('\n', 1)
-        if len(l) != 2:
-            raise Exception(error_msg)
-        cur, tmp = l
-        i = 0
-        k = self.possible_fields[i] # Private-Key: (
-        cur = cur[len(k):] + '\n'
-        while k:
-            l = tmp.split('\n', 1)
-            if len(l) != 2: # Over
-                fields_dict[k] = cur
-                break
-            l, tmp = l
-
-            newkey = 0
-            # skip fields we have already seen, this is the purpose of 'i'
-            for j in xrange(i, self.possible_fields_count):
-                f = self.possible_fields[j]
-                if l.startswith(f):
-                    fields_dict[k] = cur
-                    cur = l[len(f):] + '\n'
-                    k = f
-                    newkey = 1
-                    i = j + 1
-                    break
-            if newkey == 1:
-                continue
-            cur += l + '\n'
-
-        # modulus length
-        v = fields_dict["Private-Key: ("]
-        self.modulusLen = None
-        if v:
-            self.modulusLen = int(v.split(' bit', 1)[0])
-        if self.modulusLen is None:
-            raise Exception(error_msg)
-        
-        # public exponent
-        v = fields_dict["publicExponent:"]
-        self.pubExp = None
-        if v:
-            self.pubExp = long(v.split('(', 1)[0])
-        if self.pubExp is None:
-            raise Exception(error_msg)
-
-        tmp = {}
-        for k in ["modulus:", "privateExponent:", "prime1:", "prime2:",
-                  "exponent1:", "exponent2:", "coefficient:"]:
-            v = fields_dict[k]
-            if v:
-                s = v.replace('\n', '').replace(' ', '').replace(':', '')
-                tmp[k] = long(s, 16)
-            else:
-                raise Exception(error_msg)
-
-        self.modulus     = tmp["modulus:"]
-        self.privExp     = tmp["privateExponent:"]
-        self.prime1      = tmp["prime1:"]
-        self.prime2      = tmp["prime2:"] 
-        self.exponent1   = tmp["exponent1:"]
-        self.exponent2   = tmp["exponent2:"]
-        self.coefficient = tmp["coefficient:"]
-
-        self.key = RSA.construct((self.modulus, self.pubExp, self.privExp))
-
-    def __str__(self):
-        return self.derkey
-
-
-# We inherit from PubKey to get access to all encryption and verification
-# methods. To have that working, we simply need Cert to provide 
-# modulusLen and key attribute.
-# XXX Yes, it is a hack.
-class Cert(OSSLHelper, _EncryptAndVerify):
-    # Below are the fields we recognize in the -text output of openssl
-    # and from which we extract information. We expect them in that
-    # order. Number of spaces does matter.
-    possible_fields = [ "        Version:",
-                        "        Serial Number:",
-                        "        Signature Algorithm:",
-                        "        Issuer:",
-                        "            Not Before:",
-                        "            Not After :",
-                        "        Subject:",
-                        "            Public Key Algorithm:",
-                        "                Modulus (",
-                        "                Exponent:",
-                        "            X509v3 Subject Key Identifier:",
-                        "            X509v3 Authority Key Identifier:",
-                        "                keyid:",
-                        "                DirName:",
-                        "                serial:",
-                        "            X509v3 Basic Constraints:",
-                        "            X509v3 Key Usage:",
-                        "            X509v3 Extended Key Usage:",
-                        "            X509v3 CRL Distribution Points:",
-                        "            Authority Information Access:",
-                        "    Signature Algorithm:" ]
-    possible_fields_count = len(possible_fields)
-    
-    def __init__(self, certpath):
-        error_msg = "Unable to import certificate."
-
-        fields_dict = {}
-        for k in self.possible_fields:
-            fields_dict[k] = None
-
-        self.certpath = None
-        rawcert = None
-
-        if (not '\x00' in certpath) and os.path.isfile(certpath): # file
-            self.certpath = certpath
-            cert_size = os.path.getsize(certpath)
-            if cert_size > MAX_CERT_SIZE:
-                raise Exception(error_msg)
-            try:
-                f = open(certpath)
-                rawcert = f.read()
-                f.close()
-            except:
-                raise Exception(error_msg)     
-        else:
-            rawcert = certpath
-            
-        if rawcert is None:
-            raise Exception(error_msg)
-
-        self.rawcert = rawcert
-
-        # Let's try to get file format : PEM or DER.
-        fmtstr = 'openssl x509 -text -inform %s -noout'
-        convertstr = 'openssl x509 -inform %s -outform %s'
-        cert_header = "-----BEGIN CERTIFICATE-----"
-        cert_footer = "-----END CERTIFICATE-----"
-        l = rawcert.split(cert_header, 1)
-        if len(l) == 2: # looks like PEM
-            tmp = l[1]
-            l = tmp.split(cert_footer, 1)
-            if len(l) == 2:
-                tmp = l[0]
-                rawcert = "%s%s%s\n" % (cert_header, tmp, cert_footer)
-            else:
-                raise Exception(error_msg)
-            r,w,e = popen3((fmtstr % "PEM").split(" "))
-            w.write(rawcert)
-            w.close()
-            textcert = r.read()
-            r.close()
-            res = e.read()
-            e.close()
-            if res == '':
-                self.format = "PEM"
-                self.pemcert = rawcert
-                self.textcert = textcert
-                cmd = (convertstr % ("PEM", "DER")).split(" ")
-                self.dercert = self._apply_ossl_cmd(cmd, rawcert)
-            else:
-                raise Exception(error_msg)
-        else: # not PEM, try DER
-            r,w,e = popen3((fmtstr % "DER").split(" "))
-            w.write(rawcert)
-            w.close()
-            textcert = r.read()
-            r.close()
-            res = e.read()
-            if res == '':
-                self.format = "DER"
-                self.dercert = rawcert
-                self.textcert = textcert
-                cmd = (convertstr % ("DER", "PEM")).split(" ")
-                self.pemcert = self._apply_ossl_cmd(cmd, rawcert)
-                cmd = (convertstr % ("DER", "DER")).split(" ")     
-                self.dercert = self._apply_ossl_cmd(cmd, rawcert)
-            else:
-                raise Exception(error_msg)
-
-        self.osslcmdbase = ['openssl', 'x509', '-inform', self.format]
-                                                  
-        r,w,e = popen3('openssl asn1parse -inform DER'.split(' '))
-        w.write(self.dercert)
-        w.close()
-        self.asn1parsecert = r.read()
-        r.close()
-        res = e.read()
-        e.close()
-        if res != '':
-            raise Exception(error_msg)
-        
-        # Grab _raw_ X509v3 Authority Key Identifier, if any.
-        tmp = self.asn1parsecert.split(":X509v3 Authority Key Identifier", 1)
-        self.authorityKeyID = None
-        if len(tmp) == 2:
-            tmp = tmp[1]
-            tmp = tmp.split("[HEX DUMP]:", 1)[1]
-            self.authorityKeyID=tmp.split('\n',1)[0]
-
-        # Grab _raw_ X509v3 Subject Key Identifier, if any.
-        tmp = self.asn1parsecert.split(":X509v3 Subject Key Identifier", 1)
-        self.subjectKeyID = None
-        if len(tmp) == 2:
-            tmp = tmp[1]
-            tmp = tmp.split("[HEX DUMP]:", 1)[1]
-            self.subjectKeyID=tmp.split('\n',1)[0]            
-
-        # Get tbsCertificate using the worst hack. output of asn1parse
-        # looks like that:
-        #
-        # 0:d=0  hl=4 l=1298 cons: SEQUENCE          
-        # 4:d=1  hl=4 l=1018 cons: SEQUENCE          
-        # ...
-        #
-        l1,l2 = self.asn1parsecert.split('\n', 2)[:2]
-        hl1 = int(l1.split("hl=",1)[1].split("l=",1)[0])
-        rem = l2.split("hl=",1)[1]
-        hl2, rem = rem.split("l=",1)
-        hl2 = int(hl2)
-        l = int(rem.split("cons",1)[0])
-        self.tbsCertificate = self.dercert[hl1:hl1+hl2+l]
-
-        # Parse the -text output of openssl to make things available
-        tmp = self.textcert.split('\n', 2)[2]
-        l = tmp.split('\n', 1)
-        if len(l) != 2:
-            raise Exception(error_msg)
-        cur, tmp = l
-        i = 0
-        k = self.possible_fields[i] # Version:
-        cur = cur[len(k):] + '\n'
-        while k:
-            l = tmp.split('\n', 1)
-            if len(l) != 2: # Over
-                fields_dict[k] = cur
-                break
-            l, tmp = l
-
-            newkey = 0
-            # skip fields we have already seen, this is the purpose of 'i'
-            for j in xrange(i, self.possible_fields_count):
-                f = self.possible_fields[j]
-                if l.startswith(f):
-                    fields_dict[k] = cur
-                    cur = l[len(f):] + '\n'
-                    k = f
-                    newkey = 1
-                    i = j + 1
-                    break
-            if newkey == 1:
-                continue
-            cur += l + '\n'
-
-        # version
-        v = fields_dict["        Version:"]
-        self.version = None
-        if v:
-            self.version = int(v[1:2])
-        if self.version is None:
-            raise Exception(error_msg)
-
-        # serial number
-        v = fields_dict["        Serial Number:"]
-        self.serial = None
-        if v:
-            v = v.replace('\n', '').strip()
-            if "0x" in v:
-                v = v.split("0x", 1)[1].split(')', 1)[0]
-            v = v.replace(':', '').upper()
-            if len(v) % 2:
-                v = '0' + v
-            self.serial = v
-        if self.serial is None:
-            raise Exception(error_msg)
-
-        # Signature Algorithm        
-        v = fields_dict["        Signature Algorithm:"]
-        self.sigAlg = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.sigAlg = v
-        if self.sigAlg is None:
-            raise Exception(error_msg)
-        
-        # issuer
-        v = fields_dict["        Issuer:"]
-        self.issuer = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.issuer = v
-        if self.issuer is None:
-            raise Exception(error_msg)
-
-        # not before
-        v = fields_dict["            Not Before:"]
-        self.notBefore_str = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.notBefore_str = v
-        if self.notBefore_str is None:
-            raise Exception(error_msg)
-        try:
-            self.notBefore = time.strptime(self.notBefore_str,
-                                           "%b %d %H:%M:%S %Y %Z")
-        except:
-            self.notBefore = time.strptime(self.notBefore_str,
-                                           "%b %d %H:%M:%S %Y")
-        self.notBefore_str_simple = time.strftime("%x", self.notBefore)
-        
-        # not after
-        v = fields_dict["            Not After :"]
-        self.notAfter_str = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.notAfter_str = v
-        if self.notAfter_str is None:
-            raise Exception(error_msg)
-        try:
-            self.notAfter = time.strptime(self.notAfter_str,
-                                          "%b %d %H:%M:%S %Y %Z")
-        except:
-            self.notAfter = time.strptime(self.notAfter_str,
-                                          "%b %d %H:%M:%S %Y")            
-        self.notAfter_str_simple = time.strftime("%x", self.notAfter)
-        
-        # subject
-        v = fields_dict["        Subject:"]
-        self.subject = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.subject = v
-        if self.subject is None:
-            raise Exception(error_msg)
-        
-        # Public Key Algorithm
-        v = fields_dict["            Public Key Algorithm:"]
-        self.pubKeyAlg = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.pubKeyAlg = v
-        if self.pubKeyAlg is None:
-            raise Exception(error_msg)
-        
-        # Modulus
-        v = fields_dict["                Modulus ("]
-        self.modulus = None
-        if v:
-            v,t = v.split(' bit):',1)
-            self.modulusLen = int(v)
-            t = t.replace(' ', '').replace('\n', ''). replace(':', '')
-            self.modulus_hexdump = t
-            self.modulus = long(t, 16)
-        if self.modulus is None:
-            raise Exception(error_msg)
-
-        # Exponent
-        v = fields_dict["                Exponent:"]
-        self.exponent = None
-        if v:
-            v = v.split('(',1)[0]
-            self.exponent = long(v)
-        if self.exponent is None:
-            raise Exception(error_msg)
-
-        # Public Key instance
-        self.key = RSA.construct((self.modulus, self.exponent, ))
-        
-        # Subject Key Identifier
-
-        # Authority Key Identifier: keyid, dirname and serial
-        self.authorityKeyID_keyid   = None
-        self.authorityKeyID_dirname = None
-        self.authorityKeyID_serial  = None
-        if self.authorityKeyID: # (hex version already done using asn1parse)
-            v = fields_dict["                keyid:"]
-            if v:
-                v = v.split('\n',1)[0]
-                v = v.strip().replace(':', '')
-                self.authorityKeyID_keyid = v
-            v = fields_dict["                DirName:"]
-            if v:
-                v = v.split('\n',1)[0]
-                self.authorityKeyID_dirname = v
-            v = fields_dict["                serial:"]
-            if v:
-                v = v.split('\n',1)[0]
-                v = v.strip().replace(':', '')
-                self.authorityKeyID_serial = v                
-
-        # Basic constraints
-        self.basicConstraintsCritical = False
-        self.basicConstraints=None
-        v = fields_dict["            X509v3 Basic Constraints:"]
-        if v:
-            self.basicConstraints = {}
-            v,t = v.split('\n',2)[:2]
-            if "critical" in v:
-                self.basicConstraintsCritical = True
-            if "CA:" in t:
-                self.basicConstraints["CA"] = t.split('CA:')[1][:4] == "TRUE"
-            if "pathlen:" in t:
-                self.basicConstraints["pathlen"] = int(t.split('pathlen:')[1])
-
-        # X509v3 Key Usage
-        self.keyUsage = []
-        v = fields_dict["            X509v3 Key Usage:"]
-        if v:   
-            # man 5 x509v3_config
-            ku_mapping = {"Digital Signature": "digitalSignature",
-                          "Non Repudiation": "nonRepudiation",
-                          "Key Encipherment": "keyEncipherment",
-                          "Data Encipherment": "dataEncipherment",
-                          "Key Agreement": "keyAgreement",
-                          "Certificate Sign": "keyCertSign",
-                          "CRL Sign": "cRLSign",
-                          "Encipher Only": "encipherOnly",
-                          "Decipher Only": "decipherOnly"}
-            v = v.split('\n',2)[1]
-            l = map(lambda x: x.strip(), v.split(','))
-            while l:
-                c = l.pop()
-                if ku_mapping.has_key(c):
-                    self.keyUsage.append(ku_mapping[c])
-                else:
-                    self.keyUsage.append(c) # Add it anyway
-                    print "Found unknown X509v3 Key Usage: '%s'" % c
-                    print "Report it to arno (at) natisbad.org for addition"
-
-        # X509v3 Extended Key Usage
-        self.extKeyUsage = []
-        v = fields_dict["            X509v3 Extended Key Usage:"]
-        if v:   
-            # man 5 x509v3_config:
-            eku_mapping = {"TLS Web Server Authentication": "serverAuth",
-                           "TLS Web Client Authentication": "clientAuth",
-                           "Code Signing": "codeSigning",
-                           "E-mail Protection": "emailProtection",
-                           "Time Stamping": "timeStamping",
-                           "Microsoft Individual Code Signing": "msCodeInd",
-                           "Microsoft Commercial Code Signing": "msCodeCom",
-                           "Microsoft Trust List Signing": "msCTLSign",
-                           "Microsoft Encrypted File System": "msEFS",
-                           "Microsoft Server Gated Crypto": "msSGC",
-                           "Netscape Server Gated Crypto": "nsSGC",
-                           "IPSec End System": "iPsecEndSystem",
-                           "IPSec Tunnel": "iPsecTunnel",
-                           "IPSec User": "iPsecUser"}
-            v = v.split('\n',2)[1]
-            l = map(lambda x: x.strip(), v.split(','))
-            while l:
-                c = l.pop()
-                if eku_mapping.has_key(c):
-                    self.extKeyUsage.append(eku_mapping[c])
-                else:
-                    self.extKeyUsage.append(c) # Add it anyway
-                    print "Found unknown X509v3 Extended Key Usage: '%s'" % c
-                    print "Report it to arno (at) natisbad.org for addition"
-
-        # CRL Distribution points
-        self.cRLDistributionPoints = []
-        v = fields_dict["            X509v3 CRL Distribution Points:"]
-        if v:
-            v = v.split("\n\n", 1)[0]
-            v = v.split("URI:")[1:]
-            self.CRLDistributionPoints = map(lambda x: x.strip(), v)
-            
-        # Authority Information Access: list of tuples ("method", "location")
-        self.authorityInfoAccess = []
-        v = fields_dict["            Authority Information Access:"]
-        if v:
-            v = v.split("\n\n", 1)[0]
-            v = v.split("\n")[1:]
-            for e in v:
-                method, location = map(lambda x: x.strip(), e.split(" - ", 1))
-                self.authorityInfoAccess.append((method, location))
-
-        # signature field
-        v = fields_dict["    Signature Algorithm:" ]
-        self.sig = None
-        if v:
-            v = v.split('\n',1)[1]
-            v = v.replace(' ', '').replace('\n', '')
-            self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':')))
-            self.sigLen = len(self.sig)
-        if self.sig is None:
-            raise Exception(error_msg)
-
-    def isIssuerCert(self, other):
-        """
-        True if 'other' issued 'self', i.e.:
-          - self.issuer == other.subject
-          - self is signed by other
-        """
-        # XXX should be done on raw values, instead of their textual repr
-        if self.issuer != other.subject:
-            return False
-
-        # Sanity check regarding modulus length and the
-        # signature length
-        keyLen = (other.modulusLen + 7)/8
-        if keyLen != self.sigLen:
-            return False
-
-        unenc = other.encrypt(self.sig) # public key encryption, i.e. decrypt
-
-        # XXX Check block type (00 or 01 and type of padding)
-        unenc = unenc[1:]
-        if not '\x00' in unenc:
-            return False
-        pos = unenc.index('\x00')
-        unenc = unenc[pos+1:]
-
-        found = None
-        for k in _hashFuncParams:
-            if self.sigAlg.startswith(k):
-                found = k
-                break
-        if not found:
-            return False
-        hlen, hfunc, digestInfo =  _hashFuncParams[k]
-        
-        if len(unenc) != (hlen+len(digestInfo)):
-            return False
-
-        if not unenc.startswith(digestInfo):
-            return False
-
-        h = unenc[-hlen:]
-        myh = hfunc(self.tbsCertificate)
-
-        return h == myh
-
-    def chain(self, certlist):
-        """
-        Construct the chain of certificates leading from 'self' to the
-        self signed root using the certificates in 'certlist'. If the
-        list does not provide all the required certs to go to the root
-        the function returns a incomplete chain starting with the
-        certificate. This fact can be tested by tchecking if the last
-        certificate of the returned chain is self signed (if c is the
-        result, c[-1].isSelfSigned())
-        """
-        d = {}
-        for c in certlist:
-            # XXX we should check if we have duplicate
-            d[c.subject] = c
-        res = [self]
-        cur = self
-        while not cur.isSelfSigned():
-            if d.has_key(cur.issuer):
-                possible_issuer = d[cur.issuer]
-                if cur.isIssuerCert(possible_issuer):
-                    res.append(possible_issuer)
-                    cur = possible_issuer
-                else:
-                    break
-        return res
-
-    def remainingDays(self, now=None):
-        """
-        Based on the value of notBefore field, returns the number of
-        days the certificate will still be valid. The date used for the
-        comparison is the current and local date, as returned by 
-        time.localtime(), except if 'now' argument is provided another
-        one. 'now' argument can be given as either a time tuple or a string
-        representing the date. Accepted format for the string version
-        are:
-        
-         - '%b %d %H:%M:%S %Y %Z' e.g. 'Jan 30 07:38:59 2008 GMT'
-         - '%m/%d/%y' e.g. '01/30/08' (less precise)
-
-        If the certificate is no more valid at the date considered, then,
-        a negative value is returned representing the number of days
-        since it has expired.
-        
-        The number of days is returned as a float to deal with the unlikely
-        case of certificates that are still just valid.
-        """
-        if now is None:
-            now = time.localtime()
-        elif type(now) is str:
-            try:
-                if '/' in now:
-                    now = time.strptime(now, '%m/%d/%y')
-                else:
-                    now = time.strptime(now, '%b %d %H:%M:%S %Y %Z')
-            except:
-                warning("Bad time string provided '%s'. Using current time" % now)
-                now = time.localtime()
-
-        now = time.mktime(now)
-        nft = time.mktime(self.notAfter)
-        diff = (nft - now)/(24.*3600)
-        return diff
-
-
-    # return SHA-1 hash of cert embedded public key
-    # !! At the moment, the trailing 0 is in the hashed string if any
-    def keyHash(self):
-        m = self.modulus_hexdump
-        res = []
-        i = 0
-        l = len(m)
-        while i<l: # get a string version of modulus
-            res.append(struct.pack("B", int(m[i:i+2], 16)))
-            i += 2
-        return sha.new("".join(res)).digest()    
-
-    def output(self, fmt="DER"):
-        if fmt == "DER":
-            return self.dercert
-        elif fmt == "PEM":
-            return self.pemcert
-        elif fmt == "TXT":
-            return self.textcert
-
-    def export(self, filename, fmt="DER"):
-        """
-        Export certificate in 'fmt' format (PEM, DER or TXT) to file 'filename'
-        """
-        f = open(filename, "wb")
-        f.write(self.output(fmt))
-        f.close()
-
-    def isSelfSigned(self):
-        """
-        Return True if the certificate is self signed:
-          - issuer and subject are the same
-          - the signature of the certificate is valid.
-        """
-        if self.issuer == self.subject:
-            return self.isIssuerCert(self)
-        return False
-
-    # Print main informations stored in certificate
-    def show(self):
-        print "Serial: %s" % self.serial
-        print "Issuer: " + self.issuer
-        print "Subject: " + self.subject
-        print "Validity: %s to %s" % (self.notBefore_str_simple,
-                                      self.notAfter_str_simple)
-
-    def __repr__(self):
-        return "[X.509 Cert. Subject:%s, Issuer:%s]" % (self.subject, self.issuer)
-
-    def __str__(self):
-        return self.dercert
-
-    def verifychain(self, anchors, untrusted=None):
-        """
-        Perform verification of certificate chains for that certificate. The
-        behavior of verifychain method is mapped (and also based) on openssl
-        verify userland tool (man 1 verify).
-        A list of anchors is required. untrusted parameter can be provided 
-        a list of untrusted certificates that can be used to reconstruct the
-        chain.
-
-        If you have a lot of certificates to verify against the same
-        list of anchor, consider constructing this list as a cafile
-        and use .verifychain_from_cafile() instead.
-        """
-        cafile = create_temporary_ca_file(anchors)
-        if not cafile:
-            return False
-        untrusted_file = None
-        if untrusted:
-            untrusted_file = create_temporary_ca_file(untrusted) # hack
-            if not untrusted_file:
-                os.unlink(cafile)
-                return False
-        res = self.verifychain_from_cafile(cafile, 
-                                           untrusted_file=untrusted_file)
-        os.unlink(cafile)
-        if untrusted_file:
-            os.unlink(untrusted_file)
-        return res
-
-    def verifychain_from_cafile(self, cafile, untrusted_file=None):
-        """
-        Does the same job as .verifychain() but using the list of anchors
-        from the cafile. This is useful (because more efficient) if
-        you have a lot of certificates to verify do it that way: it
-        avoids the creation of a cafile from anchors at each call.
-
-        As for .verifychain(), a list of untrusted certificates can be
-        passed (as a file, this time)
-        """
-        cmd = ["openssl", "verify", "-CAfile", cafile]
-        if untrusted_file:
-           cmd += ["-untrusted", untrusted_file]
-        try:
-            pemcert = self.output(fmt="PEM")
-            cmdres = self._apply_ossl_cmd(cmd, pemcert)
-        except:
-            return False
-        return cmdres.endswith("\nOK\n") or cmdres.endswith(": OK\n")
-
-    def verifychain_from_capath(self, capath, untrusted_file=None):
-        """
-        Does the same job as .verifychain_from_cafile() but using the list
-        of anchors in capath directory. The directory should contain
-        certificates files in PEM format with associated links as
-        created using c_rehash utility (man c_rehash).
-
-        As for .verifychain_from_cafile(), a list of untrusted certificates
-        can be passed as a file (concatenation of the certificates in
-        PEM format)
-        """
-        cmd = ["openssl", "verify", "-CApath", capath]
-        if untrusted_file:
-            cmd += ["-untrusted", untrusted_file]
-        try:
-            pemcert = self.output(fmt="PEM")
-            cmdres = self._apply_ossl_cmd(cmd, pemcert)
-        except:
-            return False
-        return cmdres.endswith("\nOK\n") or cmdres.endswith(": OK\n")
-
-    def is_revoked(self, crl_list):
-        """
-        Given a list of trusted CRL (their signature has already been
-        verified with trusted anchors), this function returns True if
-        the certificate is marked as revoked by one of those CRL.
-
-        Note that if the Certificate was on hold in a previous CRL and
-        is now valid again in a new CRL and bot are in the list, it
-        will be considered revoked: this is because _all_ CRLs are 
-        checked (not only the freshest) and revocation status is not
-        handled.
-
-        Also note that the check on the issuer is performed on the
-        Authority Key Identifier if available in _both_ the CRL and the
-        Cert. Otherwise, the issuers are simply compared.
-        """
-        for c in crl_list:
-            if (self.authorityKeyID is not None and 
-                c.authorityKeyID is not None and
-                self.authorityKeyID == c.authorityKeyID):
-                return self.serial in map(lambda x: x[0], c.revoked_cert_serials)
-            elif (self.issuer == c.issuer):
-                return self.serial in map(lambda x: x[0], c.revoked_cert_serials)
-        return False
-
-def print_chain(l):
-    llen = len(l) - 1
-    if llen < 0:
-        return ""
-    c = l[llen]
-    llen -= 1
-    s = "_ "
-    if not c.isSelfSigned():
-        s = "_ ... [Missing Root]\n"
-    else:
-        s += "%s [Self Signed]\n" % c.subject
-    i = 1
-    while (llen != -1):
-        c = l[llen]
-        s += "%s\_ %s" % (" "*i, c.subject)
-        if llen != 0:
-            s += "\n"
-        i += 2
-        llen -= 1
-    print s
-
-# import popen2
-# a=popen3("openssl crl -text -inform DER -noout ", capturestderr=True)
-# a.tochild.write(open("samples/klasa1.crl").read())
-# a.tochild.close()
-# a.poll()
-
-class CRL(OSSLHelper):
-    # Below are the fields we recognize in the -text output of openssl
-    # and from which we extract information. We expect them in that
-    # order. Number of spaces does matter.
-    possible_fields = [ "        Version",
-                        "        Signature Algorithm:",
-                        "        Issuer:",
-                        "        Last Update:",
-                        "        Next Update:",
-                        "        CRL extensions:",
-                        "            X509v3 Issuer Alternative Name:",
-                        "            X509v3 Authority Key Identifier:", 
-                        "                keyid:",
-                        "                DirName:",
-                        "                serial:",
-                        "            X509v3 CRL Number:", 
-                        "Revoked Certificates:",
-                        "No Revoked Certificates.",
-                        "    Signature Algorithm:" ]
-    possible_fields_count = len(possible_fields)
-
-    def __init__(self, crlpath):
-        error_msg = "Unable to import CRL."
-
-        fields_dict = {}
-        for k in self.possible_fields:
-            fields_dict[k] = None
-
-        self.crlpath = None
-        rawcrl = None
-
-        if (not '\x00' in crlpath) and os.path.isfile(crlpath):
-            self.crlpath = crlpath
-            cert_size = os.path.getsize(crlpath)
-            if cert_size > MAX_CRL_SIZE:
-                raise Exception(error_msg)
-            try:
-                f = open(crlpath)
-                rawcrl = f.read()
-                f.close()
-            except:
-                raise Exception(error_msg)     
-        else:
-            rawcrl = crlpath
-
-        if rawcrl is None:
-            raise Exception(error_msg)
-
-        self.rawcrl = rawcrl
-
-        # Let's try to get file format : PEM or DER.
-        fmtstr = 'openssl crl -text -inform %s -noout'
-        convertstr = 'openssl crl -inform %s -outform %s'
-        crl_header = "-----BEGIN X509 CRL-----"
-        crl_footer = "-----END X509 CRL-----"
-        l = rawcrl.split(crl_header, 1)
-        if len(l) == 2: # looks like PEM
-            tmp = l[1]
-            l = tmp.split(crl_footer, 1)
-            if len(l) == 2:
-                tmp = l[0]
-                rawcrl = "%s%s%s\n" % (crl_header, tmp, crl_footer)
-            else:
-                raise Exception(error_msg)
-            r,w,e = popen3((fmtstr % "PEM").split(" "))
-            w.write(rawcrl)
-            w.close()
-            textcrl = r.read()
-            r.close()
-            res = e.read()
-            e.close()
-            if res == '':
-                self.format = "PEM"
-                self.pemcrl = rawcrl
-                self.textcrl = textcrl
-                cmd = (convertstr % ("PEM", "DER")).split(" ")
-                self.dercrl = self._apply_ossl_cmd(cmd, rawcrl)
-            else:
-                raise Exception(error_msg)
-        else: # not PEM, try DER
-            r,w,e = popen3((fmtstr % "DER").split(' '))
-            w.write(rawcrl)
-            w.close()
-            textcrl = r.read()
-            r.close()
-            res = e.read()
-            if res == '':
-                self.format = "DER"
-                self.dercrl = rawcrl
-                self.textcrl = textcrl
-                cmd = (convertstr % ("DER", "PEM")).split(" ")
-                self.pemcrl = self._apply_ossl_cmd(cmd, rawcrl)
-                cmd = (convertstr % ("DER", "DER")).split(" ")
-                self.dercrl = self._apply_ossl_cmd(cmd, rawcrl)
-            else:
-                raise Exception(error_msg)
-
-        self.osslcmdbase = ['openssl', 'crl', '-inform', self.format]
-
-        r,w,e = popen3(('openssl asn1parse -inform DER').split(" "))
-        w.write(self.dercrl)
-        w.close()
-        self.asn1parsecrl = r.read()
-        r.close()
-        res = e.read()
-        e.close()
-        if res != '':
-            raise Exception(error_msg)
-
-        # Grab _raw_ X509v3 Authority Key Identifier, if any.
-        tmp = self.asn1parsecrl.split(":X509v3 Authority Key Identifier", 1)
-        self.authorityKeyID = None
-        if len(tmp) == 2:
-            tmp = tmp[1]
-            tmp = tmp.split("[HEX DUMP]:", 1)[1]
-            self.authorityKeyID=tmp.split('\n',1)[0]
-
-        # Parse the -text output of openssl to make things available
-        tmp = self.textcrl.split('\n', 1)[1]
-        l = tmp.split('\n', 1)
-        if len(l) != 2:
-            raise Exception(error_msg)
-        cur, tmp = l
-        i = 0
-        k = self.possible_fields[i] # Version
-        cur = cur[len(k):] + '\n'
-        while k:
-            l = tmp.split('\n', 1)
-            if len(l) != 2: # Over
-                fields_dict[k] = cur
-                break
-            l, tmp = l
-
-            newkey = 0
-            # skip fields we have already seen, this is the purpose of 'i'
-            for j in xrange(i, self.possible_fields_count):
-                f = self.possible_fields[j]
-                if l.startswith(f):
-                    fields_dict[k] = cur
-                    cur = l[len(f):] + '\n'
-                    k = f
-                    newkey = 1
-                    i = j + 1
-                    break
-            if newkey == 1:
-                continue
-            cur += l + '\n'
-
-        # version
-        v = fields_dict["        Version"]
-        self.version = None
-        if v:
-            self.version = int(v[1:2])
-        if self.version is None:
-            raise Exception(error_msg)
-
-        # signature algorithm
-        v = fields_dict["        Signature Algorithm:"]
-        self.sigAlg = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.sigAlg = v
-        if self.sigAlg is None:
-            raise Exception(error_msg)
-
-        # issuer
-        v = fields_dict["        Issuer:"]
-        self.issuer = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.issuer = v
-        if self.issuer is None:
-            raise Exception(error_msg)
-
-        # last update
-        v = fields_dict["        Last Update:"]
-        self.lastUpdate_str = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.lastUpdate_str = v
-        if self.lastUpdate_str is None:
-            raise Exception(error_msg)
-        self.lastUpdate = time.strptime(self.lastUpdate_str,
-                                       "%b %d %H:%M:%S %Y %Z")
-        self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate)
-
-        # next update
-        v = fields_dict["        Next Update:"]
-        self.nextUpdate_str = None
-        if v:
-            v = v.split('\n',1)[0]
-            v = v.strip()
-            self.nextUpdate_str = v
-        if self.nextUpdate_str is None:
-            raise Exception(error_msg)
-        self.nextUpdate = time.strptime(self.nextUpdate_str,
-                                       "%b %d %H:%M:%S %Y %Z")
-        self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate)
-        
-        # XXX Do something for Issuer Alternative Name
-
-        # Authority Key Identifier: keyid, dirname and serial
-        self.authorityKeyID_keyid   = None
-        self.authorityKeyID_dirname = None
-        self.authorityKeyID_serial  = None
-        if self.authorityKeyID: # (hex version already done using asn1parse)
-            v = fields_dict["                keyid:"]
-            if v:
-                v = v.split('\n',1)[0]
-                v = v.strip().replace(':', '')
-                self.authorityKeyID_keyid = v
-            v = fields_dict["                DirName:"]
-            if v:
-                v = v.split('\n',1)[0]
-                self.authorityKeyID_dirname = v
-            v = fields_dict["                serial:"]
-            if v:
-                v = v.split('\n',1)[0]
-                v = v.strip().replace(':', '')
-                self.authorityKeyID_serial = v
-
-        # number
-        v = fields_dict["            X509v3 CRL Number:"]
-        self.number = None
-        if v:
-            v = v.split('\n',2)[1]
-            v = v.strip()
-            self.number = int(v)
-
-        # Get the list of serial numbers of revoked certificates
-        self.revoked_cert_serials = []
-        v = fields_dict["Revoked Certificates:"]
-        t = fields_dict["No Revoked Certificates."]
-        if (t is None and v is not None):
-            v = v.split("Serial Number: ")[1:]
-            for r in v:
-                s,d = r.split('\n', 1)
-                s = s.split('\n', 1)[0]
-                d = d.split("Revocation Date:", 1)[1]
-                d = time.strptime(d.strip(), "%b %d %H:%M:%S %Y %Z")
-                self.revoked_cert_serials.append((s,d))
-
-        # signature field
-        v = fields_dict["    Signature Algorithm:" ]
-        self.sig = None
-        if v:
-            v = v.split('\n',1)[1]
-            v = v.replace(' ', '').replace('\n', '')
-            self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':')))
-            self.sigLen = len(self.sig)
-        if self.sig is None:
-            raise Exception(error_msg)
-
-    def __str__(self):
-        return self.dercrl
-        
-    # Print main informations stored in CRL
-    def show(self):
-        print "Version: %d" % self.version
-        print "sigAlg: " + self.sigAlg
-        print "Issuer: " + self.issuer
-        print "lastUpdate: %s" % self.lastUpdate_str_simple
-        print "nextUpdate: %s" % self.nextUpdate_str_simple
-
-    def verify(self, anchors):
-        """
-        Return True if the CRL is signed by one of the provided
-        anchors. False on error (invalid signature, missing anchorand, ...)
-        """
-        cafile = create_temporary_ca_file(anchors)
-        if cafile is None:
-            return False
-        try:
-            cmd = self.osslcmdbase + ["-noout", "-CAfile", cafile]
-            cmdres = self._apply_ossl_cmd(cmd, self.rawcrl)
-        except:
-            os.unlink(cafile)
-            return False
-        os.unlink(cafile)
-        return "verify OK" in cmdres
-
-
-    
diff --git a/scapy/layers/all.py b/scapy/layers/all.py
index 8cc1d03b372c90ac51e06f78fe5f6669efde19b6..b30bb3ea824c88c920fdfc1c495f61fd896b100b 100644
--- a/scapy/layers/all.py
+++ b/scapy/layers/all.py
@@ -24,6 +24,7 @@ for _l in conf.load_layers:
     except Exception,e:
 	log.warning("can't import layer %s: %s" % (_l,e))
 
+from scapy.layers.tls.cert import *
 
 
 
diff --git a/scapy/crypto/__init__.py b/scapy/layers/tls/__init__.py
similarity index 51%
rename from scapy/crypto/__init__.py
rename to scapy/layers/tls/__init__.py
index b441863e820e6711a0aeaa73e46cc9222a261937..06203f002d043dd8fb394687fc3ac950aac8ddba 100644
--- a/scapy/crypto/__init__.py
+++ b/scapy/layers/tls/__init__.py
@@ -1,10 +1,10 @@
 ## This file is part of Scapy
 ## See http://www.secdev.org/projects/scapy for more informations
-## Copyright (C) Arnaud Ebalard <arno@natisbad.org>
+## Copyright (C) Arnaud Ebalard, Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
-Tools for handling with digital certificates.
+Tools for handling TLS sessions and digital certificates.
 """
 
 try:
@@ -13,5 +13,12 @@ except ImportError:
     import logging
     log_loading = logging.getLogger("scapy.loading")
     log_loading.info("Can't import python Crypto lib. Disabled certificate manipulation tools")
+
+try:
+    import ecdsa
+except ImportError:
+    import logging
+    log_loading = logging.getLogger("scapy.loading")
+    log_loading.info("Can't import python ecdsa lib. Disabled certificate manipulation tools")
 else:
-    from scapy.crypto.cert import *
+    from scapy.layers.tls.cert import *
diff --git a/scapy/layers/tls/cert.py b/scapy/layers/tls/cert.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ed092a8e6301e018923319c35ec2910f5dca424
--- /dev/null
+++ b/scapy/layers/tls/cert.py
@@ -0,0 +1,889 @@
+## This file is part of Scapy
+## Copyright (C) 2008 Arnaud Ebalard <arnaud.ebalard@eads.net>
+##                                   <arno@natisbad.org>
+##         2015, 2016 Maxence Tury   <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+High-level methods for PKI objects (X.509 certificates, CRLs, asymmetric keys).
+Supports both RSA and ECDSA objects.
+"""
+
+## This module relies on python-crypto and python-ecdsa.
+##
+## The classes below are wrappers for the ASN.1 objects defined in x509.py.
+## By collecting their attributes, we bypass the ASN.1 structure, hence
+## there is no direct method for exporting a new full DER-encoded version
+## of a Cert instance after its serial has been modified (for example).
+## If you need to modify an import, just use the corresponding ASN1_Packet.
+##
+## For instance, here is what you could do in order to modify the serial of
+## 'cert' and then resign it with whatever 'key':
+##     f = open('cert.der')
+##     c = X509_Cert(f.read())
+##     c.tbsCertificate.serialNumber = 0x4B1D
+##     k = PrivKey('key.pem')
+##     new_x509_cert = k.resignCert(c)
+## No need for obnoxious openssl tweaking anymore. :)
+
+import base64, os, time
+
+import ecdsa
+from Crypto.PublicKey import RSA
+
+from scapy.layers.tls.crypto.curves import import_curve
+from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip, pkcs_i2osp, mapHashFunc
+from scapy.layers.tls.crypto.pkcs1 import _EncryptAndVerifyRSA
+from scapy.layers.tls.crypto.pkcs1 import _DecryptAndSignRSA
+
+from scapy.asn1.asn1 import ASN1_BIT_STRING
+from scapy.asn1.mib import hash_by_oid
+from scapy.layers.x509 import X509_SubjectPublicKeyInfo
+from scapy.layers.x509 import RSAPublicKey, RSAPrivateKey
+from scapy.layers.x509 import ECDSAPublicKey, ECDSAPrivateKey
+from scapy.layers.x509 import RSAPrivateKey_OpenSSL, ECDSAPrivateKey_OpenSSL
+from scapy.layers.x509 import X509_Cert, X509_CRL
+from scapy.utils import binrepr
+
+# Maximum allowed size in bytes for a certificate file, to avoid
+# loading huge file when importing a cert
+MAX_KEY_SIZE = 50*1024
+MAX_CERT_SIZE = 50*1024
+MAX_CRL_SIZE = 10*1024*1024   # some are that big
+
+
+#####################################################################
+# Some helpers
+#####################################################################
+
+def der2pem(der_string, obj="UNKNOWN"):
+    """
+    Encode a byte string in PEM format. Header advertizes <obj> type.
+    """
+    pem_string = "-----BEGIN %s-----\n" % obj
+    base64_string = base64.b64encode(der_string)
+    chunks = [base64_string[i:i+64] for i in range(0, len(base64_string), 64)]
+    pem_string += '\n'.join(chunks)
+    pem_string += "\n-----END %s-----\n" % obj
+    return pem_string
+
+def pem2der(pem_string):
+    """
+    Encode every line between the first '-----\n' and the 2nd-to-last '-----'.
+    """
+    pem_string = pem_string.replace("\r", "")
+    first_idx = pem_string.find("-----\n") + 6
+    if pem_string.find("-----BEGIN", first_idx) != -1:
+        raise Exception("pem2der() expects only one PEM-encoded object")
+    last_idx = pem_string.rfind("-----", 0, pem_string.rfind("-----"))
+    base64_string = pem_string[first_idx:last_idx]
+    base64_string.replace("\n", "")
+    der_string = base64.b64decode(base64_string)
+    return der_string
+
+def split_pem(s):
+    """
+    Split PEM objects. Useful to process concatenated certificates.
+    """
+    pem_strings = []
+    while s != "":
+        start_idx = s.find("-----BEGIN")
+        if start_idx == -1:
+            break
+        end_idx = s.find("-----END")
+        end_idx = s.find("\n", end_idx) + 1
+        pem_strings.append(s[start_idx:end_idx])
+        s = s[end_idx:]
+    return pem_strings
+
+
+class _PKIObj(object):
+    def __init__(self, frmt, der, pem):
+        # Note that changing attributes of the _PKIObj does not update these
+        # values (e.g. modifying k.modulus does not change k.der).
+        self.frmt = frmt
+        self.der = der
+        self.pem = pem
+
+    def __str__(self):
+        return self.der
+
+
+class _PKIObjMaker(type):
+    def __call__(cls, obj_path, obj_max_size, pem_marker=None):
+        # This enables transparent DER and PEM-encoded data imports.
+        # Note that when importing a PEM file with multiple objects (like ECDSA
+        # private keys output by openssl), it will concatenate every object in
+        # order to create a 'der' attribute. When converting a 'multi' DER file
+        # into a PEM file, though, the PEM attribute will not be valid,
+        # because we do not try to identify the class of each object.
+        error_msg = "Unable to import data"
+
+        if obj_path is None:
+            raise Exception(error_msg)
+
+        if (not '\x00' in obj_path) and os.path.isfile(obj_path):
+            _size = os.path.getsize(obj_path)
+            if _size > obj_max_size:
+                raise Exception(error_msg)
+            try:
+                f = open(obj_path)
+                raw = f.read()
+                f.close()
+            except:
+                raise Exception(error_msg)
+        else:
+            raw = obj_path
+
+        try:
+            if "-----BEGIN" in raw:
+                frmt = "PEM"
+                pem = raw
+                der_list = split_pem(raw)
+                der = ''.join(map(pem2der, der_list))
+            else:
+                frmt = "DER"
+                der = raw
+                pem = ""
+                if pem_marker is not None:
+                    pem = der2pem(raw, pem_marker)
+                # type identification may be needed for pem_marker
+                # in such case, the pem attribute has to be updated
+        except:
+            raise Exception(error_msg)
+
+        p = _PKIObj(frmt, der, pem)
+        return p
+
+
+#####################################################################
+# PKI objects wrappers
+#####################################################################
+
+###############
+# Public Keys #
+###############
+
+class _PubKeyFactory(_PKIObjMaker):
+    """
+    Metaclass for PubKey creation.
+    It casts the appropriate class on the fly, then fills in
+    the appropriate attributes with updateWith() submethod.
+    """
+    def __call__(cls, key_path):
+
+        # First, we deal with the exceptional RSA KEA call.
+        if type(key_path) is tuple:
+            e, m, mLen = key_path
+            obj = type.__call__(cls)
+            obj.frmt = "tuple"
+            obj.modulus = m
+            obj.modulusLen = mLen
+            obj.pubExp = e
+            return obj
+
+        # Now for the usual calls, key_path may be the path to either:
+        # _an X509_SubjectPublicKeyInfo, as processed by openssl;
+        # _an RSAPublicKey;
+        # _an ECDSAPublicKey.
+        obj = _PKIObjMaker.__call__(cls, key_path, MAX_KEY_SIZE)
+        try:
+            spki = X509_SubjectPublicKeyInfo(obj.der)
+            pubkey = spki.subjectPublicKey
+            if isinstance(pubkey, RSAPublicKey):
+                obj.__class__ = PubKeyRSA
+                obj.updateWith(pubkey)
+            elif isinstance(pubkey, ECDSAPublicKey):
+                obj.__class__ = PubKeyECDSA
+                obj.updateWith(spki)
+            else:
+                raise Exception("Unsupported publicKey type")
+            marker = "PUBLIC KEY"
+        except:
+            try:
+                pubkey = RSAPublicKey(obj.der)
+                obj.__class__ = PubKeyRSA
+                obj.updateWith(pubkey)
+                marker = "RSA PUBLIC KEY"
+            except:
+                # We cannot import an ECDSA public key without curve knowledge
+                raise Exception("Unable to import public key")
+
+        if obj.frmt == "DER":
+            obj.pem = der2pem(obj.der, marker)
+        return obj
+
+
+class PubKey(object):
+    """
+    Parent class for both PubKeyRSA and PubKeyECDSA.
+    Provides a common verifyCert() method.
+    """
+    __metaclass__ = _PubKeyFactory
+
+    def verifyCert(self, cert):
+        """
+        Verifies either a Cert or an X509_Cert.
+        """
+        tbsCert = cert.tbsCertificate
+        sigAlg = tbsCert.signature
+        h = hash_by_oid[sigAlg.algorithm.val]
+        sigVal = str(cert.signatureValue)
+        return self.verify(str(tbsCert), sigVal, h=h,
+                           t='pkcs',
+                           sigdecode=ecdsa.util.sigdecode_der)
+
+
+class PubKeyRSA(_PKIObj, PubKey, _EncryptAndVerifyRSA):
+    """
+    Wrapper for RSA keys based on _EncryptAndVerifyRSA from crypto/pkcs1.py
+    Use the 'key' attribute to access original object.
+    """
+    def updateWith(self, pubkey):
+        self.modulus    = pubkey.modulus.val
+        self.modulusLen = len(binrepr(pubkey.modulus.val))
+        self.pubExp     = pubkey.publicExponent.val
+        self.key = RSA.construct((self.modulus, self.pubExp, ))
+    def encrypt(self, msg, t=None, h=None, mgf=None, L=None):
+        # no ECDSA encryption support, hence no ECDSA specific keywords here
+        return _EncryptAndVerifyRSA.encrypt(self, msg, t=t, h=h, mgf=mgf, L=L)
+    def verify(self, msg, sig, h=None,
+               t=None, mgf=None, sLen=None,
+               sigdecode=None):
+        return _EncryptAndVerifyRSA.verify(self, msg, sig, h=h,
+                                           t=t, mgf=mgf, sLen=sLen)
+
+
+class PubKeyECDSA(_PKIObj, PubKey):
+    """
+    Wrapper for ECDSA keys based on VerifyingKey from ecdsa library.
+    Use the 'key' attribute to access original object.
+    """
+    def updateWith(self, spki):
+        # For now we use from_der() or from_string() methods,
+        # which do not offer support for compressed points.
+        #XXX Try using the from_public_point() method.
+        try:
+            self.key = ecdsa.VerifyingKey.from_der(str(spki))
+            # from_der() raises an exception on explicit curves
+        except:
+            s = spki.subjectPublicKey.val_readable[1:]
+            p = spki.signatureAlgorithm.parameters
+            c = import_curve(p.fieldID.prime.val,
+                             p.curve.a.val,
+                             p.curve.b.val,
+                             p.base.val,
+                             p.order.val)
+            self.key = ecdsa.VerifyingKey.from_string(s, c)
+    def encrypt(self, msg, t=None, h=None, mgf=None, L=None):
+        # python-ecdsa does not support encryption
+        raise Exception("No ECDSA encryption support")
+    def verify(self, msg, sig, h=None,
+               t=None, mgf=None, sLen=None,
+               sigdecode=ecdsa.util.sigdecode_string):
+        try:
+            return self.key.verify(sig, msg, hashfunc=mapHashFunc(h),
+                                   sigdecode=sigdecode)
+        except ecdsa.keys.BadSignatureError:
+            return False
+
+
+################
+# Private Keys #
+################
+
+class _PrivKeyFactory(_PKIObjMaker):
+    """
+    Metaclass for PrivKey creation.
+    It casts the appropriate class on the fly, then fills in
+    the appropriate attributes with updateWith() submethod.
+    """
+    def __call__(cls, key_path):
+        """
+        key_path may be the path to either:
+            _an RSAPrivateKey_OpenSSL (as generated by openssl);
+            _an ECDSAPrivateKey_OpenSSL (as generated by openssl);
+            _an RSAPrivateKey;
+            _an ECDSAPrivateKey.
+        """
+        obj = _PKIObjMaker.__call__(cls, key_path, MAX_KEY_SIZE)
+        multiPEM = False
+        try:
+            privkey = RSAPrivateKey_OpenSSL(obj.der)
+            privkey = privkey.privateKey
+            obj.__class__ = PrivKeyRSA
+            marker = "PRIVATE KEY"
+        except:
+            try:
+                privkey = ECDSAPrivateKey_OpenSSL(obj.der)
+                privkey = privkey.privateKey
+                obj.__class__ = PrivKeyECDSA
+                marker = "EC PRIVATE KEY"
+                multiPEM = True
+            except:
+                try:
+                    privkey = RSAPrivateKey(obj.der)
+                    obj.__class__ = PrivKeyRSA
+                    marker = "RSA PRIVATE KEY"
+                except:
+                    try:
+                        privkey = ECDSAPrivateKey(obj.der)
+                        obj.__class__ = PrivKeyECDSA
+                        marker = "EC PRIVATE KEY"
+                    except:
+                        raise Exception("Unable to import private key")
+        obj.updateWith(privkey)
+
+        if obj.frmt == "DER":
+            if multiPEM:
+                # this does not restore the EC PARAMETERS header
+                obj.pem = der2pem(str(privkey), marker)
+            else:
+                obj.pem = der2pem(obj.der, marker)
+        return obj
+
+
+class PrivKey(object):
+    """
+    Parent class for both PrivKeyRSA and PrivKeyECDSA.
+    Provides common signTBSCert() and resignCert() methods.
+    """
+    __metaclass__ = _PrivKeyFactory
+
+    def signTBSCert(self, tbsCert, h=None):
+        """
+        Note that this will always copy the signature field from the
+        tbsCertificate into the signatureAlgorithm field of the result,
+        regardless of the coherence between its contents (which might
+        indicate ecdsa-with-SHA512) and the result (e.g. RSA signing MD2).
+
+        There is a small inheritance trick for the computation of sigVal
+        below: in order to use a sign() method which would apply
+        to both PrivKeyRSA and PrivKeyECDSA, the sign() methods of the
+        subclasses accept any argument, be it from the RSA or ECDSA world,
+        and then they keep the ones they're interested in.
+        Here, t will be passed eventually to pkcs1._DecryptAndSignRSA.sign(),
+        while sigencode will be passed to ecdsa.keys.SigningKey.sign().
+        """
+        sigAlg = tbsCert.signature
+        h = h or hash_by_oid[sigAlg.algorithm.val]
+        sigVal = self.sign(str(tbsCert), h=h,
+                           t='pkcs',
+                           sigencode=ecdsa.util.sigencode_der)
+        c = X509_Cert()
+        c.tbsCertificate = tbsCert
+        c.signatureAlgorithm = sigAlg
+        c.signatureValue = ASN1_BIT_STRING(sigVal, readable=True)
+        return c
+
+    def resignCert(self, cert):
+        # works with both Cert and X509_Cert types
+        return self.signTBSCert(cert.tbsCertificate)
+
+
+class PrivKeyRSA(_PKIObj, PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA):
+    """
+    Wrapper for RSA keys based on _DecryptAndSignRSA from crypto/pkcs1.py
+    Use the 'key' attribute to access original object.
+    """
+    def updateWith(self, privkey):
+        self.modulus     = privkey.modulus.val
+        self.modulusLen  = len(binrepr(privkey.modulus.val))
+        self.pubExp      = privkey.publicExponent.val
+        self.privExp     = privkey.privateExponent.val
+        self.prime1      = privkey.prime1.val
+        self.prime2      = privkey.prime2.val
+        self.exponent1   = privkey.exponent1.val
+        self.exponent2   = privkey.exponent2.val
+        self.coefficient = privkey.coefficient.val
+        self.key = RSA.construct((self.modulus, self.pubExp, self.privExp))
+    def verify(self, msg, sig, h=None,
+               t=None, mgf=None, sLen=None,
+               sigdecode=None):
+        # Let's copy this from PubKeyRSA instead of adding another baseclass :)
+        return _EncryptAndVerifyRSA.verify(self, msg, sig, h=h,
+                                           t=t, mgf=mgf, sLen=sLen)
+    def sign(self, data, h=None,
+             t=None, mgf=None, sLen=None,
+             k=None, entropy=None, sigencode=None):
+        return _DecryptAndSignRSA.sign(self, data, h=h,
+                                       t=t, mgf=mgf, sLen=sLen)
+
+
+class PrivKeyECDSA(_PKIObj, PrivKey):
+    """
+    Wrapper for ECDSA keys based on SigningKey from ecdsa library.
+    Use the 'key' attribute to access original object.
+    """
+    def updateWith(self, privkey):
+        self.privKey = pkcs_os2ip(privkey.privateKey.val)
+        self.key = ecdsa.SigningKey.from_der(str(privkey))
+        self.vkey = self.key.get_verifying_key()
+    def verify(self, msg, sig, h=None,
+               t=None, mgf=None, sLen=None,
+               sigdecode=None):
+        return self.vkey.verify(sig, msg, hashfunc=mapHashFunc(h),
+                                sigdecode=sigdecode)
+    def sign(self, data, h=None,
+             t=None, mgf=None, sLen=None,
+             k=None, entropy=None, sigencode=ecdsa.util.sigencode_string):
+        return self.key.sign(data, hashfunc=mapHashFunc(h),
+                             k=k, entropy=entropy, sigencode=sigencode)
+
+
+################
+# Certificates #
+################
+
+class _CertMaker(_PKIObjMaker):
+    """
+    Metaclass for Cert creation. It is not necessary as it was for the keys,
+    but we reuse the model instead of creating redundant constructors.
+    """
+    def __call__(cls, cert_path):
+        obj = _PKIObjMaker.__call__(cls, cert_path,
+                                    MAX_CERT_SIZE, "CERTIFICATE")
+        obj.__class__ = Cert
+        try:
+            cert = X509_Cert(obj.der)
+        except:
+            raise Exception("Unable to import certificate")
+        obj.updateWith(cert)
+        return obj
+
+
+class Cert(_PKIObj):
+    """
+    Wrapper for the X509_Cert from layers/x509.py.
+    Use the 'x509Cert' attribute to access original object.
+    """
+    __metaclass__ = _CertMaker
+
+    def updateWith(self, cert):
+        error_msg = "Unable to import certificate"
+
+        self.x509Cert = cert
+
+        tbsCert = cert.tbsCertificate
+        self.tbsCertificate = tbsCert
+
+        if tbsCert.version:
+            self.version = tbsCert.version.val + 1
+        else:
+            self.version = 1
+        self.serial = tbsCert.serialNumber.val
+        self.sigAlg = tbsCert.signature.algorithm.oidname
+        self.issuer = tbsCert.get_issuer()
+        self.issuer_str = tbsCert.get_issuer_str()
+        self.issuer_hash = hash(self.issuer_str)
+        self.subject = tbsCert.get_subject()
+        self.subject_str = tbsCert.get_subject_str()
+        self.subject_hash = hash(self.subject_str)
+
+        self.notBefore_str = tbsCert.validity.not_before.pretty_time
+        notBefore = tbsCert.validity.not_before.val
+        if notBefore[-1] == "Z":
+            notBefore = notBefore[:-1]
+        try:
+            self.notBefore = time.strptime(notBefore, "%y%m%d%H%M%S")
+        except:
+            raise Exception(error_msg)
+        self.notBefore_str_simple = time.strftime("%x", self.notBefore)
+
+        self.notAfter_str = tbsCert.validity.not_after.pretty_time
+        notAfter = tbsCert.validity.not_after.val
+        if notAfter[-1] == "Z":
+            notAfter = notAfter[:-1]
+        try:
+            self.notAfter = time.strptime(notAfter, "%y%m%d%H%M%S")
+        except:
+            raise Exception(error_msg)
+        self.notAfter_str_simple = time.strftime("%x", self.notAfter)
+
+        self.pubKey = PubKey(str(tbsCert.subjectPublicKeyInfo))
+
+        if tbsCert.extensions:
+            for extn in tbsCert.extensions:
+                if extn.extnID.oidname == "basicConstraints":
+                    self.cA = False
+                    if extn.extnValue.cA:
+                        self.cA = not (extn.extnValue.cA.val == 0)
+                elif extn.extnID.oidname == "keyUsage":
+                    self.keyUsage = extn.extnValue.get_keyUsage()
+                elif extn.extnID.oidname == "extKeyUsage":
+                    self.extKeyUsage = extn.extnValue.get_extendedKeyUsage()
+                elif extn.extnID.oidname == "authorityKeyIdentifier":
+                    self.authorityKeyID = extn.extnValue.keyIdentifier.val
+
+        self.signatureValue = str(cert.signatureValue)
+        self.signatureLen = len(self.signatureValue)
+
+    def isIssuerCert(self, other):
+        """
+        True if 'other' issued 'self', i.e.:
+          - self.issuer == other.subject
+          - self is signed by other
+        """
+        if self.issuer_hash != other.subject_hash:
+            return False
+        return other.pubKey.verifyCert(self)
+
+    def isSelfSigned(self):
+        """
+        Return True if the certificate is self-signed:
+          - issuer and subject are the same
+          - the signature of the certificate is valid.
+        """
+        if self.issuer_hash == self.subject_hash:
+            return self.isIssuerCert(self)
+        return False
+
+    def encrypt(self, msg, t=None, h=None, mgf=None, L=None):
+        # no ECDSA *encryption* support, hence only RSA specific keywords here
+        return self.pubKey.encrypt(msg, t=t, h=h, mgf=mgf, L=L)
+
+    def verify(self, msg, sig, h=None,
+               t=None, mgf=None, sLen=None,
+               sigdecode=None):
+        return self.pubKey.verify(msg, sig, h=h,
+                                  t=t, mgf=mgf, sLen=sLen,
+                                  sigdecode=sigdecode)
+
+    def remainingDays(self, now=None):
+        """
+        Based on the value of notAfter field, returns the number of
+        days the certificate will still be valid. The date used for the
+        comparison is the current and local date, as returned by
+        time.localtime(), except if 'now' argument is provided another
+        one. 'now' argument can be given as either a time tuple or a string
+        representing the date. Accepted format for the string version
+        are:
+
+         - '%b %d %H:%M:%S %Y %Z' e.g. 'Jan 30 07:38:59 2008 GMT'
+         - '%m/%d/%y' e.g. '01/30/08' (less precise)
+
+        If the certificate is no more valid at the date considered, then
+        a negative value is returned representing the number of days
+        since it has expired.
+
+        The number of days is returned as a float to deal with the unlikely
+        case of certificates that are still just valid.
+        """
+        if now is None:
+            now = time.localtime()
+        elif type(now) is str:
+            try:
+                if '/' in now:
+                    now = time.strptime(now, '%m/%d/%y')
+                else:
+                    now = time.strptime(now, '%b %d %H:%M:%S %Y %Z')
+            except:
+                print "Bad time string provided, will use localtime() instead."
+                now = time.localtime()
+
+        now = time.mktime(now)
+        nft = time.mktime(self.notAfter)
+        diff = (nft - now)/(24.*3600)
+        return diff
+
+    def isRevoked(self, crl_list):
+        """
+        Given a list of trusted CRL (their signature has already been
+        verified with trusted anchors), this function returns True if
+        the certificate is marked as revoked by one of those CRL.
+
+        Note that if the Certificate was on hold in a previous CRL and
+        is now valid again in a new CRL and bot are in the list, it
+        will be considered revoked: this is because _all_ CRLs are
+        checked (not only the freshest) and revocation status is not
+        handled.
+
+        Also note that the check on the issuer is performed on the
+        Authority Key Identifier if available in _both_ the CRL and the
+        Cert. Otherwise, the issuers are simply compared.
+        """
+        for c in crl_list:
+            if (self.authorityKeyID is not None and
+                c.authorityKeyID is not None and
+                self.authorityKeyID == c.authorityKeyID):
+                return self.serial in map(lambda x: x[0],
+                                                    c.revoked_cert_serials)
+            elif self.issuer == c.issuer:
+                return self.serial in map(lambda x: x[0],
+                                                    c.revoked_cert_serials)
+        return False
+
+    def export(self, filename, fmt="DER"):
+        """
+        Export certificate in 'fmt' format (DER or PEM) to file 'filename'
+        """
+        f = open(filename, "wb")
+        if fmt == "DER":
+            f.write(self.der)
+        elif fmt == "PEM":
+            f.write(self.pem)
+        f.close()
+
+    def show(self):
+        print "Serial: %s" % self.serial
+        print "Issuer: " + self.issuer_str
+        print "Subject: " + self.subject_str
+        print "Validity: %s to %s" % (self.notBefore_str, self.notAfter_str)
+
+    def __repr__(self):
+        return "[X.509 Cert. Subject:%s, Issuer:%s]" % (self.subject_str, self.issuer_str)
+
+
+################################
+# Certificate Revocation Lists #
+################################
+
+class _CRLMaker(_PKIObjMaker):
+    """
+    Metaclass for CRL creation. It is not necessary as it was for the keys,
+    but we reuse the model instead of creating redundant constructors.
+    """
+    def __call__(cls, cert_path):
+        obj = _PKIObjMaker.__call__(cls, cert_path, MAX_CRL_SIZE, "X509 CRL")
+        obj.__class__ = CRL
+        try:
+            crl = X509_CRL(obj.der)
+        except:
+            raise Exception("Unable to import CRL")
+        obj.updateWith(crl)
+        return obj
+
+
+class CRL(_PKIObj):
+    """
+    Wrapper for the X509_CRL from layers/x509.py.
+    Use the 'x509CRL' attribute to access original object.
+    """
+    __metaclass__ = _CRLMaker
+
+    def updateWith(self, crl):
+        error_msg = "Unable to import CRL"
+
+        self.x509CRL = crl
+
+        tbsCertList = crl.tbsCertList
+        self.tbsCertList = str(tbsCertList)
+
+        if tbsCertList.version:
+            self.version = tbsCertList.version.val + 1
+        else:
+            self.version = 1
+        self.sigAlg = tbsCertList.signature.algorithm.oidname
+        self.issuer = tbsCertList.get_issuer()
+        self.issuer_str = tbsCertList.get_issuer_str()
+        self.issuer_hash = hash(self.issuer_str)
+
+        self.lastUpdate_str = tbsCertList.this_update.pretty_time
+        lastUpdate = tbsCertList.this_update.val
+        if lastUpdate[-1] == "Z":
+            lastUpdate = lastUpdate[:-1]
+        try:
+            self.lastUpdate = time.strptime(lastUpdate, "%y%m%d%H%M%S")
+        except:
+            raise Exception(error_msg)
+        self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate)
+
+        self.nextUpdate = None
+        self.nextUpdate_str_simple = None
+        if tbsCertList.next_update:
+            self.nextUpdate_str = tbsCertList.next_update.pretty_time
+            nextUpdate = tbsCertList.next_update.val
+            if nextUpdate[-1] == "Z":
+                nextUpdate = nextUpdate[:-1]
+            try:
+                self.nextUpdate = time.strptime(nextUpdate, "%y%m%d%H%M%S")
+            except:
+                raise Exception(error_msg)
+            self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate)
+
+        if tbsCertList.crlExtensions:
+            for extension in tbsCertList.crlExtensions:
+                if extension.extnID.oidname == "cRLNumber":
+                    self.number = extension.extnValue.cRLNumber.val
+
+        revoked = []
+        if tbsCertList.revokedCertificates:
+            for cert in tbsCertList.revokedCertificates:
+                serial = cert.serialNumber.val
+                date = cert.revocationDate.val
+                if date[-1] == "Z":
+                    date = date[:-1]
+                try:
+                    revocationDate = time.strptime(date, "%y%m%d%H%M%S")
+                except:
+                    raise Exception(error_msg)
+                revoked.append((serial, date))
+        self.revoked_cert_serials = revoked
+
+        self.signatureValue = str(crl.signatureValue)
+        self.signatureLen = len(self.signatureValue)
+
+    def isIssuerCert(self, other):
+        # This is exactly the same thing as in Cert method.
+        if self.issuer_hash != other.subject_hash:
+            return False
+        return other.pubKey.verifyCert(self)
+
+    def verify(self, anchors):
+        # Return True iff the CRL is signed by one of the provided anchors.
+        for a in anchors:
+            if self.isIssuerCert(a):
+                return True
+        return False
+
+    def show(self):
+        print "Version: %d" % self.version
+        print "sigAlg: " + self.sigAlg
+        print "Issuer: " + self.issuer_str
+        print "lastUpdate: %s" % self.lastUpdate_str
+        print "nextUpdate: %s" % self.nextUpdate_str
+
+
+######################
+# Certificate chains #
+######################
+
+class Chain(list):
+    """
+    Basically, an enhanced array of Cert.
+    """
+    def __init__(self, certList, cert0=None):
+        """
+        Construct a chain of certificates starting with a self-signed
+        certificate (or any certificate submitted by the user)
+        and following issuer/subject matching and signature validity.
+        If there is exactly one chain to be constructed, it will be,
+        but if there are multiple potential chains, there is no guarantee
+        that the retained one will be the longest one.
+        As Cert and CRL classes both share an isIssuerCert() method,
+        the trailing element of a Chain may alternatively be a CRL.
+
+        Note that we do not check AKID/{SKID/issuer/serial} matching,
+        nor the presence of keyCertSign in keyUsage extension (if present).
+        """
+        list.__init__(self, ())
+        if cert0:
+            self.append(cert0)
+        else:
+            for root_candidate in certList:
+                if root_candidate.isSelfSigned():
+                    self.append(root_candidate)
+                    certList.remove(root_candidate)
+                    break
+
+        if len(self) > 0:
+            while certList:
+                l = len(self)
+                for c in certList:
+                    if c.isIssuerCert(self[-1]):
+                        self.append(c)
+                        certList.remove(c)
+                        break
+                if len(self) == l:
+                    # no new certificate appended to self
+                    break
+
+    def verifyChain(self, anchors, untrusted=None):
+        """
+        Perform verification of certificate chains for that certificate.
+        A list of anchors is required. The certificates in the optional
+        untrusted list may be used as additional elements to the final chain.
+        On par with chain instantiation, only one chain constructed with the
+        untrusted candidates will be retained. Eventually, dates are checked.
+        """
+        untrusted = untrusted or []
+        for a in anchors:
+            chain = Chain(self + untrusted, a)
+            if len(chain) == 1:             # anchor only
+                continue
+            # check that the chain does not exclusively rely on untrusted
+            found = False
+            for c in self:
+                if c in chain[1:]:
+                    found = True
+            if found:
+                for c in chain:
+                    if c.remainingDays() < 0:
+                        break
+                if c is chain[-1]:      # we got to the end of the chain
+                    return chain
+        return None
+
+    def verifyChainFromCAFile(self, cafile, untrusted_file=None):
+        """
+        Does the same job as .verifyChain() but using the list of anchors
+        from the cafile. As for .verifyChain(), a list of untrusted
+        certificates can be passed (as a file, this time).
+        """
+        try:
+            f = open(cafile)
+            ca_certs = f.read()
+            f.close()
+        except:
+            raise Exception("Could not read from cafile")
+
+        anchors = [Cert(c) for c in split_pem(ca_certs)]
+
+        untrusted = None
+        if untrusted_file:
+            try:
+                f = open(untrusted_file)
+                untrusted_certs = f.read()
+                f.close()
+            except:
+                raise Exception("Could not read from untrusted_file")
+            untrusted = [Cert(c) for c in split_pem(untrusted_certs)]
+
+        return self.verifyChain(anchors, untrusted)
+
+    def verifyChainFromCAPath(self, capath, untrusted_file=None):
+        """
+        Does the same job as .verifyChainFromCAFile() but using the list
+        of anchors in capath directory. The directory should (only) contain
+        certificates files in PEM format. As for .verifyChainFromCAFile(),
+        a list of untrusted certificates can be passed as a file
+        (concatenation of the certificates in PEM format).
+        """
+        try:
+            anchors = []
+            for cafile in os.listdir(capath):
+                anchors.append(Cert(open(cafile).read()))
+        except:
+            raise Exception("capath provided is not a valid cert path")
+
+        untrusted = None
+        if untrusted_file:
+            try:
+                f = open(untrusted_file)
+                untrusted_certs = f.read()
+                f.close()
+            except:
+                raise Exception("Could not read from untrusted_file")
+            untrusted = [Cert(c) for c in split_pem(untrusted_certs)]
+
+        return self.verifyChain(anchors, untrusted)
+
+    def __repr__(self):
+        llen = len(self) - 1
+        if llen < 0:
+            return ""
+        c = self[0]
+        s = "__ "
+        if not c.isSelfSigned():
+            s += "%s [Not Self Signed]\n" % c.subject_str
+        else:
+            s += "%s [Self Signed]\n" % c.subject_str
+        idx = 1
+        while idx <= llen:
+            c = self[idx]
+            s += "%s\_ %s" % (" "*idx*2, c.subject_str)
+            if idx != llen:
+                s += "\n"
+            idx += 1
+        return s
+
diff --git a/scapy/layers/tls/crypto/__init__.py b/scapy/layers/tls/crypto/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b30813d2c512d146ebe4ca60798765fa72ea3625
--- /dev/null
+++ b/scapy/layers/tls/crypto/__init__.py
@@ -0,0 +1 @@
+__all__ = ["curves"]
diff --git a/scapy/layers/tls/crypto/curves.py b/scapy/layers/tls/crypto/curves.py
new file mode 100644
index 0000000000000000000000000000000000000000..0512766136a7b547056ed67d231c0d9585529501
--- /dev/null
+++ b/scapy/layers/tls/crypto/curves.py
@@ -0,0 +1,406 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) 2016 Pascal Delaunay, Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Implicit elliptic curves.
+"""
+
+# Recommended curve parameters from www.secg.org/SEC2-Ver-1.0.pdf
+# and www.ecc-brainpool.org/download/Domain-parameters.pdf
+# Note that this module will overwrite curves from python-ecdsa.
+
+import math
+from ecdsa.ellipticcurve import CurveFp, Point
+from ecdsa.curves import Curve
+from ecdsa.numbertheory import square_root_mod_prime
+
+from scapy.utils import long_converter, binrepr
+from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
+
+
+##############################################################
+# Some helpers
+##############################################################
+
+def encode_point(point, point_format=0):
+    """
+    Return a string representation of the Point p, according to point_format.
+    """
+    pLen = len(binrepr(point.curve().p()))
+    x = pkcs_i2osp(point.x(), math.ceil(pLen/8))
+    y = pkcs_i2osp(point.y(), math.ceil(pLen/8))
+    if point_format == 0:
+        frmt = '\x04'
+    elif point_format == 1:
+        frmt = chr(2 + y%2)
+        y = ''
+    else:
+        raise Exception("No support for point_format %d" % point_format)
+    return frmt + x + y
+
+def extract_coordinates(g, curve):
+    """
+    Return the coordinates x and y as integers,
+    regardless of the point format of string g.
+    Second expected parameter is a CurveFp.
+    """
+    p = curve.p()
+    point_format = g[0]
+    point = g[1:]
+    if point_format == '\x04':
+        point_len = len(point)
+        if point_len % 2 != 0:
+            raise Exception("Point length is not even.")
+        x_bytes = point[:point_len>>1]
+        x = pkcs_os2ip(x_bytes) % p
+        y_bytes = point[point_len>>1:]
+        y = pkcs_os2ip(y_bytes) % p
+    elif point_format in ['\x02', '\x03']:
+        x_bytes = point
+        x = pkcs_os2ip(x_bytes) % p
+        # perform the y coordinate computation with self.tls_ec
+        y_square = (x*x*x + curve.a()*x + curve.b()) % p
+        y = square_root_mod_prime(y_square, p)
+        y_parity = ord(point_format) % 2    # \x02 means even, \x03 means odd
+        if y % 2 != y_parity:
+            y = -y % p
+    else:
+        raise Exception("Point starts with %s. This encoding "
+                        "is not recognized." % repr(point_format))
+    if not curve.contains_point(x, y):
+        raise Exception("The point we extracted does not belong on the curve!")
+    return x, y
+
+def import_curve(p, a, b, g, r, name="dummyName", oid=(1, 3, 132, 0, 0xff)):
+    """
+    Create an ecdsa.curves.Curve from the usual parameters.
+    Arguments may be either octet strings or integers,
+    except g which we expect to be an octet string.
+    """
+    if isinstance(p, str):
+        p = pkcs_os2ip(p)
+    if isinstance(a, str):
+        a = pkcs_os2ip(a)
+    if isinstance(b, str):
+        b = pkcs_os2ip(b)
+    if isinstance(r, str):
+        r = pkcs_os2ip(r)
+    curve = CurveFp(p, a, b)
+    x, y = extract_coordinates(g, curve)
+    generator = Point(curve, x, y, r)
+    return Curve(name, curve, generator, oid)
+
+
+##############################################################
+# Named curves
+##############################################################
+
+# We always provide _a as a positive integer.
+
+_p          = long_converter("""
+              ffffffff ffffffff ffffffff fffffffe ffffac73""")
+_a          = 0
+_b          = 7
+_Gx         = long_converter("""
+              3b4c382c e37aa192 a4019e76 3036f4f5 dd4d7ebb""")
+_Gy         = long_converter("""
+              938cf935 318fdced 6bc28286 531733c3 f03c4fee""")
+_r          = long_converter("""01
+              00000000 00000000 0001b8fa 16dfab9a ca16b6b3""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP160k1   = Curve("SECP160k1", curve, generator,
+                    (1, 3, 132, 0, 9), "secp160k1")
+
+_p          = long_converter("""
+              ffffffff ffffffff ffffffff ffffffff 7fffffff""")
+_a          = -3 % _p
+_b          = long_converter("""
+              1c97befc 54bd7a8b 65acf89f 81d4d4ad c565fa45""")
+_Gx         = long_converter("""
+              4a96b568 8ef57328 46646989 68c38bb9 13cbfc82""")
+_Gy         = long_converter("""
+              23a62855 3168947d 59dcc912 04235137 7ac5fb32""")
+_r          = long_converter("""01
+              00000000 00000000 0001f4c8 f927aed3 ca752257""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP160r1   = Curve("SECP160r1", curve, generator,
+                    (1, 3, 132, 0, 8), "secp160r1")
+
+_p          = long_converter("""
+              ffffffff ffffffff ffffffff fffffffe ffffac73""")
+_a          = -3 % _p
+_b          = long_converter("""
+              b4e134d3 fb59eb8b ab572749 04664d5a f50388ba""")
+_Gx         = long_converter("""
+              52dcb034 293a117e 1f4ff11b 30f7199d 3144ce6d""")
+_Gy         = long_converter("""
+              feaffef2 e331f296 e071fa0d f9982cfe a7d43f2e""")
+_r          = long_converter("""01
+              00000000 00000000 0000351e e786a818 f3a1a16b""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP160r2   = Curve("SECP160r2", curve, generator,
+                    (1, 3, 132, 0, 30), "secp160r2")
+
+_p          = long_converter("""
+              ffffffff ffffffff ffffffff ffffffff fffffffe ffffee37""")
+_a          = 0
+_b          = 3
+_Gx         = long_converter("""
+              db4ff10e c057e9ae 26b07d02 80b7f434 1da5d1b1 eae06c7d""")
+_Gy         = long_converter("""
+              9b2f2f6d 9c5628a7 844163d0 15be8634 4082aa88 d95e2f9d""")
+_r          = long_converter("""
+              ffffffff ffffffff fffffffe 26f2fc17 0f69466a 74defd8d""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP192k1   = Curve("SECP192k1", curve, generator,
+                    (1, 3, 132, 0, 31), "secp192k1")
+
+_p          = long_converter("""
+              ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff""")
+_a          = -3 % _p
+_b          = long_converter("""
+              64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1""")
+_Gx         = long_converter("""
+              188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012""")
+_Gy         = long_converter("""
+              07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811""")
+_r          = long_converter("""
+              ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP192r1   = Curve("SECP192r1", curve, generator,
+                    (1, 2, 840, 10045, 3, 1, 1), "prime192v1")
+
+_p          = long_converter("""
+              ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
+              ffffe56d""")
+_a          = 0
+_b          = 5
+_Gx         = long_converter("""
+              a1455b33 4df099df 30fc28a1 69a467e9 e47075a9 0f7e650e
+              b6b7a45c""")
+_Gy         = long_converter("""
+              7e089fed 7fba3442 82cafbd6 f7e319f7 c0b0bd59 e2ca4bdb
+              556d61a5""")
+_r          = long_converter("""01
+              00000000 00000000 00000000 0001dce8 d2ec6184 caf0a971
+              769fb1f7""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP224k1   = Curve("SECP224k1", curve, generator,
+                    (1, 3, 132, 0, 32), "secp224k1")
+
+_p          = long_converter("""
+              ffffffff ffffffff ffffffff ffffffff 00000000 00000000
+              00000001""")
+_a          = -3 % _p
+_b          = long_converter("""
+              b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943
+              2355ffb4""")
+_Gx         = long_converter("""
+              b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6
+              115c1d21""")
+_Gy         = long_converter("""
+              bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199
+              85007e34""")
+_r          = long_converter("""
+              ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945
+              5c5c2a3d""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP224r1   = Curve("SECP224r1", curve, generator,
+                    (1, 3, 132, 0, 33), "secp224r1")
+
+# (already defined as SECP256k1 by python-ecdsa)
+_p          = long_converter("""
+              ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
+              fffffffe fffffc2f""")
+_a          = 0
+_b          = 7
+_Gx         = long_converter("""
+              79be667e f9dcbbac 55a06295 ce870b07 029bfcdb 2dce28d9
+              59f2815b 16f81798""")
+_Gy         = long_converter("""
+              483ada77 26a3c465 5da4fbfc 0e1108a8 fd17b448 a6855419
+              9c47d08f fb10d4b8""")
+_r          = long_converter("""
+              ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b
+              bfd25e8c d0364141""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP256k1   = Curve("SECP256k1", curve, generator,
+                    (1, 3, 132, 0, 10), "secp256k1")
+
+_p          = long_converter("""
+              ffffffff 00000001 00000000 00000000 00000000 ffffffff
+              ffffffff ffffffff""")
+_a          = -3 % _p
+_b          = long_converter("""
+              5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6
+              3bce3c3e 27d2604b""")
+_Gx         = long_converter("""
+              6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0
+              f4a13945 d898c296""")
+_Gy         = long_converter("""
+              4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece
+              cbb64068 37bf51f5""")
+_r          = long_converter("""
+              ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84
+              f3b9cac2 fc632551""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP256r1   = Curve("SECP256r1", curve, generator,
+                    (1, 2, 840, 10045, 3, 1, 7), "prime256v1")
+
+_p          = long_converter("""
+              ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
+              ffffffff fffffffe ffffffff 00000000 00000000 ffffffff""")
+_a          = -3 % _p
+_b          = long_converter("""
+              b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112
+              0314088f 5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef""")
+_Gx         = long_converter("""
+              aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98
+              59f741e0 82542a38 5502f25d bf55296c 3a545e38 72760ab7""")
+_Gy         = long_converter("""
+              3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c
+              e9da3113 b5f0b8c0 0a60b1ce 1d7e819d 7a431d7c 90ea0e5f""")
+_r          = long_converter("""
+              ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
+              c7634d81 f4372ddf 581a0db2 48b0a77a ecec196a ccc52973""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP384r1   = Curve("SECP384r1", curve, generator,
+                    (1, 3, 132, 0, 34), "secp384r1")
+
+_p          = long_converter("""
+                  01ff ffffffff ffffffff ffffffff ffffffff ffffffff
+              ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
+              ffffffff ffffffff ffffffff ffffffff ffffffff""")
+_a          = -3 % _p
+_b          = long_converter("""
+                  0051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b
+              99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd
+              3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00""")
+_Gx         = long_converter("""
+                  00c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139
+              053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127
+              a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66""")
+_Gy         = long_converter("""
+                  0118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449
+              579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901
+              3fad0761 353c7086 a272c240 88be9476 9fd16650""")
+_r          = long_converter("""
+                  01ff ffffffff ffffffff ffffffff ffffffff ffffffff
+              ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148
+              f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+SECP521r1   = Curve("SECP521r1", curve, generator,
+                    (1, 3, 132, 0, 35), "secp521r1")
+
+_p          = long_converter("""
+              A9FB57DB A1EEA9BC 3E660A90 9D838D72 6E3BF623 D5262028
+              2013481D 1F6E5377""")
+_a          = long_converter("""
+              7D5A0975 FC2C3057 EEF67530 417AFFE7 FB8055C1 26DC5C6C
+              E94A4B44 F330B5D9""")
+_b          = long_converter("""
+              26DC5C6C E94A4B44 F330B5D9 BBD77CBF 95841629 5CF7E1CE
+              6BCCDC18 FF8C07B6""")
+_Gx         = long_converter("""
+              8BD2AEB9 CB7E57CB 2C4B482F FC81B7AF B9DE27E1 E3BD23C2
+              3A4453BD 9ACE3262""")
+_Gy         = long_converter("""
+              547EF835 C3DAC4FD 97F8461A 14611DC9 C2774513 2DED8E54
+              5C1D54C7 2F046997""")
+_r          = long_converter("""
+              A9FB57DB A1EEA9BC 3E660A90 9D838D71 8C397AA3 B561A6F7
+              901E0E82 974856A7""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+BRNP256r1   = Curve("BRNP256r1", curve, generator,
+                    (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), "brainpoolP256r1")
+
+_p          = long_converter("""
+              8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B4
+              12B1DA19 7FB71123 ACD3A729 901D1A71 87470013 3107EC53""")
+_a          = long_converter("""
+              7BC382C6 3D8C150C 3C72080A CE05AFA0 C2BEA28E 4FB22787
+              139165EF BA91F90F 8AA5814A 503AD4EB 04A8C7DD 22CE2826""")
+_b          = long_converter("""
+              04A8C7DD 22CE2826 8B39B554 16F0447C 2FB77DE1 07DCD2A6
+              2E880EA5 3EEB62D5 7CB43902 95DBC994 3AB78696 FA504C11""")
+_Gx         = long_converter("""
+              1D1C64F0 68CF45FF A2A63A81 B7C13F6B 8847A3E7 7EF14FE3
+              DB7FCAFE 0CBD10E8 E826E034 36D646AA EF87B2E2 47D4AF1E""")
+_Gy         = long_converter("""
+              8ABE1D75 20F9C2A4 5CB1EB8E 95CFD552 62B70B29 FEEC5864
+              E19C054F F9912928 0E464621 77918111 42820341 263C5315""")
+_r          = long_converter("""
+              8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B3
+              1F166E6C AC0425A7 CF3AB6AF 6B7FC310 3B883202 E9046565""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+BRNP384r1   = Curve("BRNP384r1", curve, generator,
+                    (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), "brainpoolP384r1")
+
+_p          = long_converter("""
+              AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E
+              D6639CCA 70330871 7D4D9B00 9BC66842 AECDA12A E6A380E6
+              2881FF2F 2D82C685 28AA6056 583A48F3""")
+_a          = long_converter("""
+              7830A331 8B603B89 E2327145 AC234CC5 94CBDD8D 3DF91610
+              A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5
+              7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA""")
+_b          = long_converter("""
+              3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9
+              8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA DC083E67
+              984050B7 5EBAE5DD 2809BD63 8016F723""")
+_Gx         = long_converter("""
+              81AEE4BD D82ED964 5A21322E 9C4C6A93 85ED9F70 B5D916C1
+              B43B62EE F4D0098E FF3B1F78 E2D0D48D 50D1687B 93B97D5F
+              7C6D5047 406A5E68 8B352209 BCB9F822""")
+_Gy         = long_converter("""
+              7DDE385D 566332EC C0EABFA9 CF7822FD F209F700 24A57B1A
+              A000C55B 881F8111 B2DCDE49 4A5F485E 5BCA4BD8 8A2763AE
+              D1CA2B2F A8F05406 78CD1E0F 3AD80892""")
+_r          = long_converter("""
+              AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E
+              D6639CCA 70330870 553E5C41 4CA92619 41866119 7FAC1047
+              1DB1D381 085DDADD B5879682 9CA90069""")
+curve       = CurveFp(_p, _a, _b)
+generator   = Point(curve, _Gx, _Gy, _r)
+BRNP512r1   = Curve("BRNP512r1", curve, generator,
+                    (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), "brainpoolP512r1")
+
+# we use IANA identifiers below
+named_curves = { 15: SECP160k1,
+                 16: SECP160r1,
+                 17: SECP160r2,
+                 18: SECP192k1,
+                 19: SECP192r1,
+                 20: SECP224k1,
+                 21: SECP224r1,
+                 22: SECP256k1,
+                 23: SECP256r1,
+                 24: SECP384r1,
+                 25: SECP521r1,
+                 26: BRNP256r1,
+                 27: BRNP384r1,
+                 28: BRNP512r1
+               }
+
+for cid, c in named_curves.iteritems():
+    c.curve_id = cid
+
+# replace/fill previous named curves
+import ecdsa.curves
+ecdsa.curves.curves = named_curves.values()
+
diff --git a/scapy/layers/tls/crypto/pkcs1.py b/scapy/layers/tls/crypto/pkcs1.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c43cf4a5381b6b11568286d61523f0ee7532b32
--- /dev/null
+++ b/scapy/layers/tls/crypto/pkcs1.py
@@ -0,0 +1,1158 @@
+## This file is part of Scapy
+## Copyright (C) 2008 Arnaud Ebalard <arno@natisbad.org>
+##         2015, 2016 Maxence Tury <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+PKCS #1 methods as defined in RFC 3447.
+"""
+
+import os, popen2, tempfile
+import math, random, struct
+from hashlib import md5, sha1, sha224, sha256, sha384, sha512
+from Crypto.Hash import MD2, MD4
+
+
+#####################################################################
+# Some helpers
+#####################################################################
+
+def _warning(m):
+    print "WARNING: %s" % m
+
+def randstring(l):
+    """
+    Returns a random string of length l (l >= 0)
+    """
+    tmp = map(lambda x: struct.pack("B", random.randrange(0, 256, 1)), [""]*l)
+    return "".join(tmp)
+
+def zerofree_randstring(l):
+    """
+    Returns a random string of length l (l >= 0) without zero in it.
+    """
+    tmp = map(lambda x: struct.pack("B", random.randrange(1, 256, 1)), [""]*l)
+    return "".join(tmp)
+
+def strxor(s1, s2):
+    """
+    Returns the binary XOR of the 2 provided strings s1 and s2. s1 and s2
+    must be of same length.
+    """
+    return "".join(map(lambda x,y:chr(ord(x)^ord(y)), s1, s2))
+
+def strand(s1, s2):
+    """
+    Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2
+    must be of same length.
+    """
+    return "".join(map(lambda x,y:chr(ord(x)&ord(y)), s1, s2))
+
+# OS2IP function defined in RFC 3447 for octet string to integer conversion
+def pkcs_os2ip(x):
+    """
+    Accepts a byte string as input parameter and return the associated long
+    value:
+
+    Input : x        octet string to be converted
+
+    Output: x        corresponding nonnegative integer
+
+    Reverse function is pkcs_i2osp()
+    """
+    return int(x.encode("hex"), 16)
+
+# I2OSP function defined in RFC 3447 for integer to octet string conversion
+def pkcs_i2osp(x, xLen):
+    """
+    Converts a long (the first parameter) to the associated byte string
+    representation of length l (second parameter). Basically, the length
+    parameters allow the function to perform the associated padding.
+
+    Input : x        nonnegative integer to be converted
+            xLen     intended length of the resulting octet string
+
+    Output: x        corresponding octet string
+
+    Reverse function is pkcs_os2ip().
+    """
+    # The user is responsible for providing an appropriate xLen.
+    #if x >= 256**xLen:
+    #    raise Exception("Integer too large for provided xLen %d" % xLen)
+    fmt = "%%0%dx" % (2*xLen)
+    return (fmt % x).decode("hex")
+
+def pkcs_ilen(n):
+    """
+    This is a log base 256 which determines the minimum octet string
+    length for unequivocal representation of integer n by pkcs_i2osp.
+    """
+    i = 0
+    while n > 0:
+        n >>= 8
+        i += 1
+    return i
+
+# for every hash function a tuple is provided, giving access to
+# - hash output length in byte
+# - associated hash function that take data to be hashed as parameter
+#   XXX I do not provide update() at the moment.
+# - DER encoding of the leading bits of digestInfo (the hash value
+#   will be concatenated to create the complete digestInfo).
+#
+# Notes:
+# - MD4 asn.1 value should be verified. Also, as stated in
+#   PKCS#1 v2.1, MD4 should not be used.
+# - 'tls' one is the concatenation of both md5 and sha1 hashes used
+#   by SSL/TLS when signing/verifying things
+_hashFuncParams = {
+    "md2"    : (16,
+                MD2.new,
+                lambda x: MD2.new(x).digest(),
+                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10'),
+    "md4"    : (16,
+                MD4.new,
+                lambda x: MD4.new(x).digest(),
+                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04\x05\x00\x04\x10'),
+    "md5"    : (16,
+                md5,
+                lambda x: md5(x).digest(),
+                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
+    "sha1"   : (20,
+                sha1,
+                lambda x: sha1(x).digest(),
+                '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
+    "sha224" : (28,
+                sha224,
+                lambda x: sha224(x).digest(),
+                '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c'),
+    "sha256" : (32,
+                sha256,
+                lambda x: sha256(x).digest(),
+                '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'),
+    "sha384" : (48,
+                sha384,
+                lambda x: sha384(x).digest(),
+                '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'),
+    "sha512" : (64,
+                sha512,
+                lambda x: sha512(x).digest(),
+                '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'),
+    "tls"    : (36,
+                None,
+                lambda x: md5(x).digest() + sha1(x).digest(),
+                '')
+    }
+
+def mapHashFunc(hashStr):
+    try:
+        return _hashFuncParams[hashStr][1]
+    except:
+        raise Exception("Unknown hash function %s" % hashStr)
+
+
+def pkcs_mgf1(mgfSeed, maskLen, h):
+    """
+    Implements generic MGF1 Mask Generation function as described in
+    Appendix B.2.1 of RFC 3447. The hash function is passed by name.
+    valid values are 'md2', 'md4', 'md5', 'sha1', 'tls, 'sha256',
+    'sha384' and 'sha512'. Returns None on error.
+
+    Input:
+       mgfSeed: seed from which mask is generated, an octet string
+       maskLen: intended length in octets of the mask, at most 2^32 * hLen
+                hLen (see below)
+       h      : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+                'sha256', 'sha384'). hLen denotes the length in octets of
+                the hash function output.
+
+    Output:
+       an octet string of length maskLen
+    """
+
+    # steps are those of Appendix B.2.1
+    if not _hashFuncParams.has_key(h):
+        _warning("pkcs_mgf1: invalid hash (%s) provided")
+        return None
+    hLen = _hashFuncParams[h][0]
+    hFunc = _hashFuncParams[h][2]
+    if maskLen > 2**32 * hLen:                               # 1)
+        _warning("pkcs_mgf1: maskLen > 2**32 * hLen")
+        return None
+    T = ""                                                   # 2)
+    maxCounter = math.ceil(float(maskLen) / float(hLen))     # 3)
+    counter = 0
+    while counter < maxCounter:
+        C = pkcs_i2osp(counter, 4)
+        T += hFunc(mgfSeed + C)
+        counter += 1
+    return T[:maskLen]
+
+
+def pkcs_emsa_pss_encode(M, emBits, h, mgf, sLen):
+    """
+    Implements EMSA-PSS-ENCODE() function described in Sect. 9.1.1 of RFC 3447
+
+    Input:
+       M     : message to be encoded, an octet string
+       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM),
+               where EM is the encoded message, output of the function.
+       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+               'sha256', 'sha384'). hLen denotes the length in octets of
+               the hash function output.
+       mgf   : the mask generation function f : seed, maskLen -> mask
+       sLen  : intended length in octets of the salt
+
+    Output:
+       encoded message, an octet string of length emLen = ceil(emBits/8)
+
+    On error, None is returned.
+    """
+
+    # 1) is not done
+    hLen = _hashFuncParams[h][0]                             # 2)
+    hFunc = _hashFuncParams[h][2]
+    mHash = hFunc(M)
+    emLen = int(math.ceil(emBits/8.))
+    if emLen < hLen + sLen + 2:                              # 3)
+        _warning("encoding error (emLen < hLen + sLen + 2)")
+        return None
+    salt = randstring(sLen)                                  # 4)
+    MPrime = '\x00'*8 + mHash + salt                         # 5)
+    H = hFunc(MPrime)                                        # 6)
+    PS = '\x00'*(emLen - sLen - hLen - 2)                    # 7)
+    DB = PS + '\x01' + salt                                  # 8)
+    dbMask = mgf(H, emLen - hLen - 1)                        # 9)
+    maskedDB = strxor(DB, dbMask)                            # 10)
+    l = (8*emLen - emBits)/8                                 # 11)
+    rem = 8*emLen - emBits - 8*l # additionnal bits
+    andMask = l*'\x00'
+    if rem:
+        j = chr(reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem))))
+        andMask += j
+        l += 1
+    maskedDB = strand(maskedDB[:l], andMask) + maskedDB[l:]
+    EM = maskedDB + H + '\xbc'                               # 12)
+    return EM                                                # 13)
+
+
+def pkcs_emsa_pss_verify(M, EM, emBits, h, mgf, sLen):
+    """
+    Implements EMSA-PSS-VERIFY() function described in Sect. 9.1.2 of RFC 3447
+
+    Input:
+       M     : message to be encoded, an octet string
+       EM    : encoded message, an octet string of length emLen=ceil(emBits/8)
+       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM)
+       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+               'sha256', 'sha384'). hLen denotes the length in octets of
+               the hash function output.
+       mgf   : the mask generation function f : seed, maskLen -> mask
+       sLen  : intended length in octets of the salt
+
+    Output:
+       True if the verification is ok, False otherwise.
+    """
+
+    # 1) is not done
+    hLen = _hashFuncParams[h][0]                             # 2)
+    hFunc = _hashFuncParams[h][2]
+    mHash = hFunc(M)
+    emLen = int(math.ceil(emBits/8.))                        # 3)
+    if emLen < hLen + sLen + 2:
+        return False
+    if EM[-1] != '\xbc':                                     # 4)
+        return False
+    l = emLen - hLen - 1                                     # 5)
+    maskedDB = EM[:l]
+    H = EM[l:l+hLen]
+    l = (8*emLen - emBits)/8                                 # 6)
+    rem = 8*emLen - emBits - 8*l # additionnal bits
+    andMask = l*'\xff'
+    if rem:
+        val = reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem)))
+        j = chr(~val & 0xff)
+        andMask += j
+        l += 1
+    if strand(maskedDB[:l], andMask) != '\x00'*l:
+        return False
+    dbMask = mgf(H, emLen - hLen - 1)                        # 7)
+    DB = strxor(maskedDB, dbMask)                            # 8)
+    l = (8*emLen - emBits)/8                                 # 9)
+    rem = 8*emLen - emBits - 8*l # additionnal bits
+    andMask = l*'\x00'
+    if rem:
+        j = chr(reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem))))
+        andMask += j
+        l += 1
+    DB = strand(DB[:l], andMask) + DB[l:]
+    l = emLen - hLen - sLen - 1                              # 10)
+    if DB[:l] != '\x00'*(l-1) + '\x01':
+        return False
+    salt = DB[-sLen:]                                        # 11)
+    MPrime = '\x00'*8 + mHash + salt                         # 12)
+    HPrime = hFunc(MPrime)                                   # 13)
+    return H == HPrime                                       # 14)
+
+
+def pkcs_emsa_pkcs1_v1_5_encode(M, emLen, h): # section 9.2 of RFC 3447
+    """
+    Implements EMSA-PKCS1-V1_5-ENCODE() function described in Sect.
+    9.2 of RFC 3447.
+
+    Input:
+       M    : message to be encode, an octet string
+       emLen: intended length in octets of the encoded message, at least
+              tLen + 11, where tLen is the octet length of the DER encoding
+              T of a certain value computed during the encoding operation.
+       h    : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+              'sha256', 'sha384'). hLen denotes the length in octets of
+              the hash function output.
+
+    Output:
+       encoded message, an octet string of length emLen
+
+    On error, None is returned.
+    """
+    hLen = _hashFuncParams[h][0]                             # 1)
+    hFunc = _hashFuncParams[h][2]
+    H = hFunc(M)
+    hLeadingDigestInfo = _hashFuncParams[h][3]               # 2)
+    T = hLeadingDigestInfo + H
+    tLen = len(T)
+    if emLen < tLen + 11:                                    # 3)
+        _warning("pkcs_emsa_pkcs1_v1_5_encode:"
+                "intended encoded message length too short")
+        return None
+    PS = '\xff'*(emLen - tLen - 3)                           # 4)
+    EM = '\x00' + '\x01' + PS + '\x00' + T                   # 5)
+    return EM                                                # 6)
+
+
+# XXX should add other pgf1 instance in a better fashion.
+
+def create_ca_file(anchor_list, filename):
+    """
+    Concatenate all the certificates (PEM format for the export) in
+    'anchor_list' and write the result to file 'filename'. On success
+    'filename' is returned, None otherwise.
+
+    If you are used to OpenSSL tools, this function builds a CAfile
+    that can be used for certificate and CRL check.
+
+    Also see create_temporary_ca_file().
+    """
+    try:
+        f = open(filename, "w")
+        for a in anchor_list:
+            s = a.output(fmt="PEM")
+            f.write(s)
+        f.close()
+    except:
+        return None
+    return filename
+
+def create_temporary_ca_file(anchor_list):
+    """
+    Concatenate all the certificates (PEM format for the export) in
+    'anchor_list' and write the result to file to a temporary file
+    using mkstemp() from tempfile module. On success 'filename' is
+    returned, None otherwise.
+
+    If you are used to OpenSSL tools, this function builds a CAfile
+    that can be used for certificate and CRL check.
+    """
+    try:
+        f, fname = tempfile.mkstemp()
+        for a in anchor_list:
+            s = a.output(fmt="PEM")
+            l = os.write(f, s)
+        os.close(f)
+    except:
+        return None
+    return fname
+
+def create_temporary_ca_path(anchor_list, folder):
+    """
+    Create a CA path folder as defined in OpenSSL terminology, by
+    storing all certificates in 'anchor_list' list in PEM format
+    under provided 'folder' and then creating the associated links
+    using the hash as usually done by c_rehash.
+
+    Note that you can also include CRL in 'anchor_list'. In that
+    case, they will also be stored under 'folder' and associated
+    links will be created.
+
+    In folder, the files are created with names of the form
+    0...ZZ.pem. If you provide an empty list, folder will be created
+    if it does not already exist, but that's all.
+
+    The number of certificates written to folder is returned on
+    success, None on error.
+    """
+    # We should probably avoid writing duplicate anchors and also
+    # check if they are all certs.
+    try:
+        if not os.path.isdir(folder):
+            os.makedirs(folder)
+    except:
+        return None
+
+    l = len(anchor_list)
+    if l == 0:
+        return None
+    fmtstr = "%%0%sd.pem" % math.ceil(math.log(l, 10))
+    i = 0
+    try:
+        for a in anchor_list:
+            fname = os.path.join(folder, fmtstr % i)
+            f = open(fname, "w")
+            s = a.output(fmt="PEM")
+            f.write(s)
+            f.close()
+            i += 1
+    except:
+        return None
+
+    r,w=popen2.popen2("c_rehash %s" % folder)
+    r.close(); w.close()
+
+    return l
+
+
+#####################################################################
+# Public Key Cryptography related stuff
+#####################################################################
+
+class _EncryptAndVerifyRSA(object):
+    ### Below are encryption methods
+
+    def _rsaep(self, m):
+        """
+        Internal method providing raw RSA encryption, i.e. simple modular
+        exponentiation of the given message representative 'm', a long
+        between 0 and n-1.
+
+        This is the encryption primitive RSAEP described in PKCS#1 v2.1,
+        i.e. RFC 3447 Sect. 5.1.1.
+
+        Input:
+           m: message representative, a long between 0 and n-1, where
+              n is the key modulus.
+
+        Output:
+           ciphertext representative, a long between 0 and n-1
+
+        Not intended to be used directly. Please, see encrypt() method.
+        """
+
+        n = self.modulus
+        if isinstance(m, int):
+            m = long(m)
+        if (not isinstance(m, long)) or m > n-1:
+            _warning("Key._rsaep() expects a long between 0 and n-1")
+            return None
+
+        return self.key.encrypt(m, "")[0]
+
+
+    def _rsaes_pkcs1_v1_5_encrypt(self, M):
+        """
+        Implements RSAES-PKCS1-V1_5-ENCRYPT() function described in section
+        7.2.1 of RFC 3447.
+
+        Input:
+           M: message to be encrypted, an octet string of length mLen, where
+              mLen <= k-11 (k denotes the length in octets of the key modulus)
+
+        Output:
+           ciphertext, an octet string of length k
+
+        On error, None is returned.
+        """
+
+        # 1) Length checking
+        mLen = len(M)
+        k = self.modulusLen / 8
+        if mLen > k - 11:
+            _warning("Key._rsaes_pkcs1_v1_5_encrypt(): message too "
+                    "long (%d > %d - 11)" % (mLen, k))
+            return None
+
+        # 2) EME-PKCS1-v1_5 encoding
+        PS = zerofree_randstring(k - mLen - 3)      # 2.a)
+        EM = '\x00' + '\x02' + PS + '\x00' + M      # 2.b)
+
+        # 3) RSA encryption
+        m = pkcs_os2ip(EM)                          # 3.a)
+        c = self._rsaep(m)                          # 3.b)
+        C = pkcs_i2osp(c, k)                        # 3.c)
+
+        return C                                    # 4)
+
+
+    def _rsaes_oaep_encrypt(self, M, h=None, mgf=None, L=None):
+        """
+        Internal method providing RSAES-OAEP-ENCRYPT as defined in Sect.
+        7.1.1 of RFC 3447. Not intended to be used directly. Please, see
+        encrypt() method for type "OAEP".
+
+        Input:
+           M  : message to be encrypted, an octet string of length mLen
+                where mLen <= k - 2*hLen - 2 (k denotes the length in octets
+                of the RSA modulus and hLen the length in octets of the hash
+                function output)
+           h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+                'sha256', 'sha384'). hLen denotes the length in octets of
+                the hash function output. 'sha1' is used by default if not
+                provided.
+           mgf: the mask generation function f : seed, maskLen -> mask
+           L  : optional label to be associated with the message; the default
+                value for L, if not provided is the empty string
+
+        Output:
+           ciphertext, an octet string of length k
+
+        On error, None is returned.
+        """
+        # The steps below are the one described in Sect. 7.1.1 of RFC 3447.
+        # 1) Length Checking
+                                                    # 1.a) is not done
+        mLen = len(M)
+        if h is None:
+            h = "sha1"
+        if not _hashFuncParams.has_key(h):
+            _warning("Key._rsaes_oaep_encrypt(): unknown hash function %s." % h)
+            return None
+        hLen = _hashFuncParams[h][0]
+        hFun = _hashFuncParams[h][2]
+        k = self.modulusLen / 8
+        if mLen > k - 2*hLen - 2:                   # 1.b)
+            _warning("Key._rsaes_oaep_encrypt(): message too long.")
+            return None
+
+        # 2) EME-OAEP encoding
+        if L is None:                               # 2.a)
+            L = ""
+        lHash = hFun(L)
+        PS = '\x00'*(k - mLen - 2*hLen - 2)         # 2.b)
+        DB = lHash + PS + '\x01' + M                # 2.c)
+        seed = randstring(hLen)                     # 2.d)
+        if mgf is None:                             # 2.e)
+            mgf = lambda x,y: pkcs_mgf1(x,y,h)
+        dbMask = mgf(seed, k - hLen - 1)
+        maskedDB = strxor(DB, dbMask)               # 2.f)
+        seedMask = mgf(maskedDB, hLen)              # 2.g)
+        maskedSeed = strxor(seed, seedMask)         # 2.h)
+        EM = '\x00' + maskedSeed + maskedDB         # 2.i)
+
+        # 3) RSA Encryption
+        m = pkcs_os2ip(EM)                          # 3.a)
+        c = self._rsaep(m)                          # 3.b)
+        C = pkcs_i2osp(c, k)                        # 3.c)
+
+        return C                                    # 4)
+
+
+    def encrypt(self, m, t=None, h=None, mgf=None, L=None):
+        """
+        Encrypt message 'm' using 't' encryption scheme where 't' can be:
+
+        - None: the message 'm' is directly applied the RSAEP encryption
+                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
+                Sect 5.1.1. Simply put, the message undergo a modular
+                exponentiation using the public key. Additionnal method
+                parameters are just ignored.
+
+        -'pkcs': the message 'm' is applied RSAES-PKCS1-V1_5-ENCRYPT encryption
+                scheme as described in section 7.2.1 of RFC 3447. In that
+                context, other parameters ('h', 'mgf', 'l') are not used.
+
+        -'oaep': the message 'm' is applied the RSAES-OAEP-ENCRYPT encryption
+                scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
+                7.1.1. In that context,
+
+                o 'h' parameter provides the name of the hash method to use.
+                  Possible values are "md2", "md4", "md5", "sha1", "tls",
+                  "sha224", "sha256", "sha384" and "sha512". If none is
+                  provided, sha1 is used.
+
+                o 'mgf' is the mask generation function. By default, mgf
+                  is derived from the provided hash function using the
+                  generic MGF1 (see pkcs_mgf1() for details).
+
+                o 'L' is the optional label to be associated with the message.
+                  If not provided, the default value is used, i.e the empty
+                  string. No check is done on the input limitation of the hash
+                  function regarding the size of 'L' (for instance, 2^61 - 1
+                  for SHA-1). You have been warned.
+        """
+
+        if t is None: # Raw encryption
+            m = pkcs_os2ip(m)
+            c = self._rsaep(m)
+            return pkcs_i2osp(c, self.modulusLen/8)
+
+        elif t == "pkcs":
+            return self._rsaes_pkcs1_v1_5_encrypt(m)
+
+        elif t == "oaep":
+            return self._rsaes_oaep_encrypt(m, h, mgf, L)
+
+        else:
+            _warning("Key.encrypt(): Unknown encryption type (%s) provided" % t)
+            return None
+
+    ### Below are verification related methods
+
+    def _rsavp1(self, s):
+        """
+        Internal method providing raw RSA verification, i.e. simple modular
+        exponentiation of the given signature representative 'c', an integer
+        between 0 and n-1.
+
+        This is the signature verification primitive RSAVP1 described in
+        PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.2.
+
+        Input:
+          s: signature representative, an integer between 0 and n-1,
+             where n is the key modulus.
+
+        Output:
+           message representative, an integer between 0 and n-1
+
+        Not intended to be used directly. Please, see verify() method.
+        """
+        return self._rsaep(s)
+
+    def _rsassa_pss_verify(self, M, S, h=None, mgf=None, sLen=None):
+        """
+        Implements RSASSA-PSS-VERIFY() function described in Sect 8.1.2
+        of RFC 3447
+
+        Input:
+           M: message whose signature is to be verified
+           S: signature to be verified, an octet string of length k, where k
+              is the length in octets of the RSA modulus n.
+
+        Output:
+           True is the signature is valid. False otherwise.
+        """
+
+        # Set default parameters if not provided
+        if h is None: # By default, sha1
+            h = "sha1"
+        if not _hashFuncParams.has_key(h):
+            _warning("Key._rsassa_pss_verify(): unknown hash function "
+                    "provided (%s)" % h)
+            return False
+        if mgf is None: # use mgf1 with underlying hash function
+            mgf = lambda x,y: pkcs_mgf1(x, y, h)
+        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
+            hLen = _hashFuncParams[h][0]
+            sLen = hLen
+
+        # 1) Length checking
+        modBits = self.modulusLen
+        k = modBits / 8
+        if len(S) != k:
+            return False
+
+        # 2) RSA verification
+        s = pkcs_os2ip(S)                           # 2.a)
+        m = self._rsavp1(s)                         # 2.b)
+        emLen = math.ceil((modBits - 1) / 8.)       # 2.c)
+        EM = pkcs_i2osp(m, emLen)
+
+        # 3) EMSA-PSS verification
+        Result = pkcs_emsa_pss_verify(M, EM, modBits - 1, h, mgf, sLen)
+
+        return Result                               # 4)
+
+
+    def _rsassa_pkcs1_v1_5_verify(self, M, S, h):
+        """
+        Implements RSASSA-PKCS1-v1_5-VERIFY() function as described in
+        Sect. 8.2.2 of RFC 3447.
+
+        Input:
+           M: message whose signature is to be verified, an octet string
+           S: signature to be verified, an octet string of length k, where
+              k is the length in octets of the RSA modulus n
+           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+                'sha256', 'sha384').
+
+        Output:
+           True if the signature is valid. False otherwise.
+        """
+
+        # 1) Length checking
+        k = self.modulusLen / 8
+        if len(S) != k:
+            _warning("invalid signature (len(S) != k)")
+            return False
+
+        # 2) RSA verification
+        s = pkcs_os2ip(S)                           # 2.a)
+        m = self._rsavp1(s)                         # 2.b)
+        EM = pkcs_i2osp(m, k)                       # 2.c)
+
+        # 3) EMSA-PKCS1-v1_5 encoding
+        EMPrime = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
+        if EMPrime is None:
+            _warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.")
+            return False
+
+        # 4) Comparison
+        return EM == EMPrime
+
+
+    def verify(self, M, S, t=None, h=None, mgf=None, sLen=None):
+        """
+        Verify alleged signature 'S' is indeed the signature of message 'M'
+        using 't' signature scheme where 't' can be:
+
+        - None: the alleged signature 'S' is directly applied the RSAVP1
+                signature primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
+                Sect 5.2.1. Simply put, the provided signature is applied a
+                modular exponentiation using the public key. Then, a comparison
+                of the result is done against 'M'. On match, True is returned.
+                Additional method parameters are just ignored.
+
+        -'pkcs': the alleged signature 'S' and message 'M' are applied
+                RSASSA-PKCS1-v1_5-VERIFY signature verification scheme as
+                described in Sect. 8.2.2 of RFC 3447. In that context, the hash
+                function name is passed using 'h'. Possible values are "md2",
+                "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and
+                "sha512". If none is provided, sha1 is used. Other additional
+                parameters are ignored.
+
+        -'pss': the alleged signature 'S' and message 'M' are applied
+                RSASSA-PSS-VERIFY signature scheme as described in Sect. 8.1.2.
+                of RFC 3447. In that context,
+
+                o 'h' parameter provides the name of the hash method to use.
+                   Possible values are "md2", "md4", "md5", "sha1", "tls",
+                   "sha224", "sha256", "sha384" and "sha512". If None is
+                   provided, sha1 is used.
+
+                o 'mgf' is the mask generation function. By default, mgf
+                   is derived from the provided hash function using the
+                   generic MGF1 (see pkcs_mgf1() for details).
+
+                o 'sLen' is the byte length of the salt. You can overload the
+                  default value (the byte length of the hash value for provided
+                  algorithm) by providing another one with that parameter.
+        """
+        if t is None: # RSAVP1
+            S = pkcs_os2ip(S)
+            n = self.modulus
+            if S > n-1:
+                _warning("Signature to be verified is too long for key modulus")
+                return False
+            m = self._rsavp1(S)
+            if m is None:
+                return False
+            l = int(math.ceil(math.log(m, 2) / 8.)) # Hack
+            m = pkcs_i2osp(m, l)
+            return M == m
+
+        elif t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY
+            if h is None:
+                h = "sha1"
+            return self._rsassa_pkcs1_v1_5_verify(M, S, h)
+
+        elif t == "pss": # RSASSA-PSS-VERIFY
+            return self._rsassa_pss_verify(M, S, h, mgf, sLen)
+
+        else:
+            _warning("Key.verify(): Unknown signature type (%s) provided" % t)
+            return None
+
+class _DecryptAndSignRSA(object):
+    ### Below are decryption related methods. Encryption ones are inherited
+    ### from PubKey
+
+    def _rsadp(self, c):
+        """
+        Internal method providing raw RSA decryption, i.e. simple modular
+        exponentiation of the given ciphertext representative 'c', a long
+        between 0 and n-1.
+
+        This is the decryption primitive RSADP described in PKCS#1 v2.1,
+        i.e. RFC 3447 Sect. 5.1.2.
+
+        Input:
+           c: ciphertest representative, a long between 0 and n-1, where
+              n is the key modulus.
+
+        Output:
+           message representative, a long between 0 and n-1
+
+        Not intended to be used directly. Please, see decrypt() method.
+        """
+
+        n = self.modulus
+        if isinstance(c, int):
+            c = long(c)
+        if (not isinstance(c, long)) or c > n-1:
+            _warning("Key._rsaep() expects a long between 0 and n-1")
+            return None
+
+        return self.key.decrypt(c)
+
+
+    def _rsaes_pkcs1_v1_5_decrypt(self, C):
+        """
+        Implements RSAES-PKCS1-V1_5-DECRYPT() function described in section
+        7.2.2 of RFC 3447.
+
+        Input:
+           C: ciphertext to be decrypted, an octet string of length k, where
+              k is the length in octets of the RSA modulus n.
+
+        Output:
+           an octet string of length k at most k - 11
+
+        on error, None is returned.
+        """
+
+        # 1) Length checking
+        cLen = len(C)
+        k = self.modulusLen / 8
+        if cLen != k or k < 11:
+            _warning("Key._rsaes_pkcs1_v1_5_decrypt() decryption error "
+                    "(cLen != k or k < 11)")
+            return None
+
+        # 2) RSA decryption
+        c = pkcs_os2ip(C)                           # 2.a)
+        m = self._rsadp(c)                          # 2.b)
+        EM = pkcs_i2osp(m, k)                       # 2.c)
+
+        # 3) EME-PKCS1-v1_5 decoding
+
+        # I am aware of the note at the end of 7.2.2 regarding error
+        # conditions reporting but the one provided below are for _local_
+        # debugging purposes. --arno
+
+        if EM[0] != '\x00':
+            _warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
+                    "(first byte is not 0x00)")
+            return None
+
+        if EM[1] != '\x02':
+            _warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
+                    "(second byte is not 0x02)")
+            return None
+
+        tmp = EM[2:].split('\x00', 1)
+        if len(tmp) != 2:
+            _warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
+                    "(no 0x00 to separate PS from M)")
+            return None
+
+        PS, M = tmp
+        if len(PS) < 8:
+            _warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
+                    "(PS is less than 8 byte long)")
+            return None
+
+        return M                                    # 4)
+
+
+    def _rsaes_oaep_decrypt(self, C, h=None, mgf=None, L=None):
+        """
+        Internal method providing RSAES-OAEP-DECRYPT as defined in Sect.
+        7.1.2 of RFC 3447. Not intended to be used directly. Please, see
+        encrypt() method for type "OAEP".
+
+
+        Input:
+           C  : ciphertext to be decrypted, an octet string of length k, where
+                k = 2*hLen + 2 (k denotes the byte length of the RSA modulus
+                and hLen the byte length of the hash function output)
+           h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+                'sha256', 'sha384'). 'sha1' is used if none is provided.
+           mgf: the mask generation function f : seed, maskLen -> mask
+           L  : optional label whose association with the message is to be
+                verified; the default value for L, if not provided is the empty
+                string.
+
+        Output:
+           message, an octet string of length k mLen, where mLen <= k-2*hLen-2
+
+        On error, None is returned.
+        """
+        # The steps below are the one described in Sect. 7.1.2 of RFC 3447.
+
+        # 1) Length Checking
+                                                    # 1.a) is not done
+        if h is None:
+            h = "sha1"
+        if not _hashFuncParams.has_key(h):
+            _warning("Key._rsaes_oaep_decrypt(): unknown hash function %s.", h)
+            return None
+        hLen = _hashFuncParams[h][0]
+        hFun = _hashFuncParams[h][2]
+        k = self.modulusLen / 8
+        cLen = len(C)
+        if cLen != k:                               # 1.b)
+            _warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(cLen != k)")
+            return None
+        if k < 2*hLen + 2:
+            _warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(k < 2*hLen + 2)")
+            return None
+
+        # 2) RSA decryption
+        c = pkcs_os2ip(C)                           # 2.a)
+        m = self._rsadp(c)                          # 2.b)
+        EM = pkcs_i2osp(m, k)                       # 2.c)
+
+        # 3) EME-OAEP decoding
+        if L is None:                               # 3.a)
+            L = ""
+        lHash = hFun(L)
+        Y = EM[:1]                                  # 3.b)
+        if Y != '\x00':
+            _warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(Y is not zero)")
+            return None
+        maskedSeed = EM[1:1+hLen]
+        maskedDB = EM[1+hLen:]
+        if mgf is None:
+            mgf = lambda x,y: pkcs_mgf1(x, y, h)
+        seedMask = mgf(maskedDB, hLen)              # 3.c)
+        seed = strxor(maskedSeed, seedMask)         # 3.d)
+        dbMask = mgf(seed, k - hLen - 1)            # 3.e)
+        DB = strxor(maskedDB, dbMask)               # 3.f)
+
+        # I am aware of the note at the end of 7.1.2 regarding error
+        # conditions reporting but the one provided below are for _local_
+        # debugging purposes. --arno
+
+        lHashPrime = DB[:hLen]                      # 3.g)
+        tmp = DB[hLen:].split('\x01', 1)
+        if len(tmp) != 2:
+            _warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(0x01 separator not found)")
+            return None
+        PS, M = tmp
+        if PS != '\x00'*len(PS):
+            _warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(invalid padding string)")
+            return None
+        if lHash != lHashPrime:
+            _warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(invalid hash)")
+            return None
+        return M                                    # 4)
+
+
+    def decrypt(self, C, t=None, h=None, mgf=None, L=None):
+        """
+        Decrypt ciphertext 'C' using 't' decryption scheme where 't' can be:
+
+        - None: the ciphertext 'C' is directly applied the RSADP decryption
+                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
+                Sect 5.1.2. Simply, put the message undergo a modular
+                exponentiation using the private key. Additionnal method
+                parameters are just ignored.
+
+        - 'pkcs': the ciphertext 'C' is applied RSAES-PKCS1-V1_5-DECRYPT
+                decryption scheme as described in section 7.2.2 of RFC 3447.
+                In that context, other parameters ('h', 'mgf', 'l') are not
+                used.
+
+        - 'oaep': the ciphertext 'C' is applied the RSAES-OAEP-DECRYPT
+                decryption scheme, as described in PKCS#1 v2.1, i.e. RFC 3447
+                Sect 7.1.2. In that context,
+
+                o 'h' parameter provides the name of the hash method to use.
+                  Possible values are "md2", "md4", "md5", "sha1", "tls",
+                  "sha224", "sha256", "sha384" and "sha512". If None is
+                  provided, sha1 is used by default.
+
+                o 'mgf' is the mask generation function. By default, mgf
+                  is derived from the provided hash function using the
+                  generic MGF1 (see pkcs_mgf1() for details).
+
+                o 'L' is the optional label to be associated with the
+                  message. If not provided, the default value is used, i.e
+                  the empty string. No check is done on the input limitation
+                  of the hash function regarding the size of 'L' (for
+                  instance, 2^61 - 1 for SHA-1). You have been warned.
+        """
+        if t is None:
+            C = pkcs_os2ip(C)
+            c = self._rsadp(C)
+            l = int(math.ceil(math.log(c, 2) / 8.)) # Hack
+            return pkcs_i2osp(c, l)
+
+        elif t == "pkcs":
+            return self._rsaes_pkcs1_v1_5_decrypt(C)
+
+        elif t == "oaep":
+            return self._rsaes_oaep_decrypt(C, h, mgf, L)
+
+        else:
+            _warning("Key.decrypt(): Unknown decryption type (%s) provided" % t)
+            return None
+
+    ### Below are signature related methods.
+    ### Verification methods are inherited from PubKey.
+
+    def _rsasp1(self, m):
+        """
+        Internal method providing raw RSA signature, i.e. simple modular
+        exponentiation of the given message representative 'm', an integer
+        between 0 and n-1.
+
+        This is the signature primitive RSASP1 described in PKCS#1 v2.1,
+        i.e. RFC 3447 Sect. 5.2.1.
+
+        Input:
+           m: message representative, an integer between 0 and n-1, where
+              n is the key modulus.
+
+        Output:
+           signature representative, an integer between 0 and n-1
+
+        Not intended to be used directly. Please, see sign() method.
+        """
+        return self._rsadp(m)
+
+
+    def _rsassa_pss_sign(self, M, h=None, mgf=None, sLen=None):
+        """
+        Implements RSASSA-PSS-SIGN() function described in Sect. 8.1.1 of
+        RFC 3447.
+
+        Input:
+           M: message to be signed, an octet string
+
+        Output:
+           signature, an octet string of length k, where k is the length in
+           octets of the RSA modulus n.
+
+        On error, None is returned.
+        """
+
+        # Set default parameters if not provided
+        if h is None: # By default, sha1
+            h = "sha1"
+        if not _hashFuncParams.has_key(h):
+            _warning("Key._rsassa_pss_sign(): unknown hash function "
+                    "provided (%s)" % h)
+            return None
+        if mgf is None: # use mgf1 with underlying hash function
+            mgf = lambda x,y: pkcs_mgf1(x, y, h)
+        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
+            hLen = _hashFuncParams[h][0]
+            sLen = hLen
+
+        # 1) EMSA-PSS encoding
+        modBits = self.modulusLen
+        k = modBits / 8
+        EM = pkcs_emsa_pss_encode(M, modBits - 1, h, mgf, sLen)
+        if EM is None:
+            _warning("Key._rsassa_pss_sign(): unable to encode")
+            return None
+
+        # 2) RSA signature
+        m = pkcs_os2ip(EM)                          # 2.a)
+        s = self._rsasp1(m)                         # 2.b)
+        S = pkcs_i2osp(s, k)                        # 2.c)
+
+        return S                                    # 3)
+
+
+    def _rsassa_pkcs1_v1_5_sign(self, M, h):
+        """
+        Implements RSASSA-PKCS1-v1_5-SIGN() function as described in
+        Sect. 8.2.1 of RFC 3447.
+
+        Input:
+           M: message to be signed, an octet string
+           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls'
+                'sha256', 'sha384').
+
+        Output:
+           the signature, an octet string.
+        """
+
+        # 1) EMSA-PKCS1-v1_5 encoding
+        k = self.modulusLen / 8
+        EM = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
+        if EM is None:
+            _warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode")
+            return None
+
+        # 2) RSA signature
+        m = pkcs_os2ip(EM)                          # 2.a)
+        s = self._rsasp1(m)                         # 2.b)
+        S = pkcs_i2osp(s, k)                        # 2.c)
+
+        return S                                    # 3)
+
+
+    def sign(self, M, t=None, h=None, mgf=None, sLen=None):
+        """
+        Sign message 'M' using 't' signature scheme where 't' can be:
+
+        - None: the message 'M' is directly applied the RSASP1 signature
+                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
+                5.2.1. Simply put, the message undergo a modular exponentiation
+                using the private key. Additional method parameters are just
+                ignored.
+
+        - 'pkcs': the message 'M' is applied RSASSA-PKCS1-v1_5-SIGN signature
+                scheme as described in Sect. 8.2.1 of RFC 3447. In that
+                context, the hash function name is passed using 'h'. Possible
+                values are "md2", "md4", "md5", "sha1", "tls", "sha224",
+                "sha256", "sha384" and "sha512". If none is provided, sha1 is
+                used. Other additional parameters are ignored.
+
+        - 'pss' : the message 'M' is applied RSASSA-PSS-SIGN signature scheme
+                as described in Sect. 8.1.1. of RFC 3447. In that context,
+
+                o 'h' parameter provides the name of the hash method to use.
+                   Possible values are "md2", "md4", "md5", "sha1", "tls",
+                   "sha224", "sha256", "sha384" and "sha512". If None is
+                   provided, sha1 is used.
+
+                o 'mgf' is the mask generation function. By default, mgf
+                   is derived from the provided hash function using the
+                   generic MGF1 (see pkcs_mgf1() for details).
+
+                o 'sLen' is the byte length of the salt. You can overload the
+                  default value (the byte length of the hash value for provided
+                  algorithm) by providing another one with that parameter.
+        """
+
+        if t is None: # RSASP1
+            M = pkcs_os2ip(M)
+            n = self.modulus
+            if M > n-1:
+                _warning("Message to be signed is too long for key modulus")
+                return None
+            s = self._rsasp1(M)
+            if s is None:
+                return None
+            return pkcs_i2osp(s, self.modulusLen/8)
+
+        elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN
+            if h is None:
+                h = "sha1"
+            return self._rsassa_pkcs1_v1_5_sign(M, h)
+
+        elif t == "pss": # RSASSA-PSS-SIGN
+            return self._rsassa_pss_sign(M, h, mgf, sLen)
+
+        else:
+            _warning("Key.sign(): Unknown signature type (%s) provided" % t)
+            return None
+
+
diff --git a/scapy/layers/x509.py b/scapy/layers/x509.py
index 1b4f078d9c9f84dde511f3a459129e3aa72801d6..a5ca38ccbeb6253c9f577e869f0fbcd1fe2e0812 100644
--- a/scapy/layers/x509.py
+++ b/scapy/layers/x509.py
@@ -10,6 +10,7 @@ X.509 certificates.
 
 from scapy.asn1packet import *
 from scapy.asn1fields import *
+from scapy.fields import PacketField
 
 class ASN1P_OID(ASN1_Packet):
     ASN1_codec = ASN1_Codecs.BER
@@ -671,6 +672,15 @@ class ASN1F_X509_SubjectPublicKeyInfoRSA(ASN1F_SEQUENCE):
                             RSAPublicKey)]
         ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
 
+class ASN1F_X509_SubjectPublicKeyInfoECDSA(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_PACKET("subjectPublicKey", ECDSAPublicKey(),
+                            ECDSAPublicKey)]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+
 class ASN1F_X509_SubjectPublicKeyInfo(ASN1F_SEQUENCE):
     def __init__(self, **kargs):
         seq = [ASN1F_PACKET("signatureAlgorithm",
@@ -684,7 +694,7 @@ class ASN1F_X509_SubjectPublicKeyInfo(ASN1F_SEQUENCE):
         if "rsa" in keytype.lower():
             return ASN1F_X509_SubjectPublicKeyInfoRSA().m2i(pkt, x)
         elif keytype == "ecPublicKey":
-            return c,s
+            return ASN1F_X509_SubjectPublicKeyInfoECDSA().m2i(pkt, x)
         else:
             raise Exception("could not parse subjectPublicKeyInfo")
     def dissect(self, pkt, s):
@@ -699,7 +709,8 @@ class ASN1F_X509_SubjectPublicKeyInfo(ASN1F_SEQUENCE):
             pkt.default_fields["subjectPublicKey"] = RSAPublicKey()
             return ASN1F_X509_SubjectPublicKeyInfoRSA().build(pkt)
         elif ktype == "ecPublicKey":
-            return ASN1F_SEQUENCE.build(self, pkt)
+            pkt.default_fields["subjectPublicKey"] = ECDSAPublicKey()
+            return ASN1F_X509_SubjectPublicKeyInfoECDSA().build(pkt)
         else:
             raise Exception("could not build subjectPublicKeyInfo")
 
@@ -708,6 +719,54 @@ class X509_SubjectPublicKeyInfo(ASN1_Packet):
     ASN1_root = ASN1F_X509_SubjectPublicKeyInfo()
 
 
+###### OpenSSL compatibility wrappers ######
+
+#XXX As ECDSAPrivateKey already uses the structure from RFC 5958,
+# and as we would prefer encapsulated RSA private keys to be parsed,
+# this lazy implementation actually supports RSA encoding only.
+# We'd rather call it RSAPrivateKey_OpenSSL than X509_PrivateKeyInfo.
+class RSAPrivateKey_OpenSSL(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_enum_INTEGER("version", 0, ["v1", "v2"]),
+                    ASN1F_PACKET("privateKeyAlgorithm",
+                                 X509_AlgorithmIdentifier(),
+                                 X509_AlgorithmIdentifier),
+                    ASN1F_PACKET("privateKey",
+                                 RSAPrivateKey(),
+                                 RSAPrivateKey,
+                                 explicit_tag=0x04),
+                    ASN1F_optional(
+                        ASN1F_PACKET("parameters", None, ECParameters,
+                                     explicit_tag=0xa0)),
+                    ASN1F_optional(
+                        ASN1F_PACKET("publicKey", None,
+                                     ECDSAPublicKey,
+                                     explicit_tag=0xa1)))
+
+class _PacketFieldRaw(PacketField):
+# We need this hack because ECParameters parsing below must return
+# a Padding payload, and making the ASN1_Packet class have Padding
+# instead of Raw payload would break things...
+    def getfield(self, pkt, s):
+        i = self.m2i(pkt, s)
+        remain = ""
+        if conf.raw_layer in i:
+            r = i[conf.raw_layer]
+            del(r.underlayer.payload)
+            remain = r.load
+        return remain,i
+ 
+class ECDSAPrivateKey_OpenSSL(Packet):
+    name = "ECDSA Params + Private Key"
+    fields_desc = [ _PacketFieldRaw("ecparam",
+                                    ECParameters(),
+                                    ECParameters),
+                    PacketField("privateKey",
+                                ECDSAPrivateKey(),
+                                ECDSAPrivateKey) ]
+
+
 ####### TBSCertificate & Certificate #######
 
 default_issuer = [
diff --git a/scapy/utils.py b/scapy/utils.py
index 2b13fd413810e37eefe58ce432cd30dada35c7e9..211185610594104ca118add41be1187a46a0013f 100644
--- a/scapy/utils.py
+++ b/scapy/utils.py
@@ -440,6 +440,8 @@ except NameError:
 else:
     binrepr = lambda val: bin(val)[2:]
 
+def long_converter(s):
+    return long(s.replace('\n', '').replace(' ', ''), 16)
 
 #########################
 #### Enum management ####
diff --git a/test/cert.uts b/test/cert.uts
new file mode 100644
index 0000000000000000000000000000000000000000..2b01c8b1ada29d91678c6e9b689096104b9ee962
--- /dev/null
+++ b/test/cert.uts
@@ -0,0 +1,380 @@
+# Cert extension - Regression Test Campaign
+
+# Try me with:
+# bash test/run_tests -t test/cert.uts -F
+
+
+########### PKCS helpers ###############################################
+
++ PKCS helpers tests 
+
+= PKCS os2ip basic tests
+pkcs_os2ip('\x00\x00\xff\xff') == 0xffff and pkcs_os2ip('\xff\xff\xff\xff\xff') == 0xffffffffff
+
+= PKCS i2osp basic tests
+pkcs_i2osp(0xffff, 4) == '\x00\x00\xff\xff' and pkcs_i2osp(0xffff, 2) == '\xff\xff' and pkcs_i2osp(0xffffeeee, 3) == '\xff\xff\xee\xee'
+
+
+########### PubKey class ###############################################
+
++ PubKey class tests
+
+= PubKey class : Importing PEM-encoded RSA public key
+x = PubKey("""
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nTEZukS0lLP+yj
+1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ
+2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9/NnIb/wAZwS0
+oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJd
+I8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkm
+TL1SGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvz
+AwIDAQAB
+-----END PUBLIC KEY-----
+""")
+type(x) is PubKeyRSA
+
+= PubKey class : key format is PEM
+x.frmt == "PEM"
+
+= PubKey class : Importing DER-encoded RSA Key
+y = PubKey('0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\x98Wj?\xe9\xd3\x11\x9b\xa4KIK?\xec\xa3\xd6\x03H\x9a\xc1\x08\x7f\xb3\xf6\xc9$\xee\x9d\xc7\x98\xc7\t&\xe1Q9A\x17\x83m\xbd\x8b\xe7\xf1\xcb\x03\xdaO\x98\x87\x90-*\xbf\x06\x03\xb5\x99\xe3\x9cl\xe4\x89\xd9\x85GCo\x0cC\x9e\xbe\xf0*\xdb\xea}\xbc\x8b\'\x17\xe2\x1at\x1fp1D\x08\xe1\xd1\xe7W\xfa\xad\xf2\x8a[\xd8\'\x85\xbd\xfc\xd9\xc8o\xfc\x00g\x04\xb4\xa0\x98\x9f\xfe\xd4\xe4T^\xfb\x1f&\xc0|\x97^\xe4J\x9b\xa7\xe6\xc2(\x8b\xccZv\xa6n\x1fCEL\xa3\xac\x10Y\xa3\x97@\xd6\x8d\xf6\xce\x9b\x85\x06\xb2]#\xc7fR\x9c=\x82\xd7\xf4\x17@Z\xf2Q\x99\x9b\xc5*sA\xb2]\xe5\xce%A6\xbb\xb0\xa22\xed\xcc\xef\xb0L\xe9\x92\xcbM\xca0\xe7\xe6\xd0"i&L\xbdR\x1a\x1c\xf0~)\xcc\x13W\xba\xa7q\xe6\xff\xfaC\x8e\xe2o\x15\xa66\xdaM9.\x02\xee\xca\xa79\xf6\xf1b\x07t\xe8\x95\xdc\xfc\xf8\x06\xcc6;\xf3\x03\x02\x03\x01\x00\x01')
+type(y) is PubKeyRSA
+
+= PubKey class : key format is DER
+y.frmt == "DER"
+
+= PubKey class : checking modulus value
+x.modulus == y.modulus and x.modulus == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L
+
+= PubKey class : checking public exponent value
+x.pubExp == y.pubExp and x.pubExp == 65537L
+
+= PubKey class : Importing PEM-encoded ECDSA public key
+z = PubKey("""
+-----BEGIN PUBLIC KEY-----
+MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE55WjbZjS/88K1kYagsO9wtKifw0IKLp4
+Jd5qtmDF2Zu+xrwrBRT0HBnPweDU+RsFxcyU/QxD9WYORzYarqxbcA==
+-----END PUBLIC KEY-----
+""")
+type(z) is PubKeyECDSA
+
+= PubKey class : checking curve
+z.key.curve.name == "SECP256k1"
+
+= PubKey class : checking point value
+z.key.pubkey.point.x() == 104748656174769496952370005421566518252704263000192720134585149244759951661467L
+
+
+########### PrivKey class ###############################################
+
++ PrivKey class tests
+
+= PrivKey class : Importing PEM-encoded RSA private key
+x = PrivKey("""
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5
+QReDbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0
+H3AxRAjh0edX+q3yilvYJ4W9/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bC
+KIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0Gy
+XeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1SGhzwfinME1e6p3Hm//pDjuJv
+FaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABAoIBAH3KeJZL2hhI/1GX
+NMaU/PfDgFkgmYbxMA8JKusnm/SFjxAwBGnGI6UjBXpBgpQs2Nqm3ZseF9u8hmCK
+vGiCEX2GesCo2mSfmSQxD6RBrMTuQ99UXpxzBIscFnM/Zrs8lPBARGzmF2nI3qPx
+Xtex4ABX5o0Cd4NfZlZjpj96skUoO8+bd3I4OPUFYFFFuv81LoSQ6Hew0a8xtJXt
+KkDp9h1jTGGUOc189WACNoBLH0MGeVoSUfc1++RcC3cypUZ8fNP1OO6GBfv06f5o
+XES4ZbxGYpa+nCfNwb6V2gWbkvaYm7aFn0KWGNZXS1P3OcWv6IWdOmg2CI7MMBLJ
+0LyWVCECgYEAyMJYw195mvHl8VyxJ3HkxeQaaozWL4qhNQ0Kaw+mzD+jYdkbHb3a
+BYghsgEDZjnyOVblC7I+4smvAZJLWJaf6sZ5HAw3zmj1ibCkXx7deoRc/QVcOikl
+3dE/ymO0KGJNiGzJZmxbRS3hTokmVPuxSWW4p5oSiMupFHKa18Uv8DECgYEAwkJ7
+iTOUL6b4e3lQuHQnJbsiQpd+P/bsIPP7kaaHObewfHpfOOtIdtN4asxVFf/PgW5u
+WmBllqAHZYR14DEYIdL+hdLrdvk5nYQ3YfhOnp+haHUPCdEiXrRZuGXjmMA4V0hL
+3HPF5ZM8H80fLnN8Pgn2rIC7CZQ46y4PnoV1nXMCgYBBwCUCF8rkDEWa/ximKo8a
+oNJmAypC98xEa7j1x3KBgnYoHcrbusok9ajTe7F5UZEbZnItmnsuG4/Nm/RBV1OY
+uNgBb573YzjHl6q93IX9EkzCMXc7NS7JrzaNOopOj6OFAtwTR3m89oHMDu8W9jfi
+KgaIHdXkJ4+AuugrstE4gQKBgFK0d1/8g7SeA+Cdz84YNaqMt5NeaDPXbsTA23Qx
+UBU0rYDxoKTdFybv9a6SfA83sCLM31K/A8FTNJL2CDGA9WNBL3fOSs2GYg88AVBG
+pUJHeDK+0748OcPUSPaG+pVIETSn5RRgffq16r0nWYUvSdAn8cuTqw3y+yC1pZS6
+AU8dAoGBAL5QCi0dTWKN3kf3cXaCAnYiWe4Qg2S+SgLE+F1U4Xws2rqAuSvIiuT5
+i5+Mqk9ZCGdoReVbAovJFoRqe7Fj9yWM+b1awGjL0bOTtnqx0iljob6uFyhpl1xg
+W3a3ICJ/ZYLvkgb4IBEteOwWpp37fX57vzhW8EmUV2UX7ve1uNRI
+-----END RSA PRIVATE KEY-----
+""")
+type(x) is PrivKeyRSA
+
+= PrivKey class : checking public attributes
+assert(x.modulus == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L)
+x.pubExp == 65537L
+
+= PrivKey class : checking private attributes
+assert(x.prime1 == 140977881300857803928857666115326329496639762170623218602431133528876162476487960230341078724702018316260690172014674492782486113504117653531825010840338251572887403113276393351318549036549656895326851872473595350667293402676143426484331639796163189182788306480699144107905869179435145810212051656274284113969L)
+assert(x.prime2 == 136413798668820291889092636919077529673097927884427227010121877374504825870002258140616512268521246045642663981036167305976907058413796938050224182519965099316625879807962173794483933183111515251808827349718943344770056106787713032506379905031673992574818291891535689493330517205396872699985860522390496583027L)
+assert(x.exponent1 == 46171616708754015342920807261537213121074749458020000367465429453038710215532257783908950878847126373502288079285334594398328912526548076894076506899568491565992572446455658740752572386903609191774044411412991906964352741123956581870694330173563737928488765282233340389888026245745090096745219902501964298369L)
+assert(x.exponent2 == 58077388505079936284685944662039782610415160654764308528562806086690474868010482729442634318267235411531220690585030443434512729356878742778542733733189895801341155353491318998637269079682889033003797865508917973141494201620317820971253064836562060222814287812344611566640341960495346782352037479526674026269L)
+x.privExp == 15879630313397508329451198152673380989865598204237760057319927734227125481903063742175442230739018051313441697936698689753842471306305671266572085925009572141819112648211571007521954312641597446020984266846581125287547514750428503480880603089110687015181510081018160579576523796170439894692640171752302225125980423560965987469457505107324833137678663960560798216976668670722016960863268272661588745006387723814962668678285659376534048525020951633874488845649968990679414325096323920666486328886913648207836459784281744709948801682209478580185160477801656666089536527545026197569990716720623647770979759861119273292833L
+
+= PrivKey class : Importing PEM-encoded ECDSA private key
+y = PrivKey("""
+-----BEGIN EC PRIVATE KEY-----
+MHQCAQEEIMiRlFoy6046m1NXu911ukXyjDLVgmOXWCKWdQMd8gCRoAcGBSuBBAAK
+oUQDQgAE55WjbZjS/88K1kYagsO9wtKifw0IKLp4Jd5qtmDF2Zu+xrwrBRT0HBnP
+weDU+RsFxcyU/QxD9WYORzYarqxbcA==
+-----END EC PRIVATE KEY-----
+""")
+type(y) is PrivKeyECDSA
+
+= PrivKey class : checking public attributes
+assert(y.key.curve.name == "SECP256k1")
+y.key.privkey.public_key.point.y() == 86290575637772818452062569410092503179882738810918951913926481113065456425840L
+
+= PrivKey class : checking private attributes
+y.key.privkey.secret_multiplier == 90719786431263082134670936670180839782031078050773732489701961692235185651857L
+
+
+########### Cert class ##############################################
+
++ Cert class tests
+
+= Cert class : Importing PEM-encoded X.509 Certificate
+x = Cert("""
+-----BEGIN CERTIFICATE-----
+MIIFEjCCA/qgAwIBAgIJALRecEPnCQtxMA0GCSqGSIb3DQEBBQUAMIG2MQswCQYD
+VQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRcwFQYDVQQK
+Ew5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBOIFNlcnZpY2Vz
+MSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRlMScwJQYJKoZI
+hvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnAwHhcNMDYwNzEzMDczODU5
+WhcNMjYwMzMwMDczODU5WjCBtjELMAkGA1UEBhMCRlIxDjAMBgNVBAgTBVBhcmlz
+MQ4wDAYDVQQHEwVQYXJpczEXMBUGA1UEChMOTXVzaHJvb20gQ29ycC4xHjAcBgNV
+BAsTFU11c2hyb29tIFZQTiBTZXJ2aWNlczElMCMGA1UEAxMcSUtFdjIgWC41MDkg
+VGVzdCBjZXJ0aWZpY2F0ZTEnMCUGCSqGSIb3DQEJARYYaWtldjItdGVzdEBtdXNo
+cm9vbS5jb3JwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nT
+EZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HLA9pPmIeQLSq/
+BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9
+/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA
+1o32zpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLL
+Tcow5+bQImkmTL1SGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3To
+ldz8+AbMNjvzAwIDAQABo4IBHzCCARswHQYDVR0OBBYEFPPYTt6Q9+Zd0s4zzVxW
+jG+XFDFLMIHrBgNVHSMEgeMwgeCAFPPYTt6Q9+Zd0s4zzVxWjG+XFDFLoYG8pIG5
+MIG2MQswCQYDVQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlz
+MRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBO
+IFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRl
+MScwJQYJKoZIhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnCCCQC0XnBD
+5wkLcTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQA2zt0BvXofiVvH
+MWlftZCstQaawej1SmxrAfDB4NUM24NsG+UZI88XA5XM6QolmfyKnNromMLC1+6C
+aFxjq3jC/qdS7ifalFLQVo7ik/te0z6Olo0RkBNgyagWPX2LR5kHe9RvSDuoPIsb
+SHMmJA98AZwatbvEhmzMINJNUoHVzhPeHZnIaBgUBg02XULk/ElidO51Rf3gh8dR
+/kgFQSQT687vs1x9TWD00z0Q2bs2UF3Ob3+NYkEGEo5F9RePQm0mY94CT2xs6WpH
+o060Fo7fVpAFktMWx1vpu+wsEbQAhgGqV0fCR2QwKDIbTrPW/p9HJtJDYVjYdAFx
+r3s7V77y
+-----END CERTIFICATE-----
+""")
+
+= Cert class : Checking version
+x.version == 3
+
+= Cert class : Checking certificate serial number extraction
+x.serial == 0xB45E7043E7090B71
+
+= Cert class : Checking signature algorithm
+x.sigAlg == 'sha1-with-rsa-signature' 
+
+= Cert class : Checking issuer extraction in basic format (/C=FR ...)
+x.issuer_str == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp'
+
+= Cert class : Checking subject extraction in basic format (/C=FR ...)
+x.subject_str == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp'
+
+= Cert class : Checking start date extraction in simple and tuple formats
+assert(x.notBefore_str_simple == '07/13/06')
+x.notBefore == (2006, 7, 13, 7, 38, 59, 3, 194, -1)
+
+= Cert class : Checking end date extraction in simple and tuple formats
+assert(x.notAfter_str_simple == '03/30/26')
+x.notAfter == (2026, 3, 30, 7, 38, 59, 0, 89, -1)
+
+= Cert class : Checking RSA public key
+assert(type(x.pubKey) is PubKeyRSA)
+assert(x.pubKey.modulus == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L)
+x.pubKey.pubExp == 0x10001
+
+= Cert class : Checking extensions
+assert(x.cA)
+assert(x.authorityKeyID == '\xf3\xd8N\xde\x90\xf7\xe6]\xd2\xce3\xcd\\V\x8co\x97\x141K')
+not hasattr(x, "keyUsage")
+
+= Cert class : Importing another PEM-encoded X.509 Certificate
+y = Cert("""
+-----BEGIN CERTIFICATE-----
+MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw
+CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
+ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
+RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
+Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq
+hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf
+Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q
+RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD
+AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY
+JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv
+6pZjamVFkpUBtA==
+-----END CERTIFICATE-----
+""")
+
+= Cert class : Checking ECDSA public key
+assert(type(y.pubKey) is PubKeyECDSA)
+assert(y.pubKey.key.curve.name == 'SECP384r1')
+y.pubKey.key.pubkey.point.x() == 3987178688175281746349180015490646948656137448666005327832107126183726641822596270780616285891030558662603987311874L
+
+= Cert class : Checking ECDSA signature
+y.signatureValue == '0d\x020%\xa4\x81E\x02k\x12KutO\xc8#\xe3p\xf2ur\xde|\x89\xf0\xcf\x91ra\x9e^\x10\x92YV\xb9\x83\xc7\x10\xe78\xe9X&6}\xd5\xe44\x869\x020|6S\xf00\xe5bc:\x99\xe2\xb6\xa3;\x9b4\xfa\x1e\xda\x10\x92q^\x91\x13\xa7\xdd\xa4n\x92\xcc2\xd6\xf5!f\xc7/\xea\x96cjeE\x92\x95\x01\xb4'
+
+
+########### CRL class ###############################################
+
++ CRL class tests
+
+= CRL class : Importing PEM-encoded CRL
+x = CRL("""
+-----BEGIN X509 CRL-----
+MIICHjCCAYcwDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoT
+DlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5Fw0wNjExMDIwMDAwMDBaFw0wNzAyMTcy
+MzU5NTlaMIH2MCECECzSS2LEl6QXzW6jyJx6LcgXDTA0MDQwMTE3NTYxNVowIQIQ
+OkXeVssCzdzcTndjIhvU1RcNMDEwNTA4MTkyMjM0WjAhAhBBXYg2gRUg1YCDRqhZ
+kngsFw0wMTA3MDYxNjU3MjNaMCECEEc5gf/9hIHxlfnrGMJ8DfEXDTAzMDEwOTE4
+MDYxMlowIQIQcFR+auK62HZ/R6mZEEFeZxcNMDIwOTIzMTcwMDA4WjAhAhB+C13e
+GPI5ZoKmj2UiOCPIFw0wMTA1MDgxOTA4MjFaMCICEQDQVEhgGGfTrTXKLw1KJ5Ve
+Fw0wMTEyMTExODI2MjFaMA0GCSqGSIb3DQEBBQUAA4GBACLJ9rsdoaU9JMf/sCIR
+s3AGW8VV3TN2oJgiCGNEac9PRyV3mRKE0hmuIJTKLFSaa4HSAzimWpWNKuJhztsZ
+zXUnWSZ8VuHkgHEaSbKqzUlb2g+o/848CvzJrcbeyEBkDCYJI5C3nLlQA49LGJ+w
+4GUPYBwaZ+WFxCX1C8kzglLm
+-----END X509 CRL-----
+""")
+
+= CRL class : Checking version
+x.version == 1
+
+= CRL class : Checking issuer extraction in basic format (/C=FR ...)
+x.issuer_str == '/C=US/O=VeriSign, Inc./OU=Class 1 Public Primary Certification Authority'
+
+= CRL class : Checking lastUpdate date extraction in tuple format
+x.lastUpdate == (2006, 11, 2, 0, 0, 0, 3, 306, -1)
+
+= CRL class : Checking nextUpdate date extraction in tuple format
+x.nextUpdate == (2007, 2, 17, 23, 59, 59, 5, 48, -1)
+
+= CRL class : Checking number of revoked certificates
+len(x.revoked_cert_serials) == 7 
+
+= CRL class : Checking presence of one revoked certificate
+(94673785334145723688625287778885438961L, '030109180612') in x.revoked_cert_serials
+
+########### High-level methods ###############################################
+
+= Cert class : Checking isIssuerCert()
+c0 = Cert("""
+-----BEGIN CERTIFICATE-----
+MIIFVjCCBD6gAwIBAgIJAJmDv7HOC+iUMA0GCSqGSIb3DQEBCwUAMIHGMQswCQYD
+VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEl
+MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEzMDEGA1UECxMq
+aHR0cDovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvMTQwMgYD
+VQQDEytTdGFyZmllbGQgU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcy
+MB4XDTE1MTAxMzE2NDIzOFoXDTE2MTEzMDIzMzQxOVowPjEhMB8GA1UECxMYRG9t
+YWluIENvbnRyb2wgVmFsaWRhdGVkMRkwFwYDVQQDDBAqLnRvb2xzLmlldGYub3Jn
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAseE36OuC1on62/XCS3fw
+LErecm4+E2DRqGYexK09MmDl8Jm19Hp6SFUh7g45EvnODcr1aWHHBO1uDx07HlCI
+eToOMUEW8bECZGilzfVKCsqZljUIw34nXdCpz/PnKK832LZ73fN+rm6Xf/fKaU7M
+0AbfXSebOxLn5v4Ia1J7ghF8crNG68HoeLgPy+HrvQZEWNyDULKgYlvcgbg24558
+ebKpU4rgC8lKKhM5MRO9LM+ocM+MjT0Bo4iuEgA2HR4kK9152FMBJu0oT8mGlINO
+yOEULoWzr9Ru3WlGr0ElDnqti/KSynnZezJP93fo+bRPI1zUXAOu2Ks6yhNfXV1d
+oQIDAQABo4IBzDCCAcgwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcD
+AQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQDAgWgMDwGA1UdHwQ1MDMwMaAvoC2GK2h0
+dHA6Ly9jcmwuc3RhcmZpZWxkdGVjaC5jb20vc2ZpZzJzMS0xNy5jcmwwWQYDVR0g
+BFIwUDBOBgtghkgBhv1uAQcXATA/MD0GCCsGAQUFBwIBFjFodHRwOi8vY2VydGlm
+aWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvMIGCBggrBgEFBQcB
+AQR2MHQwKgYIKwYBBQUHMAGGHmh0dHA6Ly9vY3NwLnN0YXJmaWVsZHRlY2guY29t
+LzBGBggrBgEFBQcwAoY6aHR0cDovL2NlcnRpZmljYXRlcy5zdGFyZmllbGR0ZWNo
+LmNvbS9yZXBvc2l0b3J5L3NmaWcyLmNydDAfBgNVHSMEGDAWgBQlRYFoUCY4PTst
+LL7Natm2PbNmYzArBgNVHREEJDAighAqLnRvb2xzLmlldGYub3Jngg50b29scy5p
+ZXRmLm9yZzAdBgNVHQ4EFgQUrYq0HAdR15KJB7C3hGIvNlV6X00wDQYJKoZIhvcN
+AQELBQADggEBAAxfzShHiatHrWnTGuRX9BmFpHOFGmLs3PtRRPoOUEbZrcTbaJ+i
+EZpjj4R3eiLITgObcib8+NR1eZsN6VkswZ+rr54aeQ1WzWlsVwBP1t0h9lIbaonD
+wDV6ME3KzfFwwsZWqMBgLin8TcoMadAkXhdfcEKNndKSMsowgEjigP677l24nHf/
+OcnMftgErmTm+jEdW1wUooJoWgbt8TT2uWD8MC62sIIgSQ6miKtg7LhCC1ScyVuN
+Erk3YzF8mPwouOcnNOKsUnkDXLA2REMedVp48c4ikjLClu6AcIg03ZU+o8fLNqcZ
+zd1s7DbacrRSSQ+nXDTodqw1HB+77u0RFs0=
+-----END CERTIFICATE-----
+""")
+c1 = Cert("""
+-----BEGIN CERTIFICATE-----
+MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
+ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw
+MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
+aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk
+dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg
+Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF
+pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE
+3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV
+Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+
+MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX
+v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB
+Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+
+zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB
+BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo
+LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo
+LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF
+BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv
+MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN
+QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0
+rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO
+eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ
+sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ
+7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7
+-----END CERTIFICATE-----
+""")
+c2 = Cert("""
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
+ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
+MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
+aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
+Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
+nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
+HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
+Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
+dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
+HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
+CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
+sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
+4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
+8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
+pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
+mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
+-----END CERTIFICATE-----
+""")
+c0.isIssuerCert(c1) and c1.isIssuerCert(c2) and not c0.isIssuerCert(c2)
+
+= Cert class : Checking isSelfSigned()
+c2.isSelfSigned() and not c1.isSelfSigned() and not c0.isSelfSigned()
+
+= PubKey class : Checking verifyCert()
+c2.pubKey.verifyCert(c2) and c1.pubKey.verifyCert(c0)
+
+= Chain class : Checking chain construction
+assert(len(Chain([c0, c1, c2])) == 3)
+assert(len(Chain([c0], c1)) == 2)
+len(Chain([c0], c2)) == 1
+
+= Chain class : Checking chain verification
+assert(Chain([], c0).verifyChain([c2], [c1]))
+not Chain([c1]).verifyChain([c0])
+