用JSSE定制SSL连接_第1页
用JSSE定制SSL连接_第2页
用JSSE定制SSL连接_第3页
用JSSE定制SSL连接_第4页
用JSSE定制SSL连接_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

用 JSSE 定制 SSL 连接 JSSE Java Security Socket Extension Java 安全套接字扩展 是 Sun 为了解决在 Internet 上的安全通讯而推出的解决方案 它实现了 SSL 和 TSL 传输层安全 协议 在 JSSE 中包含了数据 加密 服务器验证 消息完整性和客户端验证等技术 通过使用 JSSE 开发人员可以在客户机和服 务器之间通过 TCP IP 协议安全地传输数据 这篇文章主要描述如何使用 JSSE 接口来控制 SSL 连接 首先我通过一个简单的客户机 服务器程序来介绍如何利用 JSSE 进行编程 当建立客户端时 我们需要配置 KeyStore 和 TrustStore 文件 这样在程序中我们才可以从客户端的文件系统中加载它 们 然后文章将讨论授权和身份验证方面的问题 通过从 KeyStore 中选择不同的授权 客户端程序 可以连接到不同的服务器 运行例子程序 下载例子程序 在运行 JSSE 程序前 你需要正确安装 JSSE 如果你安装了 J2SE 1 4 JSSE 已经被自动安装并 配置好了 如果你使用的是其他版本的 Java 你需要从官方站点上下载并安装 JSSE 安装过程这里 就不再赘述 由于 JSSE 是在 J2SE 1 4 中才成为标准的 并且 J2SE 1 4 中的 JSSE 和以前的 JSSE 有 一些细微的差别 而且文中的例子都是在 J2SE 1 4 下调试的 因此推荐你使用 J2SE 1 4 运行这些例 子 在深入介绍 JSSE 之前 让我们来一个简单的客户机 服务器程序 程序中包含了两个文件 SimpleSSLServer 和 SimpleSSLClient 在运行程序之前 你需要配置下面这些 KeyStore 和 TrustStore 文件 一个客户端的 KeyStore 文件 该文件中包含了对 Alice 和 Bob 的授权 一个服务器端的 KeyStore 文件 该文件中包含了对 server 的授权 一个名为 clientTrust 的客户端 TrustStore 文件 该文件中包含了对 server 的授权 一个名为 serverTrust 的服务器端 TrustStore 文件 该文件中包含了对 Alice 和 Bob 的授权 使用 keytool 可以帮助你创建这些文件 该工具在 Java 的 bin 目录下 一个客户端的 KeyStore 文件 该文件中包含了对 Alice 和 Bob 的授权 在命令窗口中输入下面的命令 keytool genkey alias alice keystore clientKeys 窗口中会出现下面的提示 根据提示输入相应的信息 输入 keystore 密码 password 您的名字与姓氏是什么 Unknown Alice 您的组织单位名称是什么 Unknown Development 您的组织名称是什么 Unknown DCQ 您所在的城市或区域名称是什么 Unknown ChongQing 您所在的州或省份名称是什么 Unknown ChongQing 该单位的两字母国家代码是什么 Unknown CH CN Alice OU Development O DCQ L ChongQing ST ChongQing C CH 正确吗 否 是 输入的主密码 如果和 keystore 密码相同 按回车 通过相同的方式可以建立对 Bob 的授权 keytool genkey alias bob keystore clientKeys 注意在名字与姓氏一栏中填写 Bob 在完成后可以键入下面的命令来检测是否已经正确完成了授 权 keytool list v keystore clientKeys 一个服务器端的 KeyStore 文件 该文件中包含了对 server 的授权 在命令窗口中键入下面的命令 keytool genkey alias server keystore serverKeys 注意将密码设为 password 名字与姓氏设定为 Server 完成授权后同样可以通过上面提到的命 令来检测 一个名为 clientTrust 的客户端 TrustStore 文件 该文件中包含了对 server 的授权 以及 一个名为 serverTrust 的服务器端 TrustStore 文件 该文件中包含了对 Alice 和 Bob 的授权 keytool export alias server keystore clientKeys file server cer 输入 keystore 密码 password 保存在文件中的认证 keytool export alias alice keystore clientKeys file alice cer 输入 keystore 密码 password 保存在文件中的认证 keytool export alias bob keystore clientKeys file bob cer 输入 keystore 密码 password 保存在文件中的认证 这样 keytool 就在当前目录下创建了三个授权文件 然后我们将 server cer 文件导入到 clientTrust 文件中 将 alice cer 和 bob cer 导入到 serverTrust 文件中 keytool import alias server keystore clientTrust file server cer keytool import alias alice keystore serverTrust file alice cer keytool import alias bob keystore serverTrust file bob cer 到目前为止 在当前目录下包含 clientKeys serverKeys clientTrust serverTrust 四个文 件 完成了 KeyStore 和 TrustStore 的设置后就可以运行例子程序了 首先需要运行服务器程序 java D ssl keyStore serverKeys D ssl keyStorePassword password D ssl trustStore serverTrust D ssl trustStorePassword password SimpleSSLServer 在命令行中我们指定了 keyStore 属性为 serverKeys 由于服务器程序需要获得客户端的授权信 息 我们指定 trustStore 为 serverTrust 这样 SSLSimpleServer 就可以验证由 SSLSimpleClient 提供的授权信息 当服务器程序成功运行后 你会看到下面的提示 SimpleSSLServer running on port 49152 这时候服务器会等待客户端发出建立连接的申请 如果你希望在另一个端口上运行服务器程序 可以在命令中指定 port xxx 参数 其中 xxx 是端口号 然后在另一个命令窗口中运行客户端程序 java D ssl keyStore clientKeys D ssl keyStorePassword password D ssl trustStore clientTrust D ssl trustStorePassword password SimpleSSLClient 客户端程序会试图向本机的 49152 端口建立 SSL 连接 同样你可以通过 port 参数指定端口号 也可以通过 host 参数指定主机名称 当连接成功后 会出现下面的提示信息 Connected 同时在服务器端会提示用户客户端已经连接成功 SimpleSSLServer 让我们先来看一下 SimpleSSLServer 在 main 方法中 程序获得了缺省的 SSLServerSocketFactory 对象 然后利用 SSLServerSocketFactory 创建一个 SimpleSSLServer 对象 最后调用 start 方法启动 SimpleSSLServer 对象 SSLServerSocketFactory ssf SSLServerSocketFactory SSLServerSocketFactory getDefault SimpleSSLServer server new SimpleSSLServer ssf port server start 由于服务器是在一个单独的线程中运行的 main 方法启动了服务器之后就退出了 start 方法启动了一个新的线程 该线程执行 run 方法中的代码 在 run 方法中创建了一 个 SSLServerSocket 对象 然后设定服务器需要进行客户端验证 SSLServerSocket serverSocket SSLServerSocket serverSocketFactory createServerSocket port serverSocket setNeedClientAuth true 调用 run 方法后 程序进入了一个死循环 等待客户端的连接申请 循环中的每个 Socket 对应一个 HandshakeCompletedListener 对象 该对象是用来显示客户验证信息中的标识名称 distinguished name 的 Socket 的 InputStream 对象被包装在一个 InputDisplayer 对象中 这个 InputDisplayer 对象运行在另外一个线程中 用来将 Socket 接收到的数据发送到 System out 下面的代码是 SimpleSSLServer 中的主循环体 while true String ident String valueOf id 监听连接请求 SSLSocket socket SSLSocket serverSocket accept 通过使用 HandshakeCompletedListener 对象 程序进行授权验证 HandshakeCompletedListener hcl new SimpleHandshakeListener ident socket addHandshakeCompletedListener hcl InputStream in socket getInputStream new InputDisplayer ident in 程序中的 SimpleHandshakeListener 类实现了 HandshakeCompletedListerner 接口 在 SimpleHandshakeListener 类中实现了 handshakeCompleted 方法 该方法在 SSL 握手阶段完成后 将被 JSSE 调用 它将显示出客户端的标识名称 class SimpleHandshakeListener implements HandshakeCompletedListener String ident 构造函数 public SimpleHandshakeListener String ident this ident ident 当 SSL 握手过程完成后该方法被激活 public void handshakeCompleted HandshakeCompletedEvent event 显示授权信息 try X509Certificate cert X509Certificate event getPeerCertificates 0 String peer cert getSubjectDN getName System out println ident Request from peer catch SSLPeerUnverifiedException pue System out println ident Peer unverified 用红色字体表示的两行代码是这段代码的核心 getPeerCertificates 方法返回一个 X509Certificated 对象的数组 这些 X509Certificated 对象创建了客户端的身份标识 在数组中的 第一个元素是客户端的验证信息 而最后一个通常是 CA 验证 当我们有了客户端的验证信息后 我 们可以得到其中的标识名称 并将它传送到 System out SimpleSSLClient SimpleSSLClient 类比较简单 但是在后面的一些比较复杂的例子中的类会继承该类 在 getSLLSocketFactory 方法中 程序返回缺省的工厂类 protected SSLSocketFactory getSSLSocketFactory throws IOException GeneralSecurityException return SSLSocketFactory SSLSocketFactory getDefault 在 runClient 方法中 程序处理了输入参数后 获得 SSLSockFactory 对象 调用 connect 方法连接到服务器程序 在 connect 方法中 程序首先创建一个 SSLSocket 对象 然后调用 SSLSocket 对象的 startHandshang 方法启动和服务器端的握手过程 当握手过程完成后 会触发 一个 HandshakeCompletedEvent 事件 在服务器端的 HandshakeCompletedListener 对象会处理这个 事件 事实上 JSSE 可以自动启动握手过程 但是必须是在第一次有数据通过 Socket 传输的情况下 由于在例子程序中 直到用户在键盘上输入信息后才会有数据通过 Socket 传输 而我们希望服务器 端及时报告连接情况 因此我们用 startShake 方法来手工激活握手过程 public void connect SSLSocketFactory sf throws IOException socket SSLSocket sf createSocket host port try socket startHandshake catch IOException ioe 握手失败 关闭连接 try socket close catch IOException ioe2 忽略该错误 socket null throw ioe SimpleSSLClient 类中的 transmit 方法也很简单 首先程序将输入流包装到一个 Reader 对 象中 然后将输出流包装到一个 Writer 对象中 最后将数据流输出到 Socket boolean done false while done String line reader readLine if line null writer write line writer write n writer flush else done true 定制 KeyStore 和 TrustStore 还记得我们是如何运行客户端的吗 我们需要在命令行中指定 keyStore keyStorePasword trustStore 和 trustStorePassword 参数 以至于整个命令显得过于冗长 事实上你可以在程序中指 定 KeyStore 和 TrustStore 后面的例子中将告诉你如何实现这一点 同时在例子中还会演示如何配 置多个 SSLSocketFactory 对象 其中每个 SSLSocketFactory 对象对应不同的 KeyStore 和 TrustStore 设置 如果没有这种技术 在同一个虚拟机上的所有安全连接都只能使用同一个 KeyStore 和 TrustStore 对于比较小的应用程序 这也许不会产生问题 但是对于那些比较大的应 用程序来说 这绝对是一个严重的缺陷 在下面的例子中 我们将使用 CustomTrustStoreClient 来动态定义 KeyStore 和 TrustStore 首先让我们先运行一下 CustomTrustStoreClient java CustomTrustStoreClient 为什么运行 CustomTrustStoreClient 时不需要指定 KeyStore 和 TrustStore 参数呢 这是应为 在 CustomTrustStoreClient 的代码中指定了 KeyStore ClientKeys 和 TrustStore ClientTruts 以及它们的密钥 password 如果你想使用其他的 KeyStore TrustStore 或密钥 可以使用 ks kspass ts 和 tspass 参数来指定 下面让我们来看一下 CustomTrustStoreClient 的 getSSLSocketFactory 方法 该方法通过调用 getTrustManager 方法获得一个 TurstManager 对象数组 通过调用 getKeyManagers 方法获得一个 KeyManager 对 象数组 然后利用得到的 TurstManager 和 KeyManager 对象数组构造一个 SSLContext 对象 最后通 过 SSLContext 对象的 getSocketFactory 方法来配置 JSSE 需要注意的是在调用 SSLContext 类 的 init 方法时使用的参数 第一个参数是 KeyManager 对象数组 第二个参数和第一个参数类似 是 TrustManager 数组 如果前两个参数被设定为 null 程序将使用缺省的 KeyManager 和 TrustStore 缺省的 KeyStore 来源于系统属性中的 ssl keyStore 和 ssl keyStorePassword 属性 缺省的 TrustStore 来源于系统属性中的 ssl trustStore 和 ssl trustStorePassword 属性 通过设定第三个参数可 以指定 JSSE 中的随机数产生器 Random Number Generate RNG 由于在 SSL 中随机数的产生是一 个很敏感的问题 错误使用这个参数会导致安全连接变得不安全 因此我在例子中使用了 null 这 样程序将使用缺省的并且是安全的 SecureRandom 对象 protected SSLSocketFactory getSSLSocketFactory throws IOException GeneralSecurityException 调用 getTrustManagers 方法获得 trust managers TrustManager tms getTrustManagers 调用 getKeyManagers 方法获得 key manager KeyManager kms getKeyManagers 利用 KeyManagers 创建一个 SSLContext 对象 用获得的 KeyStore 和 TrustStore 初始化该 SSLContext 对象 我们使用缺省的 SecureRandom SSLContext context SSLContext getInstance SSL context init kms tms null 最后获得了 SocketFactory 对象 SSLSocketFactory ssf context getSocketFactory return ssf 下面让我们看一看 CustomKeyStoreClient 类中的 getKeyMangers 方法是如何初始化 KeyManagers 对象数组的 protected KeyManager getKeyManagers throws IOException GeneralSecurityException 获得 KeyManagerFactory 对象 String alg KeyManagerFactory getDefaultAlgorithm KeyManagerFactory kmFact KeyManagerFactory getInstance alg 配置 KeyManagerFactory 对象使用的 KeyStoree 我们通过一个文件加载 KeyStore FileInputStream fis new FileInputStream keyStore KeyStore ks KeyStore getInstance jks ks load fis keyStorePassword toCharArray fis close 使用获得的 KeyStore 初始化 KeyManagerFactory 对象 kmFact init ks keyStorePassword toCharArray 获得 KeyManagers 对象 KeyManager kms kmFact getKeyManagers return kms 首先的任务是获得一个 KeyManagerFactory 对象 但是你必须知道应该使用哪种算法 JSSE 中 提供了一个缺省的 KeyManagerFactory 算法 程序员也可以通过指定 ssl KeyManagerFacotory algorithm 属性指定缺省算法 获得 KeyManagerFactory 对象后就可以加 载 KeyStore 文件了 程序中通过一个 InputStream 对象将信息从文件送入 KeyStore 对象中 在这个 过程之前 KeyStore 对象需要知道输入流的格式 例子中我使用的是 jks 和密钥 当我们完成了 KeyStore 的加载后 我们就可以用它来初始化 KeyManagerFactory 对象了 通常在 JSSE 中 在 KeyStore 中的所有证书使用和 KeyStore 相同的密码 但是通过创建 KeyManagerFactory 对象你可以 突破这个限制 在初始化了 KeyManagerFactory 对象后 通常使用 getKeyManager 方法来获得 KeyManager 对象数组 程序员通过使用和 getKeyMangers 方法类似的流程来初始化 TrustManager 数组 这里我就不再重复了 实现一个 KeyManager 类 到目前为止 我们已经知道如何在程序中动态生成 KeyStore 和 TrustStore 了 最后一个例子将 告诉你如何实现一个 KeyManager 类 当运行前几个例子的时候 不知道大家是否注意到服务器端显示的授权的标识名称 在前面我们 授权给了两个人 Alice 和 Bob 在运行程序时 JSSE 会从中任选一个 在我的计算机上 JSSE 选择的 总是 Bob 或许在你的计算机上情况会有所不同 下面让我们来看一看最后一个例子程序 SelectAliasClient 这个例子使你能够在运行客户端时使用指定的授权 例如你需要指定使用 Alice 的授权 由于 Alice 的别名是 alice 你需要在命令窗口中键入下面的命令 java SelectAliasClient alias alice 当客户端和服务器端成功连接后 客户器端会出现下面的信息 1 New connection request 1 Request from CN Alice OU Development O DCQ L ChongQing ST ChongQing C CH 为了使程序使用指定的授权 我们需要实现 X509KeyManager 接口 X509KeyManager 是 JSSE 中 最常用的 KeyManager X509KeyManager 接口在 SSL 握手阶段使用了几个方法来获得授权 下面是 X509KeyManager 接口获得授权的过程 1 JSSE 调用 chooseClientAlias 方法获得指定的授权 2 chooseClientAlias 方法调用 X509KeyManager 接口的 getClientAlaises 方法获得 SSLSocket 对象使用的所有授权的别名 然后检查指定的授权别名是否有效 3 JSSE 将别名作为参数调用 X509KeyManager 接口的 getCertificateChain 和 getPrivateKey 方法 这样就获得了指定授权的相关信息 在例子程序中 X509KeyManager 接口的实现类是 AliasForcingKeyManager 在该类中最重要的 方法就是就是 chooseClientAlias 方法 下面是该方法的源代码 public String chooseClientAlias String keyType Principal issuers Socket socket 对于每一种类型的授权 都需要调用一次 getClientAliases 方法来验 证别名是否有效 boolean aliasFound false for int i 0 i String validAliases baseKM getClientAliases keyType i issuers if validAliases null for int j 0 j if validAliases j equals alias aliasFound true if aliasFound return alias else return null 我们可以看到在程序中 chooserClientAlias 方法实际上多次调用了 getClientAliases 方法 每次都针对不同的授权类型 AliasForingKeyManager 还实现了 X509KeyManager 接口的其他五个方法 在这里就不再一一赘述了 然后我们就可以在程序中用 AliasForingKeyManager 对象来替代 KeyManager 对象了 在 getSSLSocketFactory 方法中 我们只需要将通过调用 getKeyManagers

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论