stone2083

          SSL雙向認(rèn)證java實(shí)現(xiàn)

          本文通過模擬場(chǎng)景,介紹SSL雙向認(rèn)證的java實(shí)現(xiàn)

          默認(rèn)的情況下,我認(rèn)為讀者已經(jīng)對(duì)SSL原理有一定的了解,所以文章中對(duì)SSL的原理,不做詳細(xì)的介紹。
          如果有這個(gè)需要,那么通過GOOGLE,可以搜索到很多這樣的文章。

          模擬場(chǎng)景:
          Server端和Client端通信,需要進(jìn)行授權(quán)和身份的驗(yàn)證,即Client只能接受Server的消息,Server只能接受Client的消息。

          實(shí)現(xiàn)技術(shù):
          JSSE(Java Security Socket Extension
          是Sun為了解決在Internet上的安全通訊而推出的解決方案。它實(shí)現(xiàn)了SSL和TSL(傳輸層安全)協(xié)議。在JSSE中包含了數(shù)據(jù)加密,服務(wù)器驗(yàn)證,消息完整性和客戶端驗(yàn)證等技術(shù)。通過使用JSSE,開發(fā)人員可以在客戶機(jī)和服務(wù)器之間通過TCP/IP協(xié)議安全地傳輸數(shù)據(jù)

          為了實(shí)現(xiàn)消息認(rèn)證。
          Server需要:
          1)KeyStore: 其中保存服務(wù)端的私鑰
          2)Trust KeyStore:其中保存客戶端的授權(quán)證書
          同樣,Client需要:
          1)KeyStore:其中保存客戶端的私鑰
          2)Trust KeyStore:其中保存服務(wù)端的授權(quán)證書

          我們可以使用Java自帶的keytool命令,去生成這樣信息文件
          1)生成服務(wù)端私鑰,并且導(dǎo)入到服務(wù)端KeyStore文件中
          keytool -genkey -alias serverkey -keystore kserver.keystore
          過程中,分別需要填寫,根據(jù)需求自己設(shè)置就行
          keystore密碼:123456
          名字和姓氏:stone
          組織單位名稱:eulic
          組織名稱:eulic
          城市或區(qū)域名稱:HZ
          州或省份名稱:ZJ
          國(guó)家代碼:CN
          serverkey私鑰的密碼,不填寫和keystore的密碼一致:123456
          就可以生成kserver.keystore文件
          server.keystore是給服務(wù)端用的,其中保存著自己的私鑰

          2)根據(jù)私鑰,導(dǎo)出服務(wù)端證書
          keytool -export -alias serverkey -keystore kserver.keystore -file server.crt
          server.crt就是服務(wù)端的證書

          3)將服務(wù)端證書,導(dǎo)入到客戶端的Trust KeyStore中
          keytool -import -alias serverkey -file server.crt -keystore tclient.keystore
          tclient.keystore是給客戶端用的,其中保存著受信任的證書

          采用同樣的方法,生成客戶端的私鑰,客戶端的證書,并且導(dǎo)入到服務(wù)端的Trust KeyStore中
          1)keytool -genkey -alias clientkey -keystore kclient.keystore
          2)keytool -export -alias clientkey -keystore kclient.keystore -file client.crt
          3)keytool -import -alias clientkey -file client.crt -keystore tserver.keystore

          如此一來,生成的文件分成兩組
          服務(wù)端保存:kserver.keystore tserver.keystore
          客戶端保存:kclient.keystore  tclient.kyestore

          接下來,就采用JSSE,分別生成SSLServerSocket,SSLSocket

          服務(wù)端,生成SSLServerSocket代碼
          SSLContext ctx = SSLContext.getInstance("SSL");

          KeyManagerFactory kmf 
          = KeyManagerFactory.getInstance("SunX509");
          TrustManagerFactory tmf 
          = TrustManagerFactory.getInstance("SunX509");

          KeyStore ks 
          = KeyStore.getInstance("JKS");
          KeyStore tks 
          = KeyStore.getInstance("JKS");

          ks.load(
          new FileInputStream("data/kserver.keystore"), SERVER_KEY_STORE_PASSWORD.toCharArray());
          tks.load(
          new FileInputStream("data/tserver.keystore"), SERVER_TRUST_KEY_STORE_PASSWORD.toCharArray());

          kmf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());
          tmf.init(tks);

          ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), 
          null);

          return (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(DEFAULT_PORT);

          客戶端,生成SSLSocket的代碼,大同小異
          SSLContext ctx = SSLContext.getInstance("SSL");

          KeyManagerFactory kmf 
          = KeyManagerFactory.getInstance("SunX509");
          TrustManagerFactory tmf 
          = TrustManagerFactory.getInstance("SunX509");

          KeyStore ks 
          = KeyStore.getInstance("JKS");
          KeyStore tks 
          = KeyStore.getInstance("JKS");

          ks.load(
          new FileInputStream("data/kclient.keystore"), CLIENT_KEY_STORE_PASSWORD.toCharArray());
          tks.load(
          new FileInputStream("data/tclient.keystore"), CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());

          kmf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());
          tmf.init(tks);

          ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), 
          null);

          return (SSLSocket) ctx.getSocketFactory().createSocket(DEFAULT_HOST, DEFAULT_PORT);

          如此,就完成了服務(wù)端和客戶端之間的基于身份認(rèn)證的交互。

          client采用kclient.keystore中的clientkey私鑰進(jìn)行數(shù)據(jù)加密,發(fā)送給server
          server采用tserver.keystore中的client.crt證書(包含了clientkey的公鑰)對(duì)數(shù)據(jù)解密,如果解密成功,證明消息來自client,進(jìn)行邏輯處理

          server采用kserver.keystore中的serverkey私鑰進(jìn)行數(shù)據(jù)叫米,發(fā)送給client
          client采用tclient.keystore中的server.crt證書(包含了serverkey的公鑰)對(duì)數(shù)據(jù)解密,如果解密成功,證明消息來自server,進(jìn)行邏輯處理

          如果過程中,解密失敗,那么證明消息來源錯(cuò)誤。不進(jìn)行邏輯處理。這樣就完成了雙向的身份認(rèn)證。

          下面我附上簡(jiǎn)單的SSLServer.java SSLClient.java,供大家演示用。
          啟動(dòng)服務(wù)端的時(shí)候,大家不妨采用telnet 127.0.0.1 7777連接,看看能不能實(shí)現(xiàn)消息傳遞。

          ssl demo
          備注:
          demo是采用maven構(gòu)建項(xiàng)目的
          demo文件的編碼是用utf8,為了避免中文亂碼,請(qǐng)把workspace設(shè)置成utf8編碼

          posted on 2007-12-20 14:04 stone2083 閱讀(43501) 評(píng)論(8)  編輯  收藏 所屬分類: java

          Feedback

          # re: SSL雙向認(rèn)證java實(shí)現(xiàn) 2008-01-05 22:37 jzp

          不錯(cuò),正需要.以后多些文章分享,辛苦了.  回復(fù)  更多評(píng)論   

          # re: SSL雙向認(rèn)證java實(shí)現(xiàn) 2008-04-18 11:41 wangpeng

          我用你的方法試了,的確可以實(shí)現(xiàn)ssl加密傳輸,但是我重新生成客戶端證書后,并沒有加載到服務(wù)器端的信任庫(kù)tserver.keystore中,但是仍然可以通訊,這樣就是我隨便生成個(gè)客戶端證書只要有服務(wù)器的證書就可以通訊了,沒有真正意義上達(dá)到雙向認(rèn)證,如何解決這個(gè)問題,請(qǐng)指教  回復(fù)  更多評(píng)論   

          # re: SSL雙向認(rèn)證java實(shí)現(xiàn) 2008-04-18 11:43 wangpeng

          我的msn是steve_king211@msn.com,希望加上我,請(qǐng)教一些問題  回復(fù)  更多評(píng)論   

          # re: SSL雙向認(rèn)證java實(shí)現(xiàn) 2008-04-20 17:18 stone2083

          謝謝wangpeng朋友,找到上原先demo中的一個(gè)問題。
          因?yàn)樵赟erver端程序中,初始化的SSLServreSocket
          serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(DEFAULT_PORT);
          少寫了條語句:serverSocket.setNeedClientAuth(true); //表明需要驗(yàn)證客戶端的身份。

          由于原demo程序,不需要客戶端身份驗(yàn)證,所以即使服務(wù)端沒有客戶端證書,也能完成通訊。

          受限于自己對(duì)jsse理解非常的淺,上面的文章僅僅是覆蓋了jsse很表層的內(nèi)容。
          推薦ibm網(wǎng)站上的一篇文章,對(duì)jsse和ssl寫得很深入淺出。
          為高級(jí) JSSE 開發(fā)人員定制 :http://www.ibm.com/developerworks/cn/java/j-customssl/

          如有問題,歡迎再交流 :)
            回復(fù)  更多評(píng)論   

          # re: SSL雙向認(rèn)證java實(shí)現(xiàn) 2008-07-10 20:56 JessonWoo

          請(qǐng)問單向認(rèn)證,即客戶端對(duì)服務(wù)器端的認(rèn)證,需要為客戶端配置證書嗎,
          具體用java如何實(shí)現(xiàn)查看服務(wù)器端證書的功能,
          盼高手賜教!  回復(fù)  更多評(píng)論   

          # re: SSL雙向認(rèn)證java實(shí)現(xiàn) 2008-07-14 13:21 stone2083

          @JessonWoo
          hi,首先申明下,我自己對(duì)ssl的認(rèn)識(shí)還是很膚淺的,僅僅上次和cnnic合作的時(shí)候,為了了解安全性問題,才被迫稍微看了相應(yīng)的一些知識(shí).

          所謂認(rèn)證,是要對(duì)某臺(tái)(當(dāng)然可以是集群)服務(wù)器身份做認(rèn)證.
          認(rèn)證方式有兩種:
          自簽名認(rèn)證:服務(wù)端生成key,然后根據(jù)key導(dǎo)出證書.公布于站點(diǎn).
          通過第三方認(rèn)證機(jī)構(gòu)認(rèn)證:有服務(wù)端生成key,然后導(dǎo)出認(rèn)證信息,交由天威誠(chéng)信等第三方認(rèn)證機(jī)構(gòu)認(rèn)證,最后生成證書,公布于站點(diǎn).

          客戶端,將證書下載,確認(rèn)為可信任公司認(rèn)證信息,并且導(dǎo)入到受信任區(qū)(trustscore),建立連接與服務(wù)端進(jìn)行正常交互.至此,就完成了對(duì)服務(wù)端的認(rèn)證.
          所以客戶端必須將證書導(dǎo)入信任區(qū).

          java中,可以查看證書:
          寫個(gè)簡(jiǎn)單的方法:
          FileInputStream fis = new FileInputStream("cert.cer");
          CertificateFactory cf=CertificateFactory.getInstance("X509");
          X509Certificate c=(X509Certificate) cf.generateCertificate(fis);
          System.out.println(c.getSubjectDN());
          //可以查看下X509Certificate的一些get方法.

          其實(shí)keytool僅僅是jdk中的工具.
          openssl是更常用的一個(gè)工具
          可見:http://www.openssl.org/  回復(fù)  更多評(píng)論   

          # re: SSL雙向認(rèn)證java實(shí)現(xiàn) 2008-11-17 14:00 le

          強(qiáng)身份認(rèn)證演示:https://www.wosign.com/logindemo/  回復(fù)  更多評(píng)論   

          # re: SSL雙向認(rèn)證java實(shí)現(xiàn) 2012-12-20 21:29 郭建軍

          雙向要加setNeedClientAuth(true)吧,你這是單向的  回復(fù)  更多評(píng)論   

          主站蜘蛛池模板: 沾益县| 南溪县| 苍山县| 红安县| 抚宁县| 东乡县| 新平| 开鲁县| 德化县| 岑巩县| 隆子县| 吴堡县| 榕江县| 资中县| 神木县| 阳春市| 沈丘县| 新建县| 佳木斯市| 凌海市| 临泽县| 灵川县| 永嘉县| 仲巴县| 理塘县| 嘉兴市| 兴安县| 阿合奇县| 荆州市| 晋城| 西林县| 呼和浩特市| 武平县| 凤凰县| 萨迦县| 安康市| 宜君县| 宁强县| 江川县| 双桥区| 赤水市|