美文网首页
MQTT安全:使用ssl实现EMQ与android客户端安全通信

MQTT安全:使用ssl实现EMQ与android客户端安全通信

作者: Jack_Jiao | 来源:发表于2019-02-14 18:56 被阅读0次

    原文地址

    使用 SSL 对 MQTT的消息交换进行加密,提高安全性。

    服务器启用SSL

    我们需要数字证书来对进行ssl通信用户进行强认证。由于获得一个真正受外界信任的证书需要花费money,所有我们采用自签名证书。

    要实现双向认证(服务器认证客户端、客户端认证服务器)我们需要3个证书,一个CA证书,一个EMQ服务器证书,一个客户端证书。

    具体的生成证书操作、启用EMQ ssl和双向认证,参考这篇文章 Securing EMQ Connections with SSL

    android客户端的ssl实现

    上一步我们生成了客户端使用的证书 MyClient1.pem 和私有秘钥MyClient1.key,但是要想在android上使用需要将其转成bks格式。

    pem 转 bks

    1、首先生成.p12文件:

    openssl pkcs12 -export -nodes -in MyClient1.pem -inkey MyClient1.key -out client.p12
    
    • -inkey为私钥文件
    • -in为证书,如果pem私钥没有密码,则使用-nodes表示无密码,如果有密码使用-passin;如果私钥和证书都在同一文件里则-in-inkey指定同一个文件。

    会提示输入给.p12秘钥库设置的密码,请记住,下面会用到

    2、生成.bks证书:

    keytool -importkeystore -srckeystore client.p12 -srcstoretype pkcs12 -destkeystore client.bks -deststoretype bks -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-158.jar 
    

    使用 KeyTool 转换为 BKS 格式时,需要 bcprov-ext-jdk15on-158.jar,可以在 这里 找到。文件路径直接带在-providerpath 参数后面即可。也可以把jar包放到如下路径:jdk/jre/lib/ext,从而省略-providerpath

    会首先提示输入给bks秘钥库设置的密码,请记住,下面会用到。

    然后会提示输入p12秘钥库密码,即上一步设置的密码。

    3、查看bks证书库列表进行验证

    keytool -list -rfc -keystore client.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -storepass 'bks秘钥库密码'
    

    源码

    完整源码见 GitHub

    主要代码

    package paho.android.mqtt_example;
    
    import android.util.Log;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.security.KeyManagementException;
    import java.security.KeyStore;
    import java.security.KeyStoreException;
    import java.security.NoSuchAlgorithmException;
    import java.security.UnrecoverableKeyException;
    import java.security.cert.CertificateFactory;
    import java.security.cert.X509Certificate;
    import javax.net.ssl.KeyManagerFactory;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.TrustManagerFactory;
    import javax.security.cert.CertificateException;
    import timber.log.Timber;
    
    /**
     * Original SocketFactory file taken from https://github.com/owntracks/android
     */
    
    public class SelfSignedSocketFactory extends javax.net.ssl.SSLSocketFactory {
        private javax.net.ssl.SSLSocketFactory factory;
    
    
        public static class SocketFactoryOptions {
    
            private InputStream caCrtInputStream;
            private InputStream caClientBksInputStream;
            private String caClientBksPassword;
    
    
            /**
             *
             * @param stream the self-signed Root CA Certificate's stream
             * @return
             */
            public SocketFactoryOptions withCaInputStream(InputStream stream) {
                this.caCrtInputStream = stream;
                return this;
            }
    
    
            /**
             *
             * @param stream the self-signed client Certificate's stream .
             * @return
             */
            public SocketFactoryOptions withClientBksInputStream(InputStream stream) {
                this.caClientBksInputStream = stream;
                return this;
            }
    
    
            public SocketFactoryOptions withClientBksPassword(String password) {
                this.caClientBksPassword = password;
                return this;
            }
    
    
            public boolean hasCaCrt() {
                return caCrtInputStream != null;
            }
    
    
            public boolean hasClientBksCrt() {
                return caClientBksPassword != null;
            }
    
    
            public InputStream getCaCrtInputStream() {
                return caCrtInputStream;
            }
    
    
            public InputStream getCaClientBksInputStream() {
                return caClientBksInputStream;
            }
    
    
            public String getCaClientBksPassword() {
                return caClientBksPassword;
            }
    
    
            public boolean hasClientBksPassword() {
                return (caClientBksPassword != null) && !caClientBksPassword.equals("");
            }
        }
    
    
        public SelfSignedSocketFactory()
            throws CertificateException, KeyStoreException, NoSuchAlgorithmException, IOException, KeyManagementException,
                   java.security.cert.CertificateException, UnrecoverableKeyException {
            this(new SocketFactoryOptions());
        }
    
    
        private TrustManagerFactory tmf;
    
    
        public SelfSignedSocketFactory(SocketFactoryOptions options)
            throws KeyStoreException, NoSuchAlgorithmException, IOException, KeyManagementException,
                   java.security.cert.CertificateException, UnrecoverableKeyException {
            Log.v(this.toString(), "initializing CustomSocketFactory");
    
            tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
    
            if (options.hasCaCrt()) {
                Log.v(this.toString(), "MQTT_CONNECTION_OPTIONS.hasCaCrt(): true");
                // CA certificate is used to authenticate server
                CertificateFactory cAf = CertificateFactory.getInstance("X.509");
                X509Certificate ca = (X509Certificate) cAf.generateCertificate(options.getCaCrtInputStream());
                KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
                caKs.load(null, null);
                caKs.setCertificateEntry("ca-certificate", ca);
                tmf.init(caKs);
            } else {
                Timber.v("CA sideload: false, using system keystore");
                KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
                keyStore.load(null);
                tmf.init(keyStore);
            }
    
            if (options.hasClientBksCrt()) {
                Log.v(this.toString(), "MQTT_CONNECTION_OPTIONS.hasClientBksCrt(): true");
    
                // init client key store
                KeyStore clientkeyStore = KeyStore.getInstance("BKS");
                clientkeyStore.load(options.getCaClientBksInputStream(),
                    options.hasClientBksPassword() ? options.getCaClientBksPassword().toCharArray() : new char[0]);
                kmf.init(clientkeyStore,
                    options.hasClientBksPassword() ? options.getCaClientBksPassword().toCharArray() : new char[0]);
    
            } else {
                Log.v(this.toString(), "Client .bks sideload: false, using null CLIENT cert");
                kmf.init(null, null);
            }
    
            // Create an SSLContext that uses our TrustManager
            SSLContext context = SSLContext.getInstance("TLSv1.2");
            context.init(kmf.getKeyManagers(), getTrustManagers(), null);
            this.factory = context.getSocketFactory();
    
        }
    
    
        public TrustManager[] getTrustManagers() {
            return tmf.getTrustManagers();
        }
    
    
        @Override
        public String[] getDefaultCipherSuites() {
            return this.factory.getDefaultCipherSuites();
        }
    
    
        @Override
        public String[] getSupportedCipherSuites() {
            return this.factory.getSupportedCipherSuites();
        }
    
    
        @Override
        public Socket createSocket() throws IOException {
            SSLSocket r = (SSLSocket) this.factory.createSocket();
            r.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
            return r;
        }
    
    
        @Override
        public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
            SSLSocket r = (SSLSocket) this.factory.createSocket(s, host, port, autoClose);
            r.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
            return r;
        }
    
    
        @Override
        public Socket createSocket(String host, int port) throws IOException {
    
            SSLSocket r = (SSLSocket) this.factory.createSocket(host, port);
            r.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
            return r;
        }
    
    
        @Override
        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
            SSLSocket r = (SSLSocket) this.factory.createSocket(host, port, localHost, localPort);
            r.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
            return r;
        }
    
    
        @Override
        public Socket createSocket(InetAddress host, int port) throws IOException {
            SSLSocket r = (SSLSocket) this.factory.createSocket(host, port);
            r.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
            return r;
        }
    
    
        @Override
        public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
            throws IOException {
            SSLSocket r = (SSLSocket) this.factory.createSocket(address, port, localAddress, localPort);
            r.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
            return r;
        }
    }
    
    

    相关文章

      网友评论

          本文标题:MQTT安全:使用ssl实现EMQ与android客户端安全通信

          本文链接:https://www.haomeiwen.com/subject/jedyeqtx.html