Java WebSocket Security 加密通信實踐

最近在項目中用到了WebSocket通信的內(nèi)容

第一版的實現(xiàn)方案:spring-websocket + nv-websocket-client

第一版的模式已經(jīng)正常得到應(yīng)用土至,但有個問題,就是傳輸過程沒有加密猾昆,完全明文的方式陶因,這在當(dāng)前的時代不合時宜,所以考慮對通信過程進(jìn)行加密傳輸垂蜗。

第二版的實現(xiàn)方案:Java-WebSocket

這是一個Java的WebSocket開發(fā)組件楷扬,支持整合到后端、客戶端贴见,支持SSL/TLS加密傳輸協(xié)議烘苹。由于我目前的項目主要應(yīng)用場景在局域網(wǎng)內(nèi),所以對于常規(guī)的CA簽名證書部署并不合適蝇刀,我就用自簽名的形式進(jìn)行加密傳輸檢驗螟加。

話不多說,直接開干吞琐。

一捆探、準(zhǔn)備工作

準(zhǔn)備自簽名證書

keytool -genkey -validity 3650 -keyalg RSA -sigalg SHA256withRSA -keystore "keystore.jks" -storepass "storepassword" -keypass "keypassword" -alias "default" -dname "CN=127.0.0.1, OU=MyOrgUnit, O=MyOrg, L=MyCity, S=MyRegion, C=MyCountry"

這里要特別注意 -keyalg RSA -sigalg SHA256withRSA 這兩個屬性,后面要將jks轉(zhuǎn)換成bks才能在Android平臺使用站粟,沒有這兩個屬性會導(dǎo)致handshake的時候報“Possible no handshake recieved!”黍图,說明簽名校驗不通過。
github上的Issue說明

將jks簽名文件轉(zhuǎn)換成bks奴烙,因為Android只能讀取bks格式的簽名秘鑰助被,轉(zhuǎn)換方法有好幾種,這里介紹一個命令行方式的
轉(zhuǎn)換方法

二切诀、程序?qū)崿F(xiàn)

java后臺

        WebSocketImpl.DEBUG = true;
        ChatServer chatServer = new ChatServer(9089); // 這里可以自定義端口揩环,默認(rèn)80
        logger.info("charServer port:" + chatServer.getPort());

        // load up the key store
        String STORETYPE = "JKS";
        String KEYSTORE = "/Users/randysu/keystore.jks";
        String STOREPASSWORD = "storepassword"; // 之前在制作keystore.jks時指定的store密碼
        String KEYPASSWORD = "keypassword"; // 之前在制作keystore.jks時指定的key密碼

        KeyStore ks = KeyStore.getInstance( STORETYPE );
        File kf = new File( KEYSTORE );
        ks.load( new FileInputStream( kf ), STOREPASSWORD.toCharArray() );

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init( ks, KEYPASSWORD.toCharArray() );
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init( ks );

        SSLContext sslContext = null;
        sslContext = SSLContext.getInstance( "TLS" );
        sslContext.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );

        // DefaultSSLWebSocketServerFactory  這里用的是默認(rèn)的加密倉庫,默認(rèn)允許加載所有的簽名方式幅虑,也可以自定義加密倉庫丰滑,移除不能校驗通過的簽名方式
//        SSLEngine engine = sslContext.createSSLEngine();
//        List<String> ciphers = new ArrayList<String>( Arrays.asList(engine.getEnabledCipherSuites()));
//        ciphers.remove("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
//        List<String> protocols = new ArrayList<String>( Arrays.asList(engine.getEnabledProtocols()));
//        protocols.remove("SSLv3");
//        CustomSSLWebSocketServerFactory factory = new CustomSSLWebSocketServerFactory(sslContext, protocols.toArray(new String[]{}), ciphers.toArray(new String[]{}));
//        chatserver.setWebSocketFactory(factory);

        chatServer.setWebSocketFactory( new DefaultSSLWebSocketServerFactory( sslContext ) );

        chatServer.start();

