简体   繁体   中英

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

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 two days 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:

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

So I am guessing (correct me if I'm wrong) 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”?

I am also wondering if SecurityMode.disabled/SecurityMode.ifpossible is actually successfully establishing a TLS connection but isSecureConnection() is returning false nonetheless? Is that possible at all? In order to test this hypothesis, 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 appreciated!

Same issue here, what I wound concerning this bug was:

https://groups.google.com/forum/#!topic/android-gcm/5mA7FImpTGo

Which indicates that something is wrong with the SSL Session Cache (disabled by google). I had no luck to disable it on the server side (my side).

On the other side you may see more something like this:

org.jivesoftware.smack.roster.Roster     : Exception reloading roster
org.jivesoftware.smack.SmackException$NoResponseException: No response received within reply timeout. Timeout was 5000ms (~5s).

If so you can see the issue here: https://jira.spring.io/browse/INT-3972

My workaround for now here: https://github.com/puel/training/blob/master/gcm/gcm-server/src/main/java/org/sterl/gcm/_example/server/config/GcmConfig.java

I had the very same problem with Smack 4.1.6.

  1. SecurityMode mustn't be set to required ( https://community.igniterealtime.org/thread/55808 ).

  2. When making up the username, project NUMBER, not project ID must be used (can be found at https://console.cloud.google.com ).

  3. My stupid mistake, but still. When creating an API key for your project, choose carefully the platform: for some mysterious reason Android API keys don't work for a plain Java app (lol).

  4. It seems, the service name can be anything, just not null.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM