Java中的密钥SSL套接字连接

我正在加密服务器和客户端之间的TCP连接。 在研究和测试过程中,我倾向于使用密钥加密。 我的问题是我找不到任何有关如何实现此function的教程。 我发现的教程围绕一次性https请求,我需要的只是一个SSL Socket。

我到目前为止编写的代码如下。 我几乎可以肯定它需要扩展,我只是不知道如何。 任何帮助表示赞赏。

private ServerSocketFactory factory; private SSLServerSocket serverSocket; factory = SSLServerSocketFactory.getDefault(); serverSocket = (SSLServerSocket) factory.createServerSocket(  ); 

用于接受客户端连接的服务器代码

 SSLSocket socket = (SSLSocket) serverSocket.accept(); socket.startHandshake(); 

我只是不知道如何实际握手。

参考: http : //docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html

Java套接字连接在Java中得到很好的支持,对您来说可能是一个不错的选择。 事先要理解的一件事是SSL提供加密和服务器身份validation; 你不能轻易得到加密。 作为参考,加密可以防止网络窃听,而服务器身份validation可以防止“中间人”攻击,攻击者充当客户端和服务器之间的代理。

由于身份validation是SSL不可或缺的一部分,因此服务器需要提供SSL证书,客户端需要能够对证书进行身份validation。 服务器将需要一个存储其证书的“密钥库”文件。 客户端将需要一个“信任存储”文件,其中存储它信任的证书,其中一个必须是服务器的证书,或者可以从中跟踪“信任链”到服务器证书的证书。

请注意,您无需了解SSL的详细信息即可使用Java SSL套接字。 我认为阅读有关SSL如何工作的信息很有意思,例如在关于TLS的维基百科文章中,但复杂的多步骤握手和实际连接加密的设置都由SSLServerSocket和SSLSocket类负责处理。

代码

以上所有内容仅仅是解释以下一些代码的背景信息。 该代码假设您熟悉常规的未加密套接字。 在服务器上,您将需要这样的代码:

 /** * Returns an SSLServerSocket that uses the specified key store file * with the specified password, and listens on the specified port. */ ServerSocket getSSLServerSocket( File keyStoreFile, char[] keyStoreFilePassword, int port ) throws GeneralSecurityException, IOException { SSLContext sslContext = SSLConnections.getSSLContext(keyStoreFile, keyStoreFilePassword); SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(port); return sslServerSocket; } 

然后可以像使用任何其他ServerSocket一样使用SSLServerSocket; 身份validation,加密和解密对调用代码完全透明。 事实上,我自己的代码中的同源函数声明了一个普通的ServerSocket的返回类型,因此调用代码不会混淆。

注意:如果要将JRE的默认cacerts文件用作密钥库文件,则可以跳过创建SSLContext的行,并使用ServerSocketFactory.getDefault()获取ServerSocketFactory。 您仍然必须将服务器的公钥/私钥对安装到密钥库文件中,在本例中为cacerts文件。

在客户端,您将需要这样的代码:

 SSLSocket getSSLSocket( File trustStoreFile, char[] trustStoreFilePassword, InetAddress serverAddress, port serverPort ) throws GeneralSecurityException, IOException { SSLContext sslContext = SSLConnections.getSSLContext(trustStoreFile, trustStoreFilePassword); SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket (serverAddress, serverPort); sslSocket.startHandshake(); return sslSocket; } 

与服务器代码中的SSLServerSocket一样,这里返回的SSLSocket就像普通的Socket一样使用; 进出SSLSocket的I / O是使用未加密的数据完成的,所有加密内容都是在内部完成的。

与服务器代码一样,如果要将默认JRE cacerts文件用作信任库,则可以跳过SSLContext的创建并使用SSLSocketFactory.getDefault()而不是sslContext.getSocketFactory() 。 在这种情况下,如果服务器的证书是自签名的,或者主要证书颁发机构之一未另行签发,则只需要安装服务器的证书。 此外,为确保您不信任在您信任的证书链中合法颁发的证书,但要信任与您尝试访问的完全不同的域,您应该在该行之后添加以下(未经测试的)代码在哪里创建SSLSocket

  sslSocket.getSSLParameters().setEndpointIdentificationAlgorithm("HTTPS"); 

如果您使用自己的信任库文件,但信任该文件中的一个或多个证书颁发机构颁发的所有证书,或信任该文件中不同服务器的许多证书,这也适用。

证书,密钥库和信任库

现在是困难的,或者至少是稍微更难的部分:生成和安装证书。 我建议使用Java keytool ,最好是1.7或更高版本来完成这项工作。

如果要创建自签名证书,请首先使用以下命令从命令行生成服务器的密钥对: keytool -genkey -alias server -keyalg rsa -dname "cn=server, ou=unit, o=org, l=City, s=ST, c=US" -validity 365242 -keystore server_key_store_file -ext san=ip:192.168.1.129 -v 。 替换你自己的名字和价值观。 特别是,此命令会创建一个密钥对,该密钥对将在365242天(1000年)到期,对于IP地址为192.168.1.129的服务器。 如果客户端将通过域名系统查找服务器,请使用san=dns:server.example.com而不是san=ip:192.168.1.129 。 有关keytool选项的更多信息,请使用man keytool

系统将提示您输入密钥库的密码 – 或设置密钥库的密码(如果这是新的密钥库文件),并设置新密钥对的密码。

现在,使用keytool -export -alias server -file server.cer -keystore server_key_store_file -rfc -v导出服务器的证书。 这将创建一个server.cer文件,其中包含带有服务器公钥的证书。

最后,将server.cer文件移动到客户端计算机,并使用诸如keytool -import -alias server -file server.cer -keystore client_trust_store_file -v类的东西将证书安装到客户端的信任库中。 系统将提示您输入信任库文件的密码; 提示将显示“输入密钥库密码”,因为Java密钥工具适用于密钥存储文件和信任存储文件。 请注意,如果您使用默认的JRE cacerts文件,我相信初始密码是changeit

如果您使用从公认的认证机构购买的证书,并且您在客户端上使用默认的JRE cacerts文件,则只需将证书安装在服务器的密钥库文件中; 你不必弄乱客户端的文件。 服务器安装说明应由证书颁发机构提供,或者您可以再次检查man keytool以获取指示。

套接字有很多神秘感,特别是SSL套接字,但它们实际上非常容易使用。 在许多情况下,十行代码将避免需要复杂和脆弱的消息传递或消息队列基础结构。 考虑这个选项对你有好处。

你已经开始握手了。 这就是你所要做的一切:事实上你甚至不必这样做,因为它会自动发生。 您现在要做的就是正常的输入和输出,就像使用纯文本套接字一样。