之前发表在另一个平台的文章http://www.jointforce.com/jfperiodical/article/1218,现在发表到自己的博客上。

对于SSL/TLS协议,如果要每个开发者都自己去实现显然会带来不必要的麻烦,正是为了解决这个问题Java为广大开发者提供了Java安全套接字扩展——JSSE,它包含了实现Internet安全通信的一系列包的集合,是SSL和TLS的纯Java实现,同时它是一个开放的标准,每个公司都可以自己实现JSSE,通过它可以透明地提供数据加密、服务器认证、信息完整性等功能,就像使用普通的套接字一样使用安全套接字,大大减轻了开发者的负担,使开发者可以很轻松将SSL协议整合到程序中,并且JSSE能将安全隐患降到了最低点。

在利用SSL/TLS进行安全通信时,客户端跟服务器端都必须要支持SSL/TLS协议,不然将无法进行通信。而且客户端和服务器端都可能要设置用于证实自己身份的安全证书,并且还要设置信任对方的哪些安全证书。

关于身份认证方面有个名词叫客户端模式,一般情况客户端要对服务器端的身份进行验证,但是无需向服务器证实自己的身份,这样不用向对方证实自己身份的通信端我们就说它处于客户模式,否则成它处于服务器模式。SSLSocket的setUseClientMode(Boolean mode)方法可以设置客户端模式或服务器模式。

下面看具体实现的过程

① 解决证书问题。

一般而言作为服务器端必须要有证书以证明这个服务器的身份,并且证书应该描述此服务器所有者的一些基本信息,例如公司名称、联系人名等。证书由所有人以密码形式签名,基本不可伪造,证书获取的途径有两个:一是从权威机构购买证书,权威机构担保它发出的证书的真实性,而且这个权威机构被大家所信任,进而你可以相信这个证书的有效性;另外一个是自己用JDK提供的工具keytool创建一个自我签名的证书,这种情况下一般是我只想要保证数据的安全性与完整性,避免数据在传送的过程中被窃听或篡改,此时身份的认证已不重要,重点已经在端与端传输的秘密性上,证书的作用只体现在加解密签名。

SSL协议通信涉及密钥储存的文件格式比较多,很容易搞混,例如xxx.cer、xxx.pfx、xxx.jks、xxx.keystore、xxx.truststore等格式文件。如图3-1-7-3,搞清楚他们有助于理解后面的程序,.cer格式文件俗称证书,但这个证书中没有私钥,只包含了公钥;.pfx格式文件也称为证书,它一般供浏览器使用,而且它不仅包含了公钥,还包含了私钥,当然这个私钥是加密的,不输入密码是解不了密的;.jks格式文件表示java密钥存储器(java key store),它可以同时容纳N个公钥跟私钥,是一个密钥库;.keystore格式文件其实跟.jks基本是一样的,只是不同公司叫法不太一样,默认生成的证书存储库格式;.truststore格式文件表示信任证书存储库,它仅仅包含了通信对方的公钥,当然你可以直接把通信对方的jks作为信任库(就算如此你也只能知道通信对方的公钥,要知道密钥都是加密的,你无从获取,只要算法不被破解)。有些时候我们需要把pfx或cert转化为jks以便于用java进行ssl通信,例如一个银行只提供了pfx证书,而我们想用java进行ssl通信时就要将pfx转化为jks格式。

图3-1-7-3 密钥存储文件格式

按照理论上,我们一共需要准备四个文件,两个keystore文件和两个truststore文件,通信双方分别拥有一个keystore和一个truststore,keystore用于存放自己的密钥和公钥,truststore用于存放所有需要信任方的公钥。这里为了方便直接使用jks即keystore替代truststore(免去证书导来导去),因为对方的keystore包含了自己需要的信任公钥。

