diff --git a/appveyor.yml b/appveyor.yml
index c8300abacf9d4d2ecb7da78ec35e0d0a542d32e5..351cf4ef960119081e10177f54cd114f2891a219 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -29,7 +29,7 @@ test_script:
 
   # Secondary unit tests
   - 'del test\bpf.uts' # Don't bother with BPF regression tests
-  - "for %%t in (test\\*.uts) do (%PYTHON%\\python -m coverage run -a bin\\UTscapy -f text -t %%t -F -K combined_modes || exit /b 42)"
+  - "for %%t in (test\\*.uts) do (%PYTHON%\\python -m coverage run -a bin\\UTscapy -f text -t %%t -F -K combined_modes_ccm || exit /b 42)"
   
   # Contrib unit tests
   - "for %%t in (scapy\\contrib\\*.uts) do (%PYTHON%\\python -m coverage run -a bin\\UTscapy -f text -t %%t -F -P \"load_contrib(\'%%~nt\')\"  || exit /b 42)"
diff --git a/doc/scapy/installation.rst b/doc/scapy/installation.rst
index 9272bd4703ab57937265c01d3906cd116f2650f0..ac5ab68bf76f8fed5cae380346e6f12d78da1e71 100644
--- a/doc/scapy/installation.rst
+++ b/doc/scapy/installation.rst
@@ -166,11 +166,13 @@ Here are the topics involved and some examples that you can use to try if your i
      >>> enc=rdpcap("weplab-64bit-AA-managed.pcap")
      >>> enc.show()
      >>> enc[0]
-      >>> conf.wepkey="AA\x00\x00\x00"
-      >>> dec=Dot11PacketList(enc).toEthernet()
-      >>> dec.show()
-      >>> dec[0]
+     >>> conf.wepkey="AA\x00\x00\x00"
+     >>> dec=Dot11PacketList(enc).toEthernet()
+     >>> dec.show()
+     >>> dec[0]
  
+* PKI operations and TLS decryption. `cryptography <https://cryptography.io>` is also needed.
+
 * Fingerprinting. ``nmap_fp()`` needs `Nmap <http://nmap.org>`_. You need an `old version <http://nmap.org/dist-old/>`_ (before v4.23) that still supports first generation fingerprinting.
 
   .. code-block:: python 
@@ -187,8 +189,6 @@ Here are the topics involved and some examples that you can use to try if your i
  
 * VOIP. ``voip_play()`` needs `SoX <http://sox.sourceforge.net/>`_.
  
-* IPsec Crypto Support. ``SecurityAssociation()`` needs `Pycrypto 2.7a1 <https://github.com/dlitz/pycrypto>`_. Combined AEAD modes such as GCM and CCM are not available yet because of cryptography restrictions.
-
 Platform-specific instructions
 ==============================
 
@@ -207,7 +207,13 @@ Debian/Ubuntu
 
 Just use the standard packages::
 
-$ sudo apt-get install tcpdump graphviz imagemagick python-gnuplot python-crypto python-pyx 
+$ sudo apt-get install tcpdump graphviz imagemagick python-gnuplot python-cryptography python-pyx
+
+Scapy optionally uses python-cryptography v1.7 or later. It has not been packaged for ``apt`` in less recent OS versions (e.g. Debian Jessie). If you need the cryptography-related methods, you may install the library with:
+
+.. code-block:: text
+
+    # pip install cryptography
 
 Fedora
 ------
@@ -226,7 +232,7 @@ Some optional packages:
 
 .. code-block:: text
 
-    # yum install graphviz python-crypto sox PyX gnuplot numpy
+    # yum install graphviz python-cryptography sox PyX gnuplot numpy
     # cd /tmp
     # wget http://heanet.dl.sourceforge.net/sourceforge/gnuplot-py/gnuplot-py-1.8.tar.gz
     # tar xvfz gnuplot-py-1.8.tar.gz
@@ -288,11 +294,11 @@ Here's how to install Scapy on OpenBSD 5.9+
 Optional packages (OpenBSD only)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-py-crypto
+py-cryptography
 
 .. code-block:: text
 
- # pkg_add py-crypto
+ # pkg_add py-cryptography
 
 gnuplot and its Python binding: 
 
diff --git a/scapy/config.py b/scapy/config.py
index fe541ed5705be605d90ea517eead7de7ce2a070d..6f1f9787569350b1842f3d77f3d1f650d0d034da 100755
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -4,7 +4,7 @@
 ## This program is published under a GPLv2 license
 
 """
-Implementation for of the configuration object.
+Implementation of the configuration object.
 """
 
 import os,time,socket,sys
