Java中的密钥SSL套接字连接

Kyt*_*yte 8 java sockets ssl sslhandshakeexception sslsocketfactory

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

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

private ServerSocketFactory factory;
private SSLServerSocket serverSocket;

factory = SSLServerSocketFactory.getDefault();
serverSocket = (SSLServerSocket) factory.createServerSocket( <portNum> );
Run Code Online (Sandbox Code Playgroud)

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

SSLSocket socket = (SSLSocket) serverSocket.accept();
socket.startHandshake();
Run Code Online (Sandbox Code Playgroud)

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

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

War*_*Dew 16

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

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

请注意,您无需了解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;
}
Run Code Online (Sandbox Code Playgroud)

然后可以像使用任何其他ServerSocket一样使用SSLServerSocket; 身份验证,加密和解密对调用代码完全透明.事实上,我自己的代码中的同源函数声明了一个普通的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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

    sslSocket.getSSLParameters().setEndpointIdentificationAlgorithm("HTTPS");
Run Code Online (Sandbox Code Playgroud)

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

证书,密钥库和信任库

现在是困难的,或者至少是稍微更难的部分:生成和安装证书.我建议使用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,我相信初始密码是.

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

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

  • @WarrenDew,这是迄今为止我见过的最简洁的教程.我会放手一搏,谢谢! (2认同)
  • @Jared,你应该在Security.SE上阅读[这个问题](http://security.stackexchange.com/q/20803/2435).服务器根本不加密证书.客户端使用服务器的pubkey加密预主密钥(RSA密钥交换)(或者对于DHE交换稍微复杂一些,服务器在其中签署新生成的密钥).从那时起,它就是所有对称加密,没有公钥/私钥. (2认同)