2
votes

I'm trying to use the Smack 4.0.5 Api to connect to the Vines XMPP as part of the Growing Object Oriented Software Guided by Tests worked example. I've struggled to find any documentation that describes how to connect using TLS. Several threads have presented samples like below, but this (as I believe insecure) option fails due to:

    ConnectionConfiguration connectionConfig = new ConnectionConfiguration(XMPP_HOSTNAME, 5222);
    connectionConfig.setDebuggerEnabled(true);

    SSLContext sslContext = null;
    try {
        sslContext = SSLContext.getInstance("TLS");
        TrustManager tm = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
                throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
                throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
        sslContext.init(null, new TrustManager[] {tm}, null);
        connectionConfig.setCustomSSLContext(sslContext);

        this.connection = new XMPPTCPConnection(connectionConfig);
        this.connection.connect();
        this.connection.login(format(ITEM_ID_AS_LOGIN, itemId), AUCTION_PASSWORD, AUCTION_RESOURCE);

    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException("connecting failed", e);
    }

with

org.jivesoftware.smack.sasl.SASLErrorException: SASLError using PLAIN: not-authorized
        at org.jivesoftware.smack.SASLAuthentication.authenticate(SASLAuthentication.java:348)
        at org.jivesoftware.smack.tcp.XMPPTCPConnection.login(XMPPTCPConnection.java:244)
        at uk.me.paulswilliams.auction.fakes.FakeAuctionServer.<init>(FakeAuctionServer.java:60)
        at uk.me.paulswilliams.auction.AuctionSniperEndToEndTest.<init>(AuctionSniperEndToEndTest.java:10)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
        at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:195)
        at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:244)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:241)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
        at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
        at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
        at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

Vines presents a self signed certificate, and I've tried to implement the 'secure' way by copying the public certificate to the client, registering it using the Java Control Panel on OS X, and use it with following code:

        try {
            ConnectionConfiguration connectionConfig = new ConnectionConfiguration(XMPP_HOSTNAME, 5222);
            connectionConfig.setSecurityMode(ConnectionConfiguration.SecurityMode.required);
            this.connection = new XMPPTCPConnection(connectionConfig);
            this.connection.connect();
            this.connection.login(format(ITEM_ID_AS_LOGIN, itemId), AUCTION_PASSWORD, AUCTION_RESOURCE);
        }
        catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("connection failed");
        }

However, in this situation, I continue to receive a:

Nov 13, 2014 8:49:11 PM org.jivesoftware.smack.XMPPConnection callConnectionClosedOnErrorListener
WARNING: Connection closed with error
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 sun.security.ssl.Alerts.getSSLException(Alerts.java:192)

I suspect both are close to working, and, due the nature of this work, security really isn't a consideration.

Edit:

I've now tried to import Vines certificate into my JVM keystone using:

sudo /Library/Java/Home/bin/keytool --import --alias "localhost" -file localhost.crt -keystore  /Library/Java/Home/lib/security/cacerts

I've specified the alias to match the server name 'localhost' although the server is also known as 'rails-dev-box' but I'm not using that name in the java client. Do I need to import the ca-bundle.crt CA as well? Should Java pick up this certificate based on server name, or do I need to explicitly instruct Java to do so?

1

1 Answers

2
votes

With the assistance of Flow, I've arrived at a this working solution was:

ConnectionConfiguration connectionConfig = new ConnectionConfiguration(XMPP_HOSTNAME, 5222);    
connectionConfig.setSecurityMode(ConnectionConfiguration.SecurityMode.required);
System.setProperty("javax.net.ssl.trustStore", "akeystore.jks");
this.connection = new XMPPTCPConnection(connectionConfig);
this.connection.connect();
this.connection.login(format(ITEM_ID_AS_LOGIN, itemId), AUCTION_PASSWORD, AUCTION_RESOURCE);

I created the keystore with:

keytool -import -alias localhost -file ~/src/auctionsniperjava/localhost.crt -keystore akeystore.jks