带客户端身份验证的 SSL 套接字连接

2023-12-12

我有一个运行一些实用命令的应用程序服务器,它是用 C 编程的。 我必须使用 Java SSL 套接字通过 Java 客户端程序连接到服务器 客户端身份验证。 服务器端的密钥是使用以下命令创建的:

   openssl req -new -text -out ser.req
   openssl rsa -in privkey.pem -out ser.key
   openssl req -x509 -in ser.req -text -key ser.key -out ser.crt

我已获得服务器密钥和证书。我已将密钥和证书组合在一起 转换为 PKCS12 格式文件:

openssl pkcs12 -inkey ser.key -in ser.crt -export -out ser.pkcs12

然后使用 keytool 将生成的 PKCS12 文件加载到 JSSE 密钥库中:

keytool -importkeystore -srckeystore ser.pkcs12 -srcstoretype PKCS12 -destkeystore ser.keystore

但是当我尝试连接时,出现以下错误:

javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.ssl.Alert.createSSLException(Alert.java:131)
    at sun.security.ssl.TransportContext.fatal(TransportContext.java:324)
    at sun.security.ssl.TransportContext.fatal(TransportContext.java:267)
    at sun.security.ssl.TransportContext.fatal(TransportContext.java:262)
    at sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654)
    at sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473)
    at sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369)
    at sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:377)
    at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
    at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:422)
    at sun.security.ssl.TransportContext.dispatch(TransportContext.java:182)
    at sun.security.ssl.SSLTransport.decode(SSLTransport.java:149)
    at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1143)
    at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1054)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:394)
    at SSLSocketClient.main(SSLSocketClient.java:67)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:456)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:323)
    at sun.security.validator.Validator.validate(Validator.java:271)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:315)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:223)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129)
    at sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:638)
    ... 11 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:451)
    ... 17 more

在服务器端日志:

SSL open_server: could not accept SSL connection: sslv3 alert certificate unknown

运行命令:

java -Djavax.net.ssl.keyStore=/path/to/ser.keystore -Djavax.net.ssl.keyStorePassword=passwd SSLSocketClient <server-ip> <port>

有谁知道这个问题的原因?

更新了客户端源码:

import java.net.*;
import java.io.*;
import javax.net.ssl.*;

import java.security.cert.CertificateFactory;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.SocketFactory;

public class SSLSocketClient {

   public static void main(String [] args) throws Exception {
      String serverName = args[0];
      int port = Integer.parseInt(args[1]);
      try {

        SSLSocketFactory sf =
                (SSLSocketFactory)SSLSocketFactory.getDefault();

        Socket client = new Socket(serverName, port);

        System.out.println("Connected to " + client.getRemoteSocketAddress());
        OutputStream outToServer = client.getOutputStream();
        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outToServer));

        writeData(out);
        out.flush();

        InputStream inFromServer = client.getInputStream();
        DataInputStream in = new DataInputStream(inFromServer);

        
        readData(in);
        outToServer = client.getOutputStream();
        out = new DataOutputStream(new BufferedOutputStream(outToServer));
        writeData2(out);
        out.flush();
        
        Socket newClient = sf.createSocket(client, serverName, port, false);

        client.close();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

    private static void writeData(DataOutputStream out) throws IOException {
         char CMD_CHAR_U = 'U';
         byte b = (byte) (0x00ff & CMD_CHAR_U);

         out.writeByte(b);          // <U>
    }

    private static void writeData2(DataOutputStream out) throws IOException {
         char CMD_CHAR_S = 'S';
         byte b = (byte) (0x00ff & CMD_CHAR_S);

         out.writeByte(b);          // <S>
    }

    private static void readData(DataInputStream in) throws IOException {
        char sChar = (char) in.readByte(); 
        System.out.println("<S>\t\t" + sChar);
    }
}

现在创建信任库,如链接所示:https://jdbc.postgresql.org/documentation/head/ssl-client.html

创建步骤:

