Unflake OCSP integration test

Fixes #982.

Rather than failing immediately if two OCSP responses differ, which can happen
if ocsp-updater fires in between two requests, we wait until all OCSP responses
reach their expected state, and then check for equality.

Additionally, reorganize the OCSP checking to be somewhat cleaner, and improve
detection of verify failures (e.g. when a OpenSSL can't find a signer
certificate).
This commit is contained in:
Jacob Hoffman-Andrews 2015-10-13 18:35:57 -07:00
parent 0bbffa364c
commit 090b71b031
7 changed files with 170 additions and 111 deletions

View File

@ -37,87 +37,111 @@ def die(status):
exit_status = status
sys.exit(exit_status)
# Fetch an OCSP response, parse it with OpenSSL, and return the output.
def get_ocsp(cert_file, url):
ocsp_req_file = os.path.join(tempdir, "ocsp.req")
ocsp_resp_file = os.path.join(tempdir, "ocsp.resp")
# First generate the OCSP request in DER form
openssl_ocsp = "openssl ocsp -issuer ../test-ca.pem -cert %s.pem" % cert_file
openssl_ocsp_cmd = ("""
openssl x509 -in %s -out %s.pem -inform der -outform pem;
%s -no_nonce -reqout %s
""" % (cert_file, cert_file, openssl_ocsp, ocsp_req_file))
print openssl_ocsp_cmd
subprocess.check_output(openssl_ocsp_cmd, shell=True)
with open(ocsp_req_file) as f:
ocsp_req = f.read()
ocsp_req_b64 = base64.b64encode(ocsp_req)
def fetch_ocsp(request_bytes, url):
"""Fetch an OCSP response using POST, GET, and GET with URL encoding.
Returns a tuple of the responses.
"""
ocsp_req_b64 = base64.b64encode(request_bytes)
# Make the OCSP request three different ways: by POST, by GET, and by GET with
# URL-encoded parameters. All three should have an identical response.
get_response = urllib2.urlopen("%s/%s" % (url, ocsp_req_b64)).read()
get_encoded_response = urllib2.urlopen("%s/%s" % (url, urllib.quote(ocsp_req_b64, safe = ""))).read()
post_response = urllib2.urlopen("%s/" % (url), ocsp_req).read()
post_response = urllib2.urlopen("%s/" % (url), request_bytes).read()
if get_response != get_encoded_response:
print "OCSP responses for GET and URL-encoded GET differed."
die(ExitStatus.OCSPFailure)
elif get_response != post_response:
print "OCSP responses for GET and POST differed."
die(ExitStatus.OCSPFailure)
return (post_response, get_response, get_encoded_response)
def make_ocsp_req(cert_file, issuer_file):
"""Return the bytes of an OCSP request for the given certificate file."""
ocsp_req_file = os.path.join(tempdir, "ocsp.req")
# First generate the OCSP request in DER form
cmd = ("openssl ocsp -no_nonce -issuer %s -cert %s -reqout %s" % (
issuer_file, cert_file, ocsp_req_file))
print cmd
subprocess.check_output(cmd, shell=True)
with open(ocsp_req_file) as f:
ocsp_req = f.read()
return ocsp_req
def fetch_until(cert_file, issuer_file, url, initial, final):
"""Fetch OCSP for cert_file until OCSP status goes from initial to final.
Initial and final are treated as regular expressions. Any OCSP response
whose OpenSSL OCSP verify output doesn't match either initial or final is
a fatal error.
If OCSP responses by the three methods (POST, GET, URL-encoded GET) differ
from each other, that is a fatal error.
If we loop for more than five seconds, that is a fatal error.
Returns nothing on success.
"""
ocsp_request = make_ocsp_req(cert_file, issuer_file)
timeout = time.time() + 5
while True:
time.sleep(0.25)
if time.time() > timeout:
print("Timed out waiting for OCSP to go from '%s' to '%s'" % (
initial, final))
die(ExitStatus.OCSPFailure)
responses = fetch_ocsp(ocsp_request, url)
# This variable will be true at the end of the loop if all the responses
# matched the final state.
all_final = True
for resp in responses:
verify_output = ocsp_verify(cert_file, issuer_file, resp)
if re.search(initial, verify_output):
all_final = False
break
elif re.search(final, verify_output):
continue
else:
print verify_output
print("OCSP response didn't match '%s' or '%s'" %(
initial, final))
die(ExitStatus.OCSPFailure)
if all_final:
# Check that all responses were equal to each other.
for resp in responses:
if resp != responses[0]:
print "OCSP responses differed:"
print(base64.b64encode(responses[0]))
print(" vs ")
print(base64.b64encode(resp))
die(ExitStatus.OCSPFailure)
return
def ocsp_verify(cert_file, issuer_file, ocsp_response):
ocsp_resp_file = os.path.join(tempdir, "ocsp.resp")
with open(ocsp_resp_file, "w") as f:
f.write(get_response)
ocsp_verify_cmd = "%s -CAfile ../test-root.pem -respin %s" % (openssl_ocsp, ocsp_resp_file)
f.write(ocsp_response)
ocsp_verify_cmd = """openssl ocsp -no_nonce -issuer %s -cert %s \
-verify_other %s -CAfile ../test-root.pem \
-respin %s""" % (issuer_file, cert_file, issuer_file, ocsp_resp_file)
print ocsp_verify_cmd
try:
output = subprocess.check_output(ocsp_verify_cmd, shell=True)
output = subprocess.check_output(ocsp_verify_cmd,
shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
output = e.output
print output
print "subprocess returned non-zero: %s" % e
die(ExitStatus.OCSPFailure)
print output
# OpenSSL doesn't always return non-zero when response verify fails, so we
# also look for the string "Response Verify Failure"
verify_failure = "Response Verify Failure"
if re.search(verify_failure, output):
print output
die(ExitStatus.OCSPFailure)
return output
def verify_ocsp_good(certFile, url):
output = get_ocsp(certFile, url)
# Check if the output contains either ': good' or
# ' unauthorized (6)', if openssl produces something else fail out
# since these are the only two responses we expect. This
# allows the check to be looped until successful.
if not re.search(": good", output):
if not re.search(" unauthorized \(6\)", output):
print "Expected OCSP response 'unauthorized', got something else."
die(ExitStatus.OCSPFailure)
return False
return True
def wait_for_ocsp_good(cert_file, issuer_file, url):
fetch_until(cert_file, issuer_file, url, " unauthorized", ": good")
def verify_ocsp_revoked(certFile, url):
output = get_ocsp(certFile, url)
# Check if the output contains either ': revoked' or
# ': good', if openssl produces something else fail out
# since these are the only two responses we expect. This
# allows the check to be looped until successful.
if not re.search(": revoked", output):
if not re.search(": good", output):
print "Expected OCSP response 'good', got something else."
die(ExitStatus.OCSPFailure)
return False
return True
# loop_check expects the function passed as action will return True/False to indicate
# success/failure
def loop_check(failureStatus, action, *args):
timeout = time.time() + 5
while True:
if action(*args):
break
if time.time() > timeout:
die(failureStatus)
time.sleep(0.25)
def wait_for_ocsp_revoked(cert_file, issuer_file, url):
fetch_until(cert_file, issuer_file, url, ": good", ": revoked")
def verify_ct_submission(expectedSubmissions, url):
resp = urllib2.urlopen(url)
@ -139,38 +163,42 @@ def run_node_test():
if subprocess.Popen('npm install', shell=True).wait() != 0:
print("\n Installing NPM modules failed")
die(ExitStatus.Error)
certFile = os.path.join(tempdir, "cert.der")
keyFile = os.path.join(tempdir, "key.pem")
cert_file = os.path.join(tempdir, "cert.der")
cert_file_pem = os.path.join(tempdir, "cert.pem")
key_file = os.path.join(tempdir, "key.pem")
# Pick a random hostname so we don't run into certificate rate limiting.
domain = subprocess.check_output("openssl rand -hex 6", shell=True).strip()
# Issue the certificate and transform it from DER-encoded to PEM-encoded.
if subprocess.Popen('''
node test.js --email foo@letsencrypt.org --agree true \
--domains www.%s.com --new-reg http://localhost:4000/acme/new-reg \
--certKey %s --cert %s
''' % (domain, keyFile, certFile), shell=True).wait() != 0:
--certKey %s --cert %s && \
openssl x509 -in %s -out %s -inform der -outform pem
''' % (domain, key_file, cert_file, cert_file, cert_file_pem),
shell=True).wait() != 0:
print("\nIssuing failed")
die(ExitStatus.NodeFailure)
ee_ocsp_url = "http://localhost:4002"
issuer_ocsp_url = "http://localhost:4003"
# Also verify that the static OCSP responder, which answers with a
# pre-signed, long-lived response for the CA cert, also works.
verify_ocsp_good("../test-ca.der", issuer_ocsp_url)
# As OCSP-Updater is generating responses indepedantly of the CA we sit in a loop
# As OCSP-Updater is generating responses independently of the CA we sit in a loop
# checking OCSP until we either see a good response or we timeout (5s).
loop_check(ExitStatus.OCSPFailure, verify_ocsp_good, certFile, ee_ocsp_url)
wait_for_ocsp_good(cert_file_pem, "../test-ca.pem", ee_ocsp_url)
# Verify that the static OCSP responder, which answers with a
# pre-signed, long-lived response for the CA cert, works.
wait_for_ocsp_good("../test-ca.pem", "../test-root.pem", issuer_ocsp_url)
verify_ct_submission(1, "http://localhost:4500/submissions")
if subprocess.Popen('''
node revoke.js %s %s http://localhost:4000/acme/revoke-cert
''' % (certFile, keyFile), shell=True).wait() != 0:
''' % (cert_file, key_file), shell=True).wait() != 0:
print("\nRevoking failed")
die(ExitStatus.NodeFailure)
loop_check(ExitStatus.OCSPFailure, verify_ocsp_revoked, certFile, ee_ocsp_url)
wait_for_ocsp_revoked(cert_file_pem, "../test-ca.pem", ee_ocsp_url)
return 0