@@ -279,6 +279,18 @@ class LogLevel(object):
         obj._logLevel = val
         
 
+def isCryptographyValid():
+    """
+    Check if the cryptography library is present, and if it is recent enough
+    (v1.7 or later).
+    """
+    try:
+        import cryptography
+    except ImportError:
+        return False
+    from distutils.version import LooseVersion
+    return LooseVersion(cryptography.__version__) >= LooseVersion("1.7")
+
 
 def _prompt_changer(attr,val):
     prompt = conf.prompt
@@ -396,6 +408,7 @@ contribs: a dict which can be used by contrib layers to store local configuratio
                    "tftp", "x509", "bluetooth", "dhcp6", "llmnr",
                    "sctp", "vrrp", "ipsec", "lltd", "vxlan"]
     contribs = dict()
+    crypto_valid = isCryptographyValid()
 
 
 if not Conf.ipv6_enabled:
@@ -404,7 +417,23 @@ if not Conf.ipv6_enabled:
         if m in Conf.load_layers:
             Conf.load_layers.remove(m)
     
+if not Conf.crypto_valid:
+    log_scapy.warning("Crypto-related methods disabled for IPsec, Dot11 "
+                      "and TLS layers (needs python-cryptography v1.7+).")
 
 conf=Conf()
 conf.logLevel=30 # 30=Warning
 
+
+def crypto_validator(func):
+    """
+    This a decorator to be used for any method relying on the cryptography library.
+    Its behaviour depends on the 'crypto_valid' attribute of the global 'conf'.
+    """
+    def func_in(*args, **kwargs):
+        if not conf.crypto_valid:
+            raise ImportError("Cannot execute crypto-related method! "
+                              "Please install python-cryptography v1.7 or later.")
+        return func(*args, **kwargs)
+    return func_in
+
diff --git a/scapy/layers/dot11.py b/scapy/layers/dot11.py
index f6f92b773d77dbfc90c01374582ecb6601b605c0..8474412e4898af07273d2582557e40dc63672df7 100644
--- a/scapy/layers/dot11.py
+++ b/scapy/layers/dot11.py
@@ -10,7 +10,7 @@ Wireless LAN according to IEEE 802.11.
 import re,struct
 from zlib import crc32
 
-from scapy.config import conf
+from scapy.config import conf, crypto_validator
 from scapy.data import *
 from scapy.packet import *
 from scapy.fields import *
@@ -20,14 +20,12 @@ from scapy.layers.l2 import *
 from scapy.layers.inet import IP, TCP
 
 
-try:
+if conf.crypto_valid:
     from cryptography.hazmat.backends import default_backend
-    from cryptography.hazmat.primitives.ciphers import (
-        Cipher,
-        algorithms,
-    )
-except ImportError:
-    log_loading.info("Can't import python cryptography lib. Won't be able to decrypt WEP.")
+    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
+else:
+    default_backend = Ciphers = algorithms = None
+    log_loading.info("Can't import python-cryptography v1.7+. Disabled WEP decryption/encryption.")
 
 
 ### Fields
@@ -324,6 +322,18 @@ class Dot11WEP(Packet):
                     StrField("wepdata",None,remain=4),
                     IntField("icv",None) ]
 
+    @crypto_validator
+    def decrypt(self, key=None):
+        if key is None:
+            key = conf.wepkey
+        if key:
+            d = Cipher(
+                algorithms.ARC4(self.iv + key),
+                None,
+                default_backend(),
+            ).decryptor()
+            self.add_payload(LLC(d.update(self.wepdata) + d.finalize()))
+
     def post_dissect(self, s):
         self.decrypt()
 
@@ -332,36 +342,30 @@ class Dot11WEP(Packet):
             return Packet.build_payload(self)
         return ""
 
-    def post_build(self, p, pay):
-        if self.wepdata is None:
-            key = conf.wepkey
-            if key:
-                if self.icv is None:
-                    pay += struct.pack("<I",crc32(pay))
-                    icv = ""
-                else:
-                    icv = p[4:8]
-                e = Cipher(
-                    algorithms.ARC4(self.iv+key),
-                    None,
-                    default_backend(),
-                ).encryptor()
-                p = p[:4]+e.update(pay)+e.finalize()+icv
-            else:
-                warning("No WEP key set (conf.wepkey).. strange results expected..")
-        return p
-            
-
-    def decrypt(self,key=None):
+    @crypto_validator
+    def encrypt(self, p, pay, key=None):
         if key is None:
             key = conf.wepkey
         if key:
