I'm writing an interception proxy to monitor the requests sent from my browser. With https, what I'm doing is: when the browser makes a CONNECT request, the proxy connects to that host and gets its certificate. Then, the subject and subjectAltName of this certificate are used to generate a new certificate on the fly, which is presented to the browser to estabilish a SSL connection with the proxy.
All of these new certificates have as issuer a root self-signed certificate. The root certificate has already been imported as trusted in Firefox.
Yet, I still get an Untrusted connection warning when I try to connect and the following details:
www.google.com uses an invalid security certificate.
The certificate is not trusted because the issuer certificate is not trusted.
(Error code: sec_error_untrusted_issuer)
The browser I'm using is Firefox 28.0. I haven't quite understood why the connection results untrusted, seeing as I have installed the root certificate. Right now I'd have to add an exception for each website I visit, which is frankly very annoying and slows down everything.
I'm using pyOpenSSL to make certificates. The code I used to make the root certificate is:
from OpenSSL import crypto
CERT_FILE = 'myapp.pem'
KEY_FILE = 'myapp.key'
k = crypto.PKey()
k.generate_key(crypto.TYPE_RSA, 1024)
cert = crypto.X509()
cert.get_subject().O = "Myapp"
cert.get_subject().OU = 'MyApp Root CA'
cert.get_subject().CN = 'MyApp Root CA'
cert.set_serial_number(888)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(10*365*24*60*60)
cert.set_issuer(cert.get_subject())
cert.set_pubkey(k)
cert.sign(k, 'sha1')
with open(CERT_FILE, "wt") as cf: cf.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
with open(KEY_FILE, "wt") as kf: kf.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
Then, I use similar code to generate domain-specific certificates. Right now I'm working with an example using google only, although I'll be using some caching system as soon as I solve this.
root_cert = crypto.load_certificate(crypto.FILETYPE_PEM, 'myapp.pem')
root_key = crypto.load_privatekey(crypto.FILETYPE_PEM, 'myapp.key')
root_issuer = root_cert.get_issuer()
def make_example_cert(pem_data):
if os.path.exists('google.pem'): return #ugly hack to avoid remaking file
#load the certificate received from google
old_cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem_data)
#generate new key and certificate
pkey = crypto.PKey()
pkey.generate_key(crypto.TYPE_RSA, 1024)
new_cert = crypto.X509()
new_cert.gmtime_adj_notBefore(0)
new_cert.gmtime_adj_notAfter(10*365*24*60*60)
#set same subject of old cert
new_cert.set_subject(old_cert.get_subject())
#look for and set SNA of old cert
for i in range(old_cert.get_extension_count()):
ext = old_cert.get_extension(i)
if ext.get_short_name() == 'subjectAltName':
new_cert.add_extensions([ext])
#set root certificate as issuer
new_cert.set_issuer(root_issuer)
new_cert.set_pubkey(pkey)
new_cert.sign(root_key, 'sha1')
certfile = 'google.pem'
keyfile = 'google.key'
with open(certfile, "wt") as cf:
cf.write(crypto.dump_certificate(crypto.FILETYPE_PEM, new_cert))
with open(keyfile, "wt") as kf:
kf.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
#append root certificate to chain
with open(certfile, "at") as cf2:
cf2.write(crypto.dump_certificate(crypto.FILETYPE_PEM, root_cert))
return certfile, keyfile
I don't seem to find major bugs here, and when I view the certificate created it correctly has my own certificate root as issuer. Still, the browser says it's untrusted. It works if I add it as an exception, but for some websites I don't even have that option and the only thing I can do is viewing the details and clicking "Get me out of here".
Could the problem be in the code? Or is it a matter of configuring the browser?
If your CA cert has been imported into Firefox as a trusted CA, it should work.
"The certificate is not trusted because it is self-signed." and "The certificate is only valid for MyApp CA Root" indicate that you're visibly not serving the server certificates you think you are.
Either it's a simply bug in your code whereby you're sending the wrong certificate, or perhaps you might be sending the chain in the wrong order: CA cert first, whereas the End-Entity Certificate should be first (followed by its CAs, in signing order).