AnsweredAssumed Answered

With Smack 4.1.0 as a XMPP Client for Google’s GCM CCS, SecurityMode.required is not working

Question asked by Skinner on May 25, 2015
Latest reply on May 26, 2015 by Flow

Dear ignite realtime community,

 

I posted this question in StackOverflow a couple of days ago, but haven’t gotten any reply yet. I am hoping now somebody here will be able to help me.

 

I’m working on a Java Server that is using the Smack 4.1.0 API as a XMPP Client to connect to Google Cloud Messaging Cloud Connection Server (GCM CCS) in order to send messages to an Android app. I started with this example (https://developer.android.com/google/gcm/ccs.html), but as the Smack API has changed I adapted the code accordingly. By now my Smack Client is connecting successfully to GCM CCS, sends messages and receives ack/nack/control responses.

Unfortunately, the connection is only working properly, if I specify XMPPTCPConnectionConfiguration.Builder.setSecurityMode(SecurityMode.ifpossible) or (SecurityMode.disabled). When doing that XMPPTCPConnection.isSecureConnection() returns false.

See the (relevant) code below:

    static final String GCM_SERVER = "gcm.googleapis.com";
    static final int GCM_PORT = 5235;
    private XMPPTCPConnection connection;
    private SSLContext sslCtx;

    ...

    try {
        KeyStore windowsRootTruststore = KeyStore.getInstance("Windows-ROOT", "SunMSCAPI");
        windowsRootTruststore.load(null, null);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(windowsRootTruststore);
        sslCtx = SSLContext.getInstance("TLS");
        sslCtx.init(null, tmf.getTrustManagers(), null);
    } catch (KeyStoreException | NoSuchProviderException | NoSuchAlgorithmException
            | KeyManagementException | CertificateException e) {
        e.printStackTrace();
    }

    ...

    XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
        .setHost(GCM_SERVER)
        .setPort(GCM_PORT)
        .setServiceName(GCM_SERVER)
        .setUsernameAndPassword(GCM_SENDER_ID + "@gcm.googleapis.com", GCM_PASSWORD)
        .setCompressionEnabled(false)
        .setSecurityMode(SecurityMode.ifpossible)
        .setSendPresence(false)
        .setSocketFactory(sslCtx.getSocketFactory())
        .build();
    connection = new XMPPTCPConnection(config);
    Roster roster = Roster.getInstanceFor(connection);
    roster.setRosterLoadedAtLogin(false);
    connection.addConnectionListener(this);
    connection.addAsyncStanzaListener(this, new StanzaTypeFilter(Message.class));
    connection.addPacketInterceptor(new StanzaListener() {
            @Override
            public void processPacket(Stanza packet) throws NotConnectedException {
                System.out.println("CCS_Client sent the following message: " + packet.toXML());
            }
        }, new StanzaTypeFilter(Message.class));
    connection.connect();
    connection.login();
    System.out.println(connection.isSecureConnection());

 

According to Google “The connection has two important requirements: 1) You must initiate a Transport Layer Security (TLS) connection. …” (see link above). This sounds to me like a non-TLS encrypted connection would be refused. My problem comes into play with SecurityMode.required. When using that, Smack throws the following error code:

 

 

org.jivesoftware.smack.SmackException$SecurityRequiredByClientException: SSL/TLS required by client but not supported by server
    at org
.jivesoftware.smack.tcp.XMPPTCPConnection.afterFeaturesReceived(XMPPTCPConnection.java:898)
    at org
.jivesoftware.smack.AbstractXMPPConnection.parseFeatures(AbstractXMPPConnection.java:1367)
    at org
.jivesoftware.smack.tcp.XMPPTCPConnection.access$800(XMPPTCPConnection.java:139)
    at org
.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:998)
    at org
.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$200(XMPPTCPConnection.java:937)
    at org
.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:952)
    at java
.lang.Thread.run(Thread.java:744)

 

I have been trying for forever to figure out why I can’t establish a SecurityMode.required connection but failed.

Using the same SSLContext/SSLSocketFactory as above, it works fine, if I connect to GCM CCS without the Smack API, just opening a TLS encrypted connection. In line with Google’s comment above, when passing a regular SocketFactory (not SSLSocketFactory) to the XMPPTCPConnectionConfiguration, the connection cannot be established wtih the following error code:

 

org.jivesoftware.smack.SmackException$NoResponseException: No response received within reply timeout

 

 

So I am guessing, that GCM CCS is indeed only accepting TLS connections. But if that is the case, why is my SecurityMode.required connection attempt rejected with “SSL/TLS required by client but not supported by server”?

Could it be, that SecurityMode.disabled/SecurityMode.ifpossible is actually successfully establishing a TLS connection but isSecureConnection() is returning false nonetheless? In order to test this, I wanted to test the underlying SSLsocket that is created within Smack (with SSLSocket.getSession().getCipherSuite() and getProtocol() after the completed Handshake). In order to do that, I was trying to pass a custom SSLSocketFactory which produces a custom SSLSocket (which would just output the CipherSuite and Protocol after the completed Handshake) to XMPPTCPConnectionConfiguration. But I can’t seem to get this working either.

 

How do I get a connection to GCM CCS with SecurityMode.required established, for which isSecureConnection() returns true?

Any help would be very appreciated!

Outcomes