I am wondering if there is any harm in having a TLS client send its certificate to a server before having established trust with that server.
Yes, this is non-compliance with the TLS standard. See RFC 5246 (TLS 1.2):
The handshake protocol messages are presented below in the order they MUST be sent; sending handshake messages in an unexpected order results in a fatal error. [...]
TLS supports three authentication modes: authentication of both parties, server authentication with an unauthenticated client, and total anonymity. Whenever the server is authenticated, the channel is secure against man-in-the-middle attacks, but completely anonymous sessions are inherently vulnerable to such attacks. Anonymous servers cannot authenticate clients.
A non-anonymous server can optionally request a certificate from the client, if appropriate for the selected cipher suite. This message, if sent, will immediately follow the ServerKeyExchange message (if it is sent; otherwise, this message follows the server's Certificate message).
It is a fatal handshake_failure alert for an anonymous server
to request client authentication.
On the other part of the question:
[...] I would like to conditionally present a client certificate only to a server that I trust, but still have the option of establishing a connection to the server presenting a certificate that I do not trust (in that case however w/o a client certificate). Unfortunately, the way the Golang TLS library is written, this doesn't seem to be possible [...]
I am not familiar with the Golang TLS library, however these are my experiences with other TLS client libraries. E.g. in Java, you can create an all-trusting trust manager as follows:
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
This will make you trust whatever certificate presented by the server and allow you to proceed with the handshake even if the certificate presented by the server should not be trusted otherwise. This solves part of your problem: but still have the option of establishing a connection to the server presenting a certificate that I do not trust
.
The next step is responding to the CertificateRequest
message produced by the server. If the TLS client trusted the server in the previous step, and it was sent CertificateRequest
message, it will respond with a matching client certificate, if it has one. I am not aware of a TLS client implementation which would allow you to reach this step but introduce custom logic to either send the client certificate or not, depending on either using "real" server certificate validation logic or a "fake" one - as above.
However, this is not what should worry you most. Even if you'd implement your own TLS client which doesn't send client certificate in response to CertificateRequest
, this gives you no control over how TLS server responds. In a typical scenario, this would terminate the handshake as per the RFC:
If the client does not send any certificates, the
server MAY at its discretion either continue the handshake without
client authentication, or respond with a fatal handshake_failure
alert. Also, if some aspect of the certificate chain was
unacceptable (e.g., it was not signed by a known, trusted CA), the
server MAY at its discretion either continue the handshake
(considering the client unauthenticated) or send a fatal alert.