Java實(shí)現(xiàn)SSL雙向認(rèn)證的方法
本文實(shí)例講述了Java實(shí)現(xiàn)SSL雙向認(rèn)證的方法。分享給大家供大家參考,具體如下:
我們常見(jiàn)的SSL驗(yàn)證較多的只是驗(yàn)證我們的服務(wù)器是否是真實(shí)正確的,當(dāng)然如果你訪問(wèn)的URL壓根就錯(cuò)了,那誰(shuí)也沒(méi)有辦法。這個(gè)就是所謂的SSL單向認(rèn)證。
但是實(shí)際中,我們有可能還會(huì)驗(yàn)證客戶(hù)端是否符合要求,也就是給我們每個(gè)用戶(hù)頒發(fā)一個(gè)證書(shū),比且每個(gè)數(shù)字證書(shū)都是唯一的,不公開(kāi)的。這樣就能通過(guò)這個(gè)數(shù)字證書(shū)保證當(dāng)前訪問(wèn)我服務(wù)器的這個(gè)用戶(hù)是經(jīng)過(guò)服務(wù)器認(rèn)可的,其他人不可訪問(wèn)。
雙向認(rèn)證 從第一個(gè)層面上 確保了服務(wù)器 與客戶(hù)端 都是互相認(rèn)可的。那么他們之間要進(jìn)行通信,就會(huì)在通信協(xié)議上附加SSL協(xié)議,確保通信的內(nèi)容是加密的,即使是sniffer這樣的網(wǎng)絡(luò)嗅探工具看到的都是亂碼。以后給大家演示下不加密的情況下,用sniffer看到的是什么??峙逻@樣你就能提高警惕了。
以下內(nèi)容從網(wǎng)絡(luò)上摘抄 加以實(shí)際驗(yàn)證后修改的。
模擬場(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)證,消息完整性和客戶(hù)端驗(yàn)證等技術(shù)。通過(guò)使用JSSE,開(kāi)發(fā)人員可以在客戶(hù)機(jī)和服務(wù)器之間通過(guò)TCP/IP協(xié)議安全地傳輸數(shù)據(jù)。
為了實(shí)現(xiàn)消息認(rèn)證。
Server需要:
1)KeyStore: 其中保存服務(wù)端的私鑰
2)Trust KeyStore:其中保存客戶(hù)端的授權(quán)證書(shū)
同樣,Client需要:
1)KeyStore:其中保存客戶(hù)端的私鑰
2)Trust KeyStore:其中保存服務(wù)端的授權(quán)證書(shū)
在這里我還是推薦使用Java自帶的keytool命令,去生成這樣信息文件。當(dāng)然目前非常流行的開(kāi)源的生成SSL證書(shū)的還有OpenSSL。OpenSSL用C語(yǔ)言編寫(xiě),跨系統(tǒng)。但是我們可能在以后的過(guò)程中用java程序生成證書(shū)的方便性考慮,還是用JDK自帶的keytool。
1)生成服務(wù)端私鑰,并且導(dǎo)入到服務(wù)端KeyStore文件中
keytool -genkey -alias serverkey -keystore kserver.keystore
過(guò)程中,分別需要填寫(xiě),根據(jù)需求自己設(shè)置就行
keystore密碼:123456
名字和姓氏:jin
組織單位名稱(chēng):none
組織名稱(chēng):none
城市或區(qū)域名稱(chēng):BJ
州或省份名稱(chēng):BJ
國(guó)家代碼:CN
serverkey私鑰的密碼,不填寫(xiě)和keystore的密碼一致。這里千萬(wàn)注意,直接回車(chē)就行了,不用修改密碼。否則在后面的程序中以及無(wú)法直接應(yīng)用這個(gè)私鑰,會(huì)報(bào)錯(cuò)。
就可以生成kserver.keystore文件
server.keystore是給服務(wù)端用的,其中保存著自己的私鑰
2)根據(jù)私鑰,導(dǎo)出服務(wù)端證書(shū)
keytool -export -alias serverkey -keystore kserver.keystore -file server.crt
server.crt就是服務(wù)端的證書(shū)
3)將服務(wù)端證書(shū),導(dǎo)入到客戶(hù)端的Trust KeyStore中
keytool -import -alias serverkey -file server.crt -keystore tclient.keystore
tclient.keystore是給客戶(hù)端用的,其中保存著受信任的證書(shū)
采用同樣的方法,生成客戶(hù)端的私鑰,客戶(hù)端的證書(shū),并且導(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
如此一來(lái),生成的文件分成兩組
服務(wù)端保存:kserver.keystore tserver.keystore
客戶(hù)端保存:kclient.keystore tclient.kyestore
以下是通過(guò)Java Socket通信程序來(lái)驗(yàn)證我們生成的證書(shū)是否可用。
客戶(hù)端:
package examples.ssl; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.KeyStore; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManagerFactory; /** * SSL Client * */ public class SSLClient { private static final String DEFAULT_HOST = "127.0.0.1"; private static final int DEFAULT_PORT = 7777; private static final String CLIENT_KEY_STORE_PASSWORD = "123456"; private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456"; private SSLSocket sslSocket; /** * 啟動(dòng)客戶(hù)端程序 * * @param args */ public static void main(String[] args) { SSLClient client = new SSLClient(); client.init(); client.process(); } /** * 通過(guò)ssl socket與服務(wù)端進(jìn)行連接,并且發(fā)送一個(gè)消息 */ public void process() { if (sslSocket == null) { System.out.println("ERROR"); return; } try { InputStream input = sslSocket.getInputStream(); OutputStream output = sslSocket.getOutputStream(); BufferedInputStream bis = new BufferedInputStream(input); BufferedOutputStream bos = new BufferedOutputStream(output); bos.write("Client Message".getBytes()); bos.flush(); byte[] buffer = new byte[20]; bis.read(buffer); System.out.println(new String(buffer)); sslSocket.close(); } catch (IOException e) { System.out.println(e); } } /** * <ul> * <li>ssl連接的重點(diǎn):</li> * <li>初始化SSLSocket</li> * <li>導(dǎo)入客戶(hù)端私鑰KeyStore,導(dǎo)入客戶(hù)端受信任的KeyStore(服務(wù)端的證書(shū))</li> * </ul> */ public void init() { try { 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("E://kclient.keystore"), CLIENT_KEY_STORE_PASSWORD.toCharArray()); tks.load(new FileInputStream("E://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); sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket(DEFAULT_HOST, DEFAULT_PORT); } catch (Exception e) { System.out.println(e); } } }
服務(wù)器端:
package examples.ssl; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.security.KeyStore; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.TrustManagerFactory; /*********************************************************************************************************************** * <ul> * <li>1)生成服務(wù)端私鑰</li> * <li>keytool -genkey -alias serverkey -keystore kserver.keystore</li> * <li>2)根據(jù)私鑰,到處服務(wù)端證書(shū)</li> * <li>keytool -exoport -alias serverkey -keystore kserver.keystore -file server.crt</li> * <li>3)把證書(shū)加入到客戶(hù)端受信任的keystore中</li> * <li>keytool -import -alias serverkey -file server.crt -keystore tclient.keystore</li> * </ul> **********************************************************************************************************************/ /** * SSL Server * */ public class SSLServer { private static final int DEFAULT_PORT = 7777; private static final String SERVER_KEY_STORE_PASSWORD = "123456"; private static final String SERVER_TRUST_KEY_STORE_PASSWORD = "123456"; private SSLServerSocket serverSocket; /** * 啟動(dòng)程序 * * @param args */ public static void main(String[] args) { SSLServer server = new SSLServer(); server.init(); server.start(); } /** * <ul> * <li>聽(tīng)SSL Server Socket</li> * <li> 由于該程序不是演示Socket監(jiān)聽(tīng),所以簡(jiǎn)單采用單線程形式,并且僅僅接受客戶(hù)端的消息,并且返回客戶(hù)端指定消息</li> * </ul> */ public void start() { if (serverSocket == null) { System.out.println("ERROR"); return; } while (true) { try { Socket s = serverSocket.accept(); InputStream input = s.getInputStream(); OutputStream output = s.getOutputStream(); BufferedInputStream bis = new BufferedInputStream(input); BufferedOutputStream bos = new BufferedOutputStream(output); byte[] buffer = new byte[20]; bis.read(buffer); System.out.println(new String(buffer)); bos.write("Server Echo".getBytes()); bos.flush(); s.close(); } catch (Exception e) { System.out.println(e); } } } /** * <ul> * <li>ssl連接的重點(diǎn):</li> * <li>初始化SSLServerSocket</li> * <li>導(dǎo)入服務(wù)端私鑰KeyStore,導(dǎo)入服務(wù)端受信任的KeyStore(客戶(hù)端的證書(shū))</li> * </ul> */ public void init() { try { 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("E://kserver.keystore"), SERVER_KEY_STORE_PASSWORD.toCharArray()); tks.load(new FileInputStream("E://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); serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(DEFAULT_PORT); serverSocket.setNeedClientAuth(true); } catch (Exception e) { e.printStackTrace(); } } }
更多關(guān)于java相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
- Tomcat ssl報(bào)錯(cuò)Connector attribute SSLCertificateFile must be defined when using SSL with APR解決方法
- nginx環(huán)境下配置ssl加密(單雙向認(rèn)證、部分https)
- Nginx 下配置SSL證書(shū)的方法
- Apache SSL服務(wù)器配置SSL詳解
- php curl常見(jiàn)錯(cuò)誤:SSL錯(cuò)誤、bool(false)
- 給APACHE開(kāi)啟SSL服務(wù)
- Nginx服務(wù)器中關(guān)于SSL的安全配置詳解
- windows服務(wù)器中檢測(cè)PHP SSL是否開(kāi)啟以及開(kāi)啟SSL的方法
- 利用keytools為tomcat 7配置ssl雙向認(rèn)證的方法
相關(guān)文章
Java反射(Class類(lèi),Class對(duì)象獲取)
下面是對(duì)Java反射機(jī)制是在程序的運(yùn)行過(guò)程中,Java語(yǔ)言的反射機(jī)制的超詳細(xì)解說(shuō),點(diǎn)進(jìn)來(lái)的小伙伴不要錯(cuò)過(guò)奧2021-08-08解決Spring?Boot應(yīng)用打包后文件訪問(wèn)問(wèn)題
在Spring Boot項(xiàng)目的開(kāi)發(fā)過(guò)程中,一個(gè)常見(jiàn)的挑戰(zhàn)是如何有效地訪問(wèn)和操作資源文件,本文就來(lái)介紹一下解決Spring?Boot應(yīng)用打包后文件訪問(wèn)問(wèn)題,感興趣的可以了解一下2024-01-01java文字轉(zhuǎn)語(yǔ)音的實(shí)現(xiàn)示例
在Java中,我們可以使用第三方庫(kù)來(lái)實(shí)現(xiàn)文字轉(zhuǎn)語(yǔ)音的功能,本文主要介紹了java文字轉(zhuǎn)語(yǔ)音的實(shí)現(xiàn)示例,選擇jacob技術(shù)實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03Spring?AOP操作的相關(guān)術(shù)語(yǔ)及環(huán)境準(zhǔn)備
這篇文章主要為大家介紹了Spring?AOP操作的相關(guān)術(shù)語(yǔ)及環(huán)境準(zhǔn)備學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05idea 解決用骨架創(chuàng)建項(xiàng)目過(guò)慢的操作方式
這篇文章主要介紹了idea 解決用骨架創(chuàng)建項(xiàng)目過(guò)慢的操作方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08使用java自帶des加密算法實(shí)現(xiàn)文件加密和字符串加密
這篇文章主要介紹了使用java自帶des加密算法實(shí)現(xiàn)文件加密和字符串加密的示例,需要的朋友可以參考下2014-03-03jvm雙親委派 vs 破壞雙親委派理解加載器的權(quán)責(zé)分配
這篇文章主要為大家介紹了jvm雙親委派 vs 破壞雙親委派對(duì)比來(lái)理解加載器的權(quán)責(zé)分配,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10