Subject: [TLS] [Fwd: WWW-Authenticate challenge for client-certificates]

From: Bruno Harbulot <[email-masked]>
Date: Tue, 19 Jan 2010 14:40:54 +0000

Hello,

Apologies if this is slightly off-topic, but I thought some IETF TLS 
subscribers might find this relevant:
http://lists.w3.org/Archives/Public/ietf-http-wg/2010JanMar/0047.html

Best wishes,

Bruno.

-------- Original Message --------
Subject: WWW-Authenticate challenge for client-certificates
Date: Tue, 19 Jan 2010 13:35:23 +0000

Hello,

I'd like to suggest ideas towards the potential specification of a
challenge for client-certificate authentication using HTTP over TLS.

The current model of TLS client-certificate challenge doesn't integrate
well with HTTP, as there is no way for a server to tell the client it
requests or accepts a client-certificate at the HTTP layer: this must be
done at the TLS layer, without any proper integration with the HTTP
response status codes. This can lead to confusing behaviour.

For example, most TLS frameworks either "request" or "require" a
client-certificate (using the Apache Httpd terminology).
In 'request' mode, the server asks for a client-certificate, but will
continue the connection if the client doesn't present one
("setWantAuthentication" in Java).
In 'require' mode, the server asks for a client-certificate, but makes
the TLS handshake fail if the client doesn't present one
("setNeedAuthentication" in Java).

This leads to a couple of problems:
- The HTTP application is unable to inform the client it would rather
have another certificate than the one that is presented. Whether-or-not
to accept the certificate is done by the trust management mechanism of
the TLS layer, and it is difficult for the HTTP application to deal
with. Often, once the certificate has been negotiated, browsers usually
keep the TLS connection open and changing certificate can often only be
done by closing the browser or letting the connection time out.
- If a certificate is mandatory, the 'require' mode leads to a handshake
error code on the client side, resulting in an abrupt error message,
which is confusing for most users.

To try to solve this, I would like to suggest the introduction of
something along the lines of a 'transport' challenge, implying that the
authentication mechanism is outside the scope of HTTP itself.
This could be something like:
- WWW-Authenticate: transport
- WWW-Authenticate: transport mode="tls-client-certificate"

To this, could be added parameters describing what kind of certificate
is preferred.

As a side note, I also think that such a mechanism could partly help
with the TLS renegotiation issue (still discussed on the IETF TLS
mailing list). Indeed, if the HTTP framework was able to detect a
renegotiation had occurred, it could make the client re-send the request
post-negotiation (rather than assume that the certificate presented
during the second handshake were also valid for the request sent before).

I realise that the details of what I'm suggesting are still a bit vague,
but I'm wondering whether the general idea would be of interest.

Best wishes,

Bruno.

---

From: Nicolas Williams <[email-masked]>
Date: Tue, 19 Jan 2010 09:01:21 -0600

> http://lists.w3.org/Archives/Public/ietf-http-wg/2010JanMar/0047.html

Many of us have lamented the disconnect between the application layer
and TLS when it comes to client authentication.  This seems like a good
step forward.

Nico
--

---

From: Martin Rex <[email-masked]>
Date: Tue, 19 Jan 2010 22:34:17 +0100 (MET)

> which is confusing for most users.

Clarifying:  If the server's TLS is configure to require a client-cert,
the server's TLS will produce the error message (and terminate the
TLS handshake with a fatal alert message), which in turn will
cause the client TLS implementation to report an error.

> To this, could be added parameters describing what kind of certificate

Unfortunately, the issue is _much_ more complex.
Difficulties you have to address:

  1. whether or not the server requests a client certificate
     is a decision of the server alone.  The client must not
     send an unsolicited client certificate (that should cause
     compliant servers to abort the handshake).

  2. there currently is _no_ standardized fashion for TLS to
     negotiate certain aspects of client certificate authentication

      a) client->server signaling that client des not have
         a certificate (or is determined to not send one)

      b) client->server signaling that client has a certificate
         and is willing to use it for client certificate authentication
         (provided that server certificate validation succeeds).

      c) server->client signaling that client may send *any*
         certificate with the promise that the server will _NOT_
         abort the handshake when the server does _not_ have a
         trust anchor necessary to verify the certificate sent
         by the client (requiring the server to either withhold
         such a client cert from the server application, as if
         the client had _not_ sent any cert -- at least _not_
         provide such a client cert to server apps in the same
         fashion as authenticated client certs.

         SSL/TLS client cert authentication kind-of blindly assumes
         a PKI and SSLv3&TLSv1.0 _require_ a server to send a
         non-empty list of trusted CAs.  Semantics of empty ca_list
         in TLSv1.1&1.2 is underspecified.

   3. (Bodo Moeller recently explained this to me):
      cryptographic algorithm constraints on client certificates
      According to rfc-5246 Section 7.4.6. there are constraints
      if the client certificate is of one of these types:
      rsa_fixed_dh, dss_fixed_dh, rsa_fixed_ecdh, ecdsa_fixed_ecdh

      http://tools.ietf.org/html/rfc5246#page-56

      restricting compatible client certs that can be used for
      client cert authentication in TLS to _both_, characteristics
      of the cipher suite plus characteristics of the server certificate

      This additional restriction does not apply to client certificates
      rsa_sign, dsa_sign (and marginally to ecdsa_sign).

> during the second handshake were also valid for the request sent before).

