且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2022-05-15 21:22:13

终于找到了解决方案。

finally found a solution to this.

基于这个文档,Android API级别16(Android 4.1,Jelly Bean)支持TLS 1.1和TLS 1.2。 但默认情况下不启用,直到API等级为20+(Android 4.4用于手表,Kitkat Watch和Android 5.0 for phone,Lollipop)。

based on this documentation, TLS 1.1 and TLS 1.2 is supported from android API level 16 (Android 4.1, Jelly Bean). But it is'nt enabled by default until API level 20+ (Android 4.4 for watch, Kitkat Watch and Android 5.0 for phone, Lollipop).

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

so all we need is to enable them parogrammatically in code. how we gonna do this ? there is a solution for this problem here BUT it just resolves the problem for the case that you want to accept any certificate instead.

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

what we need is do the same thing but with our own self signed certificate. so we do it like below. the first part is just like what i did before : (keyStoreInputStream is input stream of a .pfx file)

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;
}
}