diff options
| -rw-r--r-- | docs/certinstall.rst | 17 | ||||
| -rw-r--r-- | libmproxy/cmdline.py | 2 | ||||
| -rw-r--r-- | libmproxy/models/connections.py | 13 | ||||
| -rw-r--r-- | libmproxy/proxy/config.py | 8 | ||||
| -rw-r--r-- | test/test_proxy.py | 6 | ||||
| -rw-r--r-- | test/test_server.py | 9 | 
6 files changed, 42 insertions, 13 deletions
diff --git a/docs/certinstall.rst b/docs/certinstall.rst index 5ec7b7ce..5a8cce64 100644 --- a/docs/certinstall.rst +++ b/docs/certinstall.rst @@ -175,10 +175,21 @@ no such file exists, it will be generated automatically.  Using a client side certificate  ------------------------------- -You can use a client certificate by passing the ``--client-certs DIRECTORY`` option to mitmproxy. +You can use a client certificate by passing the ``--client-certs DIRECTORY|FILE`` +option to mitmproxy. Using a directory allows certs to be selected based on +hostname, while using a filename allows a single specific certificate to be used for +all SSL connections. Certificate files must be in the PEM format and should +contain both the unencrypted private key and the certificate. + +Multiple certs by Hostname +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you've specified a directory to ``--client-certs``, then the following +behavior will be taken: +  If you visit example.org, mitmproxy looks for a file named ``example.org.pem`` in the specified -directory and uses this as the client cert. The certificate file needs to be in the PEM format and -should contain both the unencrypted private key and the certificate. +directory and uses this as the client cert. +  .. _Certificate Pinning: http://security.stackexchange.com/questions/29988/what-is-certificate-pinning/
\ No newline at end of file diff --git a/libmproxy/cmdline.py b/libmproxy/cmdline.py index 16678486..99b76e68 100644 --- a/libmproxy/cmdline.py +++ b/libmproxy/cmdline.py @@ -407,7 +407,7 @@ def proxy_ssl_options(parser):      group.add_argument(          "--client-certs", action="store",          type=str, dest="clientcerts", default=None, -        help="Client certificate directory." +        help="Client certificate file or directory."      )      group.add_argument(          "--no-upstream-cert", default=False, diff --git a/libmproxy/models/connections.py b/libmproxy/models/connections.py index 3aa522ea..0991955d 100644 --- a/libmproxy/models/connections.py +++ b/libmproxy/models/connections.py @@ -174,11 +174,14 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):      def establish_ssl(self, clientcerts, sni, **kwargs):          clientcert = None          if clientcerts: -            path = os.path.join( -                clientcerts, -                self.address.host.encode("idna")) + ".pem" -            if os.path.exists(path): -                clientcert = path +            if os.path.isfile(clientcerts): +                clientcert = clientcerts +            else: +                path = os.path.join( +                    clientcerts, +                    self.address.host.encode("idna")) + ".pem" +                if os.path.exists(path): +                    clientcert = path          self.convert_to_ssl(cert=clientcert, sni=sni, **kwargs)          self.sni = sni diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py index c7b51311..f06e55ca 100644 --- a/libmproxy/proxy/config.py +++ b/libmproxy/proxy/config.py @@ -133,10 +133,12 @@ def process_proxy_options(parser, options):      if options.clientcerts:          options.clientcerts = os.path.expanduser(options.clientcerts) -        if not os.path.exists(options.clientcerts) or not os.path.isdir(options.clientcerts): +        if not (os.path.exists(options.clientcerts) or +                os.path.isdir(options.clientcerts) or +                os.path.isfile(options.clientcerts)):              return parser.error( -                "Client certificate directory does not exist or is not a directory: %s" % -                options.clientcerts +                "Client certificate argument is not a file or directory, " +                "or does not exist: %s" % options.clientcerts              )      if options.auth_nonanonymous or options.auth_singleuser or options.auth_htpasswd: diff --git a/test/test_proxy.py b/test/test_proxy.py index b3e7258a..a0530c79 100644 --- a/test/test_proxy.py +++ b/test/test_proxy.py @@ -1,3 +1,4 @@ +import os  import mock  from OpenSSL import SSL @@ -99,8 +100,11 @@ class TestProcessProxyOptions:      def test_client_certs(self):          with tutils.tmpdir() as cadir:              self.assert_noerr("--client-certs", cadir) +            self.assert_noerr( +                "--client-certs", +                os.path.join(tutils.test_data.path("data/clientcert"), "client.pem"))              self.assert_err( -                "directory does not exist", +                "not a file or directory",                  "--client-certs",                  "nonexistent") diff --git a/test/test_server.py b/test/test_server.py index e48e46fe..09cfa381 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -1,3 +1,4 @@ +import os  import socket  import time  from OpenSSL import SSL @@ -316,6 +317,14 @@ class TestHTTPS(tservers.HTTPProxTest, CommonMixin, TcpMixin):      clientcerts = True      def test_clientcert(self): +        self.config.clientcerts = os.path.join( +            tutils.test_data.path("data/clientcert"), "client.pem") +        f = self.pathod("304") +        assert f.status_code == 304 +        assert self.server.last_log()["request"]["clientcert"]["keyinfo"] + +    def test_clientcerts(self): +        self.config.clientcerts = tutils.test_data.path("data/clientcert")          f = self.pathod("304")          assert f.status_code == 304          assert self.server.last_log()["request"]["clientcert"]["keyinfo"]  | 