View File

@ -5,8 +5,8 @@
# occurs last in the file will take priority.
#
# The response were generated with:
# openssl x509 -outform der -in test/test-ca.pem -out test/test-ca.der
# single-ocsp --issuer test/test-ca.der --responder test/test-ca.der \
# openssl x509 -outform der -in test/test-root.pem -out test/test-root.der
# single-ocsp --issuer test/test-root.der --responder test/test-root.der \
# --target test/test-ca.der --template template.json --pkcs11 ~/pkcs11.json \
# --out ocsp.rsp
# base64 ocsp.rsp | tr -d '\n' > issuer-ocsp-responses.txt

View File

@ -1,2 +1,2 @@
MIIE+QoBAKCCBPIwggTuBgkrBgEFBQcwAQEEggTfMIIE2zCBp6ADAgEAoSEwHzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EYDzIwMTUwOTI0MDA0NTAwWjBsMGowQjAJBgUrDgMCGgUABBQ55F6w46hhx/o6OXOHa+Yfe32YhgQU+3hPEvlgFYMsnxd/NBmzLjbqQYkCCQCc8ZEuqNUJCIAAGA8yMDE0MDkyMzAwMDAwMFqgERgPMjAxNTA4MjMwMDAwMDBaMA0GCSqGSIb3DQEBCwUAA4IBAQCAu0QOKqU3qd2O6jp0kCezLvISsZ6HmJKU57NOOKy3x0hXHxZg+H9gJB6P4BEAn9CFU7Qj3LpjpOmGOsXSzawa+MmyzCuXLZb6orK2g0lnEslGiQxccRcyzY3Cd2oVbiqutu/Yj/6Lpm6KE7vIFz2XTsa5uCXvwZ1EEvDtfHq/YZBuFjHWALUuQtq9fYINYVR5P+WM/+BGTpszUWehvhcWDfbc1LOXwUDKv4NXeZaEipBGLeYBHFTBZlxq5CHhIyW//10gJQP2s9a/v3xLGJZQNHMZg5Z0ZF3xDMoWHcB3OvL+YrpzTRmqT9tKWsaEo9Mkh+sQAObP+wcvh+4Ga/vloIIDGTCCAxUwggMRMIIB+aADAgECAgkAnPGRLqjVCQgwDQYJKoZIhvcNAQELBQAwHzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EwHhcNMTUwNDA3MjM1MDM4WhcNMjUwNDA0MjM1MDM4WjAfMR0wGwYDVQQDDBRoYXBweSBoYWNrZXIgZmFrZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIKR3maBcUSsncXYzQT13D5Nr+Z3mLxMMh3TUdt6sACmqbJ0btRlgXfMtNLM2OU1I6a3Ju+tIZSdn2v21JBwvxUzpZQ4zy2cimIiMQDZCQHJwzC9GZn8HaW091iz9H0Go3A7WDXwYNmsdLNRi00o14UjoaVqaPsYrZWvRKaIRqaU0hHmS0AWwQSvN/93iMIXuyiwywmkwKbWnnxCQ/gsctKFUtcNrwEx9Wgj6KlhwDTyI1QWSBbxVYNyUgPFzKxrSmwMO0yNff7ho+QT9x5+Y/7XE59S4Mc4ZXxcXKew/gSlN9U5mvT+D2BhDtkCupdfsZNCQWp27A+b/DmrFI9NqsCAwEAAaNQME4wHQYDVR0OBBYEFPt4TxL5YBWDLJ8XfzQZsy426kGJMB8GA1UdIwQYMBaAFPt4TxL5YBWDLJ8XfzQZsy426kGJMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAB30Nr5m/5OMy/s1MCaWKqdYdjp3dTERk3eEUQnnwhBUdsFlVl1bvOFGS0G9HTkrB5pzQcl4r3VMqbO9eXbUhcu+HSBw0tT+7B4Peej+yd90Hg6gWiamWNOGaCXMGqKpagoElCssIDzDlQH5F6iZFh38RhcX/pMB/ObqGv/9e3mY+JQc929i3vmUwCi9HEtJsXxNJDpvsFjEhJaM+AUBI02ok0cQi1ayZAy0COPDNv1yzTVcf2kKFUBaf0uh4wpr5KUdJitYb3f4Rysgf90ZTvq406JoPMFIq9p6EbneHbkwe47Vqc0gIm9mi9asWjhS/USeQombe8kV7nR4kaEQqXE=
MIIE+QoBAKCCBPIwggTuBgkrBgEFBQcwAQEEggTfMIIE2zCBp6ADAgEAoSEwHzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EYDzIwMTUwOTIzMjEwNjAwWjBsMGowQjAJBgUrDgMCGgUABBQ55F6w46hhx/o6OXOHa+Yfe32YhgQU+3hPEvlgFYMsnxd/NBmzLjbqQYkCCQCc8ZEuqNUJCIAAGA8yMDE1MDkyMzAwMDAwMFqgERgPMjAzMDA4MjYwMDAwMDBaMA0GCSqGSIb3DQEBCwUAA4IBAQDBftXxLECNIUCSyGyy1rqYgWN6nVyvuN3AWu2FgGpVTDer3YPC4ApLslstDdoeHAvmUUQ3dHG8pT8UYW83nuDAtDbGl7QAt+upUTxb5tkvvIF1htVoFWKTz6AJnWRYUUbe+Qfe4262UMQkoAIHsBgTqnrpDmUEUzlILu7xK2+oZWMV2o+LsTdcqimsOFj4ka24UGbDW1F24VRyaudGAW5C4NYBZmj/EKiqljdBfSm+OHob26kmixNVgDSrXz5Jikf7CW8uGzkjayKVZUWIT7vtGITxvJaGuDTY3vSAK6yPeZJKNoZ6+HQS+AiXerr2RX882p5+zL0HMbzQSGW4me5BoIIDGTCCAxUwggMRMIIB+aADAgECAgkAnPGRLqjVCQgwDQYJKoZIhvcNAQELBQAwHzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EwHhcNMTUwNDA3MjM1MDM4WhcNMjUwNDA0MjM1MDM4WjAfMR0wGwYDVQQDDBRoYXBweSBoYWNrZXIgZmFrZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIKR3maBcUSsncXYzQT13D5Nr+Z3mLxMMh3TUdt6sACmqbJ0btRlgXfMtNLM2OU1I6a3Ju+tIZSdn2v21JBwvxUzpZQ4zy2cimIiMQDZCQHJwzC9GZn8HaW091iz9H0Go3A7WDXwYNmsdLNRi00o14UjoaVqaPsYrZWvRKaIRqaU0hHmS0AWwQSvN/93iMIXuyiwywmkwKbWnnxCQ/gsctKFUtcNrwEx9Wgj6KlhwDTyI1QWSBbxVYNyUgPFzKxrSmwMO0yNff7ho+QT9x5+Y/7XE59S4Mc4ZXxcXKew/gSlN9U5mvT+D2BhDtkCupdfsZNCQWp27A+b/DmrFI9NqsCAwEAAaNQME4wHQYDVR0OBBYEFPt4TxL5YBWDLJ8XfzQZsy426kGJMB8GA1UdIwQYMBaAFPt4TxL5YBWDLJ8XfzQZsy426kGJMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAB30Nr5m/5OMy/s1MCaWKqdYdjp3dTERk3eEUQnnwhBUdsFlVl1bvOFGS0G9HTkrB5pzQcl4r3VMqbO9eXbUhcu+HSBw0tT+7B4Peej+yd90Hg6gWiamWNOGaCXMGqKpagoElCssIDzDlQH5F6iZFh38RhcX/pMB/ObqGv/9e3mY+JQc929i3vmUwCi9HEtJsXxNJDpvsFjEhJaM+AUBI02ok0cQi1ayZAy0COPDNv1yzTVcf2kKFUBaf0uh4wpr5KUdJitYb3f4Rysgf90ZTvq406JoPMFIq9p6EbneHbkwe47Vqc0gIm9mi9asWjhS/USeQombe8kV7nR4kaEQqXE=
MIIFCAoBAKCCBQEwggT9BgkrBgEFBQcwAQEEggTuMIIE6jCBrKADAgEAoS0wKzEpMCcGA1UEAwwgY2Fja2xpbmcgY3J5cHRvZ3JhcGhlciBmYWtlIFJPT1QYDzIwMTUxMDIxMjEyNjAwWjBlMGMwOzAJBgUrDgMCGgUABBSwLsMRhyg1dJUwnXWk++D57lvgagQU6aQ/7p6l5vLV13lgPJOmLiSOl6oCAhJNgAAYDzIwMTUwOTAxMDAwMDAwWqARGA8yMDE0MDEwMTAwMDAwMFowDQYJKoZIhvcNAQELBQADggEBAHlFcNKa7mZDJeWzJt1S45kx4gDqOLzyeZzflFbSjsrHRrLA7Y3RKoy0i4Y9Vi6Jfhe7xj6dgDMJy1Z1qayI/Q8QvnaU6V2kFcnaD7pah9uALu2xNYMJPllq8KsQYvDLa1E2PMvQTqDhY2/QrIuxw3jkqtzeI5aG0idFm3aF1z/v3dt6XPWjE8IlAJfXY4CeUorLvA+mK2YHJ3V7MSgymVXZdyth1rg0/0cP9v77Rlb8hmWA/EUMcIPKQqErVQK+gZiVC0SfElaMO25CD9cjY+fd904oC5+ahvhHXxOSEbXVZBT1FY2teFCKEpx86gAVcZWpGmVwJO+dpsrkgwpN786gggMjMIIDHzCCAxswggIDoAMCAQICCQDNMc/iNkPNdTANBgkqhkiG9w0BAQsFADArMSkwJwYDVQQDDCBjYWNrbGluZyBjcnlwdG9ncmFwaGVyIGZha2UgUk9PVDAeFw0xNTEwMjEyMDExNTJaFw0yMDEwMTkyMDExNTJaMCsxKTAnBgNVBAMMIGNhY2tsaW5nIGNyeXB0b2dyYXBoZXIgZmFrZSBST09UMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+TbvalHXQYO6GhJUJZI5mF2k4+nZDIvqWyrjw+2k9+UAcekuLKPpSclu9aBRvUggw3XFHAW95qW6Dv2+5gvinUmTq9Ry7kVTUYAxyZu1ydHt+wDETmFJfeY6/fpBHHIsuGLItqpUGmr8D6LROGEqfFY2B9+08O7Zs+FufDRgLHWEvLTdpPkrzeDJs9Oo6g38jfT9b4+9Ahs+FvvwqneAkbeZgBC2NWKB+drMuNBTPbF/W1a8czAzHeOs6qy0dBlTHNjL62/o9cRKNiKe3IqwHJdd01V1aLSUgIbe2HrP9EC1djnUXWR3jx3ursaKt7PTKsC52UJkRqnai80MzQj0WwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU6aQ/7p6l5vLV13lgPJOmLiSOl6owDQYJKoZIhvcNAQELBQADggEBACuwILDTvaBrdorv2zMsYnZuKvXtknWAf/DTcvF4N5PMOPBNkeHuGfv0VDe6VXpBHiU5G9E2RdU435W7o0kRSn27YcqrxaXGt9m2kArW6e49136+MnFx47jjk0p4T48s6MeaL5JVLJzxYouu1ZOZqlVokwNPO+8bxn6ALumIVUOD1jSBN7Y9pgLUS2rzO5pe5pxS2Ak/eO7Q7M21r1sEuG/uPuWqBFogk+4Z9omKVZdRDbzm9vYUATgEZdlTe2tct3BVBQ2zWbe0R2svIuCs8XzERykvfv1JawxI68I9vN0Dh9vj/xDM6udorfALlhjgQdftmbHovRLpJ1ZSOMIUNGY=
MIIFCAoBAKCCBQEwggT9BgkrBgEFBQcwAQEEggTuMIIE6jCBrKADAgEAoS0wKzEpMCcGA1UEAwwgY2Fja2xpbmcgY3J5cHRvZ3JhcGhlciBmYWtlIFJPT1QYDzIwMTUxMDIxMjA1NTAwWjBlMGMwOzAJBgUrDgMCGgUABBSwLsMRhyg1dJUwnXWk++D57lvgagQU6aQ/7p6l5vLV13lgPJOmLiSOl6oCAhJNgAAYDzIwMTUxMDIwMDAwMDAwWqARGA8yMDMwMTAyMDAwMDAwMFowDQYJKoZIhvcNAQELBQADggEBAFgnZ/Ft1LTDYPwPlecOtLykgwS4HZTelUaSi841nq/tgfLM11G3D1AUXAT2V2jxiG+0YTxzkWd5v44KJGB9Mm+qjafPMKR3ULjQkJHJ8goFHpWkUtLrIYurj8N+4HpwZ+RJccieuZIX8SMeSWRq5w83okWZPGoUrl6GRdQDteE7imrNkBa35zrzUWozPqY8k90ttKfhZHRXNCJe8YbVfJRDh0vVZABzlfHeW8V+ie15HPVDx/M341KC3tBMM88e5/bt3sLyUU8SwxGH5nOe/ohVpjhkjk2Pz4TPdwD2ZK5Auc09VBfivdLYRE84BMhd8/yOEt53VWGPIMxWUVtrUyegggMjMIIDHzCCAxswggIDoAMCAQICCQDNMc/iNkPNdTANBgkqhkiG9w0BAQsFADArMSkwJwYDVQQDDCBjYWNrbGluZyBjcnlwdG9ncmFwaGVyIGZha2UgUk9PVDAeFw0xNTEwMjEyMDExNTJaFw0yMDEwMTkyMDExNTJaMCsxKTAnBgNVBAMMIGNhY2tsaW5nIGNyeXB0b2dyYXBoZXIgZmFrZSBST09UMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+TbvalHXQYO6GhJUJZI5mF2k4+nZDIvqWyrjw+2k9+UAcekuLKPpSclu9aBRvUggw3XFHAW95qW6Dv2+5gvinUmTq9Ry7kVTUYAxyZu1ydHt+wDETmFJfeY6/fpBHHIsuGLItqpUGmr8D6LROGEqfFY2B9+08O7Zs+FufDRgLHWEvLTdpPkrzeDJs9Oo6g38jfT9b4+9Ahs+FvvwqneAkbeZgBC2NWKB+drMuNBTPbF/W1a8czAzHeOs6qy0dBlTHNjL62/o9cRKNiKe3IqwHJdd01V1aLSUgIbe2HrP9EC1djnUXWR3jx3ursaKt7PTKsC52UJkRqnai80MzQj0WwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU6aQ/7p6l5vLV13lgPJOmLiSOl6owDQYJKoZIhvcNAQELBQADggEBACuwILDTvaBrdorv2zMsYnZuKvXtknWAf/DTcvF4N5PMOPBNkeHuGfv0VDe6VXpBHiU5G9E2RdU435W7o0kRSn27YcqrxaXGt9m2kArW6e49136+MnFx47jjk0p4T48s6MeaL5JVLJzxYouu1ZOZqlVokwNPO+8bxn6ALumIVUOD1jSBN7Y9pgLUS2rzO5pe5pxS2Ak/eO7Q7M21r1sEuG/uPuWqBFogk+4Z9omKVZdRDbzm9vYUATgEZdlTe2tct3BVBQ2zWbe0R2svIuCs8XzERykvfv1JawxI68I9vN0Dh9vj/xDM6udorfALlhjgQdftmbHovRLpJ1ZSOMIUNGY=

