Java HttpsURLConnection报NoSuchAlgorithmException(Cannot recover key)求助
我在Java 1.8.0_201环境下编写了使用HttpsURLConnection连接URL的代码:
String u = "https://test.com/prova.asmx?WSDL"; URL url = new URL(u); System.out.println("Link: " + u); HttpsURLConnection con = (HttpsURLConnection)url.openConnection(); //con.setSSLSocketFactory(sc.getSocketFactory()); System.out.println("After url connection"); BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream())); String input; while ((input = br.readLine()) != null) { System.out.println(input); } br.close();
通过以下命令行启动程序:
java -Djavax.net.ssl.trustStore=ws_cacerts_prod -Djavax.net.ssl.trustStorePassword=changeit -Djavax.net.ssl.keyStore=ws_cacerts_prod -Djavax.net.ssl.keyStorePassword=changeit -Djavax.net.debug=ssl -Djdk.tls.client.protocols=TLSv1 -Dhttps.protocols=TLSv1 TestHTTPS
运行时抛出如下异常:
Exception in thread "main" java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext) at javax.net.ssl.DefaultSSLSocketFactory.throwException(Unknown Source) at javax.net.ssl.DefaultSSLSocketFactory.createSocket(Unknown Source) at sun.net.www.protocol.https.HttpsClient.createSocket(Unknown Source) at sun.net.NetworkClient.doConnect(Unknown Source) at sun.net.www.http.HttpClient.openServer(Unknown Source) at sun.net.www.http.HttpClient.openServer(Unknown Source) at sun.net.www.protocol.https.HttpsClient.<init>(Unknown Source) at sun.net.www.protocol.https.HttpsClient.New(Unknown Source) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(Unknown Source) at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source) at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source) at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unknown Source) at TestHTTPS.main(TestHTTPS.java:19) Caused by: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext) at java.security.Provider$Service.newInstance(Unknown Source) at sun.security.jca.GetInstance.getInstance(Unknown Source) at sun.security.jca.GetInstance.getInstance(Unknown Source) at javax.net.ssl.SSLContext.getInstance(Unknown Source) at javax.net.ssl.SSLContext.getDefault(Unknown Source) at javax.net.ssl.SSLSocketFactory.getDefault(Unknown Source) at javax.net.ssl.HttpsURLConnection.getDefaultSSLSocketFactory(Unknown Source) at javax.net.ssl.HttpsURLConnection.<init>(Unknown Source) at sun.net.www.protocol.https.HttpsURLConnectionImpl.<init>(Unknown Source) at sun.net.www.protocol.https.Handler.openConnection(Unknown Source) at sun.net.www.protocol.https.Handler.openConnection(Unknown Source) at java.net.URL.openConnection(Unknown Source) at TestHTTPS.main(TestHTTPS.java:18) Caused by: java.security.UnrecoverableKeyException: Cannot recover key at sun.security.provider.KeyProtector.recover(Unknown Source) at sun.security.provider.JavaKeyStore.engineGetKey(Unknown Source) at sun.security.provider.JavaKeyStore$JKS.engineGetKey(Unknown Source) at sun.security.provider.KeyStoreDelegator.engineGetKey(Unknown Source) at sun.security.provider.JavaKeyStore$DualFormatJKS.engineGetKey(Unknown Source) at java.security.KeyStore.getKey(Unknown Source) at sun.security.ssl.SunX509KeyManagerImpl.<init>(Unknown Source) at sun.security.ssl.KeyManagerFactoryImpl$SunX509.engineInit(Unknown Source) at javax.net.ssl.KeyManagerFactory.init(Unknown Source) at sun.security.ssl.SSLContextImpl$DefaultManagersHolder.getKeyManagers(Unknown Source) at sun.security.ssl.SSLContextImpl$DefaultManagersHolder.<clinit>(Unknown Source) at sun.security.ssl.SSLContextImpl$DefaultSSLContext.<init>(Unknown Source) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) at java.lang.reflect.Constructor.newInstance(Unknown Source) ... 13 more
我已尝试将密钥库中的所有证书密码修改为与密钥库密码一致,但问题仍未解决,请问该如何处理?
从异常栈根因java.security.UnrecoverableKeyException: Cannot recover key来看,核心是Java无法从指定密钥库中恢复私钥,以下是针对性排查和解决步骤:
匹配密钥库类型与启动参数
如果ws_cacerts_prod是PKCS12格式,需额外添加启动参数-Djavax.net.ssl.keyStoreType=PKCS12。Java 8默认密钥库类型为JKS,若用JKS解析PKCS12文件,会导致私钥读取失败。验证私钥密码与密钥库密码一致性
用keytool命令验证:keytool -list -v -keystore ws_cacerts_prod -storepass changeit -keypass changeit若执行失败,说明私钥密码与密钥库密码不匹配。可重新导入私钥确保密码一致,或临时添加参数指定私钥密码:
-Djavax.net.ssl.keyPassword=changeit确认密钥库包含有效私钥
用keytool查看密钥库条目:keytool -list -keystore ws_cacerts_prod -storepass changeit若仅存在
trustedCertEntry,说明该文件只是信任库,无客户端私钥。此时需移除启动参数中的-Djavax.net.ssl.keyStore和-Djavax.net.ssl.keyStorePassword——普通HTTPS请求仅需信任库验证服务端证书,无需客户端证书认证。排查TLS协议兼容性
Java 8高更新包可能默认禁用TLSv1,且多数服务器已不再支持该老旧协议。尝试将启动参数中的协议改为TLSv1.2:-Djdk.tls.client.protocols=TLSv1.2 -Dhttps.protocols=TLSv1.2重新生成密钥库
若以上步骤无效,可重新生成密钥库:- 导出原密钥库中的证书和私钥(如有)
- 用
keytool重新创建密钥库,导入时确保所有密码一致 - 使用新密钥库文件测试连接
内容的提问来源于stack exchange,提问作者Andrea Rovelli

