如何创建一个不信任服务器证书的SSLContext?

时间:2017-04-27 22:04:27

标签: java ssl apache-httpclient-4.x

我见过很多构建SSLContext的例子,它们将接受所有服务器证书。对于我的测试用例,我正在尝试完全相反,并强制客户端拒绝服务器的证书。

所以我正在尝试创建一个不包含根证书的KeyStore对象,但是当我尝试使用它时,我得到一个InvalidAlgorithmParameterException,其中包含'trustAnchors参数必须为非空的消息”。我试过这个:

KeyStore emptyTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
emptyTrustStore.load(null, null);
sslcontext = SSLContexts.custom().loadTrustMaterial(emptyTrustStore, new TrustNoOneStrategy()).build();

和此:

KeyStore emptyTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
emptyTrustStore.setCertificateEntry("notreal", null);
sslcontext = SSLContexts.custom().loadTrustMaterial(emptyTrustStore, new TrustNoOneStrategy()).build();

和(来自评论中的想法),这:

KeyStore emptyTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustNoOneStrategy()).build();

但这些方法都没有解决问题。

显然我可以简单地创建一个包含伪根证书的JKS文件并使用SSLContexts.loadTrustMaterial(File)方法加载,但这看起来真的很难看:肯定有一种方法可以在代码中执行此操作吗?

3 个答案:

答案 0 :(得分:3)

试试这个:

SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
            throws CertificateException {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
            throws CertificateException {
        throw new CertificateException();
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
}}, null);

请注意,信任管理员将针对每个挑战抛出异常。

您可以看到上下文拒绝不良的stackoverflow证书:

HttpsURLConnection connection =
        (HttpsURLConnection)
                new URL("https://stackoverflow.com/").openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.getResponseCode();

答案 1 :(得分:2)

您不需要为此创建SSLContext。只需通过关联的系统属性将其指向空的信任库即可。

答案 2 :(得分:0)

感谢您的建议。我最终得到的解决方案是使用OpenSSL创建虚拟根CA,并在我的测试中将PEM格式的证书包含为String值,从X509Certificate构建String },将其安装在新创建的KeyStore对象中,然后使用KeyStore作为信任存储。

由于我创建了证书,然后在获得证书后立即删除了私钥,我知道这个虚拟CA实际上永远不会签署服务器证书。

KeyStore dummyTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    try {
        dummyTrustStore.load(null, null);
        dummyTrustStore.setCertificateEntry("dummyroot", buildDummyCertificate());
    } catch (CertificateException | IOException e) {
        // handle exception
    }
sslcontext = SSLContexts.custom().loadTrustMaterial(dummyTrustStore, new TrustNoOneStrategy()).build();

buildDummyCertificate()方法是:

private X509Certificate buildDummyCertificate() throws CertificateException {
    CertificateFactory cFactory = CertificateFactory.getInstance("X.509");
    ByteArrayInputStream is = new ByteArrayInputStream(PEM_CERTIFICATE.getBytes(StandardCharsets.US_ASCII));
    return (X509Certificate) cFactory.generateCertificate(is);
}

private static final String PEM_CERTIFICATE = "-----BEGIN CERTIFICATE-----\n"
                                                  + "MIIEMDCCAxigAwIBAgI..."
                                                  ....
                                                  + "...n1xJLO3k=-----END CERTIFICATE-----";

一个有趣的问题是需要-----BEGIN CERTIFICATE-----之后的换行符:如果没有这个,证书创建将失败并出现此错误:

java.security.cert.CertificateException: Could not parse certificate: java.io.IOException: Incomplete data
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:104)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339)
...