如何信任我的自签名证书

时间:2014-04-19 19:51:27

标签: java security ssl certificate truststore

我想通过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

我需要做些什么才能让这个工作?有人可以解释一个不熟悉cryto字段的人所需的步骤吗?

编辑:根据Donal Fellows的建议,我尝试了使用自定义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();

    }

};

1 个答案:

答案 0 :(得分:0)

如果您确实将事情锁定,可以通过安装自定义X509TrustManager进行测试,以查看所使用的证书与您认为的证书相等应该是(你肯定知道的;你已经生成了它)。这实际上非常安全,但允许没有操作灵活性;如果服务器受到攻击并且您必须重新生成密钥,则所有客户端也需要更新。

因为这非常令人讨厌(并且不会像WWW那样扩展),所以使用信任根更为正常。信任根是一种自签名CA证书,通常具有较长的使用寿命,用于签署部署到服务器的工作证书。 (您可以使用CA证书本身,但通常最好将其保持脱机状态,可能在防火保险箱中的可移动介质上。)然后将CA证书放入信任库(即只保存证书的密钥库) )在客户端上并告诉Java使用它。

在实践中,你几乎就在那里。您可能只需要告诉Java使用您的cacerts信任库。您可以通过设置correct system property

来实现
// Be careful on Windows; this property (unusually!) uses “/” instead of “\”
System.setProperty("javax.net.ssl.trustStore", "/path/to/cacerts");