安全网络通信(SSL&JSSE)

目录

一、概念介绍

1.SSL简介

2.加密通信

3.安全证书

4.SSL握手

二、keytool工具生成证书

三、JSSE简介

1.KeyStore、KeyManager与TrustManager类

2.SSLContext类

3.SSLServerSocketFactory和SSLSocketFactory类

4.SSLSocket类

4.1.设置加密套件

4.2.处理握手结束事件

4.3.管理SSL会话

4.4.客户模式

5.SSLServerSocket类

三、基于SSL的安全服务器与客户端

1.服务器端实现

2.客户端实现

四、SSLEngine结合NIO实现非阻塞的安全通信

1.SSL握手实现

2.服务器端实现

3.客户端实现

五、加签与验签

1.概念介绍

2.Java提供的加签验签API


在网络上,信息由源主机发送到目标主机的传输过程中会经过一些其他计算机,在数据传输的过程中,可以利用工具,将网络中正在传播的数据截获,进行一些其他操作。比如在使用网银时,网络上的信息可能被非法分子监听,从而导致个人隐私泄露,也可能替换用户发出的原始信息。基于安全考虑,Netscape公司提出了SSL协议,旨在数据信息能在网络上安全保密地传输。

Java安全套接字扩展(Java Secure Socket Extension,JSSE)为基于SSL和TLS协议地Java网络应用程序提供了API及参考实现。使用JSSE,能够保证采用各种应用层协议(比如HTTP、FTP等)地客户程序与服务器程序安全地交换数据。

一、概念介绍

1.SSL简介

SSL(Secure Socket Layer,安全套接字层)是一种保证网络上地两个节点进行安全通信地协议。它是介于传输层和应用层之间的。IEIF(Internet Engineering Task Force)国际组织对SSL作了标准化,指定了RFC2246规范,并将其称为传输层安全(Transport Layer Security, TLS)(升级版SSL)。SSL和TLS都建立在TCP/IP基础之上,采用加密技术来实现安全通信,保证通信数据的保密性和完整性,并且保证通信双方可以验证对方身份,一些应用层协议,都可以采用SSL来保证安全通信,比如建立在SSL协议上地HTTP被称为HTTPS协议。

2.加密通信

加密技术的基本原理是:数据从一端发送到另一端发送到另一端时,发送者先对数据进行加密,然后把密文发送给接收者。这样,在网络上传输的是经过加密的数据,如果有人截获了这批数据,由于没有解密的密钥,就无法获得真正的原始数据。接收者接收到加密数据后,先对数据进行解密,然后处理。

3.安全证书

除了对数据加密通信,SSL还 采用了身份认证机制,确保通信双方可以验证对方的真实身份。就像”电子身份证“一样,不过SSL是通过安全证书来证明客户或服务器的身份。当客户通过安全的连接和服务器通信时,服务器会先向客户端出示它的安全证书,这个证书声明该服务器是安全的,而且的确是这个服务器。每个证书在全世界范围内都是唯一的,其他非法服务器无法假冒原始服务器的身份。

对于单个客户来说,到公认的权威机构去获取安全证书是一件麻烦事。为了扩大客户群并且便于客户的访问,许多服务器不要求客户出示安全证书。在某些情况下,服务器也会要求客户出示安全证书,以便核实该客户的身份,这主要是在B2B(Business to Business)事务中。

获取安全证书有两种方式:

  • 获取权威机构获得证书;
  • 创建自我签名证书。

从权威机构获得证书

安全证书采用加密技术制作而成,他人几乎无法伪造。安全证书由国际权威机构颁发,申请安全证书时,必须支付一定的费用。一个安全证书只对一个IP地址有效,如果系统环境中有多个IP地址,就必须为每个IP地址都购买安全证书。

创建自我签名证书

在某些场合,通信双方只关心在网络上可以被安全传输,并不需要对方进行身份验证,这种情况下,可以使用自我签名证书,JDK提供的keytool工具就可以创建这样的证书。这样的证书就像自己制作名片一样,缺乏权威性,达不到身份认证的目的。(假设你向对方递交名片,名片上写的身份信不信只能由对方自己去判断)。但是无论是从权威机构获得的证书还是自己制作的证书,采用的加密技术都是一样的,使用这些证书,都可以实现安全地加密通信

4.SSL握手

安全证书既包含了用于加密数据的密钥,又包含了用于证实身份的数字签名。安全证书采用公钥加密技术。公钥加密指使用一对非对称的密钥进行加密或解密。每一对密钥由公和私钥组成。公钥被公布,私钥是隐秘的,不公开。用于公钥加密的数据只能被私钥解密。反过来,使用私钥加密的数据只能被公钥解密。这个非对称的特性使得公钥加密很有用。

