¿Por qué Java intenta validar la ruta de certificación aunque configuré explícitamente un administrador de confianza que no valida ningún certificado?

Tengo una pregunta sobre una comunicación simple sobre SSL usando Sockets y Java 7.

Hasta ahora, he creado dos Ejecutables - uno para el servidor y otro para el cliente. Eventualmente, quiero crear una prueba simple en la que mi aplicación envíe datos a un servidor simulado usando SSL. Todo lo que se supone que debe hacer el servidor es recibir la cadena enviada por el cliente, compararla con el contenido de un archivo y enviar una cadena de respuesta que depende de si la comparación fue exitosa o no.

Sin embargo, estoy atascado en un javax.net.ssl.SSLHandshakeException aunque yo explícitamente no ¡Quiero que el cliente sea autenticado! En el lado del cliente, creo un administrador de confianza que no valida ningún certificado. En el lado del servidor, configurando this.sslServerSocket.setWantClientAuth(falso) y/o this.sslServerSocket.setNeedClientAuth(falso) tampoco ayuda. Para fines de prueba, tanto el cliente como el servidor usan el mismo archivo de almacenamiento de claves llamado "keystore.ks".

Marqué el punto donde ocurre la excepción con una flecha (----->) a continuación. ¡Tal vez alguien pueda darme una pista de lo que está mal allí!


Esta es mi clase 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");
        }
    }
}

Esta es mi clase de cliente:

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");
        }
    }
}

Finalmente, la salida:

---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---

preguntado el 31 de julio de 12 a las 09:07

1 Respuestas

Obtenga SSLSocketFactory del SSLContext que construyó con tanto cuidado, no del contexto predeterminado.

Llamar a isConnected() donde lo hace es inútil. No es posible que sea falso en ese punto. Se habría lanzado una excepción si hubiera un problema de conexión.

Respondido 31 Jul 12, 10:07

¡Ya veo! Realmente no vi el bosque para los árboles aquí. Eso fue un error estúpido... Lo siento por tomar su tiempo. - Walter

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.