百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

Java网络编程进阶:通过JSSE创建安全的数据通信

yund56 2025-02-25 01:07 12 浏览



小编说:本文作者孙卫琴,知名IT作家和Java专家。本文将通过一个范例向大家介绍JSSE是如何实现安全的网络通信的。



在网络上,信息在由源主机到目标主机的传输过程中会经过其他计算机。一般情况下,中间的计算机不会监听路过的信息。但在使用网上银行或者进行信用卡交易时,网络上的信息有可能被非法分子监听,从而导致个人隐私的泄露。

由于Internet和Intranet体系结构存在一些安全漏洞,总有某些人能够截获并替换用户发出的原始信息。

随着电子商务的不断发展,人们对信息安全的要求越来越高,于是Netscape公司提出了SSL协议,目的为了能在开放网络上安全保密地传输信息

Java安全套接字扩展(JSSE,Java Secure Socket Extension)为基于SSL和TLS协议的Java网络应用程序提供了Java API以及参考实现。

JSSE支持数据加密、服务器端身份验证、数据完整性以及可选的客户端身份验证。使用JSSE,能保证采用各种应用层协议(比如HTTP、Telnet和FTP等)的客户程序与服务器程序安全地交换数据。

JSSE封装了底层复杂的安全通信细节,使得开发人员能方便地利用它来开发安全的网络应用程序。

下文参考了《Java网络编程核心技术详解》一书的第15章,将结合具体范例来向大家介绍JSSE的用法。


JSSE API 简介

JSSE封装了底层复杂的安全通信细节,使得开发人员能方便地用它来开发安全的网络应用程序。JSSE主要包括四个包:

  • javax.net.ssl包:包括进行安全通信的类,比如SSLServerSocket和SSLSocket类。
  • javax.net包:包括安全套接字的工厂类,比如SSLServerSocketFactory和SSLSocketFactory类。
  • java.security.cert包:包括处理安全证书的类,如X509Certificate类。X.509是由国际电信联盟(ITU-T)制定的安全证书的标准。
  • com.sun.net.ssl包:包括Oracle公司提供的JSSE的实现类。

JSSE具有以下重要特征:

  • 纯粹用Java语言编写。
  • 可以出口到大多数国家。
  • 提供了支持SSL的JSSE API和JSSE实现。
  • 提供了支持TLS的JSSE API和JSSE实现。
  • 提供了用于创建安全连接的类,如SSLSocket、 SSLServerSocket 和 SSLEngine。
  • 支持加密通信。
  • 支持客户端和服务器端的身份验证。
  • 支持SSL会话。
  • JSSE的具体实现会支持一些常用的加密算法,比如RSA(加密长度2048位)、RC4(密钥长度128位)和DH(密钥长度1024位)。

下面展示了JSSE API的主要类框图。

JSSE中负责安全通信的最核心的类是 SSLServerSocket 类与SSLSocket 类,它们分别是 ServerSocket 与 Socket 类的子类。SSLSocket 对象由 SSLSocketFactory 创建,此外, SSLServerSocket 的 accept() 方法也会创建 SSLSocket。SSLServerSocket 对象由 SSLServerSocketFactory 创建。SSLSocketFactory 、 SSLServerSocketFactory 以及 SSLEngine 对象都由 SSLContext 对象创建。SSLEngine 类用于支持非阻塞的安全通信。


创建安全服务器


