test-https: test basic functions of client certificate authentication
authorYuya Nishihara <yuya@tcha.org>
Thu, 07 May 2015 17:38:22 +0900
changeset 25413 4d705f6a3c35
parent 25412 443d4635e630
child 25414 f7ccbc2776b7
test-https: test basic functions of client certificate authentication Because hgweb doesn't support client certificates, I just patched it to require client certificates that are signed and verified by the server certificate. This won't be ideal for production servers, but should be okay for the test. The encrypted key file will be used by future patches. I couldn't figure out a way to redirect a password prompt provided by OpenSSL, so it isn't tested for now.
tests/hghave.py
tests/test-https.t
--- a/tests/hghave.py	Mon Jun 01 14:16:52 2015 -0400
+++ b/tests/hghave.py	Thu May 07 17:38:22 2015 +0900
@@ -315,6 +315,15 @@
     except ImportError:
         return False
 
+@check("sslcontext", "python >= 2.7.9 ssl")
+def has_sslcontext():
+    try:
+        import ssl
+        ssl.SSLContext
+        return True
+    except (ImportError, AttributeError):
+        return False
+
 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
 def has_defaultcacerts():
     from mercurial import sslutil
--- a/tests/test-https.t	Mon Jun 01 14:16:52 2015 -0400
+++ b/tests/test-https.t	Thu May 07 17:38:22 2015 +0900
@@ -81,6 +81,53 @@
   > EOT
   $ cat priv.pem pub-expired.pem > server-expired.pem
 
+Client certificates created with:
+ openssl genrsa -aes128 -passout pass:1234 -out client-key.pem 512
+ openssl rsa -in client-key.pem -passin pass:1234 -out client-key-decrypted.pem
+ printf '.\n.\n.\n.\n.\n.\nhg-client@localhost\n.\n.\n' | \
+ openssl req -new -key client-key.pem -passin pass:1234 -out client-csr.pem
+ openssl x509 -req -days 9000 -in client-csr.pem -CA pub.pem -CAkey priv.pem \
+ -set_serial 01 -out client-cert.pem
+
+  $ cat << EOT > client-key.pem
+  > -----BEGIN RSA PRIVATE KEY-----
+  > Proc-Type: 4,ENCRYPTED
+  > DEK-Info: AES-128-CBC,C8B8F103A61A336FB0716D1C0F8BB2E8
+  > 
+  > JolMlCFjEW3q3JJjO9z99NJWeJbFgF5DpUOkfSCxH56hxxtZb9x++rBvBZkxX1bF
+  > BAIe+iI90+jdCLwxbILWuFcrJUaLC5WmO14XDKYVmr2eW9e4MiCYOlO0Q6a9rDFS
+  > jctRCfvubOXFHbBGLH8uKEMpXEkP7Lc60FiIukqjuQEivJjrQirVtZCGwyk3qUi7
+  > Eyh4Lo63IKGu8T1Bkmn2kaMvFhu7nC/CQLBjSq0YYI1tmCOkVb/3tPrz8oqgDJp2
+  > u7bLS3q0xDNZ52nVrKIoZC/UlRXGlPyzPpa70/jPIdfCbkwDaBpRVXc+62Pj2n5/
+  > CnO2xaKwfOG6pDvanBhFD72vuBOkAYlFZPiEku4sc2WlNggsSWCPCIFwzmiHjKIl
+  > bWmdoTq3nb7sNfnBbV0OCa7fS1dFwCm4R1NC7ELENu0=
+  > -----END RSA PRIVATE KEY-----
+  > EOT
+
+  $ cat << EOT > client-key-decrypted.pem
+  > -----BEGIN RSA PRIVATE KEY-----
+  > MIIBOgIBAAJBAJs4LS3glAYU92bg5kPgRPNW84ewB0fWJfAKccCp1ACHAdZPeaKb
+  > FCinVMYKAVbVqBkyrZ/Tyr8aSfMz4xO4+KsCAwEAAQJAeKDr25+Q6jkZHEbkLRP6
+  > AfMtR+Ixhk6TJT24sbZKIC2V8KuJTDEvUhLU0CAr1nH79bDqiSsecOiVCr2HHyfT
+  > AQIhAM2C5rHbTs9R3PkywFEqq1gU3ztCnpiWglO7/cIkuGBhAiEAwVpMSAf77kop
+  > 4h/1kWsgMALQTJNsXd4CEUK4BOxvJIsCIQCbarVAKBQvoT81jfX27AfscsxnKnh5
+  > +MjSvkanvdFZwQIgbbcTefwt1LV4trtz2SR0i0nNcOZmo40Kl0jIquKO3qkCIH01
+  > mJHzZr3+jQqeIFtr5P+Xqi30DJxgrnEobbJ0KFjY
+  > -----END RSA PRIVATE KEY-----
+  > EOT
+
+  $ cat << EOT > client-cert.pem
+  > -----BEGIN CERTIFICATE-----
+  > MIIBPjCB6QIBATANBgkqhkiG9w0BAQsFADAxMRIwEAYDVQQDDAlsb2NhbGhvc3Qx
+  > GzAZBgkqhkiG9w0BCQEWDGhnQGxvY2FsaG9zdDAeFw0xNTA1MDcwNjI5NDVaFw0z
+  > OTEyMjcwNjI5NDVaMCQxIjAgBgkqhkiG9w0BCQEWE2hnLWNsaWVudEBsb2NhbGhv
+  > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAmzgtLeCUBhT3ZuDmQ+BE81bzh7AH
+  > R9Yl8ApxwKnUAIcB1k95opsUKKdUxgoBVtWoGTKtn9PKvxpJ8zPjE7j4qwIDAQAB
+  > MA0GCSqGSIb3DQEBCwUAA0EAfBTqBG5pYhuGk+ZnyUufgS+d7Nk/sZAZjNdCAEj/
+  > NFPo5fR1jM6jlEWoWbeg298+SkjV7tfO+2nt0otUFkdM6A==
+  > -----END CERTIFICATE-----
+  > EOT
+
   $ hg init test
   $ cd test
   $ echo foo>foo
