Vulnerability in BC (D)TLS server, fix in new beta release 1.51b12
David Hook <dgh-lQXO3U89oAbxy1ys+oinMti2O/[email protected]> Thu, 17 Apr 2014 16:55:29 +1000
| Newsgroups | gmane.comp.encryption.bouncy-castle.announce,gmane.comp.encryption.bouncy-castle.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi All,
With thanks to Andreas Ritter, we have been made aware of a serious
vulnerability for anyone using our TLS server implementation with
client-authentication enabled. The verification checks are broken in
releases up to and including 1.51b11, and _fail silently_.
By default, we have not enabled client-auth, but it is possible that
users are already overriding TlsServer.getCertificateRequest in a
similar way to our unit tests, thinking it works, and therefore may be
affected.
- Affects any DTLS or TLS implementation (at any protocol version) that
is overriding TlsServer.getCertificateRequest() to prompt the client to
authenticate.
- Fails silently, allowing any client to "authenticate" by mere
possession of a valid certificate.
- DTLS or TLS client code is not affected.
- The C# TLS API does not yet support server-side operation and so is
unaffected.
We have now fixed the silent failure problem, and completed the client-auth
server-side implementation for (D)TLS 1.2 (thanks Andreas Ritter again
for patch), and added test coverage for various client-auth scenarios,
including expected failure cases. These changes are all available now in
BC beta release 1.51b12 (http://www.downloads.bouncycastle.org/betas).
If you are affected by this, as in you are relying on client-auth, we
strongly
recommend you to upgrade to this beta release. A full release will
follow in May.
If you are unable to upgrade _do not_ use client-auth unless you are
using a work around. As mentioned earlier client-auth is disabled by
default
(TlsServer.getCertificateRequest returns null, and that is the default).
There is a workaround that can be used with 1.50 if you are using a TLS
(not DTLS) server at version 1.1 or earlier (i.e. not TLS 1.2), by
overriding the receiveCertificateVerifyMessage as follows:
TlsServerProtocol serverProtocol = new TlsServerProtocol(...)
{
protected void
receiveCertificateVerifyMessage(ByteArrayInputStream buf)
throws IOException
{
DigitallySigned clientCertificateVerify =
DigitallySigned.parse(getContext(), buf);
assertEmpty(buf);
if (TlsUtils.isTLSv12(getContext()))
{
throw new
TlsFatalAlert(AlertDescription.decrypt_error);
}
// Verify the CertificateVerify message contains a
correct signature.
boolean verified = false;
try
{
byte[] certificateVerifyHash =
TlsProtocol.getCurrentPRFHash(getContext(),
prepareFinishHash, null);
org.bouncycastle.asn1.x509.Certificate x509Cert =
this.peerCertificate.getCertificateAt(0);
SubjectPublicKeyInfo keyInfo =
x509Cert.getSubjectPublicKeyInfo();
AsymmetricKeyParameter publicKey =
PublicKeyFactory.createKey(keyInfo);
TlsSigner tlsSigner =
TlsUtils.createTlsSigner(this.clientCertificateType);
tlsSigner.init(getContext());
verified =
tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
clientCertificateVerify.getSignature(),
publicKey, certificateVerifyHash);
}
catch (Exception e)
{
}
if (!verified)
{
throw new
TlsFatalAlert(AlertDescription.decrypt_error);
}
}
};
If you use this workaround or another of your own devising, please take
extra care to test client-auth failure scenarios.
We welcome questions to [email protected] if you would
rather not ask on the public list.
Regards,
Pete Dettman (via David Hook)