您好我试图写一个访问我们的云服务器的Rest客户端(Rest Webservices)。该连接使用SSL客户端证书进行保护,如果我理解正确,则不会由任何证书颁发机构签名,并且遇到问题。
我知道证书工作正常,因为我可以在其他编程语言中使用它(例如C#,PHP等),也因为我使用Postman测试API,但是我无法真正理解如何在Java中执行此操作
我尝试过使用P12证书文件,我也有.key和.crt文件,但仍然没有改变。我使用keytool.exe创建的.JKS文件,我认为它是正确的(据我所知)。
这是我正在使用的代码:
String keyPassphrase = certPwd;
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("C:\\Test\\Certificate\\idscertificate.jks"), keyPassphrase.toCharArray());
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStore, certPwd.toCharArray())
.build();
HttpClient httpClient = HttpClients.custom().setSslcontext(sslContext).build();
HttpResponse response = httpClient.execute(new HttpGet(
"https://url_that_I_am_using_to_call_my_rest_web_service"));
但每次启动时我都会收到错误:
"无法找到所请求目标的有效证书路径"。
据我所知,这是因为我没有指定证书颁发机构,我是否正确? 任何人都可以帮我这个吗?
谢谢大家的帮助
托马索
/ ******************* 这就是我将P12导入Keystore的方法。我尝试了不同的方法,我尝试的最后一个是:
首先创建了JKS: keytool -genkey -alias myName -keystore c:\ Test \ Certificate \ mykeystoreName.jks
然后"清理: keytool -delete -alias myName -keystore c:\ Test \ Certificate \ myKeystoreName.jks
然后导入P12文件: keytool -v -importkeystore -srckeystore c:\ Test \ Certificate \ idscertificate.p12 -srcstoretype PKCS12 -destkeystore c:\ Test \ Certificate \ myKeystoreName.jks -deststoretype JKS < / p>
获得的结果: 成功导入别名idsclientcertificate的条目。 导入命令已完成:已成功导入1个条目,0个条目失败或已取消
如果我检查密钥库的内容,我会找到我导入的证书。 不过我仍然得到同样的错误。
感谢您的帮助。
/ **************************** 2月8日更新*************** ****
好的,我尝试了所有的东西,但实际上一切都在慢慢放弃......情况如下:
到目前为止使用以下代码:
SSLContextBuilder sslContext = new SSLContextBuilder();
sslContext.loadKeyMaterial(readKeyStore(), userPwd.toCharArray());
//sslContext.loadTrustMaterial(readKeyStore(), new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext.build());
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
HttpGet httpGet = new HttpGet("https://myhost.com/myrest/status");
httpGet.addHeader("Accept", "application/json;charset=UTF8");
httpGet.addHeader("Cookie", "sessionids=INeedThis");
String encoded = Base64.getEncoder().encodeToString((userName+":"+userPwd).getBytes(StandardCharsets.UTF_8));
httpGet.addHeader("Authorization", "Basic "+encoded);
httpGet.addHeader("Cache-Control", "no-cache");
HttpResponse response = client.execute(httpGet);
遗憾的是仍然无法正常工作。我尝试了以下方法: - 在默认的java cacerts中包含我的证书, - 将别名指定为我的主机名, - 创建一个新的jks, - 加载p12文件,仍然没有,同样的错误。 我收到的错误消息是:
javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到所请求目标的有效证书路径< / EM>
如果我没有使用证书,我会收到另一个错误,表明证书已丢失,因此证书已加载(我也在IDE中看到它)。
如果我使用来自其他平台(c#或使用浏览器)的完全相同的证书文件,我会得到正确的响应和对象(因此证书/密码有效)
有什么方法可以停止验证认证路径吗?
答案 0 :(得分:1)
而不是使用loadKeyMaterial
使用loadTrustMaterial
,第一个用于为服务器创建SSLContext
,第二个用于为客户端创建。{/ p>
示例:
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(keyStore, new TrustSelfSignedStrategy())
.build();
答案 1 :(得分:1)
首先感谢大家的帮助。我终于按照以下步骤开始工作了: 1 - 我使用命令确定了我的根CA Cert:
openssl s_client -showcerts -connect my.root.url.com:443
然后我使用Portecle.exe (https://sourceforge.net/projects/portecle/)导入了此证书,但您也可以使用普通的keytool命令将其导入我的默认Java密钥库(jre / lib / security / cacerts中) 的 - &GT;确保将根URL指定为别名(例如,如果要连接到Google API,则为* .google.com)。这似乎非常重要。
然后我使用了以下代码: 首先创建了ServerSocketFactory:
private static SSLSocketFactory getSocketFactory()
{
try
{
SSLContext context = SSLContext.getInstance("TLS");
// Create a key manager factory for our personal PKCS12 key file
KeyManagerFactory keyMgrFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
char[] keyStorePassword = pk12Password.toCharArray(); // --> This is the Password for my P12 Client Certificate
keyStore.load(new FileInputStream(pk12filePath), keyStorePassword); // --> This is the path to my P12 Client Certificate
keyMgrFactory.init(keyStore, keyStorePassword);
// Create a trust manager factory for the trust store that contains certificate chains we need to trust
// our remote server (I have used the default jre/lib/security/cacerts path and password)
TrustManagerFactory trustStrFactory = TrustManagerFactory.getInstance("SunX509");
KeyStore trustStore = KeyStore.getInstance("JKS");
char[] trustStorePassword = jksTrustStorePassword.toCharArray(); // --> This is the Default password for the Java KEystore ("changeit")
trustStore.load(new FileInputStream(trustStorePath), trustStorePassword);
trustStrFactory.init(trustStore);
// Make our current SSL context use our customized factories
context.init(keyMgrFactory.getKeyManagers(),
trustStrFactory.getTrustManagers(), null);
return context.getSocketFactory();
}
catch (Exception e)
{
System.err.println("Failed to create a server socket factory...");
e.printStackTrace();
return null;
}
}
然后我使用以下方法创建了连接:
public static void launchApi()
{
try
{
// Uncomment this if your server cert is not signed by a trusted CA
HostnameVerifier hv = new HostnameVerifier()
{
public boolean verify(String urlHostname, SSLSession session)
{
return true;
}};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
URL url = new URL("https://myRootUrl.com/to/launch/api");
HttpsURLConnection.setDefaultSSLSocketFactory(getSocketFactory());
HttpsURLConnection urlConn = (HttpsURLConnection)url.openConnection();
String encoded = Base64.getEncoder().encodeToString((userName+":"+userPwd).getBytes(StandardCharsets.UTF_8)); //Acc User Credentials if needed to log in
urlConn.setRequestProperty ("Authorization", "Basic "+encoded);
urlConn.setRequestMethod("GET"); // Specify all needed Request Properties:
urlConn.setRequestProperty("Accept", "application/json;charset=UTF8");
urlConn.setRequestProperty("Cache-Control", "no-cache");
urlConn.connect();
/* Dump what we have found */
BufferedReader in =
new BufferedReader(
new InputStreamReader(urlConn.getInputStream()));
String inputLine = null;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
这对我有用。谢谢大家,也要感谢: this article that guided me on the right direction
侨