TLS or not TLS, that is the question.

Issue #71 closed
Former user created an issue

I'm having a little trouble implementing pre-binding with TcpConnectionConfiguration, but I'm not sure where the error lies, and I've created an issue because I suspect there might be a problem either with the documentation, or with StartTls's isMandatory(). (Of course it's more likely I'm missing something blindingly obvious). Could someone lend a hand?

I'm developing a servlet on Windows 7, Java 8, which will provide pre-binding for a CentOS server running Prosody 0.9 on port 5222 with optional encryption. The dev machine has both the local and public certs of the server installed. If I attempt to connect without SSL, using

TcpConnectionConfiguration tcpConfiguration = TcpConnectionConfiguration.builder()
    .hostname("my-host-LAN-name")
    .port(5222)
    .proxy(Proxy.NO_PROXY)
    .keepAliveInterval(20)
    .build();

try (   XmppClient xmppClient = new XmppClient("my-host-public-name", tcpConfiguration);    )
{
    xmppClient.connect();
}

then I get

rocks.xmpp.core.XmppException: javax.xml.stream.XMLStreamException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at rocks.xmpp.core.session.XmppSession.throwAsXmppExceptionIfNotNull(XmppSession.java:230)
    at rocks.xmpp.core.session.XmppClient.connect(XmppClient.java:210)
    at rocks.xmpp.core.session.XmppSession.connect(XmppSession.java:246)

as if the server is requiring an TLS connection.

if I add

.secure(false)

then I get

rocks.xmpp.core.stream.StreamNegotiationException: The server requires TLS, but you disabled it.
    at rocks.xmpp.core.session.SecurityManager.processNegotiation(SecurityManager.java:55)
    at rocks.xmpp.core.stream.StreamFeaturesManager.negotiateNextFeature(StreamFeaturesManager.java:219)
    at rocks.xmpp.core.stream.StreamFeaturesManager.processFeatures(StreamFeaturesManager.java:172)
    at rocks.xmpp.core.session.XmppSession.handleElement(XmppSession.java:809)
    at rocks.xmpp.core.session.XmppStreamReader.lambda$startReading$40(XmppStreamReader.java:150)

If I then try to connect via SSL, using

SSLSocketFactory sslFactory = null;
try
{
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    FileInputStream trustStore = new FileInputStream(new File("C:/Java/jre8/lib/security/cacerts"));
    keyStore.load(trustStore, "password".toCharArray());
    trustStore.close();

    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(keyStore);
    SSLContext ctx = SSLContext.getInstance("TLS");
    ctx.init(null, tmf.getTrustManagers(), null);
    sslFactory = ctx.getSocketFactory();
}
catch (Exception e)
{
    e.printStackTrace();
}

TcpConnectionConfiguration tcpConfiguration = TcpConnectionConfiguration.builder()
    .hostname("my-host-local-name")
    .port(5222)
    .secure(true)
    .proxy(Proxy.NO_PROXY)
    .keepAliveInterval(20)
    .socketFactory(sslFactory)
    .build();

try (   XmppClient xmppClient = new XmppClient("my-host-public-name", tcpConfiguration);    )
{
    xmppClient.connect();
}

then I get

rocks.xmpp.core.XmppException: javax.xml.stream.XMLStreamException: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
    at rocks.xmpp.core.session.XmppSession.throwAsXmppExceptionIfNotNull(XmppSession.java:230)
    at rocks.xmpp.core.session.XmppClient.connect(XmppClient.java:210)
    at rocks.xmpp.core.session.XmppSession.connect(XmppSession.java:246)

as if the server is not expecting an SSL connection.

I've examined my Prosody config several times now, and I'm pretty sure the problem isn't there.

Am I fundamentally misunderstanding the TcpConnectionConfiguration builder? Why am I being asked for an SSL cert when I'm trying to make a plaintext connection to an unencrypted socket? Or am I just being dumb?

Thanks,

Comments (4)

  1. Christian Schudt repo owner

    You were definitively on the right track and you nearly got it right.

    Usually (as per the specification RFC 6120), an XMPP connections start plain and then later is upgraded to a secure SSL connection via starttls.

    secure(false) means, that you (the client) don't want to upgrade to a TLS connection and that you want to communicate plain. However, servers can require the upgrade (as in your case), that's why you got the "The server requires TLS" error.

    There are some servers, which allow an SSL encrypted connection right from the beginning, usually over port 5223 (e.g. Openfire). It's not specified by the XMPP spec, but supported nonetheless. In that case you want to provide an SSLSocketFactory, which encrypts all traffic immediately. Because your server sends plain text, but you already send encrypted, you get the "Unrecognized SSL message?" error.

    What you want is:

    TcpConnectionConfiguration tcpConfiguration = TcpConnectionConfiguration.builder()
        .hostname("my-host-local-name")
        .port(5222)
        .secure(true) // Already the default
        .sslContext(ctx)
        .keepAliveInterval(20)
        .build();
    

    Which starts normally as plain text connection, and then upgrades to an SSL connection during negotiation.

    Will clarify this in the JavaDoc...

  2. Log in to comment