# HG changeset patch # User Yuya Nishihara # Date 1430987902 -32400 # Node ID 4d705f6a3c353f83dd6dcb10c8b527b016eca02d # Parent 443d4635e630cf5603afb6ba4bfcf6a0d68cd7a6 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. diff -r 443d4635e630 -r 4d705f6a3c35 tests/hghave.py --- 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 diff -r 443d4635e630 -r 4d705f6a3c35 tests/test-https.t --- 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