以下EchoServer类创建了一个基于SSL的安全服务器,它处于服务器模式。

  1/*?EchoServer.java*/
  2import?java.net.*;
  3import?java.io.*;
  4import?javax.net.ssl.*;
  5import?java.security.*;
  6
  7public?class?EchoServer?{
  8??private?int?port=8000;
  9??private?SSLServerSocket?serverSocket;
 10
 11??public?EchoServer()?throws?Exception?{
 12????//输出跟踪日志
 13????//System.setProperty("javax.net.debug",?"all");
 14????SSLContext?context=createSSLContext();
 15????SSLServerSocketFactory?factory=context.getServerSocketFactory();
 16????serverSocket?=(SSLServerSocket)factory.createServerSocket(port);
 17????System.out.println("服务器启动");
 18????System.out.println(
 19???????????????serverSocket.getUseClientMode()??"客户模式":"服务器模式");
 20????System.out.println(serverSocket.getNeedClientAuth()?
 21?????????????"需要验证对方身份":"不需要验证对方身份");
 22
 23????String[]?supported=serverSocket.getSupportedCipherSuites();
 24????serverSocket.setEnabledCipherSuites(supported);
 25??}
 26
 27??public?SSLContext?createSSLContext()?throws?Exception?{
 28????//服务器用于证实自己身份的安全证书所在的密钥库
 29????String?keyStoreFile?=?"test.keystore";
 30????String?passphrase?=?"123456";
 31????KeyStore?ks?=?KeyStore.getInstance("JKS");
 32????char[]?password?=?passphrase.toCharArray();
 33????ks.load(new?FileInputStream(keyStoreFile),?password);
 34????KeyManagerFactory?kmf?=?KeyManagerFactory.getInstance("SunX509");
 35????kmf.init(ks,?password);
 36
 37????SSLContext?sslContext?=?SSLContext.getInstance("SSL");
 38????sslContext.init(kmf.getKeyManagers(),?null,?null);
 39
 40????//当要求客户端提供安全证书时,服务器端可创建TrustManagerFactory,
 41????//并由它创建TrustManager,TrustManger根据与之关联的KeyStore中的信息,
 42????//来决定是否相信客户提供的安全证书。
 43
 44????//客户端用于证实自己身份的安全证书所在的密钥库
 45????//String?trustStoreFile?=?"test.keystore";??
 46????//KeyStore?ts?=?KeyStore.getInstance("JKS");
 47????//ts.load(new?FileInputStream(trustStoreFile),?password);
 48????//TrustManagerFactory?tmf?=
 49????//????TrustManagerFactory.getInstance("SunX509");
 50????//tmf.init(ts);
 51????//sslContext.init(kmf.getKeyManagers(),
 52????//?????????????????tmf.getTrustManagers(),?null);
 53
 54????return?sslContext;
 55??}
 56
 57??public?String?echo(String?msg)?{
 58????return?"echo:"?+?msg;
 59??}
 60
 61??private?PrintWriter?getWriter(Socket?socket)throws?IOException{
 62????OutputStream?socketOut?=?socket.getOutputStream();
 63????return?new?PrintWriter(socketOut,true);
 64??}
 65??private?BufferedReader?getReader(Socket?socket)throws?IOException{
 66????InputStream?socketIn?=?socket.getInputStream();
 67????return?new?BufferedReader(new?InputStreamReader(socketIn));
 68??}
 69
 70??public?void?service()?{
 71????while?(true)?{
 72??????Socket?socket=null;
 73??????try?{
 74????????socket?=?serverSocket.accept();??//等待客户连接
 75????????System.out.println("New?connection?accepted?"
 76????????????????????????+socket.getInetAddress()
 77???????????????????????+?":"?+socket.getPort());
 78????????BufferedReader?br?=getReader(socket);
 79????????PrintWriter?pw?=?getWriter(socket);
 80
 81????????String?msg?=?null;
 82????????while?((msg?=?br.readLine())?!=?null)?{
 83??????????System.out.println(msg);
 84??????????pw.println(echo(msg));
 85??????????if?(msg.equals("bye"))?//如果客户发送的消息为“bye”,就结束通信
 86????????????break;
 87????????}
 88??????}catch?(IOException?e)?{
 89?????????e.printStackTrace();
 90??????}finally?{
 91?????????try{
 92???????????if(socket!=null)socket.close();??//断开连接
 93?????????}catch?(IOException?e)?{e.printStackTrace();}
 94??????}
 95????}
 96??}
 97
 98??public?static?void?main(String?args[])throws?Exception?{
 99????new?EchoServer().service();
100??}
101}


以上EchoServer类先创建了SSLContext对象,然后由它创建SSLServerSocketFactory对象,再由该工厂对象创建SSLServerSocket对象。对于以下程序代码:

1System.out.println(serverSocket.getUseClientMode()?
2???????????????"客户模式":"服务器模式");
3System.out.println(serverSocket.getNeedClientAuth()?
4???????????????"需要验证对方身份":"不需要需要验证对方身份");


打印结果为:

1服务器模式
2不需要验证对方身份


由此可见,默认情况下,SSLServerSocket处于服务器模式,必须向对方证实自身的身份,但不需要验证对方的身份,即不要求对方出示安全证书。

如果希望程序运行时输出底层JSSE实现的日志信息,可以把“javax.net.debug”系统属性设为“all”:

1System.setProperty("javax.net.debug",?"all");


创建安全客户程序


