【问题标题】:Android Smack SSL/TLS connection to XMPP Ejabberd server with CA CertificateAndroid Smack SSL/TLS 连接到带有 CA 证书的 XMPP Ejabberd 服务器
【发布时间】:2016-10-15 10:06:22
【问题描述】:

我正在开发我的第一个 XMPP Android 应用程序,我在 XMPP 方面没有太多实践,但实际上我能够成功地将我的 Smack 客户端连接到我的 Ejabberd 服务器,当我尝试做同样的事情时问题就出现了使用 TLS(带有 CA 证书)。

这里是关于 TLS 的 ejabberd.yml 配置:

hosts:
  - "localhost"
  - "mydomain.com"

listen:
  - 
    port: 5222
    module: ejabberd_c2s
    ##
    ## If TLS is compiled in and you installed a SSL
    ## certificate, specify the full path to the
    ## file and uncomment these lines:
    ##
    certfile: "/home/matt/ssl-cert/stunnel.pem"
    starttls: true

pem 文件必须有效,因为我将它用于 SSL WebSocket 连接没有问题。

这里是我的 XMPP Java 类中用于初始化 TLS 连接的方法:

private void initialiseConnection() {

    XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration
            .builder();
    config.setSecurityMode(ConnectionConfiguration.SecurityMode.ifpossible);
    config.setServiceName(serverAddress); //mydomain.com
    config.setHost(serverAddress);
    config.setPort(5222);
    config.setDebuggerEnabled(true);

    SSLContext sslContext = null;

    try {
        sslContext = createSSLContext(context);
    } catch (KeyStoreException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (CertificateException e) {
        e.printStackTrace();
    }

    config.setCustomSSLContext(sslContext);
    config.setSocketFactory(sslContext.getSocketFactory());

    XMPPTCPConnection.setUseStreamManagementResumptiodDefault(true);
    XMPPTCPConnection.setUseStreamManagementDefault(true);

    connection = new XMPPTCPConnection(config.build());
    XMPPConnectionListener connectionListener = new XMPPConnectionListener();
    connection.addConnectionListener(connectionListener);
}

private SSLContext createSSLContext(Context context) throws KeyStoreException,
        NoSuchAlgorithmException, KeyManagementException, IOException, CertificateException {

    KeyStore trustStore;
    InputStream in = null;
    trustStore = KeyStore.getInstance("BKS");

    in = context.getResources().openRawResource(R.raw.my_keystore);

    trustStore.load(in, "MyPassword123".toCharArray());

    TrustManagerFactory trustManagerFactory = TrustManagerFactory
            .getInstance(KeyManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(trustStore);
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
    return sslContext;
}

请注意,没有 SSL/TLS 部分(并且没有 Ejabberd 配置中的 SSL/TLS 部分)一切正常。

p.s 对于密钥库创建和 SSL 方法集成,我遵循了 page 中的 lqbal 教程。

现在,Android Monitor 日志 (Android Studio) 只给了我一行关于连接问题的信息。

E/(onCreate): IOException: Handshake failed

仅此而已,但在 Ejabberd 服务器日志中,我有以下行:

2016-06-14 15:57:26.461 [info] <0.14993.0>@ejabberd_listener:accept:333 (#Port<0.73878>) Accepted connection xx.xx.xx.xx:xxxxx -> xx.xxx.xx.xx:5222
2016-06-14 15:57:26.466 [debug] <0.15099.0>@ejabberd_receiver:process_data:284 Received XML on stream = <<22,3,1,0,133,1,0,0,129,3,3,159,29,211,249,221,88,135,177,183,150,98,234,76,6,91,52,30,26,186,202,176,199,127,245,56,211,198,43,66,35,237,140,0,0,40,192,43,192,44,192,47,192,48,0,158,0,159,192,9,192,10,192,19,192,20,0,51,0,57,192,7,192,17,0,156,0,157,0,47,0,53,0,5,0,255,1,0,0,48,0,23,0,0,0,13,0,22,0,20,6,1,6,3,5,1,5,3,4,1,4,3,3,1,3,3,2,1,2,3,0,11,0,2,1,0,0,10,0,8,0,6,0,23,0,24,0,25>>
2016-06-14 15:57:26.466 [debug] <0.15100.0>@ejabberd_c2s:send_text:1832 Send XML on stream = <<"<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='17298480576042278904' from='mydomain.com' version='1.0'>">>
2016-06-14 15:57:26.466 [debug] <0.15100.0>@ejabberd_c2s:send_text:1832 Send XML on stream = <<"<stream:error><xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'></xml-not-well-formed></stream:error>">>
2016-06-14 15:57:26.466 [debug] <0.15100.0>@ejabberd_c2s:send_text:1832 Send XML on stream = <<"</stream:stream>">>

我无法理解这个收到的“

怎么了?

【问题讨论】:

  • 为什么要设置socket工厂?
  • 谢谢!删除 setSocketFactory 行帮助我开始与 Ejabberd 进行正确通信,但尚未解决.. 似乎存在与证书匹配的问题,在 Ejabberd 日志上我有“错误匹配,SSL CTX 使用 PrivateKey 文件归档”和“不匹配右手值”。对于密钥库,我使用了一个有效的 .crt 文件,与我在服务器上的 pem 文件中使用的顺序相同: 1 .crt 2 COMODORSADomainValidationSecureServerCA.crt 3 COMODORSAAddTrustCA.crt 4 AddTrustExternalCARoot .crt
  • 不客气。因为我想了解我的用户如何思考并在未来防止此类使用错误,所以为什么您认为设置套接字工厂是一个好主意的问题的答案对我很有价值。那么,你当初为什么要设置它呢?
  • 正如我所写的,这是我第一次使用 xmpp 并真诚地使用 android 开发,我没有太注意代码,我知道,但我想解决问题并很好地理解工作。

标签: android ssl xmpp ejabberd smack


【解决方案1】:

&lt;&lt;22,3,1... 数据包是一个 TLS 握手数据包。它实际上不是 XML。它被打印为十进制的单个字节。 22 字节表示“握手”,3 和 1 分别是主要和次要版本号。版本“3.1”实际上代表 TLS 1.0。详情请见the description on Wikipedia

似乎发生的情况是您的 Java 代码在连接后立即启动 TLS 握手,但 ejabberd 期望它首先协商 STARTTLS。这在section 5 of RFC 6120 中有描述。基本上,服务器会发送一个功能列表,包括 STARTTLS:

<stream:features>
   <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>
     <required/>
   </starttls>
</stream:features>

客户端要求 STARTTLS:

<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>

服务器告诉客户端继续:

<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>

然后客户端可以开始TLS握手。


一定有办法让 Smack 做上面的 STARTTLS 协商,但我不知道怎么做。但是,您可以通过更改配置让 ejabberd 接受这种风格的 TLS 握手:

port: 5223
starttls: false

传统上,XMPP 服务器接受在端口 5222 上执行 STARTTLS 的“普通”连接,在端口 5223 上接受“即时 TLS”连接,因此我建议遵循该约定以减少混淆。

【讨论】:

  • 感谢legoscia,我已尝试按照您的建议切换到5223,但错误是相同的,直到按照Flow的建议删除了setSocketFactory,现在似乎存在与证书匹配的问题,请参阅我在 Flow answer 下的错误描述。
【解决方案2】:

不要设置套接字工厂。

【讨论】:

    【解决方案3】:

    好的,最后经过一些研究并感谢之前的答案,我能够将我的 Smack 客户端连接到我的 Ejabberd 服务器。下面是所有的编辑。

    这是 XMPP 类的最终代码,我删除了 config.setSocketFactory 行并将连接端口更改为 5223。

    private void initialiseConnection() {
    
        XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration
                .builder();
        config.setSecurityMode(ConnectionConfiguration.SecurityMode.ifpossible);
        config.setServiceName(serverAddress);
        config.setHost(serverAddress);
        config.setPort(5223);
        config.setDebuggerEnabled(true);
    
        SSLContext sslContext = null;
    
        try {
            sslContext = createSSLContext(context);
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        }
    
        config.setCustomSSLContext(sslContext);
    
        XMPPTCPConnection.setUseStreamManagementResumptiodDefault(true);
        XMPPTCPConnection.setUseStreamManagementDefault(true);
    
        connection = new XMPPTCPConnection(config.build());
        XMPPConnectionListener connectionListener = new XMPPConnectionListener();
        connection.addConnectionListener(connectionListener);
    }
    
    private SSLContext createSSLContext(Context context) throws KeyStoreException,
            NoSuchAlgorithmException, KeyManagementException, IOException, CertificateException {
    
        KeyStore trustStore;
        InputStream in = null;
        trustStore = KeyStore.getInstance("BKS");
    
        in = context.getResources().openRawResource(R.raw.my_keystore);
    
        trustStore.load(in, "MyPassword123".toCharArray());
    
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(KeyManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
        return sslContext;
    }
    

    这些是 ejabberd.yml 文件中的新端口设置

    port: 5223
    module: ejabberd_c2s
    certfile: "/etc/ejabberd/ejabberd.pem"
    starttls: true
    

    这里的证书,我在客户端和服务器上都错了,对于客户端部分,我遵循this page 上的 lqbal 教程,通过第 2 步和第 3 步,我能够创建一个密钥库文件和验证它,但我使用了错误的证书文件,正确的是外部 CA 根证书(在我的例子中是“AddTrustExternalCARoot.crt”COMODO)。

    对于我使用 pem 链的服务器,其中的证书放置错误,ejabberd.pem 的正确方法如下(您可以找到所有详细信息here): 1. 私钥( .key) 2. 证书 (domain.crt) 3. 链 (.ca-bundle)。最后,我将 ejabberd.pem 移到了 /etc/ejabberd 文件夹中。

    现在 TLS 连接有效:

    SMACK: SENT (0): <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>
    SMACK: RECV (0): <proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
    

    【讨论】:

      猜你喜欢
      • 2015-05-24
      • 2015-01-26
      • 1970-01-01
      • 2023-03-24
      • 2016-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-02
      相关资源
      最近更新 更多