Vulnerability in BC (D)TLS server, fix in new beta release 1.51b12

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Vulnerability in BC (D)TLS server, fix in new beta release 1.51b12

David Hook

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 [hidden email] if you would
rather not ask on the public list.

Regards,

Pete Dettman (via David Hook)