-            d = Cipher(
-                algorithms.ARC4(self.iv+key),
+            if self.icv is None:
+                pay += struct.pack("<I", crc32(pay))
+                icv = ""
+            else:
+                icv = p[4:8]
+            e = Cipher(
+                algorithms.ARC4(self.iv + key),
                 None,
                 default_backend(),
-            ).decryptor()
-            self.add_payload(LLC(d.update(self.wepdata)+d.finalize()))
+            ).encryptor()
+            return p[:4] + e.update(pay) + e.finalize() + icv
+        else:
+            warning("No WEP key set (conf.wepkey).. strange results expected..")
+            return None
+
+    def post_build(self, p, pay):
+        if self.wepdata is None:
+            p = self.encrypt(p, pay)
+        return p
 
 
 bind_layers( PrismHeader,   Dot11,         )
diff --git a/scapy/layers/ipsec.py b/scapy/layers/ipsec.py
index dd9f5a9b7f76bbde36fe2e008ec0111ec7cfdef9..9153ac7c4494af93fabf0b313270241afe75a137 100644
--- a/scapy/layers/ipsec.py
+++ b/scapy/layers/ipsec.py
@@ -44,19 +44,15 @@ import os
 import socket
 import struct
 
-from scapy.error import warning
-
+from scapy.config import conf, crypto_validator
 from scapy.data import IP_PROTOS
 from scapy.error import log_loading
-
-from scapy.fields import ByteEnumField, ByteField, StrField, XIntField, IntField, \
-    ShortField, PacketField
-
+from scapy.fields import (ByteEnumField, ByteField, StrField, XIntField,
+                          IntField, ShortField, PacketField)
 from scapy.packet import Packet, bind_layers, Raw
-
 from scapy.layers.inet import IP, UDP
-from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \
-    IPv6ExtHdrRouting
+from scapy.layers.inet6 import (IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt,
+                                IPv6ExtHdrRouting)
 
 
 #------------------------------------------------------------------------------
@@ -142,26 +138,20 @@ class _ESPPlain(Packet):
         return str(self.data) + self.padding + chr(self.padlen) + chr(self.nh)
 
 #------------------------------------------------------------------------------
-try:
+if conf.crypto_valid:
     from cryptography.exceptions import InvalidTag
     from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives import interfaces
     from cryptography.hazmat.primitives.ciphers import (
         Cipher,
         algorithms,
         modes,
     )
