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

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!

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

Because GCM’s XMPP endpoint does not support the (mandatory) STARTTLS extension of XMPP.

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

You can’t, isSecureConnection() will only return true if STARTTLS is used.

But you can however use and set a socket factory that wraps the XMPP stream in TLS right away.

1 Like

Hello Flow. Thanks a lot for your answer!! You saved my day! Or actually my week :wink:

I read that GCM is not supporting STARTTLS, but I was unaware that this is what “hides” behind the SecurityMode.required option.

But you can however use and set a socket factory that wraps the XMPP stream in TLS right away.

This is exactly what I am trying to do. I have two additional questions as to how to do that, though.

  1. Is it sufficient to pass a SSLSocketFactory instance (as for example in my code snippet in my question above) to XMPPTCPConnectionConfiguration…setSocketFactory() in order to wrap the XMPP stream in TLS?

  2. Does the .setSecurityMode() method have any effect with regard to wrapping the stream im TLS? I mean, does it make any difference, if I leave the default SecurityMode.ifpossible or manually set SecurityMode.disabled?

Thanks again for your help.

  1. Is it sufficient to pass a SSLSocketFactory instance (as for example in my code snippet in my question above) to XMPPTCPConnectionConfiguration…setSocketFactory() in order to wrap the XMPP stream in TLS?
    Possible. But note that this means you have to take care of the TLS certificate verification. If it’s not done right, your connection will be prone to attacks.
  1. Does the .setSecurityMode() method have any effect with regard to wrapping the stream im TLS? I mean, does it make any difference, if I leave the default SecurityMode.ifpossible or manually set SecurityMode.disabled?
    I would recommend using ‘disabled’, as you don’t want Smack to you perform STARTTLS. ‘ifpossible’ should also work, because the GCM XMPP endpoint likely does not announce support for STARTTLS.
1 Like