jsse使用远程的证书池_使用JSSE进行安全的套接字通信

关于本教程

本教程是关于什么的?

本教程说明了JDK 1.4中包含的Java安全套接字扩展(JSSE)软件包的用法。 使用JSSE的复杂性不在于通信本身,而在于配置。 在运行客户端/服务器软件之前,必须创建加密算法所需的密钥,并且这些密钥必须由软件正确加载后才能创建安全套接字。

本教程提供了食谱样式的说明,用于在客户端/服务器应用程序环境中创建和安装JSSE加密密钥。 完成本教程后,您将知道如何轻松地将任何现有的客户端/服务器应用程序转换为使用加密,以及如何从头开始创建安全的应用程序。

先决条件

要遵循本教程中的讨论,您需要了解有关Java语言的一些知识,首先要了解客户机/服务器环境中Java编程的基础知识(即,使用类,对象,线程等)。 因为流和套接字通信是我们讨论的核心,所以您需要知道如何使用流和套接字。 特别是,您应该知道什么是流及其用途。 您应该知道如何创建一个Socket和一个ServerSocket ,如何从每个ServerSocket获取流,以及如何使用这些流进行通信。 您还应该知道如何使用JDK或IDE创建和编译.java文件。

可以从参考资料主题中访问的两个developerWorks教程“ Java套接字101”和“ Java I / O简介”都提供了有用的背景信息。

您无需了解有关加密技术的任何知识即可完成本教程。 尽管对加密的全面概述超出了此处讨论的范围,但是会为您提供在应用程序中创建安全的客户端/服务器连接所需的信息。

安装要求

要运行本教程中的示例,您需要以下工具和组件:

  • Sun Microsystems的JDK 1.4
  • 开发环境-IDE或合适的命令外壳
  • 您可以在其上运行Internet服务器的计算机

请参阅相关主题下载JDK 1.4和本教程所必需的完成完整的源文件。

Java密码体系结构

总览

在过去的几年中,Java平台的安全性和加密功能得到了极大的发展。 JDK 1.4(又名Merlin)发行版现在捆绑了许多与安全性相关的软件包,包括Java密码术扩展(JCE),Java安全套接字扩展(JSSE)和Java身份验证和授权服务(JAAS)。 所有这些组件都是Java密码体系结构(JCA)的一部分,如下图所示:

Java密码体系结构

在本教程中,我们将主要使用JCA的JSSE组件。

JCA和JSSE

JCA的最重要功能之一是它不依赖任何一种特定的加密算法。 每种众所周知的加密算法都有其优点和缺点,并且一直在开发新的算法。 JCA允许在开发新算法时插入它们。 它使用了加密服务提供程序 (CSP)的概念,该概念类似于安全性插件。 CSP提供了特定算法的实现。 JDK 1.4与包括SunJSSE在内的CSP捆绑在一起,提供了许多标准算法。 总而言之,这些对于大多数用途就足够了。

JSSE为Java 2平台提供安全的套接字通信。 更准确地说,它实现了安全套接字层(SSL)和传输层安全性(TLS),这两个标准化协议用于在Internet上实现安全通信。 SSL和TLS都依赖于公共密钥加密 ,这将在下一个面板中进行介绍。

公钥加密

许多密码算法的问题是它们需要分配共享密钥 。 密码是共享密钥的一个很好的例子。 共享密钥的问题在于,必须先在通信实体之间共享它们,然后才能开始安全通信。 但是,共享过程可能容易被窃听,从而导致“鸡与蛋”问题:在安全交换数据之前,我们必须首先安全交换密钥。

惠特菲尔德·迪菲(Whitfield Diffie)和马丁·海尔曼(Martin Hellman)于1976年通过创建公钥密码术解决了这一问题。 在Diffie-Hellman公钥系统中,每个通信方都拥有一对密钥-一个公钥和一个私钥。 私钥仅是通信方知道的,而公钥可以提供给任何人。 使用其中一个密钥加密的数据只能与另一个密钥解密。