-except ImportError:
-    log_loading.info("Can't import python cryptography lib. "
+else:
+    log_loading.info("Can't import python-cryptography v1.7+. "
                      "Disabled IPsec encryption/authentication.")
-    algorithms = None
-    Cipher = None
-    modes = None
-
-try:
-    from Crypto.Cipher.AES import MODE_GCM
-    from Crypto.Cipher.AES import MODE_CCM
-except ImportError:
-    warning("Combined crypto modes not available for IPsec (pycrypto 2.7a1 required).")
+    InvalidTag = default_backend = interfaces = None
+    Cipher = algorithms = modes = None
 
 #------------------------------------------------------------------------------
 def _lcm(a, b):
@@ -201,8 +191,9 @@ class CryptAlgo(object):
         self.mode = mode
         self.icv_size = icv_size
 
-        if self.mode is not None:
-            self.is_aead = issubclass(self.mode, modes.ModeWithAuthenticationTag)
+        if modes and self.mode is not None:
+            self.is_aead = issubclass(self.mode,
+                                      modes.ModeWithAuthenticationTag)
         else:
             self.is_aead = False
 
@@ -248,6 +239,7 @@ class CryptAlgo(object):
         # XXX: random bytes for counters, so it is not wrong to do it that way
         return os.urandom(self.iv_size - self.salt_size)
 
+    @crypto_validator
     def new_cipher(self, key, iv, digest=None):
         """
         @param key:    the secret key, a byte string
@@ -430,15 +422,13 @@ if algorithms:
                                     mode=modes.CBC)
 
 #------------------------------------------------------------------------------
-try:
+if conf.crypto_valid:
     from cryptography.hazmat.primitives.hmac import HMAC
     from cryptography.hazmat.primitives.cmac import CMAC
     from cryptography.hazmat.primitives import hashes
-except ImportError:
+else:
     # no error if cryptography is not available but authentication won't be supported
-    HMAC = None
-    CMAC = None
-    hashes = None
+    HMAC = CMAC = hashes = None
 
 #------------------------------------------------------------------------------
 class IPSecIntegrityError(Exception):
@@ -478,6 +468,7 @@ class AuthAlgo(object):
             raise TypeError('invalid key size %s, must be one of %s' %
                             (len(key), self.key_size))
 
+    @crypto_validator
     def new_mac(self, key):
         """
         @param key:    a byte string
diff --git a/scapy/layers/tls/__init__.py b/scapy/layers/tls/__init__.py
index 98ee163d88ece95c6548dff00d36d276d714d492..2f20630887846cc99eb5259ebc8ed45c5d589353 100644
--- a/scapy/layers/tls/__init__.py
+++ b/scapy/layers/tls/__init__.py
@@ -7,12 +7,12 @@
 Tools for handling TLS sessions and digital certificates.
 """
 
-try:
-    import cryptography
-except ImportError:
+from scapy.config import conf
+
+if not conf.crypto_valid:
     import logging
     log_loading = logging.getLogger("scapy.loading")
-    log_loading.info("Can't import python cryptography lib. Disabled certificate manipulation tools")
+    log_loading.info("Can't import python-cryptography v1.7+. Disabled PKCS #1 signing/verifying.")
 
 try:
     import ecdsa
diff --git a/scapy/layers/tls/cert.py b/scapy/layers/tls/cert.py
index 56e505fb290287e45829e8ee69a3726d8b324a5c..8d81097e0668b2d7938dfb7e5816061966d65b41 100644
--- a/scapy/layers/tls/cert.py
+++ b/scapy/layers/tls/cert.py
@@ -29,9 +29,13 @@ Supports both RSA and ECDSA objects.
 import base64, os, time
 
 import ecdsa
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives.asymmetric import rsa
-from cryptography.hazmat.primitives import hashes
+
+from scapy.config import conf, crypto_validator
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.asymmetric import rsa
+else:
+    default_backend = rsa = None
 
 from scapy.layers.tls.crypto.curves import import_curve
 from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip, pkcs_i2osp, mapHashFunc
@@ -199,7 +203,7 @@ class _PubKeyFactory(_PKIObjMaker):
                 obj.__class__ = PubKeyECDSA
                 obj.updateWith(spki)
             else:
-                raise Exception("Unsupported publicKey type")
+                raise
             marker = "PUBLIC KEY"
         except:
             try:
@@ -241,6 +245,7 @@ class PubKeyRSA(_PKIObj, PubKey, _EncryptAndVerifyRSA):
     Wrapper for RSA keys based on _EncryptAndVerifyRSA from crypto/pkcs1.py
     Use the 'key' attribute to access original object.
     """
+    @crypto_validator
     def updateWith(self, pubkey):
         self.modulus    = pubkey.modulus.val
         self.modulusLen = len(binrepr(pubkey.modulus.val))
@@ -391,6 +396,7 @@ 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.
     """
+    @crypto_validator
     def updateWith(self, privkey):
         self.modulus     = privkey.modulus.val
         self.modulusLen  = len(binrepr(privkey.modulus.val))
diff --git a/scapy/layers/tls/crypto/pkcs1.py b/scapy/layers/tls/crypto/pkcs1.py
index 3ae21e543cc1b70374d00107127c24e77b7158f6..956268e79adaa7078a8f00da58365df2b7e7c015 100644
--- a/scapy/layers/tls/crypto/pkcs1.py
+++ b/scapy/layers/tls/crypto/pkcs1.py
@@ -10,10 +10,14 @@ PKCS #1 methods as defined in RFC 3447.
 import os, popen2, tempfile
 import math, random, struct
 
-from cryptography.exceptions import InvalidSignature
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives import hashes
-from cryptography.hazmat.primitives.asymmetric import padding
+from scapy.config import conf, crypto_validator
+if conf.crypto_valid:
+    from cryptography.exceptions import InvalidSignature
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives import hashes
+    from cryptography.hazmat.primitives.asymmetric import padding
+else:
+    InvalidSignature = dafault_backend = hashes = padding = None
 
 
 #####################################################################
@@ -108,40 +112,44 @@ def pkcs_ilen(n):
 #   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
-def _hashWrapper(hash_algo, message, backend=default_backend()):
-    digest = hashes.Hash(hash_algo, backend).update(message)
-    return digest.finalize()
-
-_hashFuncParams = {
-    "md5"    : (16,
-                hashes.MD5,
-                lambda x: _hashWrapper(hashes.MD5, x),
-                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
-    "sha1"   : (20,
-                hashes.SHA1,
-                lambda x: _hashWrapper(hashes.SHA1, x),
-                '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
-    "sha224" : (28,
-                hashes.SHA224,
-                lambda x: _hashWrapper(hashes.SHA224, x),
-                '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c'),
-    "sha256" : (32,
-                hashes.SHA256,
-                lambda x: _hashWrapper(hashes.SHA256, x),
-                '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'),
-    "sha384" : (48,
-                hashes.SHA384,
-                lambda x: _hashWrapper(hashes.SHA384, x),
-                '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'),
-    "sha512" : (64,
-                hashes.SHA512,
-                lambda x: _hashWrapper(hashes.SHA512, x),
-                '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'),
-    "tls"    : (36,
-                None,
-                lambda x: _hashWrapper(hashes.MD5, x) + _hashWrapper(hashes.SHA1, x),
-                '')
-    }
+
+_hashFuncParams = {}
+if conf.crypto_valid:
+
+    def _hashWrapper(hash_algo, message, backend=default_backend()):
+        digest = hashes.Hash(hash_algo, backend).update(message)
+        return digest.finalize()
+
+    _hashFuncParams = {
+        "md5"    : (16,
+                    hashes.MD5,
+                    lambda x: _hashWrapper(hashes.MD5, x),
+                    '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
+        "sha1"   : (20,
+                    hashes.SHA1,
+                    lambda x: _hashWrapper(hashes.SHA1, x),
+                    '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
+        "sha224" : (28,
+                    hashes.SHA224,
+                    lambda x: _hashWrapper(hashes.SHA224, x),
+                    '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c'),
+        "sha256" : (32,
+                    hashes.SHA256,
+                    lambda x: _hashWrapper(hashes.SHA256, x),
+                    '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'),
+        "sha384" : (48,
+                    hashes.SHA384,
+                    lambda x: _hashWrapper(hashes.SHA384, x),
+                    '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'),
+        "sha512" : (64,
+                    hashes.SHA512,
+                    lambda x: _hashWrapper(hashes.SHA512, x),
+                    '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'),
+        "tls"    : (36,
+                    None,
+                    lambda x: _hashWrapper(hashes.MD5, x) + _hashWrapper(hashes.SHA1, x),
+                    '')
+        }
 
 def mapHashFunc(hashStr):
     try:
@@ -424,6 +432,7 @@ def create_temporary_ca_path(anchor_list, folder):
 #####################################################################
 
 class _EncryptAndVerifyRSA(object):
+    @crypto_validator
     def encrypt(self, m, t=None, h=None, mgf=None, L=None):
         """
         Encrypt message 'm' using 't' encryption scheme where 't' can be:
@@ -484,6 +493,7 @@ class _EncryptAndVerifyRSA(object):
             _warning("Key.encrypt(): Unknown encryption type (%s) provided" % t)
             return None
 
+    @crypto_validator
     def verify(self, M, S, t=None, h=None, mgf=None, sLen=None):
         """
         Verify alleged signature 'S' is indeed the signature of message 'M'
diff --git a/test/ipsec.uts b/test/ipsec.uts
index 9707dce8b4c09a4d851c0abbbd24e4a92a4feed7..c271cc8ee3ee1a1cb0f4f2d63be4dc8a96bb6c8a 100644
--- a/test/ipsec.uts
+++ b/test/ipsec.uts
@@ -2491,7 +2491,6 @@ assert(d[TCP] == p[TCP])
 
 #######################################
 = IPv4 / ESP - Tunnel - AES-GCM - NULL
-~ combined_modes
 
 p = IP(src='1.1.1.1', dst='2.2.2.2')
 p /= TCP(sport=45012, dport=80)
@@ -2526,7 +2525,7 @@ assert(d == p)
 
 #######################################
 = IPv4 / ESP - Tunnel - AES-CCM - NULL
-~ combined_modes combined_modes_ccm
+~ combined_modes_ccm
 
 p = IP(src='1.1.1.1', dst='2.2.2.2')
 p /= TCP(sport=45012, dport=80)