workaround for application bundling tools (#3235)

* cx_freeze support for default_backend

* updated tabing to spaces

* corrected spacing

* moved finding backend to backends __init__

* update to check to see if sys is frozen

* corrected pep8 issues

* update based on comments

* changes to simplify, support testing, and improve comments

* add changelog entry

* right, coverage. I remember now. Time for some contortions.

* updated with review feedback
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 9ca4c12..f6c0a3a 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -22,6 +22,8 @@
   support to :class:`~cryptography.x509.CertificateRevocationList`.
 * Added support for :class:`~cryptography.hazmat.primitives.kdf.scrypt.Scrypt`
   when using OpenSSL 1.1.0.
+* Added a workaround to improve compatibility with Python application bundling
+  tools like ``PyInstaller`` and ``cx_freeze``.
 * Added support for generating a
   :meth:`~cryptography.x509.random_serial_number`.
 * Added support for encoding ``IPv4Network`` and ``IPv6Network`` in X.509
diff --git a/src/cryptography/hazmat/backends/__init__.py b/src/cryptography/hazmat/backends/__init__.py
index e66f9e1..161a187 100644
--- a/src/cryptography/hazmat/backends/__init__.py
+++ b/src/cryptography/hazmat/backends/__init__.py
@@ -16,7 +16,7 @@
     global _available_backends_list
 
     if _available_backends_list is None:
-        _available_backends_list = [
+        entry_point_backends = [
             # DeprecatedIn16
             # setuptools 11.3 deprecated support for the require parameter to
             # load(), and introduced the new resolve() method instead.
@@ -29,8 +29,38 @@
             )
         ]
 
+        _available_backends_list = _backend_import_fallback(
+            entry_point_backends
+        )
+
     return _available_backends_list
 
+
+def _backend_import_fallback(backends):
+    # If backends already exist just return them. This branch is here
+    # to get full line coverage from our tests.
+    if backends:
+        return backends
+
+    # if iter_entry_points fails to find any backends then manually try to
+    # import our current backends as a workaround for issues with application
+    # bundlers like pyinstaller, cx_freeze, etc
+
+    # OpenSSL is guaranteed to be present until we unbundle the backends.
+    from cryptography.hazmat.backends.openssl.backend import backend as be_ossl
+    backends = [be_ossl]
+    try:
+        from cryptography.hazmat.backends.commoncrypto.backend import (
+            backend as be_cc
+        )
+    except ImportError:
+        pass
+    else:
+        backends.append(be_cc)
+
+    return backends
+
+
 _default_backend = None
 
 
diff --git a/tests/hazmat/backends/test_backendinit.py b/tests/hazmat/backends/test_backendinit.py
new file mode 100644
index 0000000..b930f99
--- /dev/null
+++ b/tests/hazmat/backends/test_backendinit.py
@@ -0,0 +1,17 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+from cryptography.hazmat.backends import _backend_import_fallback
+
+
+def test_backend_import_fallback_empty_backends():
+    backends = _backend_import_fallback([])
+    assert len(backends) >= 1
+
+
+def test_backend_import_fallback_existing_backends():
+    backend_list = [1, 2, 3, 4]
+    assert backend_list == _backend_import_fallback(backend_list)