openssl x509 -in server.crt -out server.crt.der -outform der
keytool -keystore mystore -alias clientstore -import -file server.crt.der
java -Djavax.net.ssl.trustStore=mystore -Djavax.net.ssl.trustStorePassword=mypassword com.mycompany.MyApp

注意 - 服务器端使用 TLSv1 协议

但还是没能挺过去。我究竟做错了什么? 我想要的是服务器对客户端的crt进行身份验证。

与服务器的登录协议;我们使用的 SSL 仅用于身份验证 不保证传输安全:

    -------------------------------------------------------------
    client                                            server 
    -------------------------------------------------------------

    sock = connect()                                 sock = accept()
                      <U><LOGIN_SSL=501>
                 --------------------------------->
                       'S'|'E'
                 <---------------------------------
                       'S'
                 --------------------------------->
    SSL_connect(sock)                               SSL_accept(sock)

                      <R><LOGIN_SSL>
                 <---------------------------------

我认为您的设置有几个问题。

要正确配置与 JSSE 的 SSL 连接,您需要做一些事情,具体取决于您是否需要验证服务器、客户端或执行相互验证。

让我们假设稍后更完整的相互身份验证用例。

目标是配置一个SSLSocketFactory您可以使用它来联系您的服务器。

要配置一个SSLSocketFactory, 你需要一个SSLContext.

对于相互身份验证用例,该元素又需要至少两个元素,一个KeyManagerFactory,客户端 SSL 身份验证所需的,即服务器信任客户端,以及TrustManagerFactory,配置客户端信任服务器所需的。

Both KeyManagerFactory and TrustManagerFactory需要正确配置的密钥库以及必要的加密材料。

因此,第一步将包括生成这种加密材料。

您已经使用服务器证书创建了密钥库:

keytool -keystore serverpublic.keystore -alias clientstore -import -file server.crt.der -storepass yourserverpublickeystorepassword

请注意,与服务器情况类似,您还需要为客户端创建一对公钥和私钥,当然,这与服务器不同。

您通过 OpenSSL 提供的相关代码和keytool看起来合适。请为客户端重复该过程:

openssl req -new -text -out client.csr
openssl rsa -in clientpriv.pem -out client.key
openssl req -x509 -in client.csr -text -key client.key -out client.crt
// You can use PKCS12 also with Java but it is also ok on this way
openssl pkcs12 -inkey client.key -in client.crt -export -out client.pkcs12
// Do not bother yourself and, in this use case, use always the same password for the key and keystore
keytool -importkeystore -srckeystore client.pkcs12 -srcstoretype PKCS12 -destkeystore client.keystore -storepass "yourclientkeystorepassword"

准备好正确的密钥库后,请尝试执行以下操作来与服务器进行交互:

// First, let's configure the SSL for client authentication
KeyStore clientKeyStore = KeyStore.getInstance("JKS");
clientKeyStore.load(
  new FileInputStream("/path/to/client.keystore"),
  "yourclientkeystorepassword".toCharArray()
);

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); // SunX509
kmf.init(clientKeyStore, "yourclientkeystorepassword".toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();

// Now, let's configure the client to trust the server
KeyStore serverKeyStore = KeyStore.getInstance("JKS");
serverKeyStore.load(
  new FileInputStream("/path/to/serverpublic.keystore"),
  "yourserverpublickeystorepassword".toCharArray()
);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); // SunX509
tmf.init(serverKeyStore);
TrustManager[] trustManagers = tmf.getTrustManagers();

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null); // You can provide SecureRandom also if you wish

// Create the SSL socket factory and establish the connection
SSLSocketFactory sf = sslContext.getSocketFactory();
SSLSocket socket = (SSLSocket)sf.createSocket(serverName, port);

