Archive

Howto: Check if a browser has CACert installed as an authority

Well, I am working on a project to sell virtualization instances, a.k.a. vps, in <a href="http://bitcoin.org/">Bitcoin</a>.

Anyway, that has nothing to do with this. The thing is that I decided to go for <a href="http://cacert.org/">CACert.org</a>'s certificates. Why?

  • They're free
  • They do the work; which is encrypting the connection with the browser AND verifying that I am me.
  • They're cause is cool

So, that said, there is a big problem. You need to check if your clients; more appropriately, their browsers, have CACert as an authority; which, most probably, they don't.

This makes me furious. Why don't they? Well:

Mozilla:
https://bugzilla.mozilla.org/show_bug.cgi?id=215243
Chromium:
http://code.google.com/p/chromium/issues/detail?id=78272
Opera:
http://my.opera.com/community/forums/topic.dml?id=71660
The rest:
http://wiki.cacert.org/InclusionStatus

So, as you can see, some are interested; some aren't; which is ridiculous!

If you have the time, the nerve, the words, the persuasive power, please, help us bump/solve these issues in the various fronts.

Anyway, I'll just let you know how to do it.

The technique

Through Javascript <yuck !>, we will require a link that is in the https realm. The link should offer nothing special. In my case, it's just your IP.

If it works; without complaints, your client will have CACert as an authority and you don't need to worry.

If it doesn't work, you will show an overlay with the links to the certs and revocation lists.

Also, in the case of chromium/Google Chrome; which use the system's store, we will offer a link to the wiki; where they can find out how to solve it.

The code

So, now, I'll show you my... code:

// main.js

// this function changes the display of the overlay section/div to block
function showOverlay() {
    document.getElementById( 'overlay' ).style.display = 'block';
}

// this function changes the display of the overlay section/div to none; it will make it disappear.
function removeOverlay() {
    document.getElementById( 'overlay' ).style.display = 'none';
}

// this one checks for the url and it will fail if you don't have the certs installed.
function detectCACert() {
    try {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "https://btcsrv.com/ip", false);
        xhr.send();
    } catch( err ) {
        console.log( 'There was an error: ' + err )
        console.log( 'CACert root certificate not installed.' );
        showOverlay();
    }

    if ( xhr.readyState == 4 && xhr.status == 200 ) {
        console.log( 'CACert root certificate installed.' );
    }
}

Now, it's important to have some CSS, right? I'd like that section/div to have "display: none" in the first place; so:

# CSS
/* Overlay */
section#overlay {
    background: rgba( 0, 0, 0, .7 );
    display: none;
    position: absolute; top: 0; left: 0;
    width: 100%; height: 100%;
    z-index: 10;
}

section.overlayContent {
    background: rgba( 255, 255, 255, 1 );
    border-radius: 20px;
    box-shadow: 10px 10px 5px #222;
    display: block;
    height: 420px;
    margin: auto;
    padding: 2em;
    position: relative;
    top: 10em;
    width: 50%;
    z-index: 10;
}

section.overlayContent button { position: relative; top: -1.5em; left: 98%; }
section.overlayContent h1 { text-align: center; }
section.overlayContent ul.links a { text-decoration: none; font-weight: bold; }

Now, I will use the detection function on the body tag of every page:

<body onload='detectCACert()'>

Now, there is one very important thing: <a href="https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS">Cross Site HTTP Requests</a>

Yep, you need to allow it. And, the way to do that in Sinatra is:

class MyApp < Sinatra::Application
    get "/ip/?" do
        cross_origin :allow_origin => [ 'http://btcsrv.com', 'https://btcsrv.com' ], :allow_methods => [:get]
        request.ip
    end
end

This will allow your website; in my case, http://btcsrv.com WITHOUT A FINAL SLASH to access https://btcsrv.com/ip with a GET method.

Well, that done, you can just try it out. Check out my http://btcsrv.com/ website without the certs and try installing them. Use Firefox, Opera or anything that isn't Chromium because, the example, is not gonna work just like that in chromium; a script is needed; which I will include right about now:

#!/usr/bin/env bash
# installCACertOnChrome: installs CACert on Google Chrome or Chromium; using the user's Store.
curl -O 'http://www.cacert.org/certs/root.crt'
curl -O 'http://www.cacert.org/certs/class3.crt'

certutil -d sql:$HOME/.pki/nssdb -A -t TC -n "CAcert.org" -i root.crt
certutil -d sql:$HOME/.pki/nssdb -A -t TC -n "CAcert.org Class 3" -i class3.crt

rm -f root.crt class3.crt

It is no secret that I am no Javascript wiz so, if you have any suggestions, they're welcome!&lt;/yuck&gt;