如何使用自签名证书连接到Android(paho客户端)中的Mqtt服务器?

我在使用自签名证书连接到mqtt服务器时遇到问题。 即时通讯使用Paho客户端,并希望使用TLSv1.2连接到服务器。 实际上,我成功连接Android API 20+,但在此版本以下没有成功。

到目前为止我做了什么:

1-创建了一个PKCS#12密钥库并放入.crt文件并为其提供密码并保存(它将是.pfx文件)

2-将.pfx文件添加到android项目中的原始文件夹中

3-使用下面的代码加载自签名证书:

 connection = createConnection(mqttCallback); MqttConnectOptions connOpts = optionsFromModel(connectionModel); connOpts.setSocketFactory(getSSLSocketFactory(keyStoreInputStream, keyStorePassword)); connection.addConnectionOptions(connOpts); 

getSSLSocketFactory方法是最重要的部分:

  public SSLSocketFactory getSSLSocketFactory (InputStream keyStore, String password) throws MqttSecurityException { try{ SSLContext ctx = null; SSLSocketFactory sslSockFactory=null; KeyStore ks; ks = KeyStore.getInstance("PKCS12"); ks.load(keyStore, password.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); tmf.init(ks); TrustManager[] tm = tmf.getTrustManagers(); ctx = SSLContext.getInstance("TLS"); ctx.init(null, tm, null); sslSockFactory = ctx.getSocketFactory(); return sslSockFactory; } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException | KeyManagementException e) { throw new MqttSecurityException(e); } } 

这种方法很完美但在20岁以下的Android API中没有成功。

终于找到了解决方案。

基于此文档,Android API级别16(Android 4.1,Jelly Bean)支持TLS 1.1和TLS 1.2。 但默认情况下它没有启用,直到API级别20+(Android 4.4用于手表,Kitkat Watch和Android 5.0用于手机,Lollipop)。

所以我们需要的是在代码中以编程方式启用它们。 我们该怎么做? 这里有一个解决这个问题的方法,但它只是解决了你想要接受任何证书的问题。

我们需要的是做同样的事情,但使用我们自己的自签名证书。 所以我们就像下面这样做。 第一部分就像我之前做的那样:( keyStoreInputStream是.pfx文件的输入流)

 connection = createConnection(mqttCallback); MqttConnectOptions connOpts = optionsFromModel(connectionModel); connOpts.setSocketFactory(getSSLSocketFactory(keyStoreInputStream, keyStorePassword)); connection.addConnectionOptions(connOpts); 

getSSLSocketFactory方法更改为:

  public SSLSocketFactory getSSLSocketFactory (InputStream keyStore, String password) throws MqttSecurityException { try{ SSLContext ctx = null; SSLSocketFactory sslSockFactory=null; KeyStore ks; ks = KeyStore.getInstance("PKCS12"); ks.load(keyStore, password.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); tmf.init(ks); TrustManager[] tm = tmf.getTrustManagers(); ctx = SSLContext.getInstance("TLS"); ctx.init(null, tm, null); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { sslSockFactory = new TLSSocketFactory(tm); } else { sslSockFactory = ctx.getSocketFactory(); } return sslSockFactory; } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException | KeyManagementException e) { throw new MqttSecurityException(e); } } 

TLSSocketFactory类如下所示:

 import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; public class TLSSocketFactory extends SSLSocketFactory { private SSLSocketFactory internalSSLSocketFactory; public TLSSocketFactory(TrustManager[] trustManagers) throws KeyManagementException, NoSuchAlgorithmException { SSLContext context = SSLContext.getInstance("TLS"); context.init(null, trustManagers, null); internalSSLSocketFactory = context.getSocketFactory(); } @Override public String[] getDefaultCipherSuites() { return internalSSLSocketFactory.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return internalSSLSocketFactory.getSupportedCipherSuites(); } @Override public Socket createSocket() throws IOException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket()); } @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose)); } @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort)); } private Socket enableTLSOnSocket(Socket socket) { if(socket != null && (socket instanceof SSLSocket)) { ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2", "TLSv1.1"}); } return socket; } }