Binary file not shown.

View File

@ -1,24 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEHDCCAwSgAwIBAgICEl4wDQYJKoZIhvcNAQELBQAwKzEpMCcGA1UEAwwgY2Fj
a2xpbmcgY3J5cHRvZ3JhcGhlciBmYWtlIFJPT1QwHhcNMTUxMDIwMjAzMTQyWhcN
MjAxMDE4MjAzMTQyWjAfMR0wGwYDVQQDDBRoYXBweSBoYWNrZXIgZmFrZSBDQTCC
MIIEijCCA3KgAwIBAgICEk0wDQYJKoZIhvcNAQELBQAwKzEpMCcGA1UEAwwgY2Fj
a2xpbmcgY3J5cHRvZ3JhcGhlciBmYWtlIFJPT1QwHhcNMTUxMDIxMjAxMTUyWhcN
MjAxMDE5MjAxMTUyWjAfMR0wGwYDVQQDExRoYXBweSBoYWNrZXIgZmFrZSBDQTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIKR3maBcUSsncXYzQT13D5
Nr+Z3mLxMMh3TUdt6sACmqbJ0btRlgXfMtNLM2OU1I6a3Ju+tIZSdn2v21JBwvxU
zpZQ4zy2cimIiMQDZCQHJwzC9GZn8HaW091iz9H0Go3A7WDXwYNmsdLNRi00o14U
joaVqaPsYrZWvRKaIRqaU0hHmS0AWwQSvN/93iMIXuyiwywmkwKbWnnxCQ/gsctK
FUtcNrwEx9Wgj6KlhwDTyI1QWSBbxVYNyUgPFzKxrSmwMO0yNff7ho+QT9x5+Y/7
XE59S4Mc4ZXxcXKew/gSlN9U5mvT+D2BhDtkCupdfsZNCQWp27A+b/DmrFI9NqsC
AwEAAaOCAVQwggFQMBIGA1UdEwEB/wQIMAYBAf8CAQAwQgYDVR0eBDswOaE3MAWC
A21pbDAKhwgAAAAAAAAAADAihyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAADAOBgNVHQ8BAf8EBAMCAYYwMQYIKwYBBQUHAQEEJTAjMCEGCCsGAQUFBzAB
hhVodHRwOi8vbG9jYWxob3N0OjQwMDMwHwYDVR0jBBgwFoAUz0nVEcH3uFO1Wlnl
fuQ+5Dpx0EcwSwYDVR0gBEQwQjAIBgZngQwBAgEwNgYLKwYBBAGC3xMBAQEwJzAl
BggrBgEFBQcCARYZaHR0cDovL2xvY2FsaG9zdDo0MDAwL2NwczAmBgNVHR8EHzAd
MBugGaAXhhVodHRwOi8vbG9jYWxob3N0OjU1NTUwHQYDVR0OBBYEFPt4TxL5YBWD
LJ8XfzQZsy426kGJMA0GCSqGSIb3DQEBCwUAA4IBAQCPKuy6CFUte+8CFWTm+jtP
tnwLFnvznPGsF/GGb/JHRFJMvMoQ4ycvbOfY6jztQqmbf+R54pdPkpKxApQHvwcF
Zd4IsgA39kQcQBQR8ToxsURrCMqTM5EXzXP/RFQHAf8aIcKuplxh1pjyJjbTEAx5
n8W0HieqvWwy4JmBRI9N7KkpH4Xj5HBkesl4pGZOmt9kgYtN38jRX9BwJMM4IrXM
EdHHgSwOW2BGr1PivJw3KYu20gw0ArPOLgc0M+3CCa8VipVVJwQks8HfXX58gd91
kfobxltCCeRtKfeVWvF29TfxtHMxkwpZaGLxx+QKWytXqfj8kKF+tYl8PAE7DGJt
AwEAAaOCAcIwggG+MBIGA1UdEwEB/wQIMAYBAf8CAQAwQwYDVR0eBDwwOqE4MAaC
BC5taWwwCocIAAAAAAAAAAAwIocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAwDgYDVR0PAQH/BAQDAgGGMH8GCCsGAQUFBwEBBHMwcTAyBggrBgEFBQcw
AYYmaHR0cDovL2lzcmcudHJ1c3RpZC5vY3NwLmlkZW50cnVzdC5jb20wOwYIKwYB
BQUHMAKGL2h0dHA6Ly9hcHBzLmlkZW50cnVzdC5jb20vcm9vdHMvZHN0cm9vdGNh
eDMucDdjMB8GA1UdIwQYMBaAFOmkP+6epeby1dd5YDyTpi4kjpeqMFQGA1UdIARN
MEswCAYGZ4EMAQIBMD8GCysGAQQBgt8TAQEBMDAwLgYIKwYBBQUHAgEWImh0dHA6
Ly9jcHMucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcwPAYDVR0fBDUwMzAxoC+gLYYr
aHR0cDovL2NybC5pZGVudHJ1c3QuY29tL0RTVFJPT1RDQVgzQ1JMLmNybDAdBgNV
HQ4EFgQU+3hPEvlgFYMsnxd/NBmzLjbqQYkwDQYJKoZIhvcNAQELBQADggEBAA0Y
AeLXOklx4hhCikUUl+BdnFfn1g0W5AiQLVNIOL6PnqXu0wjnhNyhqdwnfhYMnoy4
idRh4lB6pz8Gf9pnlLd/DnWSV3gS+/I/mAl1dCkKby6H2V790e6IHmIK2KYm3jm+
U++FIdGpBdsQTSdmiX/rAyuxMDM0adMkNBwTfQmZQCz6nGHw1QcSPZMvZpsC8Skv
ekzxsjF1otOrMUPNPQvtTWrVx8GlR2qfx/4xbQa1v2frNvFBCmO59goz+jnWvfTt
j2NjwDZ7vlMBsPm16dbKYC840uvRoZjxqsdc3ChCZjqimFqlNG/xoPA8+dTicZzC
XE9ijPIcvW6y1aa3bGw=
-----END CERTIFICATE-----

