{"id":229,"date":"2013-07-28T23:27:09","date_gmt":"2013-07-28T21:27:09","guid":{"rendered":"https:\/\/blogs.gentoo.org\/mgorny\/?p=229"},"modified":"2013-08-02T11:48:41","modified_gmt":"2013-08-02T09:48:41","slug":"ssl-certificate-login-for-okupy","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/mgorny\/2013\/07\/28\/ssl-certificate-login-for-okupy\/","title":{"rendered":"SSL certificate login for okupy"},"content":{"rendered":"<p>In\u00a0this year&#8217;s GSoC, I&#8217;m co-working on\u00a0<em>identity.gentoo.org<\/em> that intends to\u00a0become a\u00a0central place for\u00a0logging in to various Gentoo sites. One of\u00a0the\u00a0fancy features that I&#8217;d like to implement is SSL-certificate based login.<\/p>\n<p>The\u00a0client certificate-based authentication is a\u00a0kind of\u00a0public key authentication. In\u00a0order to login, you provide the\u00a0server with your certificate (containing your public key) and\u00a0a\u00a0signature made with your private key. Your private key is never exposed to the\u00a0server which greatly improves security, and\u00a0you don&#8217;t have to have a\u00a0store of\u00a0random passwords.<\/p>\n<p>Unlike many common authentication methods that are implemented in\u00a0application space, client certificates are part of\u00a0the\u00a0SSL\/TLS protocols. As a\u00a0result, they&#8217;re supported quite widely by\u00a0web browsers. But this also introduces a\u00a0few limitations that will affect the\u00a0use.<\/p>\n<p><!--more--><\/p>\n<h2>How does it work?<\/h2>\n<p>Well, first of\u00a0all you need to obtain a\u00a0client certificate. The\u00a0easy way is to use a\u00a0CA like <a rel='external' href='https:\/\/www.cacert.org\/'>cacert.org<\/a>. The\u00a0hard way is to do some voodoo using OpenSSL or\u00a0some other SSL tools. But\u00a0note that with the\u00a0latter, you&#8217;re going to get a\u00a0certificate that others will not trust.<\/p>\n<p>When using cacert to generate the\u00a0certificate, the\u00a0site generates a\u00a0certificate request for your browser. Your browser generates the\u00a0private key and\u00a0doesn&#8217;t disclose it to cacert, just the\u00a0public key. Then, cacert gives you a\u00a0nice, signed certificate to download and\u00a0install. Well, if your browser supports it.<\/p>\n<p>If you&#8217;re using the\u00a0all-so-awesome Chrome\/Chromium, start another browser first (Opera and\u00a0Firefox will work). Generate the\u00a0key and\u00a0install the\u00a0cert as usual. Then export is as PKCS\u00a012 (with the\u00a0secret key) and\u00a0import that to\u00a0Chromium.<\/p>\n<p>Now, whenever you connect to a\u00a0SSL\/TLS-enabled site a\u00a0handshake is performed. During the\u00a0handshake, encryption is negotiated and\u00a0secret that will be used to encrypt the\u00a0actual data is\u00a0created. The\u00a0server authenticates itself with a\u00a0certificate so that can verify if it&#8217;s the\u00a0server he intended to connect.<\/p>\n<p>Optionally, the\u00a0server may request the\u00a0user to authenticate himself with a\u00a0client certificate. When that happens, all web browsers I&#8217;ve tested open a\u00a0certificate selection pop-up (even if\u00a0there&#8217;s only one certificate installed) and\u00a0ask user to\u00a0either choose a\u00a0certificate or\u00a0refuse the\u00a0authentication.<\/p>\n<p>Along with the\u00a0certificate, a\u00a0signature made using user&#8217;s secret key is\u00a0sent. At\u00a0the\u00a0very least, server checks the\u00a0signature to\u00a0ensure that user is\u00a0eligible to use the\u00a0certificate. Additionally, server may check whether the\u00a0certificate was issued by a\u00a0trusted issuer.<\/p>\n<p>The\u00a0result of\u00a0the\u00a0check along with the\u00a0client certificate becomes property of\u00a0the\u00a0SSL session. It is therefore exposed to the\u00a0application within each HTTPS request it serves.<\/p>\n<h2>How the\u00a0user is authenticated?<\/h2>\n<p>So far we&#8217;ve covered the\u00a0certificate exchange and\u00a0validation. However, even if\u00a0we know that the\u00a0certificate is valid and\u00a0the\u00a0client is eligible to use it, we still need to somehow map it to a\u00a0real user account. There are various approaches to that.<\/p>\n<p>One of\u00a0the\u00a0simpler approaches is the\u00a01:1 certificate-user mapping. It is used by\u00a0<a rel='external' href='http:\/\/www.certifi.ca\/'>certifi.ca<\/a> for\u00a0example (though done poorly). In\u00a0this case, the\u00a0site stores explicit mapping of\u00a0client certificates (usually through their fingerprints) to\u00a0users.<\/p>\n<p>The\u00a0advantage of\u00a0this method is that it doesn&#8217;t require any specifics from the\u00a0certificates. The\u00a0certificates don&#8217;t even need to be\u00a0issued by\u00a0a\u00a0trusted CA since the\u00a0fingerprint requirement provides good enough security. It also gives admin the\u00a0full control over accepted certificates. The\u00a0disadvantage is that every time the\u00a0certificate expires, admin needs to update the\u00a0fingerprint in the\u00a0database. For\u00a0example, certifi.ca failed to\u00a0support this and\u00a0leaves your account stale after certificate revocation.<\/p>\n<p>Alternate approach is through dedicated login information. This could some custom data or\u00a0just reuse of\u00a0one of\u00a0the\u00a0standard fields. It could contain your username, some kind of\u00a0identifier. This would avoid the\u00a0above issues since you could generate more certificates with the\u00a0same login data and\u00a0you could make the\u00a0data easily match what&#8217;s in the\u00a0user database.<\/p>\n<p>However, since certificates are basically semi-public, the\u00a0server needs to be\u00a0restricted to support only certificates from\u00a0your CA or\u00a0otherwise others will be able to create \u00abfake\u00bb certificates with the\u00a0same login data. And\u00a0since your CA is usually untrusted, your users may have some trouble importing the\u00a0certificate. So it&#8217;s more work for\u00a0you and\u00a0your users.<\/p>\n<p>As\u00a0a\u00a0special case of\u00a0the\u00a0above, the\u00a0e-mail address can be used. Assuming that each user&#8217;s e-mail address is globally unique and\u00a0that each CA is supposed to verify the\u00a0e-mail address before creating a\u00a0certificate using it, it should allow you use to\u00a0use practically any certificate issued by\u00a0a\u00a0trusted CA.<\/p>\n<p>Our site will most likely use the\u00a0last variant. That will allow us to transparently support client certificates issued by\u00a0cacert and\u00a0therefore establish the\u00a0certificate login with no\u00a0need for\u00a0creating our own certificates or\u00a0maintaining the\u00a0list of\u00a0trusted certificates.<\/p>\n<h2>When the\u00a0user will be\u00a0authenticated?<\/h2>\n<p>The\u00a0another question is when are we going to authenticate the\u00a0user. The\u00a0primitive methods, such as password authentication, usually involve dedicated login form and\u00a0POST submission. However, the\u00a0certificate authentication needs to be performed during SSL handshake (or\u00a0renegotiation) and\u00a0that is\u00a0done by\u00a0the\u00a0HTTP server rather than the\u00a0application. Therefore, we&#8217;re basically limited to what the\u00a0server permits us to do.<\/p>\n<p>The\u00a0simpler solution is transparent all-site authentication. That is, to enable the\u00a0optional client verification for the\u00a0whole site. The\u00a0advantage of\u00a0that method is that the\u00a0login process is\u00a0almost immediate for most users. If\u00a0user with the\u00a0client certificate installed visits the\u00a0site, he is instantly asked for it.<\/p>\n<p>Supposedly, user with no client certificates does not even notice the\u00a0certificate login support. However, user which has client certificates but\u00a0doesn&#8217;t want to use them gets asked for\u00a0them unnecessarily on\u00a0every visit.<\/p>\n<p>Worse than that, there&#8217;s practically no way of\u00a0implementing logout or\u00a0certificate switching this way. If\u00a0user chooses a\u00a0wrong certificate or\u00a0simply wishes to end the\u00a0session, he actually needs to end the\u00a0SSL session. No browser I know of allows user to force renegotiation or\u00a0session shutdown explicitly other way than through closing it (the\u00a0browser, not just the\u00a0tab). And\u00a0the\u00a0application can&#8217;t enforce that either.<\/p>\n<p>Therefore, you&#8217;ve got just one shot at\u00a0it. The\u00a0site asks for your certificate before you even see it \u2014 e.g. Chromium doesn&#8217;t allow you to\u00a0see the\u00a0site certificate first, so you may end up disclosing privacy sensitive information such as\u00a0your e-mail address. If you refuse, you won&#8217;t be able to login using certificate without closing the\u00a0browser. If you choose the wrong certificate, you have to close the\u00a0browser.<\/p>\n<p>The\u00a0alternative solution is to use dedicated virtual hosts for\u00a0SSL certificate login and\u00a0enable certificate verification only on those hosts. That is, put a\u00a0link like \u00abSSL client certificate login\u00bb in\u00a0the\u00a0login form, and\u00a0make it point to another virtual host, with the\u00a0hostname being unique to the\u00a0request (e.g. some random identifier). Verify the\u00a0certificate there, then redirect back.<\/p>\n<p>In\u00a0this case, client first sees the\u00a0site. Whenever he wants to login, he needs to explicitly state that he wants to use a\u00a0client certificate. The\u00a0element of\u00a0surprise is gone.<\/p>\n<p>The\u00a0SSL certificate login is done on\u00a0dedicated vhost, and\u00a0the\u00a0client certificate choice doesn&#8217;t leave that domain. That is, as\u00a0soon as\u00a0certificate is verified, the\u00a0user is redirected back to certificate-free zone where the\u00a0login was purely part of\u00a0application logic. If\u00a0user wishes to\u00a0log out, we just wipe the\u00a0login from\u00a0his application. If\u00a0he wishes to\u00a0log in again, we give him a\u00a0new hostname so that he can safely choose the\u00a0certificate again.<\/p>\n<p>The\u00a0disadvantage of\u00a0this method is that it is no\u00a0longer fast and\u00a0transparent. You need to explicitly request the\u00a0login and\u00a0choose the\u00a0method. You need to resolve additional hostname and\u00a0start a\u00a0new SSL session for a\u00a0single request \u2014 that is a\u00a0fairly high price for\u00a0login but\u00a0likely inevitable one. Plus we need a\u00a0server certificate with wildcards.<\/p>\n<p>We haven&#8217;t yet decided which variant to use but most likely the\u00a0second one will be preferred. It gives certificate login the\u00a0flexibility of\u00a0primitive login methods, so I&#8217;d consider it combining the\u00a0advantages of\u00a0both.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In\u00a0this year&#8217;s GSoC, I&#8217;m co-working on\u00a0identity.gentoo.org that intends to\u00a0become a\u00a0central place for\u00a0logging in to various Gentoo sites. One of\u00a0the\u00a0fancy features that I&#8217;d like to implement is SSL-certificate based login. The\u00a0client certificate-based authentication is a\u00a0kind of\u00a0public key authentication. In\u00a0order to login, you provide the\u00a0server with your certificate (containing your public key) and\u00a0a\u00a0signature made with your private &hellip; <a href=\"https:\/\/blogs.gentoo.org\/mgorny\/2013\/07\/28\/ssl-certificate-login-for-okupy\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;SSL certificate login for okupy&#8221;<\/span><\/a><\/p>\n","protected":false},"author":137,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[9],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/229"}],"collection":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/users\/137"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/comments?post=229"}],"version-history":[{"count":19,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/229\/revisions"}],"predecessor-version":[{"id":248,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/229\/revisions\/248"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/media?parent=229"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/categories?post=229"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/tags?post=229"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}