我见过很多构建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)
方法加载,但这看起来真的很难看:肯定有一种方法可以在代码中执行此操作吗?
答案 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)
...