尽管我明确设置了一个不验证任何证书的信任管理器,但为啥 Java 会尝试验证证书路径?

Posted

技术标签:

【中文标题】尽管我明确设置了一个不验证任何证书的信任管理器,但为啥 Java 会尝试验证证书路径?【英文标题】:Why does Java try to validate the certification path although I explictly set up a trust manager which does not validate any certificates?尽管我明确设置了一个不验证任何证书的信任管理器,但为什么 Java 会尝试验证证书路径? 【发布时间】:2012-07-28 23:52:38 【问题描述】:

我有一个关于使用套接字和 Java 7 通过 SSL 进行简单通信的问题。

到目前为止,我已经创建了两个 Runnables - 一个用于服务器,一个用于客户端。最后,我想创建一个简单的测试,其中我的应用程序使用 SSL 将数据发送到模型服务器。服务器要做的就是接收客户端发送的字符串,将其与文件内容进行比较,然后根据比较是否成功返回响应字符串。

但是,我被困在 javax.net.ssl.SSLHandshakeException 上,尽管我明确地希望客户端通过身份验证!在客户端,我创建了一个不验证任何证书的信任管理器。在服务器端,设置 this.sslServerSocket.setWantClientAuth(false) 和/或 this.sslServerSocket.setNeedClientAuth(false) 也无济于事。 出于测试目的,客户端和服务器都使用名为“keystore.ks”的相同密钥文件。

我用下面的箭头 (----->) 标记了异常发生的点。也许有人可以提示我那里出了什么问题!


这是我的 SSLServer 类:

public class SSLServer implements Runnable
    
    private SSLServerSocketFactory sslServerSocketFactory;
    private SSLServerSocket sslServerSocket;
    private SSLSocket sslSocket;
    private KeyStore keystore;
    private KeyManager[] keyManagers;
    private SSLContext sslContext;
    private SSLSession sslSession;
    private int port = 8081;
    private static final Character EOL = '\n';
    private static final Character EOF = '\u0017';

    public SSLServer() throws IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException, CertificateException, UnrecoverableKeyException
    
        char[] passphrase = "changeit".toCharArray();
        this.keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        this.keystore.load(new FileInputStream("keystore.ks"), passphrase);

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(this.keystore, passphrase);

        this.sslContext = SSLContext.getInstance("TLS");

        this.keyManagers = keyManagerFactory.getKeyManagers();

        this.sslContext.init(this.keyManagers, null, null);

        this.sslServerSocketFactory = this.sslContext.getServerSocketFactory();
        this.sslServerSocket = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(this.port);
        this.sslServerSocket.setSoTimeout(30000);
        this.sslServerSocket.setEnabledProtocols(new String []  "TLSv1", "TLSv1.1", "TLSv1.2" );
        this.sslServerSocket.setUseClientMode(false);
        this.sslServerSocket.setWantClientAuth(false);
        this.sslServerSocket.setNeedClientAuth(false);
    

    @Override
    public void run()
    
        InputStream inputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader input = null;
        OutputStream outputStream = null;
        PrintWriter output = null;

        try
        
            System.out.println("Server started");

            System.out.println("  Waiting for connection from client...");
            this.sslSocket = (SSLSocket) this.sslServerSocket.accept();

            // Connection was accepted
            System.out.println("  Accepted connection from " + this.sslSocket.getInetAddress().getHostAddress() + ", port " + this.sslSocket.getPort());

            // set up a SSL session
            this.sslSession = this.sslSocket.getSession();
            System.out.println("  Cipher suite used for this session: " + this.sslSession.getCipherSuite());

            inputStream = (InputStream) this.sslSocket.getInputStream();
            inputStreamReader = new InputStreamReader(inputStream);
            input = new BufferedReader(inputStreamReader);

            outputStream = this.sslSocket.getOutputStream();
            output = new PrintWriter(outputStream);

            System.out.println("  Server -> receiving...");
            StringBuffer receiver = new StringBuffer();
            Character serverReceived;
            while ((serverReceived = (char)input.read()) != EOF)
            
                receiver.append(serverReceived);
            
            System.out.println("    Server received: " + serverReceived);

            System.out.println("  Server -> sending...");

            String serverSendSuccess = "Hello client, how are you?" + EOL + EOF;
            String serverSendFail = "Who are you?" + EOL + EOF;

            if (receiver.toString().contains("Hello server! I am the client!"))
            
                System.out.println("    Server sent: " + serverSendSuccess);
                output.println(serverSendSuccess);
                output.flush();
            
            else
            
                System.out.println("    Server sent: " + serverSendFail);
                output.println(serverSendFail);
                output.flush();
            
        
        catch (IOException ex)
        
            ex.printStackTrace();
        
        finally
        
            try
            
                inputStream.close();
                outputStream.close();
                this.sslSocket.close();
            
            catch(Exception ex) 
            System.out.println("Server ended");
        
    