下面使用jdk自带的工具分别生成服务器端证书,通过如下命令并输入姓名、组织单位名称、组织名称、城市、省份、国家信息即可生成证书密码为tomcat的证书,此证书存放在密码也为tomcat的tomcat.jks证书存储库中。如果你继续创建证书将继续往tomcat.jks证书存储库中添加证书。如果你仅仅输入keytool -genkey -alias tomcat -keyalg RSA -keypass tomcat -storepass tomcat,不指定证书存储库的文件名及路径,则工具会在用户的home directory目录下生产一个“.keystore”文件作为证书存储库。

类似的,客户端证书也用此方式进行生成。如下

② 服务端TomcatSSLServer.java

public class TomcatSSLServer {

private static final String SSL_TYPE = "SSL";

private static final String KS_TYPE = "JKS";

private static final String X509 = "SunX509";

private final static int PORT = 443;

private static TomcatSSLServer sslServer;

private SSLServerSocket svrSocket;

public static TomcatSSLServer getInstance() throws Exception {

if (sslServer == null) {

sslServer = new TomcatSSLServer();

}

return sslServer;

}

private TomcatSSLServer() throws Exception{

SSLContext sslContext = createSSLContext();

SSLServerSocketFactory serverFactory = sslContext.getServerSocketFactory();

svrSocket =(SSLServerSocket) serverFactory.createServerSocket(PORT);

svrSocket.setNeedClientAuth(true);

String[] supported = svrSocket.getEnabledCipherSuites();

svrSocket.setEnabledCipherSuites(supported);

}

private SSLContext createSSLContext() throws Exception{

KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);

String serverKeyStoreFile = "c:\\tomcat.jks";

String svrPassphrase = "tomcat";

char[] svrPassword = svrPassphrase.toCharArray();

KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);

serverKeyStore.load(new FileInputStream(serverKeyStoreFile), svrPassword);

kmf.init(serverKeyStore, svrPassword);

String clientKeyStoreFile = "c:\\client.jks";

String cntPassphrase = "client";

char[] cntPassword = cntPassphrase.toCharArray();

KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);

clientKeyStore.load(new FileInputStream(clientKeyStoreFile),cntPassword);

tmf.init(clientKeyStore);

SSLContext sslContext  = SSLContext.getInstance(SSL_TYPE);

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

return sslContext;

}

public void startService() {

SSLSocket cntSocket = null;

BufferedReader ioReader = null;

PrintWriter ioWriter = null;

String tmpMsg = null;

while( true ) {

try {

cntSocket =(SSLSocket) svrSocket.accept();

ioReader = new BufferedReader(new InputStreamReader(cntSocket.getInputStream()));

ioWriter = new PrintWriter(cntSocket.getOutputStream());

while ( (tmpMsg = ioReader.readLine()) != null) {

System.out.println("客户端通过SSL协议发送信息:"+tmpMsg);

tmpMsg="欢迎通过SSL协议连接";

ioWriter.println(tmpMsg);

ioWriter.flush();

}

} catch(IOException e) {

e.printStackTrace();

} finally {

try {

if(cntSocket != null) cntSocket.close();

} catch(Exception ex) {ex.printStackTrace();}

}

}

}

public static void main(String[] args) throws Exception {

TomcatSSLServer.getInstance().startService();

}

}

基本顺序是先得到一个SSLContext实例,再对SSLContext实例进行初始化,密钥管理器及信任管理器作为参数传入,证书管理器及信任管理器按照指定的密钥存储器路径和密码进行加载。接着设置支持的加密套件,最后让SSLServerSocket开始监听客户端发送过来的消息。

③ 客户端TomcatSSLClient.java

public class TomcatSSLClient {

private static final String SSL_TYPE = "SSL";

private static final String X509 = "SunX509";

private static final String KS_TYPE = "JKS";

private SSLSocket sslSocket;

public TomcatSSLClient(String targetHost,int port) throws Exception {

SSLContext sslContext = createSSLContext();

SSLSocketFactory sslcntFactory =(SSLSocketFactory) sslContext.getSocketFactory();

sslSocket = (SSLSocket) sslcntFactory.createSocket(targetHost, port);

String[] supported = sslSocket.getSupportedCipherSuites();

sslSocket.setEnabledCipherSuites(supported);

}

private SSLContext createSSLContext() throws Exception{

KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);

String clientKeyStoreFile = "c:\\client.jks";

String cntPassphrase = "client";

char[] cntPassword = cntPassphrase.toCharArray();

KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);