因此,如果要创建仅由特定方读取的消息,请使用其公共密钥进行加密,然后他们使用其私钥对消息解密。

同样,如果您使用私钥加密邮件,那么拥有公钥副本的任何人都可以使用它来解密邮件。 这可以确保接收方的人该消息来自您而不是其他人,因为只有您拥有私钥。 用这种方式加密的消息带有数字签名 。

证书和证书颁发机构

证书是由受信任方进行数字签名以证明它是有效的公共密钥的公共密钥。 该受信方称为证书颁发机构 (CA)。 从某种意义上说,CA提供的证明是公钥确实确实属于拥有它的人。

您可以付费使用商业CA,也可以创建自己的CA,这完全取决于在数字领域中证明自己的身份时希望使用的权限。 如果实体签署自己的公钥,则称为自签名证书 。 在本教程中,我们使用自签名证书。

SSL和TLS

如前所述,JSSE框架与SunJSSE提供程序一起实现了SSL和TLS协议套件-TLS实际上只是SSL的最新版本。

SSL使用公共密钥密码术来交换一组共享密钥,然后使用标准共享密钥加密来交换数据。 共享密钥既用于加密数据(使他人无法读取),又用于认证数据(确保它不是来自冒名顶替者)。

SSL握手协议

在通过SSL连接发送数据之前,两端必须协商并交换密钥信息。 这称为握手协议 。 在这里,我们不会详细讨论握手协议,因为对于我们的目的而言,它不是必需的。 为了我们的目的,您需要知道握手涉及以下步骤:

  1. 服务器将其证书发送给客户端,然后客户端验证服务器证书。
  2. 客户端将其证书发送到服务器,服务器验证客户端证书。
  3. 客户端使用服务器的公共密钥加密密码信息,然后将其发送到服务器。 连接的每个端都使用此密码信息来生成相同的密钥,然后将其用于传输数据。

客户端身份验证(步骤2)是可选的:服务器可以请求客户端提供其证书,但是不需要进行此请求。 在示例中,我们将使用客户端身份验证。

既然您已经掌握了JCA和JSSE的基础结构,那么我们就可以继续学习本教程的更活跃的部分:使用一个实时示例。

白板:示例应用程序

总览

为了演示JSSE在现实世界中的工作方式,我们将采用一个简单的分布式白板系统 。 白板是允许用户将文本消息写到空白画布上的程序。 然后,这些文本消息将显示在连接到同一白板服务器的其他用户的画布上。 这允许不同位置的人们进行交流。

因为我们对程序的安全性比对白板功能本身更感兴趣,所以我们将使应用程序非常简单。 我们的白板将允许用户创建短信,但不允许他们删除短信。 通常,我们只关注白板如何实现JSSE,从而确保可以在用户之间安全地发送消息。

在本节中,我们将讨论白板的基本结构及其如何实现JSSE。 在下一节中,我们将开始直接使用白板应用程序的代码。

使用白板

下图说明了我们将要使用的简单白板。 每个客户的消息均以不同的颜色显示。

显示不同用户发送的消息的分布式白板

白板

在以下两个图中,您将看到两个白板客户端窗口。 想象每个窗口都在不同的计算机上运行。 第一个用户单击了他的窗口中的某个位置并键入了一条消息,但尚未按回车键。

第一个用户正在键入消息,但尚未按回车键。 因此,第二用户尚未收到消息。
使用白板:用户1a
使用白板:用户1b

现在,假设第一个用户按下Return键,这将导致他的消息显示在第二个用户的窗口中。 然后,第二个用户发送一个响应,如下图两个图所示。

第一个用户按下回车键并发送了一条消息,然后从第二个用户那里收到了答复。 第二个用户收到第一个用户的消息,然后发送响应。
使用白板:用户2a
使用白板:用户2b

每个用户都看到自己的黑色文本。 其他用户的文本以服务器分配的不同颜色显示。

客户端/服务器结构

在白板应用程序的服务器端,我们有一个名为Server的类。 此类侦听指定端口上的传入连接。 每次建立连接时, Server都会创建一个ConnectionProcessor来处理连接。 处理连接意味着接收文本消息并将其发送回其他客户端。 Server为每个客户端提供一个ConnectionProcessor