Android端

        // load up the key store
        String STORETYPE = "bks";
        String KEYSTORE = Environment.getExternalStorageDirectory().getPath() + File.separator + "keystore.bks";
        String STOREPASSWORD = "storepassword";
        String KEYPASSWORD = "keypassword";

        KeyStore ks = KeyStore.getInstance(STORETYPE);
        File kf = new File(KEYSTORE);
        ks.load(new FileInputStream(kf), STOREPASSWORD.toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
        kmf.init(ks, KEYPASSWORD.toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
        tmf.init(ks);

        SSLContext sslContext = null;
        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        SSLSocketFactory factory = sslContext.getSocketFactory();

        URI socketUri = new URI("wss://192.168.1.230:9089/server");
        SSLWebSocketClient socketClient = new SSLWebSocketClient(socketUri, mWsListener);
        socketClient.setSocket(factory.createSocket());
        socketClient.connect();
//        socketClient.connectBlocking();  // 阻塞當(dāng)前線程直至后臺返回連接成功或失敗

SSLWebSocketClient 繼承 WebSocketClient(Java-WebSocketde的對象),mWsListener是一個連接狀態(tài)的回調(diào)接口倒庵,在里面處理連接狀態(tài)褒墨。

mWsListener = new WsListener() {
            @Override
            public void onConnected(ServerHandshake handshakedata) {
                //連接成功
                Logger.i("websoeket opened connection");
               
            }

            @Override
            public void onTextMessage(String message) {
                //服務(wù)端消息來了
                Logger.i("websoeket received:" + message);
                
            }

            @Override
            public void onDisconnected(int code, String reason, boolean remote) {
                //連接斷開,remote判定是客戶端斷開還是服務(wù)端斷開
                Logger.i("websoeket Connection closed by " + (remote ? "remote server" : "us"));
                
            }

            @Override
            public void onConnectError(Exception ex) {
                // 連接錯誤
                Logger.i("websoeket error:" + ex);
                
            }
        };
    }

java后臺向Android發(fā)送消息

WebSocket對象的send(String str)方法

Android端向java后臺發(fā)送消息

SSLWebSocketClient的send(String str)方法

這里要注意一點(diǎn)擎宝,Android定義的證書格式是“ X509”郁妈,Java后臺定義的證書格式是“ SunX509”,如果Android定義“SunX509”會報錯绍申,同理也不能在Java后臺定義“X509”噩咪。

三、實測結(jié)果

屏幕快照 2018-06-27 下午4.27.24.png

推薦一本書《HTTPS權(quán)威指南》
Done!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末极阅,一起剝皮案震驚了整個濱河市胃碾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涂屁,老刑警劉巖书在,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拆又,居然都是意外死亡儒旬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門帖族,熙熙樓的掌柜王于貴愁眉苦臉地迎上來栈源,“玉大人,你說我怎么就攤上這事竖般∩蹩眩” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長艰亮。 經(jīng)常有香客問我闭翩,道長,這世上最難降的妖魔是什么迄埃? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任疗韵,我火速辦了婚禮,結(jié)果婚禮上侄非,老公的妹妹穿的比我還像新娘蕉汪。我一直安慰自己,他們只是感情好逞怨,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布者疤。 她就那樣靜靜地躺著,像睡著了一般叠赦。 火紅的嫁衣襯著肌膚如雪驹马。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天眯搭,我揣著相機(jī)與錄音窥翩,去河邊找鬼。 笑死鳞仙,一個胖子當(dāng)著我的面吹牛寇蚊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棍好,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼仗岸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了借笙?” 一聲冷哼從身側(cè)響起扒怖,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎业稼,沒想到半個月后盗痒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡低散,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年俯邓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熔号。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡稽鞭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出引镊,到底是詐尸還是另有隱情朦蕴,我是刑警寧澤篮条,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站吩抓,受9級特大地震影響涉茧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜琴拧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一降瞳、第九天 我趴在偏房一處隱蔽的房頂上張望嘱支。 院中可真熱鬧蚓胸,春花似錦、人聲如沸除师。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汛聚。三九已至锹安,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間倚舀,已是汗流浹背叹哭。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留痕貌,地道東北人风罩。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像舵稠,于是被迫代替她去往敵國和親超升。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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