这是我的客户端类:

public class SSLClient implements Runnable

    private SSLSocketFactory sslSocketFactory;
    private SSLSocket sslSocket;
    private KeyStore keystore;
    private KeyManager[] keyManagers;
    private TrustManager[] trustManagers;
    private SSLContext sslContext;
    private String hostname = "localhost";
    private int port = 8081;
    private static final Character EOL = '\n';
    private static final Character EOF = '\u0017';

    public SSLClient() throws UnknownHostException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException, CertificateException, NoSuchProviderException, UnrecoverableKeyException
    
        char[] passphrase = "changeit".toCharArray();        
        this.keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        this.keystore.load(new FileInputStream("keystore.ks"), passphrase);

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(this.keystore, passphrase);

        this.sslContext = SSLContext.getInstance("TLS");

        this.keyManagers = keyManagerFactory.getKeyManagers();

        this.trustManagers = new TrustManager[]
            new X509TrustManager() 
                public java.security.cert.X509Certificate[] getAcceptedIssuers()
                
                    return null;
                
                public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
                

                
                public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
                

                
            
        ;

        this.sslContext.init(this.keyManagers, this.trustManagers, new SecureRandom());

        this.sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
    

    @Override
    public void run()
    
        InputStream inputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader input = null;
        OutputStream outputStream = null;
        PrintWriter output = null;

        try
        
            System.out.println("Start client");

            this.sslSocket = (SSLSocket) this.sslSocketFactory.createSocket(this.hostname, this.port);

            if(this.sslSocket.isConnected())
            
                inputStream = (InputStream) this.sslSocket.getInputStream();
                inputStreamReader = new InputStreamReader(inputStream);
                input = new BufferedReader(inputStreamReader);

                outputStream = this.sslSocket.getOutputStream();                
                output = new PrintWriter(outputStream);

                System.out.println("  Client -> sending...");

                String clientSend = "Hello server! I am the client!" + EOL + EOF;

                output.println(clientSend);
----->          output.flush(); // exception occurs here!

                System.out.println("    Client sent: " + clientSend);

                StringBuffer receiver = new StringBuffer();
                Character clientReceived;
                while ((clientReceived = (char)input.read()) != EOF)
                
                    receiver.append(clientReceived);
                

                System.out.println("    Client received: " + receiver.toString());
            
            else
            
                System.err.println("Connection to server lost!");
            
        
        catch (IOException ex)
        
            ex.printStackTrace();
        
        finally
        
            try
            
                inputStream.close();
                outputStream.close();
                this.sslSocket.close();
            
            catch(Exception ex) 
            System.out.println("End client");
        
    

最后是输出:

---Start test---
>> Start server thread
>> Start client thread
Start client
Server started
  Waiting for connection from client...
  Accepted connection from 127.0.0.1, port 55287
  Client -> sending...
    Client sent: Hello server! I am the client!
  Cipher suite used for this session: SSL_NULL_WITH_NULL_NULL
  Server -> receiving...
javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLHandshakeException: 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.ssl.SSLSocketImpl.checkEOF(SSLSocketImpl.java:1458)
    [...]
Caused by: javax.net.ssl.SSLHandshakeException: 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.ssl.Alerts.getSSLException(Alerts.java:192)
    [...]
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:385)
    [...]
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196)
    [...]
Server ended
End client
---End test---

【问题讨论】:

【参考方案1】:

从您精心构建的 SSLContext 中获取 SSLSocketFactory,而不是默认上下文。

在你做的地方调用 isConnected() 是徒劳的。在这一点上它不可能是假的。如果存在连接问题,则会引发异常。

【讨论】:

我明白了!我真的没有看到这里的树木。那是个愚蠢的错误……抱歉耽误了您的时间。

以上是关于尽管我明确设置了一个不验证任何证书的信任管理器,但为啥 Java 会尝试验证证书路径?的主要内容,如果未能解决你的问题,请参考以下文章

苹果7plus在那看受信任证书

Java 信任管理器对过期证书的行为

“iOS受信任设备列表不可用”是怎么回事?

不小心把苹果手机iOS的信任证书删除了怎么办

信任与真实,XFS 的证书管理

AWS证书管理器DNS验证方法不适用于API