Changing/switching client identities is also a somewhat underspecified area.

In theory, clients can offer some means to "flush" the client-side
TLS session cache (several browsers have that, I think), but it
may be a function hidden in some configuration options many casual
users have never used or maybe even looked at.

I'm not sure that current / commonly used browsers, which offer
multi-windows and tabbed browsing, have an adequate client-side
session cache management (in the browser) to reliably manage many
parallel sessions with the exact same server and individual
TLS client identities/credentials for each session.

-Martin

---

From: Bruno Harbulot <[email-masked]>
Date: Wed, 20 Jan 2010 15:33:25 +0000

Hello,

> cause the client TLS implementation to report an error.

Indeed.

>> To this, could be added parameters describing what kind of certificate

Just to clarify, I'm not suggesting implementing the client-certificate 
negotiation (or renegotiation) at the HTTP level.

I'd just like to address a gap in the HTTP authentication space, more 
specifically, the 401 status code says there MUST be one or more 
'WWW-Authenticate' header (with one or more challenges each).

If an HTTP client tries to access resource on an HTTP server that is 
protected and where authentication is required, the HTTP server will 
respond to the request with an HTTP 401 status code, and, for example 
"WWW-Authenticate: Basic xxxxx", thereby telling the client that it 
should send the request again, with appropriate authentication information.

This mechanism is useful in that:
- the WWW-Authenticate in the HTTP header is sufficient for the browser 
to know what to do (e.g. pop-up).
- a entity can still be associated with a response with 401 status code: 
there can be a page telling the user why access wasn't granted, where to 
get an account, etc.

At the moment, if a browser tries to connect to a page that requires TLS 
client-certificate authentication for granting access, and if this 
certificate isn't provided (irrespectively of how the TLS handshake 
went), the server will be most likely to return a 403 status, which 
implies that "the request SHOULD NOT be repeated" [RFC2616].

The current behaviour could be summarised as follows:
- Server configured for 'require' - Client sends a suitable certificate.
   - If this user is authorised: HTTP 200 OK, otherwise HTTP 403.

- Server configured for 'require' - Client doesn't send a certificate.
   - Server's TLS error message -> Client error message.
  This is the problematic behaviour in browsers: it confuses users and 
doesn't give much of a chance to tell them what was wrong with their 
certificate or where to get their certificate.

- Server configured for 'request' - Client sends a suitable certificate.
   - If this user is authorised: HTTP 200 OK, otherwise HTTP 403.

- Server configured for 'request' - Client doesn't send a certificate.
   - Will probably get 403, without a way to tell to send a certificate 
this time.

In HTTP terms, the 'require' configuration is just "rude" and the 
current 'request' configuration (without proper 401/WWW-Authenticate 
response) doesn't give much opportunity to provide an explanation and to 
try again.

This also prevents the server to present the client-cert authentication 
as one of a number of challenges. For example, using 'WWW-Authenticate', 
a server can offer an HTTP Basic challenge and a SPNEGO challenge at the 
same time, leaving the choice to the client.

>      compliant servers to abort the handshake).

Indeed, but I don't think there would need to be a "pre-emptive" 
'Authorization' header in the same way in can be done with HTTP Basic 
for example. In fact, I don't think a request, even as a response to a 
401 status would need to have such an 'Authorization' header. All it 
would need, would be to have sent the client-certificate, through the 
TLS channel (perhaps re-negotiated, perhaps after a new connection).

This could be a good way to indicate to a browser that, even on the 
first attempt, it hadn't presented a certificate by default (assuming 
the server is in 'request' mode) for the first attempt, it should tell 
its TLS layer to send one for the second attempt.
Perhaps some indication of "realm" or something similar might help the 
browser make a better choice as to which certificate to use (this could 
be combined with improvement in user interfaces).

>          (provided that server certificate validation succeeds).