安全证书中包含了这一对非对称的密钥,只有安全证书的所有者才知道私钥。如下图,当A将自己的安全证书发送给B时,实际上发给B的是公钥,接着B可以向A发送用公钥加密的数据,只有A才能使用私钥对数据解密,从而获得A发送的原始数据。

客户和服务器通信时,首先要进行SSL握手,SSL握手主要完成以下任务:

  • 协商使用的加密套件。加密套件中包含一组加密参数,这些参数指定了加密算法密钥的长度等信息。
  • 验证对方身份。(可选操作)
  • 确定使用的加密算法。

SSL握手的过程采用非对称加密方法传递数据,由此来建立一个安全的会话。SSL握手完成后,通信双方将采用对称加密方法传递实际的应用数据。(对称加密即通信双方采用同样的密钥来加密数据)。

SSL握手的具体流程如下:

  1. 客户将自己的SSL版本号、加密参数、与会话有关的数据以及其他一些必要信息发送到服务器;
  2. 服务器将自己的SSL版本号、加密参数、与会话有关的数据以及其他一些必要信息发送给客户,同时发送给客户的还有服务器的证书。如果服务器需要验证客户身份,那么服务器还会发出要求客户提供安全证书的请求。
  3. 客户端验证服务器证书,如果验证失败,就提示不能建立SSL连接。若成功,则继续下一步;
  4. 客户端为本次会话生成预备主密码(pre-master secret),并将其用服务器公钥加密后发送给服务器;
  5. 如果服务器要求验证客户身份,那么客户端还要再对另外一些数据签名后,将其与客户端证书一起发送给服务器;
  6. 如果服务器要求验证客户身份,则检查签署客户证书的CA(证书机构)是否可信。如果不再信任列表中,则结束本次会话。若检查通过,那么服务器用自己的私钥解密收到的预备主密码,并用它通过某些算法生成本次会话的主密码
  7. 客户端与服务器均使用此主密码生成本次会话的会话密钥(对称密钥)在双方SSL握手结束后传递任何消息均使用此会话密钥。这样做的主要原因是对称加密比非对称加密的运算量低一个数量级以上,能够显著提高双方会话时的运算速度;
  8. 客户端通知服务器此后发送的消息都使用这个会话密钥进行加密,并通知服务器客户端已经完成了本次SSL握手;
  9. 服务器通知客户端此后发送的消息都使用这个会话密钥进行加密,并通知客户端服务器已经完成本次SSL握手;
  10. 本次握手结束,会话已经建立。在接下来的会话过程中,双方使用同一个会话密钥分别对发送以及接收的信息进行加密和解密

二、keytool工具生成证书

获得安全证书有两种方式,一种是到权威机构购买,还有一种是创建自我签名的证书。下面使用JDK内置的证书制作工具keytool创建自我签名的证书。,该工具位置为<JDK 根目录>\bin\keytool.exe、

keytool工具提出了密钥库的概念。密钥库中可以包含多个条目。每个条目包括一个自我签名的安全证书以及一对非对称密钥

基于SSL安全协议的客户端与服务器端通信。首先服务器端需要准备自己的安全证书,在SSL握手时需要将安全证书发送给客户端,客户端可以验证服务器的身份。如下为创建密钥库的命令:

keytool -genkeypair -alias ssl_server -keyalg RSA -keystore ./server_ks.keystore

//以上命令将生成一个密钥库,这个密钥库中有一个名为"ssl_server"的证书条目
-genkeypair:生成一对非对称密钥
-alias:指定条目以及密钥对的别名,该别名是公开的
-keyalg:指定加密算法,上面是RSA算法
-keystore:设定密钥库文件的存放路径以及文件名字

执行上面命令后如下:

这个时候就已经生成了server_ks.keystore这个文件,它是服务端保存证书的仓库。

通过如下命令查看server_ks.keystore密钥库的信息,会列出所包含的证书的信息。

keytool -list -v -keystore ./server_ks.keystore -storepass "password"

通过上述操作就为服务端生成了一个自我签名的数字证书。由于这个不是权威机构颁发的,在SSL握手时,客户端相不相信这个证书那只能由客户端自己判断,所以需要为客户端也生成一个证书仓库,这个仓库用于存放客户端信任的证书。在客户端的程序中将这个信任的证书仓库设置进去就行。

首先为客户端生成一个空白的证书仓库(用于存储客户端信任的证书)

//1.生成客户端信任的证书仓库client_trust_ks.keystore,包含一个名为ssl_client的证书条目
keytool -genkeypair -alias ssl_client -keyalg RSA -keystore ./client_trust_ks.keystore

