MQTT安全:使用ssl實現(xiàn)EMQ與android客戶端安全通信

原文地址

使用 SSL 對 MQTT的消息交換進行加密,提高安全性肩杈。

服務(wù)器啟用SSL

我們需要數(shù)字證書來對進行ssl通信用戶進行強認證档址。由于獲得一個真正受外界信任的證書需要花費money领迈,所有我們采用自簽名證書压彭。

要實現(xiàn)雙向認證(服務(wù)器認證客戶端运翼、客戶端認證服務(wù)器)我們需要3個證書双谆,一個CA證書壳咕,一個EMQ服務(wù)器證書,一個客戶端證書顽馋。

具體的生成證書操作谓厘、啟用EMQ ssl和雙向認證,參考這篇文章 Securing EMQ Connections with SSL

android客戶端的ssl實現(xiàn)

上一步我們生成了客戶端使用的證書 MyClient1.pem 和私有秘鑰MyClient1.key寸谜,但是要想在android上使用需要將其轉(zhuǎn)成bks格式竟稳。

pem 轉(zhuǎn) bks

1、首先生成.p12文件:

openssl pkcs12 -export -nodes -in MyClient1.pem -inkey MyClient1.key -out client.p12
  • -inkey為私鑰文件
  • -in為證書熊痴,如果pem私鑰沒有密碼他爸,則使用-nodes表示無密碼,如果有密碼使用-passin果善;如果私鑰和證書都在同一文件里則-in-inkey指定同一個文件诊笤。

會提示輸入給.p12秘鑰庫設(shè)置的密碼,請記住巾陕,下面會用到

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 轉(zhuǎn)換為 BKS 格式時纪他,需要 bcprov-ext-jdk15on-158.jar,可以在 這里 找到晾匠。文件路徑直接帶在-providerpath 參數(shù)后面即可茶袒。也可以把jar包放到如下路徑:jdk/jre/lib/ext,從而省略-providerpath凉馆。

會首先提示輸入給bks秘鑰庫設(shè)置的密碼弹谁,請記住,下面會用到句喜。

然后會提示輸入p12秘鑰庫密碼,即上一步設(shè)置的密碼沟于。

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市旷太,隨后出現(xiàn)的幾起案子展懈,更是在濱河造成了極大的恐慌,老刑警劉巖供璧,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件存崖,死亡現(xiàn)場離奇詭異,居然都是意外死亡睡毒,警方通過查閱死者的電腦和手機来惧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來演顾,“玉大人供搀,你說我怎么就攤上這事∧浦粒” “怎么了葛虐?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長棉钧。 經(jīng)常有香客問我屿脐,道長,這世上最難降的妖魔是什么宪卿? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任的诵,我火速辦了婚禮,結(jié)果婚禮上佑钾,老公的妹妹穿的比我還像新娘奢驯。我一直安慰自己,他們只是感情好次绘,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布瘪阁。 她就那樣靜靜地躺著撒遣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪管跺。 梳的紋絲不亂的頭發(fā)上义黎,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音豁跑,去河邊找鬼廉涕。 笑死,一個胖子當(dāng)著我的面吹牛艇拍,可吹牛的內(nèi)容都是我干的狐蜕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼卸夕,長吁一口氣:“原來是場噩夢啊……” “哼层释!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起快集,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤贡羔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后个初,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乖寒,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年院溺,在試婚紗的時候發(fā)現(xiàn)自己被綠了楣嘁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡珍逸,死狀恐怖马澈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情弄息,我是刑警寧澤痊班,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站摹量,受9級特大地震影響涤伐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缨称,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一凝果、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧睦尽,春花似錦器净、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纠俭。三九已至,卻和暖如春浪慌,著一層夾襖步出監(jiān)牢的瞬間冤荆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工权纤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钓简,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓汹想,卻偏偏與公主長得像外邓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子古掏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 信息安全三要素 1. 保密性:信息在傳輸時不被泄露 2. 完整性:信息在傳輸時不被篡改 3. 身份認證:用于確定你...
    Jason1226閱讀 1,226評論 0 0
  • 一损话、作用 不使用SSL/TLS的HTTP通信,就是不加密的通信冗茸。所有信息明文傳播,帶來了三大風(fēng)險匹中。 (1)竊聽風(fēng)險...
    XLsn0w閱讀 10,482評論 2 44
  • 原文地址 http://blog.csdn.net/u012409247/article/details/4985...
    0fbf551ff6fb閱讀 3,513評論 0 13
  • 編寫TCP服務(wù)器和客戶端 Vert.x 允許您很輕松的編寫一個非阻塞的TCP客戶端和服務(wù)器夏漱。 創(chuàng)建TCP服務(wù)器 使...
    安靜點就睡吧閱讀 6,525評論 1 4
  • 今天下午,我預(yù)習(xí)了二年級下冊顶捷,第一單元第二課的課文挂绰,名字叫《找春天》。 詞語辨析:近義詞:脫掉一脫...
    劉俊艷閱讀 2,740評論 0 0