Как доверять моему самоподписанному сертификату

Я хочу подключиться к своему серверу через SSL. Поэтому я сгенерировал сертификаты на сервере с помощью следующих команд:

openssl genrsa -out server.pem 2048
openssl req -new -x509 -nodes -sha1 -days 3650 -key server.pem > server.cert

Соединение работает, если я доверяю всем сертификатам на клиенте с помощью TrustManager следующим образом:

X509TrustManager tm = new X509TrustManager() {

    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
    }

    @Override
    public void checkClientTrusted(
            X509Certificate[] certs, String authType) {
    }

    @Override
    public void checkServerTrusted(
            X509Certificate[] certs, String authType) {
    }
};

Но доверять всем сертификатам я конечно не хочу, а только своим. Я попробовал несколько команд для импорта сертификатов, например:

keytool -import -alias ca -file server.cert -keystore cacerts

Но я всегда получаю эту ошибку:

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

Что мне нужно сделать, чтобы это заработало? Может ли кто-нибудь объяснить шаги, необходимые для человека, не очень знакомого с критополем?

Редактировать: по предложению Донала Феллоуза я попробовал этот подход с пользовательским X509TrustManager, и он работает. Но так ли это безопасно? Если я просто верну "null" в методе "getAcceptedIssuers", он тоже сработает, и я не совсем уверен, почему:

X509TrustManager tm = new X509TrustManager() {

    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {

        X509Certificate[] trustedCerts = new X509Certificate[1];
        try{
            InputStream inStream = new FileInputStream("server.cert");
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
            inStream.close();
            trustedCerts[0] = cert;
        }catch(Exception e){
            e.printStackTrace();
        }

        return trustedCerts;
    }

    @Override
    public void checkClientTrusted(
            X509Certificate[] certs, String authType) {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {

        boolean match = false;
        try{


            InputStream inStream = new FileInputStream("server.cert");
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
            inStream.close();

            for(X509Certificate c : chain){
                if(c.equals(cert)){
                    match = true;
                }
            }
        }catch(Exception e){
            throw new CertificateException();
        }

        if(!match)
            throw new CertificateException();

    }

};

person jan    schedule 19.04.2014    source источник


Ответы (1)


Если вы действительно блокируете вещи, вы можете сделать это, установив собственный X509TrustManager, который проверяет, является ли используемый сертификат равным сертификату, который, по вашему мнению, должен быть (который вы точно знаете; вы его сгенерировали). На самом деле это довольно безопасно, но не обеспечивает никакой операционной гибкости; если сервер будет скомпрометирован и вам придется заново сгенерировать ключ, все клиенты также потребуют обновления.

Поскольку это действительно раздражает (и не масштабируется до таких вещей, как WWW), более нормально использовать доверительный корень. Корень доверия — это самозаверяющий сертификат ЦС, обычно с длительным сроком службы, который используется для подписи рабочего сертификата, который вы развертываете на своем сервере. (Вы можете использовать сам сертификат ЦС, но обычно лучше хранить его в автономном режиме, возможно, на съемном носителе в огнеупорном сейфе.) Затем вы помещаете сертификат ЦС в хранилище доверенных сертификатов (т. е. хранилище ключей, в котором хранятся только сертификаты) на клиенте. и скажите Java использовать его.

На практике вы почти у цели. Вероятно, вам просто нужно указать Java использовать ваше хранилище доверенных сертификатов cacerts. Вы делаете это, устанавливая правильное системное свойство.

// Be careful on Windows; this property (unusually!) uses “/” instead of “\”
System.setProperty("javax.net.ssl.trustStore", "/path/to/cacerts");
person Donal Fellows    schedule 19.04.2014