//执行下面命令将这个ssl_client的证书删除掉,此时客户端信任的证书仓库中就是空白的。
keytool -delete -alias ssl_client -keystore ./client_trust_ks.keystore

接下来我们要将服务器端的证书导出来(做一个副本),并将其导入到客户端信任的证书仓库中。

//将ssl_server这个证书导出到./ssl_server.crt文件中(注意:这个文件只包含密钥对中的公钥,不包含私钥)
keytool -export -alias ssl_server -keystore ./server_ks.keystore -file ./ssl_server.crt -storepass "password"

//执行下面命令进行导入。他会询问你是否信任此证书,输入‘Y’即可(导入的也只包含公钥,不包含私钥)
keytool -import -alias ssl_server -keystore ./client_trust_ks.keystore -file ./ssl_server.crt -storepass "password"

SSL协议通信涉及密钥储存的文件格式比较多,很容易搞混,例如xxx.cer、xxx.pfx、xxx.jks、xxx.keystore、xxx.truststore等格式文件。那么这些究竟分别是什么?

  • .crt.cer格式文件俗称证书,但这个证书中没有私钥,只包含了公钥;
  • .pfx格式文件也称为证书,它一般供浏览器使用,而且它不仅包含了公钥,还包含了私钥,当然这个私钥是加密的,不输入密码是解不了密的;
  • .jks格式文件表示java密钥存储器(javakey store),它可以同时容纳N个公钥跟私钥,是一个密钥库;
  • .keystore格式文件其实跟.jks基本是一样的,只是不同公司叫法不太一样,默认生成的证书存储库格式;
  • .truststore格式文件表示信任证书存储库,它仅仅包含了通信对方的公钥,当然你可以直接把通信对方的jks作为信任库(就算如此你也只能知道通信对方的公钥,要知道密钥都是加密的,你无从获取,只要算法不被破解)。

有些时候我们需要把pfx或cert转化为jks以便于用java进行ssl通信,例如一个银行只提供了pfx证书,而我们想用java进行ssl通信时就要将pfx转化为jks格式。 

三、JSSE简介

JSSE封装了底层复杂的安全通信细节,使得开发人员能方便地用它来开发安全地网络应用程序。JSSE地API允许采用第三方提供地实现,该实现可作为插件集成到JSSE中。这些插件必须支持Oracle指定地加密套件。(加密套件包括一组加密参数,这些参数指定了加密算法地密钥长度等信息。)如:

SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
采用SSL协议,密钥交换算法为DHE,加密算法为RSA

 JSSE的主要类图如下

1.KeyStore、KeyManager与TrustManager类

在进行安全通信时,要求客户端与服务器端都支持SSL或TCL协议。客户端与服务器端可能都需要设置用于证实自身身份的安全证书,还要设置信任对方的哪些安全证书。更常见的情况是,服务器端只需要设置用于证实自身身份的安全证书,而客户端只需要设置信任服务器的哪些安全证书。

KeyStore类用于存放包含安全证书的密钥库。创建KeyStore对象方式如下:

public static void main(String[] args) throws Exception {
    // 密钥库口令
    String passphrase = "password";
    // 密钥库对象,JKS是Java支持的密钥库类型JavaKeyStore的缩写。
    // 通过KeyStore.getDefaultType()也可以获得平台默认的密钥库类型。
    KeyStore keyStore = KeyStore.getInstance("JKS");
    char[] password = passphrase.toCharArray();
    // 加载密钥库(如果想创建一个空的密钥库,那么下面两个参数可以为null)
    keyStore.load(new FileInputStream("E:\\server_ks.keystore"), password);
    // 加载进来之后就可以操作了
    // 证书条目名称
    String alias = "ssl_server";

    // 如下几个对象中方法可根据API查得
    // 证书对象
    Certificate certificate = keyStore.getCertificate(alias);
    // 获取公钥对象
    PublicKey publicKey = certificate.getPublicKey();
    // 获取私钥对象,这里填私钥密码。(注意:这里需要输入该证书条目中私钥的密码,此处与密钥库密码一致)
    PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) keyStore.getEntry(alias,new KeyStore.PasswordProtection(passphrase.toCharArray()))).getPrivateKey();
    // 将密钥以Base64编码并输出
    String strPrivateKey = Base64.getEncoder().encodeToString(privateKey.getEncoded());
    String strPublicKey = Base64.getEncoder().encodeToString(publicKey.getEncoded());
    System.out.println("私钥:" + strPrivateKey);
    System.out.println("公钥:" + strP
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值