客户端首次启动时,它将启动与服务器的连接。 客户端在整个白板会话期间都保持此连接打开。 每个文本消息都是通过此连接发送的。

下图说明了白板应用程序的客户端/服务器过程。

白板的客户端/服务器过程

关键文件

正如我们已经讨论过的,白板使用JSSE,而JSSE实现了SSL和TLS协议。 这些协议使用公钥加密来确保通过Internet发送的消息的私密性。 在公共密钥加密系统中,客户端和服务器都必须具有一对密钥,一个公共密钥,一个私有密钥。 在我们的白板甚至无法开始在用户之间调解消息之前,我们必须生成这些密钥。

生成密钥后,我们将为客户端提供一个包含其公共密钥和私有密钥的文件。 它还将具有服务器的公共密钥证书的副本。 密钥存储在一种特殊格式的文件中,该文件称为密钥库。

下表描述了我们将使用的密钥库文件。

密钥库文件 它包含什么 去哪里
客户。私人 客户的公钥/私钥对 客户端
服务器公共 服务器的公钥证书 客户端
server.private 服务器的公用/专用密钥对 服务器端
客户 客户公钥证书 服务器端

按键保护

服务器还具有一个文件,该文件包含其自己的公钥和私钥以及客户端的公钥证书。 回想一下,可以免费提供公钥-无需将其隐藏于任何其他方。

客户端/服务器连接的每一端都只有正常工作所需的密钥文件,这一点很重要。 特别重要的是,只有服务器才能拥有其自己的私钥的副本。 如果使用不当,此密钥可能造成很大的损害,因为它实际上将使恶意实体将自己隐藏在服务器的身份下。

现在,您已经了解了白板的组合方式,我们可以开始更直接地使用其每个组件。 在下一部分中,您将学习如何在客户端/服务器系统中生成和管理公钥/私钥对。

密钥管理

总览

密钥生成和操作通过keytool程序执行,该程序包含在JDK 1.4中的JSSE软件包中。 keytool可以用于多种用途。 在这里,我们将使用它来创建公用/专用密钥对,并从这些对中提取公用密钥证书并将其放置在自己的文件中。

生成密钥对

以下keytool命令用于生成新的公用/专用密钥对:

keytool -genkey -keystore [filename]

当您运行此命令时,将询问您一系列问题。 这些问题涉及您作为一个实体(您的姓名,组织等)。 您提供的信息将用于创建自签名证书,该证书将信息与公钥关联,并证明关联的真实性。 您还将被要求输入密钥库的密码,或者输入正在创建的密钥对的密码。

从命令行工作

以下是一个完整的命令,该命令生成一个公共/私有密钥对,并指定所有必需的实体信息,而不会问您有关身份的任何问题; 该信息直接在命令行上提供。 下表解释了命令中的每个选项。

keytool -genkey -alias clientprivate -keystore client.private
-storetype JKS -keyalg rsa -dname "CN=Your Name, OU=Your
Organizational Unit, O=Your Organization, L=Your City, S=Your State,
C=Your Country" -storepass clientpw -keypass clientpw
选项 这是什么意思
-genkey 告诉keytool生成密钥对。
-alias clientprivate 标识密钥库中的新密钥对。
-keystore client.private 使用文件client.private作为密钥库。
型JKS 声明密钥库的类型。 JKS是默认设置。
-keyalg rsa 声明要使用的算法; 我们使用的是RSA公钥算法,这是默认设置。
-dname“ CN =您的名字...” 提供有关拥有密钥对的实体的信息。
-storepass clientpw 指定整个密钥库的密码。
-keypass clientpw 指定新密钥对的密码。

客户端和服务器密钥对

准备我们的算法的第一步是为客户端生成公共/专用密钥对,为服务器生成另一对。 以下命令将生成文件client.private,这是客户端的密钥对:

keytool -genkey -alias clientprivate -keystore client.private
-storetype JKS -keyalg rsa -dname "CN=Your Name, OU=Your
Organizational Unit, O=Your Organization, L=Your City, S=Your State,
C=Your Country" -storepass clientpw -keypass clientpw

这是生成文件server.private的命令,这是服务器的密钥对:

keytool -genkey -alias serverprivate -keystore server.private
-storetype JKS -keyalg rsa -dname "CN=Your Name, OU=Your
Organizational Unit, O=Your Organization, L=Your City, S=Your State,
C=Your Country" -storepass serverpw -keypass serverpw

密钥文件部分所述,专用客户端和服务器密钥文件需要安装在特定位置。 client.private文件安装在客户端。 server.private文件安装在服务器端。

提取公钥

下一步是提取公钥,以便可以将其与客户端和服务器一起安装。 具体来说,客户端软件必须具有服务器端的公钥,反之亦然。

要获取公共密钥,我们从client.private和server.private文件中提取它们,并将它们放在临时文件中。 然后,将它们插入各自的密钥库中,分别称为client.public和server.public。

临时密钥文件temp.key用于在从私有密钥库复制到公共密钥库(例如,从client.private到client.public)的过程中临时保存每个公共密钥。 完成导出/导入过程后,您将要从当前目录中删除temp.key。

导出/导入命令

我们将使用keytool -export命令将公钥提取到文件中,然后使用keytool -import命令将其插入新的密钥库中。 这是提取客户端公钥的命令:

keytool -export -alias clientprivate -keystore client.private -file
temp.key -storepass clientpw

这是将客户端的私钥插入其自己的密钥库的命令:

keytool -import -noprompt -alias clientpublic -keystore client.public
-file temp.key -storepass public

我们还将提取并存储服务器的公钥。 这是提取密钥的命令:

keytool -export -alias serverprivate -keystore server.private -file
temp.key -storepass serverpw

这是将其放置在其自己的密钥库中的命令:

keytool -import -noprompt -alias serverpublic -keystore server.public
-file temp.key -storepass public

脚本generatekeys.sh(对于UNIX)和generatekeys.bat(对于DOS或Microsoft Windows)会自动为您生成客户端和服务器密钥文件,并清除所有临时文件。 请参阅下载以下载脚本。

安装文件

在继续进行下一部分之前,重要的是要确保密钥文件已正确安装在客户端和服务器端。 客户端上应安装以下文件:

  • 客户类
  • 客户$ 1.class
  • 客户$ 2.class
  • Client $ ClientCanvas.class
  • 发布类
  • 客户。私人
  • 服务器公共

这些文件应安装在服务器端:

  • 服务器类
  • 发布类
  • ConnectionProcessor.class
  • 客户
  • server.private

在客户端和服务器系统上正确安装了公钥/私钥对之后,我们就可以开始使用白板应用程序交换消息了。 在下一节中,我们将检查白板应用程序本身的代码。

使用JSSE套接字

总览

我们的白板是一个客户端/服务器应用程序,它使用套接字与服务器进行通信。 因为我们希望我们的通信是私有的,所以我们将使用JSSE安全套接字而不是常规套接字。 安全套接字的使用方式与常规套接字相同,只不过它们透明地加密通过它们的所有数据。

在探讨JSSE如何创建和管理安全连接之前,我们应该查看如何启动和接受常规的非安全连接。

非安全套接字:评论

以下代码段通常用于启动套接字连接。 本示例在远程计算机host创建到端口port的新Socket连接:

Socket socket = new Socket( host, port );

同样,以下代码演示了我们如何侦听传入的连接。 本示例创建一个侦听端口port的ServerSocket ,然后进入一个无限循环,接受并处理传入的连接:

ServerSocket serverSocket = new ServerSocket( port );
while (true) {
    Socket socket = serverSocket.accept();
    doSomethingWithNewConnection( socket );
}

安全套接字的工作方式非常相似,但是在为示例实现安全套接字之前,我们必须完成几个步骤。 我们将在以下各节中介绍这些步骤。

连接设置