I suppose you mean signaling before the handshake? Otherwise, sending an 
empty or non-empty Certificate message (from client to server) during 
the handshake should cover those two cases, respectively. Isn't this 
standard? I'm not sure what the problem would be otherwise.

>          in TLSv1.1&1.2 is underspecified.

In my experience with SSLv3 and TLSv1.0, more particularly Java's JSSE 
and Apache Httpd, even though there was no mention of emtpy ca_list in 
these versions, it works fine.

In Apache Httpd, using SSLCADNRequestFile with a file containing just a 
line return is enough to make it send an empty ca_list. Combined with 
"SSLVerifyClient optional_no_ca", it can accept any self-signed (or not) 
certificate.

In Java, setting the TrustManager do to this works fine:
     public X509Certificate[] getAcceptedIssuers() {
         return new X509Certificate[0];
     }

(That's something we use doing the FOAF+SSL work, where we verify the 
certificate at the application level, once the handshake is finished.)

This being said, I'm not suggesting making changes to the TLS 
specification, just conveying some extra information at the HTTP layer 
regarding the authentication that happens at the TLS layer, for better 
integration with the HTTP mechanisms.

>       rsa_sign, dsa_sign (and marginally to ecdsa_sign).

That's something I would leave as it is, for the TLS handshake to do. I 
don't think an WWW-Authenticate header should influence this.

If a 'WWW-Authenticate' for TLS certificate authentication was also to 
specify which certificate was preferred (something I'm not sure about at 
the moment), then the client would probably have to abide by the TLS 
requirements first and use the HTTP preference as a further hint only. 
For example, the ca_list sent by the server could contain a number of 
DNs, but the 'WWW-Authenticate' would have an order of preference, for 
this particular resource; if the preference list at the HTTP level was 
incompatible with the ca_list at the TLS layer (or other TLS 
requirements as you mention), then the TLS layer would prevail.

> users have never used or maybe even looked at.

Indeed, in Firefox you can do this by logging out of the software 
security device (Preferences -> Advanced -> Encryption -> Security 
Devices -> 'Software Security Device' -> Log Out), and, in Internet 
Explorer: Internet Options -> Content -> Clear SSL State.

> TLS client identities/credentials for each session.

That's unfortunately not specific to TLS authentication. Browsers tend 
to have a single sign-on across tabs and windows, where you're logged on 
forever across all of them, for Basic/Digest/TLS/Cookie authentication 
with the exception that only cookies/forms tend to have a convenient log 
out mechanism. (Again, logging out of the software security device also 
logs you out in Firefox, but that's quite far away.)

Best wishes,

Bruno.

---

From: Joe Orton <[email-masked]>
Date: Thu, 21 Jan 2010 16:58:44 +0000

...
> which is confusing for most users.

I don't see how either of these problems are a consequence of protocol 
design, merely server implementation or configuration.

It is simple to configure mod_ssl to avoid the second problem - you make 
client certificates requested but optional ("SSLVerifyClient optional"), 
and then use an HTTP authorization-level check to present an HTTP 403 
error if in fact the TLS layer did not receive a client cert (using 
"SSLRequire").  The 403 error page can be customized as usual.

You could do something similar for the first problem though the 
configuration might be a little awkward.

Regards, Joe

---

From: Bruno Harbulot <[email-masked]>
Date: Fri, 22 Jan 2010 11:25:04 +0000

Hello,

> "SSLRequire").  The 403 error page can be customized as usual.

Yes, but 403 is exactly the problem. That's not really a TLS problem, 
but more of how TLS authentication is handled by HTTP, differently from 
the other HTTP-specified authentication mechanisms (except form/cookies, 
but someone is working on a spec for these already).

As RFC 2616 (HTTP) says:
"10.4.4 403 Forbidden
The server understood the request, but is refusing to fulfill it. 
Authorization will not help and the request SHOULD NOT be repeated. [...]"

This is why I think that, when no certificate or an insufficient 
certificate is presented, 401 would be more appropriate:

"10.4.2 401 Unauthorized
The request requires user authentication. The response MUST include a 
WWW-Authenticate header field (section 14.47) containing a challenge 
applicable to the requested resource. The client MAY repeat the request 
with a suitable Authorization header field (section 14.8). If the 
request already included Authorization credentials, then the 401 
response indicates that authorization has been refused for those 
credentials. If the 401 response contains the same challenge as the 
prior response, and the user agent has already attempted authentication 
at least once, then the user SHOULD be presented the entity that was 
given in the response, since that entity might include relevant 
diagnostic information."

In addition, the use of a 401 status code would allow for configurations 
where the server can present two challenges at the same time, for example:

