使用没有私钥的导入证书创建SSL套接字

68de4m5k  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(87)

我很新的SSL和坦率地说,我发现实现是相当混乱,所以很抱歉,如果我的问题似乎毫无头绪或小康基地。
所以我有一个SSL套接字服务器使用自签名证书的形式的p12文件。我想有一个客户端套接字能够连接到这个服务器,而无需访问私钥。然而,我希望客户端能够验证服务器不是冒名顶替者,并有私钥。
我的问题是,我似乎不能创建一个SSLContext,因此,一个SSLSocketFactory使用任何东西以外的p12文件。理想情况下,我会创建我的SSLSocketFactory使用证书根文件。
这里的方式我有我的客户端设置:

public class SSLClient {
    
    private static final String TLS_VERSION = "TLSv1.2";

    public static void run(int serverPort, String certName, char[] certPass) throws Exception {

        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream certInputStream = TLSClient.class.getResourceAsStream("/" + certName);
        trustStore.load(certInputStream, certPass);
        certInputStream.close();
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream keyStoreInputStream = TLSClient.class.getResourceAsStream("/" + certName);
        keyStore.load(keyStoreInputStream, certPass);
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, certPass);

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), SecureRandom.getInstanceStrong());

        SocketFactory factory = sslContext.getSocketFactory();

        try (Socket connection = factory.createSocket("localhost", serverPort)) {
            ((SSLSocket) connection).setEnabledProtocols(new String[] {TLS_VERSION});
            SSLParameters sslParams = new SSLParameters();
            sslParams.setEndpointIdentificationAlgorithm("HTTPS");
            ((SSLSocket) connection).setSSLParameters(sslParams);

            BufferedReader input = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            System.out.println(input.readLine());
            new PrintWriter(connection.getOutputStream(),true).println("client reply");
        }
    }
}

字符串
我尝试使用根证书(.crt)和PEM文件,但它们都给我一个输入流错误给予:

Exception in thread "main" java.io.IOException: toDerInputStream rejects tag type 45
    at java.base/sun.security.util.DerValue.toDerInputStream(DerValue.java:1155)
    at java.base/sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2013)
    at java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:221)
    at java.base/java.security.KeyStore.load(KeyStore.java:1473)
    at TLSClient.run(TLSClient.java:16)
    at Main.main(Main.java:12)

我目前的方法/解决方案:我使用服务器根证书为客户端创建了一个“空”p12文件。这似乎是预期的工作.这样做有问题吗?

使用:
openssl pkcs12 -export -in myCert.crt -out myCertPks.p12 -name alias

qyzbxkaa

qyzbxkaa1#

如果要专门使用Java SSLContext,可以使用直接信任指定证书的自定义TrustManager。

public static TrustManager[] getTrustManagers(String certificateFilePath) throws Exception {
    // Load the certificate
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
    TrustManager[] trustManagers;
    
    try (InputStream certificateInputStream = new FileInputStream(certificateFilePath)) {
        X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(certificateInputStream);

        // Create a TrustManager that trusts only the specified certificate
        trustManagers = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                        // Allow any client certificates
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                        // Check if the server certificate matches the specified certificate
                        if (!chain[0].equals(certificate)) {
                            throw new CertificateException("Server certificate not trusted");
                        }
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                }
        };
        return trustManagers;
    }
}

字符串
然后使用创建的TrustManager创建SSLContext,

String certificateFilePath = "path/to/your/certificate.crt";
        TrustManager[] trustManagers = getTrustManagers(certificateFilePath);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagers, null);

oalqel3c

oalqel3c2#

一种简单的方法是将受信任的CA公共证书导入Java密钥库中(您可以使用keytool(随Java提供的CLI工具)操作它们)。
如果您使用JAVA_HOME的子文件夹中的文件 * caccounts *,JVM将自动识别它。
你可以在JDK的tool docs中找到关于keytool的文档(链接到Java 21,但版本可能不是那么相关)。这里还记录了关于 * caccounts * 文件的确切位置,默认密码等的更好描述。
通过这种方式,您不必在KeyStoreTrustManager等周围捣乱,只需使用默认的SSLSocketFactory创建SSLSocket,就可以了。

相关问题