diff options
author | Nick Hudson <nick.hudson@isode.com> | 2012-01-18 14:22:56 (GMT) |
---|---|---|
committer | Kevin Smith <git@kismith.co.uk> | 2012-02-13 15:12:46 (GMT) |
commit | 9d6f73f13b981fb9430765b171d7b419cbe632cc (patch) | |
tree | 5bfdbdfeaa9dc3dc3ff87e92be2612f7aebb03b3 /src/com/isode/stroke/tls/java/JavaTrustManager.java | |
parent | 12740e2b70c48e478af53de31624c388e1e89e0f (diff) | |
download | stroke-9d6f73f13b981fb9430765b171d7b419cbe632cc.zip stroke-9d6f73f13b981fb9430765b171d7b419cbe632cc.tar.bz2 |
Initial implementation of TLS support
Note that TLS won't be enabled with this patch unless you uncomment the
change in PlatformTLSFactories. With that comment removed, then a new
CoreClient session will attempt to negotiate TLS if the server supports
it.
Further changes are required to support this properly, as there
appears not to be comprehensive support in the CoreClient class for
dealing with situations when the server's certificate is not acceptable.
There's also no support yet for setting up client certificates.
Further changes will also be needed (see below) to support full
parsing of subjectAltNames from server certificates.
Significant changes are as follows
- TLSProceed - FIXME comments removed
- JavaConnection - changed so that it reads bytes from the socket's
InputStream, rather than reading chars and then constructing a
String out of them from which a byte array is then extracted.
While this seemed to work for non-binary data (e.g. non-encrypted
XMPP sessions), it breaks when you start sending binary (i.e. TLS)
data.
- JavaTLSConnectionFactory - implemented
- PlatformTLSFactories - By having this return a JSSEContextFactory, then
this will cause the client to try TLS if possible. But because other
changes are needed to make this work properly, the current code still
returns null.
- JSSEContext - new class which uses an SSLEngine to handle TLS handshake
and subsequent encryption/decryption. This is the main substance of
the SSL implementation
Note the "hack" in here to cope with SSLEngine requiring that some data
be sent from the application before it will do a TLS handshake
- JSSEContextFactory - just creates JSSEContexts
- JavaCertificate - this wraps an X509Certificate and does *some* of the
parsing of a certificate to look for stuff that is expected when
verifying an XMPP server certificate (RFC 6120 and RFC 6125). Note that
the JDK classes for parsing certificates don't provide an easy way
to decode "OTHER" subjectAltNames, and so this implementation does
not find XMPP or SRV subjectaltnames from the server certificate. This
will need extra work.
- JavaTrustManager - obtains the server certificate from the TLS handshake
and verifies it. Currently the only verification done is to check that
it's in date. More work will be needed to perform proper validation
- Where necessary, Remko's copyright comments were changed from GNU to
"All rights reserved". Isode copyright notices updated to "2012"
Test-information:
Set up XMPP server with its own certificate, and checked that TLS gets
negotiated and starts OK (provided the server cert contains e.g. a DNS
subjectAltName matching its own name). Subsequent operation appears
to be as expected.
Diffstat (limited to 'src/com/isode/stroke/tls/java/JavaTrustManager.java')
-rw-r--r-- | src/com/isode/stroke/tls/java/JavaTrustManager.java | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/src/com/isode/stroke/tls/java/JavaTrustManager.java b/src/com/isode/stroke/tls/java/JavaTrustManager.java new file mode 100644 index 0000000..4be7edf --- /dev/null +++ b/src/com/isode/stroke/tls/java/JavaTrustManager.java @@ -0,0 +1,151 @@ +/* Copyright (c) 2012, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written licence agreement from Isode Limited, + * or a written licence from an organisation licensed by Isode Limited Limited + * to grant such a licence. + * + */ + +package com.isode.stroke.tls.java; + +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLException; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +/** + * A concrete X509TrustManager implementation which provides a trust manager + * based on the default java "pkcs12" keystore. + */ +public class JavaTrustManager implements X509TrustManager { + + /** + * Construct a new object + * @param jsseContext reference to JSSEContext; must not be null. + * + * @throws SSLException if it was not possible to initialise the + * TrustManager or KeyStore + */ + JavaTrustManager(JSSEContext jsseContext) throws SSLException { + + if (jsseContext == null) { + throw new NullPointerException("JSSEContext may not be null"); + } + this.jsseContext = jsseContext; + + try { + // create a "default" JSSE X509TrustManager. + + KeyStore ks = KeyStore.getInstance("PKCS12"); + /* + + // This is how you could load trust anchors + ks.load(new FileInputStream("trustedCerts"), + "passphrase".toCharArray()); + */ + TrustManagerFactory tmf = + TrustManagerFactory.getInstance("PKIX"); + tmf.init(ks); + + TrustManager tms [] = tmf.getTrustManagers(); + + /* + * Iterate over the returned trustmanagers, look + * for an instance of X509TrustManager. If found, + * use that as our "default" trust manager. + */ + for (int i = 0; i < tms.length; i++) { + if (tms[i] instanceof X509TrustManager) { + pkixTrustManager = (X509TrustManager) tms[i]; + return; + } + } + /* + * Find some other way to initialize, or else we have to fail the + * constructor. + */ + throw new SSLException("Couldn't initialize"); + } + catch (KeyStoreException e) { + throw new SSLException(e); + } + catch (NoSuchAlgorithmException e) { + throw new SSLException(e); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + // It's not expected that a Stroke application will ever be in the + // position of checking client certificates. Just delegate to + // default trust manager + pkixTrustManager.checkClientTrusted(chain, authType); + + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + CertificateException certificateException = null; + + + // TODO: + // Note that we don't call the superclass method here yet, because + // it will fail with like this until the TrustManagerFactory has + // been initialised with a suitable list of trust anchors + // java.lang.RuntimeException: Unexpected error: + // java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty + + /* + try { + pkixTrustManager.checkServerTrusted(chain, authType); + } catch (CertificateException e) { + certificateException = e; + } + catch (Exception e) { + emitError(e,"checkServerTrusted failed"); + } + */ + + // TODO: The only type of verification done is the certificate validity. + // Need to make "checkServerTrusted" do certificate verification properly + // and pass in an appropriate CertificateException + if (chain != null && chain.length > 0) { + try { + chain[0].checkValidity(); + } + catch (CertificateException e) { + certificateException = e; + } + } + + jsseContext.setPeerCertificateInfo(chain, certificateException); + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + // TODO Auto-generated method stub + return null; + } + + /* + * The default PKIX X509TrustManager, to which decisions can be + * delegated when we don't make them ourselves. + */ + X509TrustManager pkixTrustManager; + + /** + * The object who wants to know what server certificates appear + */ + JSSEContext jsseContext; +} |