Introducing IndieCert
Published on 2015-02-02 | Last modified on 2015-03-08
More feedback is required before this document can be considered finished. See "Issues" section below.
Authenticating to web servers with a client certificate, installed in the user's browser, is unfortunately not widely used. They are convenient and safe to use once the initial hurdle of installing them is taken.
There is now a Proof of Concept instance! Check it out here!
The main benefit is easy and secure authentication. There is no need to provide a password to any service, only the URL to your home page. So there is no chance you will leak your password to some service.
In addition, this proposal describes a way to make the use of client certificates feasible:
- A self-signed CA is used instead of a 'browser trusted' CA to issue the client certificates, this works without browser warnings;
- Users authorize the certificates by publishing the certificate fingerprint on their home page allowing to claim ownership of a URL and by authenticating with the certificate directly;
- Allow identity (certificate) linking using the user's home page address to allow registration of multiple fingerprints to support multiple devices and browsers.
A possible drawback is that many users do not have a home page anymore. They have a Facebook profile or a Twitter account, but no home page. However, this is not relevant for IndieWeb as all users should have their own home page running on their own domain anyway :)
As a fallback it is possible to allow authenticating using existing social networks, similar to IndieAuth, instead of using the client certificates. Some browsers and operating systems unfortunately do not support easy client certificate enrollment.
Protocol for Relying Parties
The protocol follows the protocol proposed for IndieAuth. The protocol is based on the IndieAuth protocol and aims to be compatible with it.
Request Authentication
The service redirects the user to https://indiecert.net/auth
to
start the authentication phase. Two parameters need to be specified:
-
redirect_uri:
- the URL the browser should be redirected back to after the authentication is successful. This MUST be a valid HTTPS URL;
-
me:
- the URL to the user's home page (see Retrieving the User's Home Page);
Example
Below is an example of a browser redirect. The process can also be initiated
with a <form>
submit using the GET
method.
HTTP/1.1 302 Found
Location: https://indiecert.net/auth?redirect_uri=https://example.org/callback&me=https://tuxed.net/fkooman
IndieCert will at this point take care of the authentication and optional enrollment process if that was not already done.
Authentication Response
IndieCert will redirect the browser back to the redirect_uri
specified in the authentication request after the user is authenticated at
IndieCert.
Example
HTTP/1.1 302 Found
Location: https://example.org/callback?code=wW3OLXJZn35d7zFwg9YGmWti
Verification Request
The code
parameter can now be used to request the claimed user
identity. The following parameters are required:
Now a HTTP POST
can be used to obtain the user's (normalized) home
page URL:
POST /auth HTTP/1.1
Host: indiecert.net
Content-Type: application/x-www-form-urlencoded
Accept: application/json
redirect_uri=https%3A%2F%2Fexample.org%2Fcallback&code=wW3OLXJZn35d7zFwg9YGmWti
The response will be formatted as JSON indicating the actual user home page in
the me
parameter.
This is the identity that MUST be used by the relying party to identify the
user as it could differ from the initial me
specified by the user,
e.g.: redirects were followed to reach the user's home page.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"me": "https://www.tuxed.net/fkooman/"
}
In case there was a failure in verifying the code, e.g. it was already used, or not valid the following response can be expected:
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"error":"invalid_request"
}
Protocol for the IndieCert Service
The IndieCert service has to deal with certificates, issuing, installing and
verifying them. The most convenient method is to use the HTML
<keygen>
tag. This makes the browser generate a private key
and certificate request (SPKAC) that is then sent to the service for signing.
The service will sign this with the CA generated for this particular IndieCert
instance. The signed certificate is sent back to the client and (automatically)
imported in the browser certificate store.
After this, the certificate can be used to authenticate to the IndieCert
service, but it still needs to be linked to the URL of the user's home page.
This can be done by using the rel="me"
attribute in a
<link>
header tag or <a>
body tag.
<link rel="me" href="ni://indiecert.net/sha-256;WXXyBZDo1pZYiLbrCNGFtSdSamqEvSJJBRzpx-MNIUA?ct=application/x-x509-user-cert">
Once the user has placed this HTML element on their home page the URL it will
be found when the IndieCert instance will fetch the URL and extract all
<link>
and <a>
tags to look for the
rel="me"
attribute. The fingerprint of the client certificate
used to authenticate to IndieCert needs to be listed on the home page.
After that, the URL of the home page will be used as an accepted identifier.
Retrieving the User's Home Page
The user's home page MUST be fetched over HTTPS only, MUST follow redirects and
MUST NOT have any HTTP URLs in its redirect path. If the URL does not start
with https://
it MUST be added by IndieCert. If a URL starts with
http://
or is otherwise invalid it MUST be rejected. The final
URL, the URL that returns a 200 OK
status, MUST be used in the
response to the verification request, this is the normalized home page URL.
For example the user provides tuxed.net/fkooman
. IndieCert makes
this https://tuxed.net/fkooman
and starts the fetching process. It
is then redirected to https://www.tuxed.net/fkooman
and then to
https://www.tuxed.net/fkooman/
. The claimed identity thus becomes
https://www.tuxed.net/fkooman/
and this value is returned in
response to the verification request.
Identity Linking
One of the benefits of publishing fingerprints on the user's home page is that additional client certificate fingerprints can easily be added to the home page to allow those certificates to claim the same identity.
<!-- laptop -->
<link rel="me" href="ni://indiecert.net/sha-256;WXXyBZDo1pZYiLbrCNGFtSdSamqEvSJJBRzpx-MNIUA?ct=application/x-x509-user-cert">
<!-- phone -->
<link rel="me" href="ni://indiecert.net/sha-256;ThIaJ7TJQ1oAIKCGKe0BdBO3Bh8NzxZeyAa-WCTuzpU?ct=application/x-x509-user-cert">
Certificate revocation is done by removing the entry from the home page and it can no longer be used to claim the identity.
Fingerprint Generation
The fingerprint of the certificate is generated by calculating the SHA-256 hash over the DER encoded certificate and base64url encoding the resulting binary string according to RFC 6920 "Naming Things with Hashes" and RFC 4648 "The Base16, Base32, and Base64 Data Encodings". An example of calculating a fingerprint in PHP:
<?php
$string = 'Hello World!';
echo rtrim(strtr(base64_encode(hash('SHA256', $string, true)), '+/', '-_'),'=');
Distributed IndieCert
One of the important issues to solve is how to make this protocol distributed, i.e.: how to allow multiple instances of IndieCert to be used by different services whilst allowing a smooth user experience.
The relying party can choose to join an existing IndieCert instance, or run their own if they don't trust any of the existing instances. The benefit of running their own is better control and more trust and no requirement on third parties for the authentication to their service.
Assuming they want to run their own instance that would require the user to generate a new certificate and put the fingerprint on their home page as well. This results in additional overhead, for the user, but may be worth it from a security perspective. And the process needs to be repeated once per IndieCert instance, per device or even per browser. This the greatest weakness of this proposal, that and adoption. If everyone runs their own IndieCert instance it will quickly become a management nightmare for the user if they have to keep track of all certificates in all their browsers on all their devices. This could be solved by a tool allowing users to easily add fingerprints to their website.
For the actual user experience, the user just selects the specific certificate for the specific IndieCert instance (it can be pre-selected by most browsers based on the CA) so that should not be a problem.
Issues
There are a number of issues we have to solve before this document can be considered stable:
- CSRF. User X can generate a code for his identity and trick user Y into going to a relying party with user X's code to work under user X's identity, possibly storing private data under user X's identity instead of user Y's. We need to implement some kind of state generating/checking;
- Rate limiting on the verify endpoint;
- Many browsers, many devices, many instances of IndieCert is a scalability nightmare for the user...