aboutsummaryrefslogtreecommitdiffstats
path: root/posts/indiecert.md
blob: 8f5fedca2d7fa5b48a74e45ecbdcd0f5584a3f2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
---
title: Introducing IndieCert
published: 2015-02-02
modified: 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](https://indiecert.net)!
    
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.
    
<video controls="controls" width="575">
    <source src="https://storage.tuxed.net/fkooman/public/upload/blog/indiecert_auth_flow.webm" type="video/webm">
    <source src="https://storage.tuxed.net/fkooman/public/upload/blog/indiecert_auth_flow.mp4" type="video/mp4">
</video>

In addition, this proposal describes a way to make the use of client certificates 
feasible:
    
1. A self-signed CA is used instead of a 'browser trusted' CA to issue the client certificates, this works without browser warnings;
2. 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;
3. 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](https://indiewebcamp.com/) 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](https://indieauth.com), instead 
of using the client certificates. Some browsers and operating systems 
unfortunately do not support easy client certificate enrollment.

<img src="https://storage.tuxed.net/fkooman/public/upload/blog/keep-calm-and-use-certificates.png" width="300" height="350" alt="keep calm and use certificates">

### 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](#retrieve_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&amp;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:
    
<dl>
    <dt>`code`</dt>
    <dd>the code obtained in the authentication response query parameter `code`.</dd>
    <dt>`redirect_uri`</dt>
    <dd>the URL the browser was redirected back to after the successful authentication;</dd>
</dl>

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&amp;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.

<video controls="controls" width="575">
    <source src="https://storage.tuxed.net/fkooman/public/upload/blog/indiecert_enroll.webm" type="video/webm">
    <source src="https://storage.tuxed.net/fkooman/public/upload/blog/indiecert_enroll.mp4" type="video/mp4">
</video>

#### 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...
    
### References

- [IndieAuth](https://indieauth.com)
- [IndieWebCamp](https://indiewebcamp.com)
- [RelMeAuth](http://microformats.org/wiki/RelMeAuth)
- [Certificate Download Specification](https://wiki.mozilla.org/CA:Certificate_Download_Specification)
- [Keygen Tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen)
- [Naming Things with Hashes](http://tools.ietf.org/html/rfc6920) (RFC 6920)
- [The Base16, Base32, and Base64 Data Encodings](http://tools.ietf.org/html/rfc4648) (RFC 4648)
- [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749) (RFC 6749)