// Interact with your server. Place your code here
// Please, consider the following link for alternatives approaches on how to 
// interchange information with the server:
// https://web.mit.edu/java_v1.5.0_22/distrib/share/docs/guide/security/jsse/samples/sockets/client/SSLSocketClient.java
// It also suggest the use of startHandshake explicitly if your are using PrintWriter for the reason explained in the example an in the docs:
// https://docs.oracle.com/en/java/javase/11/docs/api/java.base/javax/net/ssl/SSLSocket.html

//...

// Close the socket
socket.close();

所描述的方法可以扩展为使用更高级别的抽象组件来代替套接字,例如HttpsURLConnection和 HTTP 客户端 - 除了以不同方式处理 SSL 的 Apache HttpClient - 就像OkHttp在幕后,使用SSLSocketFactory和相关的东西。

请也考虑查看此内容很棒的文章来自 IBM 的 DeveloperWorks,除了解释上述许多要点之外,还将为您在必要时为客户端和服务器生成密钥库提供很好的指导。

另请注意,根据您的服务器代码,您可能需要将其配置为信任提供的客户端证书。

根据您的评论,您使用的服务器端代码类似于 Postgresql 8.1 提供的代码。请参阅相关文件为了在该数据库中配置 SSL,如果您使用一些类似的服务器端代码,它可能会有所帮助。

最好的方法可能是生成从服务器信任的根证书派生的客户端证书,而不是使用自签名证书。

我认为它也与您的服务器端 SSL 证书和关联的私钥相关:首先,创建一个根自签名证书(您的 CA 证书),配置您的服务器端 C 代码以信任它,然后派生客户端和服务器端来自该 CA 的 SSL 加密材料:可能会简化您的设置并使一切正常工作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

带客户端身份验证的 SSL 套接字连接 的相关文章