HTTP/1.0 401 Authorization Required
WWW-Authenticate: Basic realm="..."
WWW-Authenticate: Transport mode="tls-client-certificate"

I don't see this as just being an implementation or configuration issue. 
The problem being that "[...the 401 response] MUST include a 
WWW-Authenticate header field", and that no such header is specified for 
client certificates (or perhaps SRP).

Best wishes,

Bruno.

---

From: Joe Orton <[email-masked]>
Date: Fri, 22 Jan 2010 11:43:25 +0000

On Fri, Jan 22, 2010 at 11:25:04AM +0000, Bruno Harbulot wrote:
> Yes, but 403 is exactly the problem. That's not really a TLS
> problem, but more of how TLS authentication is handled by HTTP,
> differently from the other HTTP-specified authentication mechanisms

I don't see what difference it makes to have a fake 401 challenge as 
opposed to a 403.  If the client doesn't have a cert to present when 
challenged, the best you can do is present an error page describing to 
them how they obtain a client cert.  The client *already knows* that a 
client cert was requested; telling them again in a fake 401 doesn't add 
additional information.

> In addition, the use of a 401 status code would allow for
> configurations where the server can present two challenges at the
> same time, for example:

Again, that is effectively already possible.  Send a client cert 
request, and a 401 challenge if none is presented.

Regards, Joe

---

From: Bruno Harbulot <[email-masked]>
Date: Fri, 22 Jan 2010 16:13:27 +0000

> client cert was requested; telling them again in a fake 401 doesn't add 

It has to do with the 'retry' ability 401 gives to the client. 403 
explicitly tells the client not to retry, even with different 
credentials. Thus, 403 always gives an error message to the end-user in 
a browser.
In contrast, 401 presents the challenge to the browser which is then 
able to repeat the request with an appropriate response to the challenge.
Like 302 redirections, this can be done transparently; the behaviour of 
the user-interface then depends on the configuration and the 
implementation, but a 401 response gives the opportunity to do something 
at the HTTP level.

> request, and a 401 challenge if none is presented.

Not really, this creates an imbalance that gives priority to the 
certificate. RFC 2617 (HTTP Basic and Digest auth spec) says "The user 
agent MUST choose to use one of the challenges with the strongest 
auth-scheme it understands and request credentials from the user based 
upon that challenge."
While between TLS certs and clear username/password with HTTP Basic, the 
certificate is probably always the strongest auth-scheme. It wouldn't 
necessarily be a clear cut in all cases, for example if the service 
offers both TLS certificate authentication and SPNEGO/Kerberos 
authentication. (There are probably crypto experts on this list who 
might have an opinion on this, but I don't think it's easy to say which 
one of Kerberos or TLS client certificates is the strongest, not sure it 
even makes sense to compare.)

With a 'Transport' scheme (aimed at whatever HTTP can't deal with 
directly, in particular, TLS certificates) you could have something as 
follows, letting the client decide which one it prefers:

HTTP/1.1 401 Authorization Required
WWW-Authenticate: Negotiate ...
WWW-Authenticate: Transport mode="tls-client-certificate"

Otherwise, if it presents a certificate, it won't be aware that Kerberos 
authentication could also have been used. This might have been a better 
choice (especially if the certificate that this client has is accepted 
by the server, but not sufficient to grant authorisation to the resource).

In addition, and that's a slightly separate issue, this could be a clean 
way to manage change of identity or credentials during renegotiation, 
which isn't really specified at the moment as far as I know.

More information at the HTTP layer, regarding specific resources, could 
be useful for choosing a certificate in cases where the client has more 
than one certificate accepted by the server's TLS settings (whether or 
not by the same CA).

This could be useful for the renegotiation bug. For example, in the 
following exchange where the renegotiation happens between "X-Ignore: " 
and "GET":

* Client->MitM->Server:
POST /create/something/bad HTTP/1.0
X-Ignore: GET / HTTP/1.1

* Server->Client:
HTTP/1.0 201 Created
...

Instead, if the server was able to convey to the HTTP layer that a 
renegotiation had happened, it would send a 401, telling the client 
simply to re-send its original request, to make sure it was indeed the 
client:

* Client->MitM->Server:
POST /create/something/bad HTTP/1.0
X-Ignore: GET / HTTP/1.1

* Server->Client (a new handshake was detected, so it sends 401 for safety):
HTTP/1.0 401 Authorization Required
WWW-Authenticate: Transport
...

* Client->Server:
GET / HTTP/1.1

* Server->Client:
HTTP/1.0 200 OK
...

(If the MitM tried to renegotiate again, there would be another 401, etc.)

Best wishes,

Bruno.