使用SSLSockets的正确方法是什么?为什么我会收到这些错误?

时间:2011-04-19 20:32:03

标签: java sockets ssl certificate

我正在尝试保护一个应用程序系统,这些应用程序可以通过多台计算机连接。当我开始从普通套接字切换到SSLSockets时,我开始遇到两个错误:

客户端:

  

“javax.net.ssl.SSLHandshakeException:   sun.security.validator.ValidatorException:   PKIX路径构建失败:   sun.security.provider.certpath.SunCertPathBuilderException:   无法找到有效的认证   请求目标的路径“

服务器端:

  

“javax.net.ssl.SSLHandshakeException:   收到致命警报:   certificate_unknown“

我将代码简化为套接字并将它们放入一个程序中:

package sslTest;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class TestMain { 
    public class TestModuleReceive {
        int port = 4445;
        SSLServerSocket ss;
        SSLSocket socket;

        TestModuleReceive() {
            initServerSocket();
            receiveConnection();
        }

        private void initServerSocket() {
            try {
                KeyStore ks = KeyStore.getInstance("JKS");
                ks.load(new FileInputStream("/home/uName/.keystore"), ("password").toCharArray());

                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                kmf.init(ks, ("password").toCharArray());
                SSLContext sslcontext = SSLContext.getInstance("SSLv3");
                sslcontext.init(kmf.getKeyManagers(), null, null);
                ServerSocketFactory ssf =
                    sslcontext.getServerSocketFactory();
                ss = (SSLServerSocket)
                    ssf.createServerSocket();
                ss.setReceiveBufferSize(8200);
                ss.bind(new InetSocketAddress(port));
            } 
            catch (KeyStoreException e) { e.printStackTrace(); } 
            catch (NoSuchAlgorithmException e) { e.printStackTrace(); } 
            catch (CertificateException e) { e.printStackTrace(); } 
            catch (FileNotFoundException e) { e.printStackTrace(); } 
            catch (IOException e) { e.printStackTrace(); } 
            catch (UnrecoverableKeyException e) { e.printStackTrace(); } 
            catch (KeyManagementException e) { e.printStackTrace(); }
        }

        public void receiveConnection() {
            new Thread() { public void run() {
                try {
                    socket = (SSLSocket) ss.accept();
                    receiveData();
                } 
                catch (IOException e) { e.printStackTrace(); } 
            }}.start();
        }

        public void receiveData() {
            try {
                System.out.println(socket.getInputStream().read());
            } 
            catch (IOException e) { e.printStackTrace(); } 
        }
    }

    public class TestModuleSend {

        int port = 4445;
        String address = "localhost";
        SSLSocketFactory factory;
        SSLSocket socket;

        TestModuleSend() {
            initFactory();
            addConnection();
            sendData();
        }

        private void initFactory() {
            try {
                KeyStore ks = KeyStore.getInstance("JKS");
                ks.load(new FileInputStream("/home/uName/.keystore"), ("password").toCharArray());
                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                kmf.init(ks, ("password").toCharArray());
                SSLContext sslcontext = SSLContext.getInstance("SSLv3");
                sslcontext.init(kmf.getKeyManagers(), null, null);
                factory = sslcontext.getSocketFactory();
            } 
            catch (KeyStoreException e) { e.printStackTrace(); } 
            catch (NoSuchAlgorithmException e) { e.printStackTrace(); } 
            catch (CertificateException e) { e.printStackTrace(); } 
            catch (FileNotFoundException e) { e.printStackTrace(); } 
            catch (IOException e) { e.printStackTrace(); } 
            catch (UnrecoverableKeyException e) { e.printStackTrace(); } 
            catch (KeyManagementException e) { e.printStackTrace(); }
        }

        public void addConnection() {
            // Initialize socket
            try {
                socket = (SSLSocket) factory.createSocket(InetAddress.getByName(address), port);
            } 
            catch (UnknownHostException e1) { e1.printStackTrace(); } 
            catch (IOException e1) { e1.printStackTrace(); }
        }

        public void sendData() {
            // Send single byte
            try {
                socket.getOutputStream().write((byte)0x00);
            } 
            catch (IOException e) { e.printStackTrace(); }
        }
    }

    TestMain() {
        TestModuleReceive receive = new TestModuleReceive();
        TestModuleSend send = new TestModuleSend();
    }

    public static void main(String[] args) {
        TestMain testMain = new TestMain();
    }
}

当我运行此代码时,我得到以下堆栈跟踪:

javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
    at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1731)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:974)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1158)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:773)
    at sun.security.ssl.AppInputStream.read(AppInputStream.java:94)
    at sun.security.ssl.AppInputStream.read(AppInputStream.java:69)
    at sslTest.TestMain$TestModuleReceive.receiveData(TestMain.java:71)
    at sslTest.TestMain$TestModuleReceive$1.run(TestMain.java:63)
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)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1665)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:258)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:252)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1165)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:154)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:610)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:546)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:913)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1158)
    at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:652)
    at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:78)
    at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:92)
    at sslTest.TestMain$TestModuleSend.sendData(TestMain.java:121)
    at sslTest.TestMain$TestModuleSend.<init>(TestMain.java:87)
    at sslTest.TestMain.<init>(TestMain.java:129)
    at sslTest.TestMain.main(TestMain.java:133)
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:324)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:224)
    at sun.security.validator.Validator.validate(Validator.java:235)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1144)
    ... 12 more
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:197)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:319)
    ... 18 more

有谁能告诉我我做错了什么?

感谢。

1 个答案:

答案 0 :(得分:5)

在客户端,您不应使用KeyManager,而应使用TrustManager,因为您的客户必须决定信任谁。

更改

            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            ...
            sslcontext.init(kmf.getKeyManagers(), null, null);

            TrustManagerFactory kmf = TrustManagerFactory.getInstance("SunX509");
            ...
            sslcontext.init(null, tmf.getTrustManagers(), null);

当然,在实际应用程序中,客户端的密钥库应该只包含带有公钥的证书,而不是服务器的私钥。