随机推荐

  • 列表中的平均分

    第一次发帖 如果写得不好请见谅 我在一个文件中有一份列表 其中包含学生的姓名 ID 分数等 见下文 我想计算另一个文件中的平均分数 但我不知道如何只取分数并将平均值写入另一个文件中 Thanks name surname student i
  • 我们可以在 Chrome 扩展程序中检索机器序列号吗?

    我们可以在 Chrome 扩展程序中检索机器序列号吗 例如 我可以通过在 shell 中执行以下命令来获取 Windows 中的序列号 wmic BIOS 获取序列号 如何在 Chrome 扩展程序中获取此序列号 不会 Chrome 扩展程
  • 使用 HTML 和 JavaScript 返回 PartialView

    我正在进行 AJAX 调用 使用 jQuery 来检索PartialView 除了 HTML 之外 我还想发回视图正在显示的对象的 JSON 表示形式 我一直使用的蹩脚方法是将属性作为隐藏输入嵌入到 HTML 中 这很快就会变得笨拙并且紧密
  • 实体数据栏和数据栏最小值的手动版本和编码版本之间的外观不一致

    我正在尝试在 EPPlus 4 0 4 中创建可靠的数据栏 但遇到了两个问题 首先 我一直无法弄清楚如何创建纯色填充颜色 其次 至少对于较小的值 条形图没有按照我期望的方式显示 下面的屏幕截图说明了这两个问题 在这两种情况下 所需的结果都是
  • VC++ 2008,OpenProcess 总是返回错误 5(访问被拒绝)

    有人知道为什么当我尝试使用 PROCESS ALL ACCESS 作为我所需的访问权限调用 OpenProcess 时 MSVC 2008 总是在 GetLastError 上返回错误 5 吗 PROCESS VM READ 工作得很好 我
  • 为什么我不能使用 pygame.image.load 作为类属性?它说“如果没有初始化 pygame.display 就无法转换”

    以下块产生错误cannot convert without pygame display initialized当用作类属性时 class Tile hidden image pygame image load image0 bmp hid
  • EF6 数据库首先将存储过程设为异步

    在异步模式下运行 EF6 存储过程 数据库优先 的正确方法是什么 我读到ToListAsync 但我没有看到存储过程可用 还不确定当实际调用返回 1 OUT 参数或 2 项目列表时是否有不同的方式来调用存储过程 Case 1 using D
  • 为什么必须分配一个指针才能使 realloc 工作而不改变内存块中的第一个值?

    int ptr realloc ptr count sizeof int or ptr realloc ptr count sizeof int 我注意到如果我多次使用选项号一 第一个内存地址的值 ptr指向 变为未定义 尽管内存块中的所有
  • Swift 3.0 删除字典数组中的重复项

    我正在努力删除 swift 3 0 中字典数组中的重复字典 下面是 let Dict1 String String messageTo Madhu let Dict2 String String messageTo Kiran let Di
  • python如何对int、str列表的列表进行排序[关闭]

    Closed 这个问题不符合堆栈溢出指南 目前不接受答案 给定一个 int str 列表 我需要找到一种方法将其从最高到最低排序 而不使用排序 所以如果我有 list 1 orange 3 banana 2 pear 1 apple 我应该
  • 如何从 SQL Server Management Studio 历史记录中删除“服务器名称”项目

    当尝试连接到 Management Studio 特别是 2008 中的服务器时 有一个字段可供您输入服务器名称 该字段还有一个下拉列表 其中显示您尝试连接的服务器的历史记录 如何删除单个项目 从那段历史 如何删除 登录字段历史记录中的项目
  • 确定 Cassandra 中分区的节点

    这可能是一个特殊的问题 但是是否可以确定分区键的节点 示例 我有一个分区键 id int 并且我使用默认值分区器 Murmur3Partitioner 具有 3 个节点和复制因子 1 我可以确定id 3的一个节点吗 CREATE TABLE
  • Java 中的字符串比较...? [复制]

    这个问题在这里已经有答案了 可能的重复 如何在 Java 中比较字符串 为什么第一次比较 s1 s2 显示相等 而第二次比较 s1 s3 显示不相等 public class StringComparison public static v
  • htaccess 反向目录

    是否可以让htaccess查找与url相关的特定文件 如果没有找到则返回上一步 Example example here where from Htaccess 会查看 example here where from 是否确实是某种类型的文
  • JavaFX GUI Updater 实用程序类中的并发问题

    我正在 JavaFX 中为一个相当大的 Java 项目构建一个 GUI 该项目有许多不同的工作线程在后台进行一些繁重的计算 我试图在 GUI 中可视化这些工作线程的进度 我所说的进度不仅指纯粹的百分比 还指任务类中未包含的其他变量 例如 例
  • 如何创建从 UIViewController 到 UISplitViewController 的 Segue

    这是我对 iPad 应用程序的设置 我使用单视图应用程序创建了一个新项目UIStoryboard XCode 创建了主UIViewController作为入口点 在视图中 我放置了一个带有按钮的工具栏 然后我插入了一个UISplitView
  • Jquery 使用图像作为复选框

    我正在追求将图像实现为复选框 现在我正在尝试一个示例 下面的代码包含一个简单的图像 旁边有一个提交按钮 单击提交按钮时 我希望图像在其周围形成边框 单击提交按钮时 我希望传递复选框值
  • 使用 JavaScript 添加内容后重新应用样式

    我将尝试扩展我的问题 我有一个链接 a href Display a 我有一些风格 以及以下js代码 function printf MyHtml document write MyHtml function Display printf
  • Meteor 账户-twitter 无法使用

    我一直在尝试Meteor 我想使用 OAuth 对我网站上的用户进行身份验证 因为我不想自己实现登录功能 目前我的网站非常简单 计数器 单击按钮计数器就会加一 当用户转到另一台机器并登录其计数时 这个想法就会被保留 我已按照以下步骤操作流星
  • 带客户端身份验证的 SSL 套接字连接

    我有一个运行一些实用命令的应用程序服务器 它是用 C 编程的 我必须使用 Java SSL 套接字通过 Java 客户端程序连接到服务器 客户端身份验证 服务器端的密钥是使用以下命令创建的 openssl req new text out