要启动到远程服务器的安全套接字连接,我们必须执行以下步骤:

  1. 创建一个SecureRandom ,它是安全随机数的来源。 安全随机数是足够随机的数字,它们不会使加密容易受到攻击。
  2. 创建一个包含远程服务器公钥的KeyStore对象。 这是从server.public中读取的。
  3. 创建一个包含客户端的公用/专用密钥对(包括其公用密钥证书)的KeyStore对象。 这是从client.private中读取的。
  4. 从远程服务器的KeyStore创建一个TrustManagerFactory 。 这用于验证远程服务器。
  5. 从客户端的KeyStore创建一个KeyManagerFactory 。 这用于加密和解密数据。
  6. 使用KeyManagerFactoryTrustManagerFactorySecureRandom创建一个SSLContext对象。
  7. 使用SSLContext创建一个SSLSocketFactory
  8. 使用SSLSocketFactory创建一个SSLSocket ,除了安全性外,它的行为与常规Socket相同。

听力设置

要侦听传入的连接,我们必须执行一组类似的步骤:

  1. 创建一个SecureRandom ,它是安全随机数的来源。
  2. 创建一个包含远程客户端公钥的KeyStore对象。 这是从client.public中读取的。
  3. 创建一个包含服务器的公用/专用密钥对(包括其公用密钥证书)的KeyStore对象。 这是从server.private中读取的。
  4. 从远程客户端的KeyStore创建一个TrustManagerFactory 。 这用于验证远程客户端。
  5. 从服务器的KeyStore创建一个KeyManagerFactory 。 这用于加密和解密数据。
  6. 使用KeyManagerFactoryTrustManagerFactorySecureRandom创建一个SSLContext对象。
  7. 使用SSLContext创建一个SSLServerSocketFactory
  8. 使用SSLServerSocketFactory创建SSLServerSocket ,除了安全性外,其行为与常规ServerSocket相同。
  9. 调用SSLServerSocketaccept()方法以等待传入连接。

这一切都非常复杂,但是每次的过程都是相同的,因此继续学习并观察其工作原理是有意义的。 在以下各节中,我们将逐步完成执行这些步骤的代码。 我们将仅详细检查客户端进程,因为服务器端进程几乎相同。 之后,我们将注意到双方之间的差异。

创建一个SecureRandom

创建SecureRandom对象很容易; 只需在代码中使用以下几行:

secureRandom = new SecureRandom();
      secureRandom.nextInt();

第一行实际上创建了SecureRandom 。 创建SecureRandom需要大量计算,并且只有在实际使用该计算后,才能执行该计算。 通过在此处调用nextInt()方法,我们可以继续创建过程,并确保延迟将在程序开始时发生,而不是在可能给我们带来不便的情况下发生。

创建密钥库

接下来,我们需要创建一些KeyStore对象。 我们使用静态方法KeyStore.getInstance()创建一个空的KeyStore ,并使用其load()方法对其进行初始化,如下所示:

private void setupServerKeystore()
        throws GeneralSecurityException, IOException {
      serverKeyStore = KeyStore.getInstance( "JKS" );
      serverKeyStore.load( new FileInputStream( "server.public" ),
                          passphrase.toCharArray() );
    }

请注意,我们已经创建了类型为"JKS"KeyStore ; 这是JCA中使用的标准密钥库格式。

读取密钥库

在上一节中,我们从server.public中读取密钥信息,其中包含服务器端的公共密钥。 我们还需要从client.private读取客户端密钥对,如下所示:

private void setupClientKeyStore()
        throws GeneralSecurityException, IOException {
      clientKeyStore = KeyStore.getInstance( "JKS" );
      clientKeyStore.load( new FileInputStream( "client.private" ),
                         "public".toCharArray() );
    }

设立工厂

下一步是使用我们创建的KeyStore对象初始化密钥和信任管理器工厂。 我们将从服务器密钥库创建一个TrustManagerFactory ; 这将用于认证(即开始信任)远程服务器:

TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
      tmf.init( serverKeyStore );