28
test/test-root.key Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD5Nu9qUddBg7oa
ElQlkjmYXaTj6dkMi+pbKuPD7aT35QBx6S4so+lJyW71oFG9SCDDdcUcBb3mpboO
/b7mC+KdSZOr1HLuRVNRgDHJm7XJ0e37AMROYUl95jr9+kEcciy4Ysi2qlQaavwP
otE4YSp8VjYH37Tw7tmz4W58NGAsdYS8tN2k+SvN4Mmz06jqDfyN9P1vj70CGz4W
+/Cqd4CRt5mAELY1YoH52sy40FM9sX9bVrxzMDMd46zqrLR0GVMc2Mvrb+j1xEo2
Ip7cirAcl13TVXVotJSAht7Yes/0QLV2OdRdZHePHe6uxoq3s9MqwLnZQmRGqdqL
zQzNCPRbAgMBAAECggEBAPY6wPJbSkMA069OzLUSdLWGUJSfSESNOVA6YDAFbSlA
qd/idCzHBohQ7cDiVeCek0CL6+R0B90LgXmuXB8PPygIuk7b6XJiLKAp+QcpQcPW
FzwY1dxbYzFxeHfX+t0QMf88GvPf9pjje4g3ZmA+IKwYwemlrjEezRpdcbxmQ1+o
Cgm2jNieexTpn/sttNA9OccaCcxUPB4PZ3EnYa2rXCOEIb89L/bsr0R6Xw6Cwqbq
v/CrUdohGhgQS0UTa06OxRmFYwi7ARFg5NIQuqVfMLgrL3uF+aFkrIo3lQ/B06aO
UlEIv9xmaskMlRQeWwI3U0mvsD3ABUFjW6r7ZIrcKAECgYEA/qehThgYazhTp76p
dvztHCpqhocJdb7n5hM5psP3VXAn1/zyfTGJGs4LQTNLrJ7gfQ+1YxVVyfpHEHtq
BxHVoDuA8v342l9xP/NJL4HMagoUyhBlJ3EOIEIqtiiF3xlq8QQlRP1s9jVF3b06
kPsV0Ck/I6U8I3v9U61vFrETwRsCgYEA+ofyxJAPKwgnZX8zPaML9lJiGmbbIrDn
yyPFgOntoIpAGEl+0aMklz3dMcnUCJEZmcAKmSvOL3naViKZNgK0+iNHSaiWE8ne
8IECP6KAMd1Dm0MzpxEmPahMeV+4U9GHSz+OxL5B/kj9FCq3oEodAm69IqSvAprR
WphwwCBiDcECgYAPLWp9vw3lhgvmWYS8JML1BMoojm/P5rrniYnMGK3rF64oP1ks
gQFM6a7eCfKerTFwArmq2CCu0w3dO53MIhH7ZNCAqwZj7YBQcW2ROUk3oLYwfN2q
hiBzZ74n8S5ZZ6hqCPc3r2sJGY+6cYbGEVDxgSPUOgrlioREsneGgLNOtQKBgBRV
Sk/HvWNpswDa1QbQn9zrDMlFxc1H/FgRXCs9USrxbYhLFr7e9c5MmBI9ZjcXx7Mh
0fpigsZ5pk3NWw/2IkgW6udAhoWuoah1YABYKP1jDuSgDKYnjyn76dEEAsrSu59Q
1j6Djomb1OZ5HRQmT7pt0G3qXcXhWNJ4gtYlCrBBAoGBAL+pAZQ4nDhb3KviCb9P
hlwgULkLwvlDycQEeRcZ6hW0jzvAABVL0v4fogCsIb+eCkVtCK9lfAFxEhoiSSqS
5mW0wdRU6RoRXE6WmWU/InjWEeNkw2NYKBdKOHWOO00GdYI2WhAOREAngLfa/wqf
r2J9yZi/JwN3TmIbPXEXK01A
-----END PRIVATE KEY-----