以下EchoClient类创建了一个基于SSL的安全客户,它处于客户模式

 1/*?EchoClient.java?*/
 2import?java.net.*;
 3import?java.io.*;
 4import?javax.net.ssl.*;
 5import?java.security.*;
 6
 7public?class?EchoClient?{
 8??private?String?host="localhost";
 9??private?int?port=8000;
10??private?SSLSocket?socket;
11
12??public?EchoClient()throws?IOException{
13????SSLContext?context=createSSLContext();
14????SSLSocketFactory?factory=context.getSocketFactory();
15????socket=(SSLSocket)factory.createSocket(host,port);
16????String[]?supported=socket.getSupportedCipherSuites();
17????socket.setEnabledCipherSuites(supported);
18????System.out.println(socket.getUseClientMode()?
19???????????????????????????"客户模式":"服务器模式");
20??}
21
22??public?SSLContext?createSSLContext()?throws?Exception?{
23????String?passphrase?=?"123456";
24????char[]?password?=?passphrase.toCharArray();
25
26????//设置客户端所信任的安全证书所在的密钥库
27????String?trustStoreFile?=?"test.keystore";????
28????KeyStore?ts?=?KeyStore.getInstance("JKS");
29????ts.load(new?FileInputStream(trustStoreFile),?password);
30????TrustManagerFactory?tmf?=
31?????????????????TrustManagerFactory.getInstance("SunX509");
32????tmf.init(ts);
33
34????SSLContext?sslContext?=?SSLContext.getInstance("SSL");
35????sslContext.init(null,tmf.getTrustManagers(),?null);
36????return?sslContext;
37??}
38??public?static?void?main(String?args[])throws?IOException{
39????new?EchoClient().talk();
40??}
41??private?PrintWriter?getWriter(Socket?socket)throws?IOException{
42????OutputStream?socketOut?=?socket.getOutputStream();
43????return?new?PrintWriter(socketOut,true);
44??}
45??private?BufferedReader?getReader(Socket?socket)throws?IOException{
46????InputStream?socketIn?=?socket.getInputStream();
47????return?new?BufferedReader(new?InputStreamReader(socketIn));
48??}
49??public?void?talk()throws?IOException?{
50????try{
51??????BufferedReader?br=getReader(socket);
52??????PrintWriter?pw=getWriter(socket);
53??????BufferedReader?localReader=
54??????????new?BufferedReader(new?InputStreamReader(System.in));
55??????String?msg=null;
56??????while((msg=localReader.readLine())!=null){
57????????pw.println(msg);
58????????System.out.println(br.readLine());
59
60????????if(msg.equals("bye"))
61??????????break;
62??????}
63????}catch(IOException?e){
64???????e.printStackTrace();
65????}finally{
66???????try{socket.close();}catch(IOException?e){e.printStackTrace();}
67????}
68??}
69}


以上EchoClient类先创建了一个SSLSocketFactory对象,然后由它创建了SSLSocket对象。对于以下程序代码:

1System.out.println(socket.getUseClientMode()?"客户模式":"服务器模式");


打印结果为:

1客户模式


由此可见,默认情况下,由SSLSocketFactory创建的SSLSocket对象处于客户模式,不必向对方证实自身的身份。

EchoClient类依靠TrustManager来决定是否信任EchoServer出示的安全证书。EchoClient类的SSLSocketFactory对象是由SSLContext对象来创建的。这个SSLContext对象通过TrustManager来管理所信任的安全证书。在本例中,TrustManager所信任的安全证书位于test.keystore密钥库文件中。


在本例中,服务器端向客户端出示的安全证书位于test.keystore密钥库文件中。在实际应用中,服务器端的密钥库文件中包含密钥对,从安全角度出发,客户端所信任的密钥库文件中应该仅仅包含公钥,所以服务器和客户端应该使用不同的密钥库文件。

在 IT 行业,大多数 Java 程序员都看过孙卫琴老师的书。

孙老师的书,清晰严谨,把复杂的技术架构层层剖析,结合典型的实例细致讲解,读者只要静下心来好好品读,就能深入 Java 技术的殿堂!

如今,Java 在网络应用开发领域得到了非常广泛的运用,孙卫琴老师新书上市之际,博文视点学院联合孙老师共同打造技术视频课《Java网络编程核心技术详解》(含同名新书一本)!

通过课程+图书的学习,不仅可以帮助你掌握网络编程的实用技术,还可以进一步提高按照面向对象的思想来设计和开发Java软件的能力!



热文推荐


  • 声纹技术:让智能语音助手真正“认得”自己
  • 为什么人人都需要懂一点高阶(中台)产品思维
  • 苏杰:如果可以重来,你还会做工作狂么?
  • 超详细丨完整的推荐系统架构设计