请注意, TrustManagerFactory的类型为"SunX509" ; 509是我们在整个计划中使用的认证协议的名称。 在第二行代码中, TrustManagerFactory装入了服务器的密钥库。

我们还必须从客户端的KeyStore创建一个KeyManagerFactory ,如下所示:

KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
      kmf.init( clientKeyStore, passphrase.toCharArray() );

创建一个SSLContext

我们即将完成我们的安全套接字设置,所以请和我在一起! 下一步是创建SSLContextSSLContext包含到目前为止我们提到的所有密钥和证书信息,并用于创建SSLSocketFactory ,后者又创建安全套接字。

一旦在应用程序的启动处创建了SSLContext ,就可以将其用于需要建立的每个连接,只要每个连接使用相同的密钥即可。

要创建SSLContext ,我们使用我们的工厂和SecureRandom ,如下所示:

sslContext = SSLContext.getInstance( "TLS" );
      sslContext.init( kmf.getKeyManagers(),
                       tmf.getTrustManagers(),
                       secureRandom );

请注意,我们已经创建了类型为"TLS"SSLContext 。 您怀疑,它代表传输层安全性,它是安全套接字层或SSL的新名称。 然后,我们使用向后创建的TrustManagerFactoryKeyManagerFactory对象对其进行初始化。

建立连接

我们到了最后一步。 到目前为止,我们所做的所有工作都为我们提供了SSLContext ,我们将使用它来建立与远程计算机的连接,如下面的代码示例所示:

SSLSocketFactory sf = sslContext.getSocketFactory();
        SSLSocket socket = (SSLSocket)sf.createSocket( host, port );

我们已经与机器主机上的端口port建立了安全连接。

服务器端设置

设置服务器端与设置客户端大致相同,因此我们将不对其进行详细介绍。 当然,服务器从client.public和server.private而不是从server.public和client.private读取其关键信息。

此外,对于服务器端,执行最后一步(建立连接)的代码略有不同,如下所示:

SSLServerSocketFactory sf = sslContext.getServerSocketFactory();
        SSLServerSocket ss = (SSLServerSocket)sf.createServerSocket( port );
        ss.setNeedClientAuth( true );

请注意,我们称为SSLServerSocket.setNeedClientAuth() 。 这是服务器调用,指示客户端应进行身份验证。 客户端应用程序默认情况下不会对自己进行身份验证,因此,如果您希望客户端身份验证成为握手过程的一部分,则必须进行此调用。

Wrapping up

绑在一起

在本教程中,我们专注于使用JSSE的最复杂方面:正确配置和安装客户端和服务器密钥对。 我们简要地讨论了新的Java密码体系结构,但主要关注由JSSE实现的密钥对对安全加密的使用。 我们使用了一个非常简单的白板示例,使本教程中的讨论更加具体,详细介绍了生成密钥对,正确安装密钥对以及启动基于套接字的安全客户端/服务器连接所必需的每个步骤。

您可以在“ 相关主题”中找到白板示例的完整代码源。 通过学习该代码源并密切注意上一节中概述的步骤,可以在您这里学到的知识的基础上继续前进。 您可能还想练习所学的知识,并在下一部分中建议的练习中进行扩展。 无论您做什么,在开发下一个基于套接字的安全应用程序时,请务必使用在此学到的知识。

进一步练习:使用CA

在我们的白板示例中,我们假定您可以控制客户端和服务器端安装。 在其他配置中,情况并非如此。 例如,Web服务器几乎可以与任何Web浏览器一起使用,而安装Web服务器软件的人员与安装浏览器软件的人员无关。 因此,Web服务器和其他公共协议服务器使用第三方证书颁发机构提供密钥证书。

使用这些系统的说明超出了本教程的范围,但是每个系统通常都提供自己的详细说明。 在许多情况下,您可以申请免费证书,该证书可用于测试目的-这使您可以“购买前先试用”。 请参阅相关信息的链接,第三方认证机构。


翻译自: https://www.ibm.com/developerworks/java/tutorials/j-jsse/j-jsse.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值