View File

@ -1,19 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDGzCCAgOgAwIBAgIJANKZDvEMm9hrMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNV
BAMMIGNhY2tsaW5nIGNyeXB0b2dyYXBoZXIgZmFrZSBST09UMB4XDTE1MTAyMDIw
MzE0MloXDTI1MTAxNzIwMzE0MlowKzEpMCcGA1UEAwwgY2Fja2xpbmcgY3J5cHRv
MIIDGzCCAgOgAwIBAgIJAM0xz+I2Q811MA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNV
BAMMIGNhY2tsaW5nIGNyeXB0b2dyYXBoZXIgZmFrZSBST09UMB4XDTE1MTAyMTIw
MTE1MloXDTIwMTAxOTIwMTE1MlowKzEpMCcGA1UEAwwgY2Fja2xpbmcgY3J5cHRv
Z3JhcGhlciBmYWtlIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCuiPhqvx+SJ1QQo+qFKJ05tDJ5IfOyxbtxuzQnC2JhHhB8PHonpUfYE/8Y96Pd
MWg7+TDRwKYSe1p+Z1VtWk9WmrO2xNMUqJ0+wfSzkg7RIOYARBQNgpd/edGweCjc
mpKe2m7SgBaMlmJWWoJtkYziejMGXnh2NIQStT0EDQTxIG42X5JVCOHl3vrYG/M8
LkhU+1/Z3EwodMayh7XpAih33qr9pAaHLJYQ9I9q0GbXCBd7g50P6q1nc4DMOZxr
lKbakL6OLKmk5iencbZIER0Mi2HR6W1aep/dlvOSXrrhfkdiuwrw9yRhG6VoCi8G
ctwabow/+4NeRp8sB6I9zglZAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTPSdURwfe4U7VaWeV+5D7kOnHQRzANBgkq
hkiG9w0BAQsFAAOCAQEAQOWe0K86JT5RsCQGFdPF5gx+YUTPLhsR8o+wnFc3R0GH
mmVDz/FUfHg8RrpcIcc7Hn9PMpSChVJFxc0aOxGmetSQHOboieVq+wQ95fIgAkBE
YtW0LvVCg1AqPwOZOLHWfrnhAz9YUkouBfpUQv+7P3XWsaiJEWdv9TgeknaUSxt/
Md/tUGLQC7+LaFIss/28QQm0n19KPhQ9Tj/Fyb/SPPHhJ5WHAH52H/m9rQy6q7Ts
8+ls03XFriP6QoU7c3Q+kW6TLw7mXGPccGHy8nY2GmBNwHA04fIVXTwok83wohnO
ge17w48vRJ+gNCvr3V5ucNQlA/wbGS71nExCSGE5dA==
AQD5Nu9qUddBg7oaElQlkjmYXaTj6dkMi+pbKuPD7aT35QBx6S4so+lJyW71oFG9
SCDDdcUcBb3mpboO/b7mC+KdSZOr1HLuRVNRgDHJm7XJ0e37AMROYUl95jr9+kEc
ciy4Ysi2qlQaavwPotE4YSp8VjYH37Tw7tmz4W58NGAsdYS8tN2k+SvN4Mmz06jq
DfyN9P1vj70CGz4W+/Cqd4CRt5mAELY1YoH52sy40FM9sX9bVrxzMDMd46zqrLR0
GVMc2Mvrb+j1xEo2Ip7cirAcl13TVXVotJSAht7Yes/0QLV2OdRdZHePHe6uxoq3
s9MqwLnZQmRGqdqLzQzNCPRbAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTppD/unqXm8tXXeWA8k6YuJI6XqjANBgkq
hkiG9w0BAQsFAAOCAQEAK7AgsNO9oGt2iu/bMyxidm4q9e2SdYB/8NNy8Xg3k8w4
8E2R4e4Z+/RUN7pVekEeJTkb0TZF1TjflbujSRFKfbthyqvFpca32baQCtbp7j3X
fr4ycXHjuOOTSnhPjyzox5ovklUsnPFii67Vk5mqVWiTA0877xvGfoAu6YhVQ4PW
NIE3tj2mAtRLavM7ml7mnFLYCT947tDszbWvWwS4b+4+5aoEWiCT7hn2iYpVl1EN
vOb29hQBOARl2VN7a1y3cFUFDbNZt7RHay8i4KzxfMRHKS9+/UlrDEjrwj283QOH
2+P/EMzq52it8AuWGOBB1+2Zsei9EuknVlI4whQ0Zg==
-----END CERTIFICATE-----