Revert "Revert "Revert "Revert "net: add OCSP tests.""""
(First landed in r127486, reverted in r127493 because it broke on
Windows XP, relanded in r127518 and reverted in r127520 because Android got
upset about an unused function.)
I was getting increasingly unhappy altering EV and revocation checking
semantics without any tests. We historically haven't had tests because
online revocation checking is inherently flaky so I amended testserver
with the minimum code to be able to sign and vend OCSP responses.
These tests do not test the final EV/CRLSet/revocation checking
semantics. They are intended to be altered in future CLs.
BUG=none
TEST=net_unittests
https://chromiumcodereview.appspot.com/9663017/
git-svn-id: http://src.chromium.org/svn/trunk/src/net/tools/testserver@127680 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
diff --git a/testserver.py b/testserver.py
index ff20c0f..6461997 100755
--- a/testserver.py
+++ b/testserver.py
@@ -19,15 +19,17 @@
import cgi
import errno
import httplib
+import minica
import optparse
import os
import random
import re
import select
-import SocketServer
import socket
-import sys
+import SocketServer
import struct
+import sys
+import threading
import time
import urllib
import urlparse
@@ -105,25 +107,35 @@
class HTTPServer(ClientRestrictingServerMixIn, StoppableHTTPServer):
- """This is a specialization of StoppableHTTPerver that adds client
+ """This is a specialization of StoppableHTTPServer that adds client
verification."""
pass
+class OCSPServer(ClientRestrictingServerMixIn, BaseHTTPServer.HTTPServer):
+ """This is a specialization of HTTPServer that serves an
+ OCSP response"""
+
+ def serve_forever_on_thread(self):
+ self.thread = threading.Thread(target = self.serve_forever,
+ name = "OCSPServerThread")
+ self.thread.start()
+
+ def stop_serving(self):
+ self.shutdown()
+ self.thread.join()
class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
ClientRestrictingServerMixIn,
StoppableHTTPServer):
- """This is a specialization of StoppableHTTPerver that add https support and
+ """This is a specialization of StoppableHTTPServer that add https support and
client verification."""
- def __init__(self, server_address, request_hander_class, cert_path,
+ def __init__(self, server_address, request_hander_class, pem_cert_and_key,
ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers,
record_resume_info):
- s = open(cert_path).read()
- self.cert_chain = tlslite.api.X509CertChain().parseChain(s)
- s = open(cert_path).read()
- self.private_key = tlslite.api.parsePEMKey(s, private=True)
+ self.cert_chain = tlslite.api.X509CertChain().parseChain(pem_cert_and_key)
+ self.private_key = tlslite.api.parsePEMKey(pem_cert_and_key, private=True)
self.ssl_client_auth = ssl_client_auth
self.ssl_client_cas = []
for ca_file in ssl_client_cas:
@@ -1889,6 +1901,20 @@
return my_data_dir
+class OCSPHandler(BasePageHandler):
+ def __init__(self, request, client_address, socket_server):
+ handlers = [self.OCSPResponse]
+ self.ocsp_response = socket_server.ocsp_response
+ BasePageHandler.__init__(self, request, client_address, socket_server,
+ [], handlers, [], handlers, [])
+
+ def OCSPResponse(self):
+ self.send_response(200)
+ self.send_header('Content-Type', 'application/ocsp-response')
+ self.send_header('Content-Length', str(len(self.ocsp_response)))
+ self.end_headers()
+
+ self.wfile.write(self.ocsp_response)
class TCPEchoHandler(SocketServer.BaseRequestHandler):
"""The RequestHandler class for TCP echo server.
@@ -1969,19 +1995,55 @@
server_data = {}
server_data['host'] = host
+ ocsp_server = None
+
if options.server_type == SERVER_HTTP:
- if options.cert:
- # let's make sure the cert file exists.
- if not os.path.isfile(options.cert):
- print 'specified server cert file not found: ' + options.cert + \
- ' exiting...'
- return
+ if options.https:
+ pem_cert_and_key = None
+ if options.cert_and_key_file:
+ if not os.path.isfile(options.cert_and_key_file):
+ print ('specified server cert file not found: ' +
+ options.cert_and_key_file + ' exiting...')
+ return
+ pem_cert_and_key = file(options.cert_and_key_file, 'r').read()
+ else:
+ # generate a new certificate and run an OCSP server for it.
+ ocsp_server = OCSPServer((host, 0), OCSPHandler)
+ print ('OCSP server started on %s:%d...' %
+ (host, ocsp_server.server_port))
+
+ ocsp_der = None
+ ocsp_revoked = False
+ ocsp_invalid = False
+
+ if options.ocsp == 'ok':
+ pass
+ elif options.ocsp == 'revoked':
+ ocsp_revoked = True
+ elif options.ocsp == 'invalid':
+ ocsp_invalid = True
+ else:
+ print 'unknown OCSP status: ' + options.ocsp_status
+ return
+
+ (pem_cert_and_key, ocsp_der) = \
+ minica.GenerateCertKeyAndOCSP(
+ subject = "127.0.0.1",
+ ocsp_url = ("http://%s:%d/ocsp" %
+ (host, ocsp_server.server_port)),
+ ocsp_revoked = ocsp_revoked)
+
+ if ocsp_invalid:
+ ocsp_der = '3'
+
+ ocsp_server.ocsp_response = ocsp_der
+
for ca_cert in options.ssl_client_ca:
if not os.path.isfile(ca_cert):
print 'specified trusted client CA file not found: ' + ca_cert + \
' exiting...'
return
- server = HTTPSServer((host, port), TestPageHandler, options.cert,
+ server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key,
options.ssl_client_auth, options.ssl_client_ca,
options.ssl_bulk_cipher, options.record_resume)
print 'HTTPS server started on %s:%d...' % (host, server.server_port)
@@ -2061,10 +2123,15 @@
startup_pipe.write(server_data_json)
startup_pipe.close()
+ if ocsp_server is not None:
+ ocsp_server.serve_forever_on_thread()
+
try:
server.serve_forever()
except KeyboardInterrupt:
print 'shutting down server'
+ if ocsp_server is not None:
+ ocsp_server.stop_serving()
server.stop = True
if __name__ == '__main__':
@@ -2095,10 +2162,16 @@
'server will listen on an ephemeral port.')
option_parser.add_option('', '--data-dir', dest='data_dir',
help='Directory from which to read the files.')
- option_parser.add_option('', '--https', dest='cert',
- help='Specify that https should be used, specify '
- 'the path to the cert containing the private key '
- 'the server should use.')
+ option_parser.add_option('', '--https', action='store_true', dest='https',
+ help='Specify that https should be used.')
+ option_parser.add_option('', '--cert-and-key-file', dest='cert_and_key_file',
+ help='specify the path to the file containing the '
+ 'certificate and private key for the server in PEM '
+ 'format')
+ option_parser.add_option('', '--ocsp', dest='ocsp', default='ok',
+ help='The type of OCSP response generated for the '
+ 'automatically generated certificate. One of '
+ '[ok,revoked,invalid]')
option_parser.add_option('', '--https-record-resume', dest='record_resume',
const=True, default=False, action='store_const',
help='Record resumption cache events rather than'