相关推荐

没有获得Windows 10 20H2升级通知,怎样直接升级

微软公司已经正式发布Windows1020H2操作系统,在正常情况下,微软只会首先推送到少量电脑,然后一边推送一边采集遥测数据。收集遥测数据可以确定哪些电脑可以更新,哪些电脑在更新后可能会失败,微...

不想让人随便卸载你安装的程序,用这四招,他将无计可施

Windows10不提供设置删除应用程序限制的功能,有几种间接方法可以防止用户删除操作系统中的程序和游戏。一、WindowsInstaller服务使用Windows工具,可以部分限制用户的权限。如...

一文看懂苹果全球开发者大会 五大系统全面升级

来源:环球网【环球网智能报道记者张阳】北京时间6月23日凌晨1点,苹果全球开发者大会(WWDC2020)如期举行,还是那个熟悉的乔布斯剧院,依旧是高水准的视频展示,但是这届WWDC,却是苹果历史...

无需等待微软分批推送,23H2可借助注册表快速获取Win11 24H2更新

IT之家10月15日消息,Windows1124H2正在分批推送,但由于存在多种Bug,微软已经开始放缓其推送节奏。WindowsLatest发现,Windows1123H2...

办公小技巧:剑走偏锋 PPT中打造动态图表

年底到了少不了又要制作各种总结报表,为了让自己的报表与众不同,我们可以借助PowerPoint动画组件+报表的方式,打造出更为出彩的动态图表。下面以PowerPoint2016为例,介绍如何使用三维...

文档表格 版本差异何在

在办公过程中,对文档或表格的修改是司空见惯的事。那么,一份文档做了内容改动,如何知道差异在哪里?一份表格改动部分数据,如何知道哪些有所变动?不要说审阅和修订功能,因为不是所有人都会用这些功能来标注的,...

Excel VBA自制日历组件16色可选 完美替代VBA日期控件

本日期组件可跟随单元格跟随窗体中ActiveX文本框组合框控件16种配色可选私信回复880日历可体验效果使用说明1打开自己需要应用日历面板的Excel表,注意必须是启用VBA的格式2在...

如何从交互角度读懂产品需求文档

作为设计师,理解产品经理提供的需求文档是交互设计工作的重要前提与起点,然而对于很多设计师来说,需求文档内容通常非常复杂,设计师们需要花费大量时间去消化、理解和归纳。本文作者结合公司示例,分析设计师如何...

植入让文档变得更强大

有效地利用文档置入技术,会让我们的常用文档功能变得更加强大,实现更加高效或有趣的应用。1.写字板文档嵌入其他文档有时,我们要组织一个大型的文档,但是这些文档的内容可能来自于不同种类的文档编辑器,比如...

Office 2016滚动文本框 顺手就来

【电脑报在线】如果一页PPT内容较多无法在完全显示,就需要用到滚动文本框,在PPT2016中借助控件即可快速制作滚动文本框。在“告诉我你想要做什么”输入“文本框控件”,在搜索结果点击“文本框(Acti...

Axure的多状态复选树

本文将详细介绍如何在Axure中实现一种增强型的多状态复选树组件,它不仅支持全选、半选和未选等状态,还具备动态加载、关键字筛选等高级功能。多状态复选树(Multi-StateCheckboxTre...

办公小技巧:PPT中控件图表巧联动

在利用PPT进行图表演示时,操作者有可能要与图表进行交互联动,比如通过输入数据来预测产品的生产情况等,这时就需要用到“开发工具”中的控件了。几个控件配合几句VBA代码,就可以轻松实现上述交互联动效果(...

用好插件——找回火狐的旧功能

现在的软件,特别是浏览器类软件,更新换代速度都很快,而且无论是外观界面还是系统组件都会有较大的变化,这样会让很多朋友无所适从。以大家常用的火狐浏览器为例,它就已经升级到了最新的35版,而且在新版中对很...

重新认识控件(二)

图片和文字,都是一种数据形式。我平时对文本框的录入,报错和提交的设计比较多。最近涉及到图片控件的设计,细细琢磨一下,这玩意还有一些平时没太注意的细节点,感觉对于其他控件的设计有指导意义,特此总结一下传...

JSA宏教程——在文档中添加复合框控件

上一期,我们初步认识了控件Control,本节我们将继续控件的相关内容。这几期我们将逐一介绍相关控制。本节先介绍复合框(也叫组合框)Combobox。复合框的作用复合框就是一个下拉选项框,一次显示一个...