我对SSL和Webservices如何在细粒度级别上工作有点痴迷。我正在开发一个调用多个Web服务的系统,其中一些具有安全URL,另一些则没有什么问题。但是,目前我正在与Endicia的LabelServer Web API进行集成。网络服务用于计算和打印邮资。
测试URL和WSDL位于:https://www.envmgr.com/LabelService/EwsLabelService.asmx
我使用wsimport创建和设置Java客户端以连接到此Web服务,但是当我尝试所有这些时,我收到错误
PKIX路径验证失败:java.security.cert.CertPathValidatorException:Path不与任何信任锚链接
此错误记录在此处:Java7 Refusing to trust certificate in trust store
其中讨论了Java 7如何使用带有“坏”关键字的自签名证书强制出错。在这种情况下,错误定义为不包含keyCertSign。 Web服务确实可以使用Java 6.我可以相信这种情况可能适用于此证书,因为它仅用作测试服务器,但我不知道如何验证。
有关于它的错误报告已经解决(http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7018897),但我不确定如何解决这个问题转换为修复Windows Tomcat环境的问题。我将证书导出到我的机器上,但不确定如何从那里开始。
编辑: 我尝试使用OpenSSL修改证书并将其添加到我的密钥库,如“拒绝信任信任存储中的证书”链接中所述,它不起作用。看起来这是一个由证书所有者完成的过程,对吗?我想知道是否有一些方法可以配置我的Java 7环境以通过此证书。
答案 0 :(得分:11)
默认的Java证书检查非常严格,并且显然变得更加严格。一种解决方法是使用自定义SSLContext
初始化X509TrustManager
。我曾经为测试写过的无所事事,即完全不安全的信任经理看起来像这样:
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs,
String authType )
{
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs,
String authType )
{
}
}
};
显然,您希望在实际程序中实际检查证书链。然后,您可以尝试使用它初始化SSLContext
,如果您的API没有其他配置SSL的方法,请致电SSLContext.setDefault()
。如果API使用默认的SSL上下文,那么这应该可行。
在这种情况下,密钥用法似乎不是问题,因为证书链不是自签名的。测试URL似乎表明叶证书不是自签名的,(2)链中的其他两个证书似乎已启用证书签名。另一种可能性是Java 6和Java 7具有单独的信任存储,并且根证书不在Java 7存储中。您可能需要仔细检查。如果您有权访问OpenSSL,则可以从服务器获取证书链:
openssl s_client -host www.example.com -port 443 -showcerts
显然更新信任存储是关键(双关语)。 OP报告:
我下载了OpenSSL for Windows 64,然后使用此命令下载证书链:
openssl s_client -host www.webserviceurl.com -port 443 -showcerts > c:\temp\certchain_output.crt
然后我想将它导入我的浏览器的密钥库(从JDK的主目录/ jre / lib / security):
keytool -import -alias ca -file certchain_output.crt -keystore cacerts -storepass changeit
我相信使用X509TrustManager也可以提供有效的解决方案。
答案 1 :(得分:7)
证书链验证逻辑确实在Java 6和Java 7之间发生了变化。似乎证书链被认为是无效的,因为中间证书的有效期结束日期是根证书的有效期结束日期之后。这是使用JDK 1.7.0_72和1.8.0_25修复的a Java bug。
如果您无法升级JDK,那么这是一种解决方法,因为您说在调试环境中并且您可以控制您的密钥库。您当然不能更改任何证书(因为您没有私钥),但您可以将中间证书或服务器证书导入本地密钥库,这意味着默认情况下它将受信任,其余的链条验证没有任何问题。
www.envmgr.com
或www.starfieldtech.com
的证书下载为 trusted_certificate.pem keytool
将证书导入名为 my_keystore 的本地密钥库keytool -keystore my_keystore -importcert -file trusted_certificate.pem
wsimport -J-Djavax.net.ssl.trustStore=my_keystore https://www.envmgr.com/LabelService/EwsLabelService.asmx
答案 2 :(得分:2)
尝试使用keytool将您需要的证书导入位于jdk_xxx / jre / lib / security / cacerts的java信任库中
设置jvm参数Djavax.net.debug = ssl以查看更多调试信息
答案 3 :(得分:1)
Rhashimoto帮助我找到了对我有用的解决方案:
我下载了OpenSSL for Windows 64,然后使用此命令下载证书链:
openssl s_client -host www.webserviceurl.com -port 443 -showcerts > c:\temp\certchain_output.crt
然后我想将它导入我的浏览器的密钥库(从JDK的主目录/ jre / lib / security):
keytool -import -alias ca -file certchain_output.crt -keystore cacerts -storepass changeit
我相信使用X509TrustManager也可以提供有效的解决方案。
答案 4 :(得分:0)
尝试此OUT,它接受所有
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
} };
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) { return true; }
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);