@@ -297,3 +344,51 @@
   pulling from https://localhost:$HGPORT2/
   abort: error: *certificate verify failed* (glob)
   [255]
+
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
+
+#if sslcontext
+
+Start patched hgweb that requires client certificates:
+
+  $ cat << EOT > reqclientcert.py
+  > import ssl
+  > from mercurial.hgweb import server
+  > class _httprequesthandlersslclientcert(server._httprequesthandlerssl):
+  >     @staticmethod
+  >     def preparehttpserver(httpserver, ssl_cert):
+  >         sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+  >         sslcontext.verify_mode = ssl.CERT_REQUIRED
+  >         sslcontext.load_cert_chain(ssl_cert)
+  >         # verify clients by server certificate
+  >         sslcontext.load_verify_locations(ssl_cert)
+  >         httpserver.socket = sslcontext.wrap_socket(httpserver.socket,
+  >                                                    server_side=True)
+  > server._httprequesthandlerssl = _httprequesthandlersslclientcert
+  > EOT
+  $ cd test
+  $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
+  > --config extensions.reqclientcert=../reqclientcert.py
+  $ cat ../hg0.pid >> $DAEMON_PIDS
+  $ cd ..
+
+without client certificate:
+
+  $ P=`pwd` hg id https://localhost:$HGPORT/
+  abort: error: *handshake failure* (glob)
+  [255]
+
+with client certificate:
+
+  $ cat << EOT >> $HGRCPATH
+  > [auth]
+  > l.prefix = localhost
+  > l.cert = client-cert.pem
+  > EOT
+
+  $ P=`pwd` hg id https://localhost:$HGPORT/ \
+  > --config auth.l.key=client-key-decrypted.pem
+  5fed3813f7f5
+
+#endif