clientKeyStore.load(new FileInputStream(clientKeyStoreFile),cntPassword);

String serverKeyStoreFile = "c:\\tomcat.jks";

String svrPassphrase = "tomcat";

char[] svrPassword = svrPassphrase.toCharArray();

KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);

serverKeyStore.load(new FileInputStream(serverKeyStoreFile), svrPassword);

kmf.init(clientKeyStore, cntPassword);

tmf.init(serverKeyStore);

SSLContext sslContext  = SSLContext.getInstance(SSL_TYPE);

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

return sslContext;

}

public String sayToSvr(String sayMsg) throws IOException{

BufferedReader ioReader = new BufferedReader(new InputStreamReader(

sslSocket.getInputStream()));

PrintWriter ioWriter = new PrintWriter(sslSocket.getOutputStream());

ioWriter.println(sayMsg);

ioWriter.flush();

return ioReader.readLine();

}

public static void main(String[] args) throws Exception {

TomcatSSLClient sslSocket = new TomcatSSLClient("127.0.0.1",443);

BufferedReader ioReader = new BufferedReader(new InputStreamReader(System.in));

String sayMsg = "";

String svrRespMsg= "";

while( (sayMsg = ioReader.readLine())!= null ) {

svrRespMsg = sslSocket.sayToSvr(sayMsg);

if(svrRespMsg != null && !svrRespMsg.trim().equals("")) {

System.out.println("服务器通过SSL协议响应:"+svrRespMsg);

}

}

}

}

客户端的前面操作基本跟服务器端的一样,先创建一个SSLContext实例,再用密钥管理器及信任管理器对SSLContext进行初始化,当然这里密钥存储的路径是指向客户端的client.jks。接着设置加密套件,最后使用SSLSocket进行通信。

注意服务器端有行代码svrSocket.setNeedClientAuth(true);它是非常重要的一个设置方法,用于设置是否验证客户端的身份。假如我们把它注释掉或设置为false,此时客户端将不再需要自己的密钥管理器,即服务器不需要通过client.jks对客户端的身份进行验证,把密钥管理器直接设置为null也可以跟服务器端进行通信。

最后谈谈信任管理器,它的职责是决定是否信任远端的证书,那么它凭借什么去判断呢?如果不显式设置信任存储器的文件路径,将遵循如下规则:①如果系统属性javax.net.ssl.truststore指定了truststore文件,那么信任管理器将去jre路径下的lib/security目录寻找这个文件作为信任存储器;②如果没设置①中的系统属性,则去寻找一个%java_home%/lib/security/jssecacerts文件作为信任存储器;③如果jssecacerts不存在而cacerts存在,则cacerts作为信任存储器。

至此,一个利用JSSE实现BIO模式的SSL协议通信的例子已完成。

喜欢java的同学交个朋友:

