How to use a self signed certificate to connect to

2019-02-16 00:52发布

问题:

Im having problem connecting to a mqtt server using self signed certificate. im using Paho client and want to connect to a server using a TLSv1.2. actually i was successful to connect in Android APIs 20+ but no success for below this version.

what ive done till now :

1- created a PKCS#12 keystore and put the .crt file and assing a password to it and save it (it will be a .pfx file)

2- added the .pfx file to raw folder of recourse in android project

3- used below code to load self signed certificate:

connection = createConnection(mqttCallback);

MqttConnectOptions connOpts = optionsFromModel(connectionModel);

connOpts.setSocketFactory(getSSLSocketFactory(keyStoreInputStream, keyStorePassword));

connection.addConnectionOptions(connOpts);

and the getSSLSocketFactory method which is the most important part is :

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

this works perfectly but no success in Android APIs below 20.

回答1:

finally found a solution to this.

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.

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

the getSSLSocketFactory method changes to:

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

and the TLSSocketFactory class is like below:

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