Search code examples
firefoxsslproxyopensslself-signed

Coding multiple certificates with same self-signed CA?


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?


Solution

  • 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).