如何让你的传输更安全--基于SSL协议的通信模式的更多相关文章

  1. JAVA基础知识之网络编程——-基于UDP协议的通信例子

    UDP是一种不可靠的协议,它在通信两端各建立一个socket,这两个socket不会建立持久的通信连接,只会单方面向对方发送数据,不检查发送结果. java中基于UDP协议的通信使用DatagramS ...

  2. Python中的端口协议之基于UDP协议的通信传输

    UDP协议: 1.python中基于udp协议的客户端与服务端通信简单过程实现 2.udp协议的一些特点(与tcp协议的比较)        3.利用socketserver模块实现udp传输协议的并 ...

  3. 基于SSL协议的双向认证 - SSL协议 [1]

    1  概要说明 在互联网通信方式中,目前用的最广泛的是HTTPS配合SSL和数字证书来保证传输和认证安全了. 2  详细介绍 2.1    HTTPS HTTPS全称:Hypertext Transf ...

  4. python中基于tcp协议的通信(数据传输)

    tcp协议:流式协议(以数据流的形式通信传输).安全协议(收发信息都需收到确认信息才能完成收发,是一种双向通道的通信) tcp协议在OSI七层协议中属于传输层,它上承用户层的数据收发,下启网络层.数据 ...

  5. Android之基于HTTP协议的通信详解

    Android系统中本身是有下载机制的,比如浏览器使用的DownloadManager.可遗憾的是,DownloadManager只提供给浏览器使用,一般的应用程序没法调用它. 另外,如果下载调用频繁 ...

  6. 基于SSL协议的双向认证 - 双向认证 [3]

    1      SSL双向认证的实现 这里是基于SSL和Tomcat配置实现的,配置方法如下: 1.1    生成CA数字证书 首先需要配置OPENSSL环境变量. 我的OPENSSL配置文件路径是“D ...

  7. 基于TCP协议Socket通信

    服务器线程处理类 package demo4; import java.io.*; import java.net.Socket; /** * 服务器线程处理类 * @ClassName Server ...

  8. Tftp文件传输服务器(基于UDP协议)

    一个简单的UDP服务端与客户端 服务端: from socket import * #创建套接字 udp_server = socket(AF_INET,SOCK_DGRAM) msg_server ...

  9. 为何基于tcp协议的通信比基于udp协议的通信更可靠?

    tcp协议一定是先建好双向链接,发一个数据包要得到确认才算发送完成,没有收到就一直给你重发:udp协议没有链接存在,udp直接丢数据,不管你有没有收到. TCP的可靠保证,是它的三次握手双向机制,这一 ...

随机推荐

  1. Winform DevExpress控件库(一) DevExpress控件库的安装与新建第一个DevExpress项目

    前言:因为这段时间要接触到DevExpress控件库,而我本身甚至对winform的控件都了解甚少,所以处在学习中,写下博客主要是为了方便后期的回顾,当然也可以给一些新人第一次接触时做为学习的参考,以 ...

  2. 安卓高级6 SnackBar

    引言 文/李牧羊(简书作者) 原文链接:http://www.jianshu.com/p/2654e6bda3b1 著作权归作者所有,转载请联系作者获得授权,并标注"简书作者". ...

  3. Android存储之SQLite数据库

    Android存储之SQLite数据库数据库 创建数据库 package --; import android.content.Context; import android.database.sql ...

  4. LauncherModel.Callbacks接口

    public interface Callbacks { //如果Launcher在加载完成之前被强制暂停,那么需要通过这个回调方法通知 //launcher,在它再次显示的时候重新执行加载过程 pu ...

  5. ROS机器人程序设计(原书第2版)补充资料 (捌) 第八章 导航功能包集入门 navigation

    ROS机器人程序设计(原书第2版)补充资料 (捌) 第八章 导航功能包集入门 navigation 书中,大部分出现hydro的地方,直接替换为indigo或jade或kinetic,即可在对应版本中 ...

  6. setting.py

    """ Django settings for sitea project. For more information on this file, see https:/ ...

  7. 用户创建,删除and并发注册and系统登陆的API研究(学习汇总网上资料)

    一.系统登陆链接实现 比如有一个外围支持系统,用户需要在外围系统登录之后点个link就可以登录到Oracle ERP系统中,那么我们需要先把外围系统的用户创建在Oracle ERP中,并且分配职责给他 ...

  8. SELinux策略语言--客体类别和许可

    1. 简介 SELinux策略语言主要描述policy.conf的相关语法,其相关部分如下图所示: 2. 客体类别 定义内核支持的客体类别和许可的策略语言指令,并对SELinux系统中内核客体类别标准 ...

  9. scala模式匹配的使用

    Scala模式匹配 Tip1:模式总是从上往下匹配,如果匹配不到则匹配case_项(类似Java中的default) Tip2:与Java和C语言不同,不需要在每个分支末尾使用break语句退出(不会 ...

  10. Android开源框架ViewPagerIndicator的基本使用

    转载本博客请注明出处:点击打开链接    http://blog.csdn.net/qq_32059827/article/details/52495647 很多新闻资讯类的app都有一些共性,那就是 ...