https不了解,对https双向认证更是一脸懵java
客户方要求系统提供https的服务,一年前申请某网站的免费证书,下载后包含了各类web容器的证书,应用程序的web容器为tomcat8,最后选用了tomcat下的证书,为jks格式。web
一年后免费服务到期,须要更换证书,客户申请了阿里云的证书,下载下来的格式为pfx,客户说只有这种格式的。坑已挖好。apache
百度搜索结果说能够将pfx转换成jks
命令以下:浏览器
keytool -importkeystore -srckeystore xxx.pfx -destkeystore xxx.jks -srcstoretype PKCS12 -deststoretype JKS -srcstorepass xxx -deststorepass xxx -srcalias alias -destalias destalias
由于原来tomcat中配置了jks相关的信息,想着直接将新的jks的文件名和密码保持一致,直接替换原文件就能够了。结果被坑。tomcat
按原来的文件名、密码生成新证书后,启动tomcat,事实证实想的仍是太简单。
报错信息以下:服务器
15-Nov-2019 12:20:20.221 SEVERE [main] org.apache.coyote.AbstractProtocol.init Failed to initialize end point associated with ProtocolHandler ["http-nio-443"] 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 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 org.apache.tomcat.util.net.jsse.JSSESocketFactory.getKeyManagers(JSSESocketFactory.java:617) at org.apache.tomcat.util.net.jsse.JSSESocketFactory.getKeyManagers(JSSESocketFactory.java:546) . . . Caused by: org.apache.catalina.LifecycleException: Protocol handler initialization failed at org.apache.catalina.connector.Connector.initInternal(Connector.java:962) at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:102) ... 12 more Caused by: java.security.UnrecoverableKeyException: Cannot recover key at sun.security.provider.KeyProtector.recover(Unknown Source) at sun.security.provider.JavaKeyStore.engineGetKey(Unknown Source)
处理办法:
将新的jks的密码和pfx的密码保持一致,修改tomcat配置文件中的密码。app
其实tomcat能够直接配置pfx格式证书,须要指定 keystoreType="PKCS12"
keystoreFile="/证书路径/名称.pfx" keystoreType="PKCS12" keystorePass="证书密码"
但此次的客户有作https双向认证,以前的代码已经写死支持jks证书库,因此须要生成jkside
15-Nov-2019 11:59:55.017 SEVERE [main] org.apache.coyote.AbstractProtocol.init Failed to initialize end point associated with ProtocolHandler ["http-nio-443"] java.io.IOException: Keystore was tampered with, or password was incorrect at sun.security.provider.JavaKeyStore.engineLoad(Unknown Source) at sun.security.provider.JavaKeyStore$JKS.engineLoad(Unknown Source) . . . Caused by: java.security.UnrecoverableKeyException: Password verification failed ... 25 more
异常信息以下:post
15-Nov-2019 12:20:20.221 SEVERE [main] org.apache.coyote.AbstractProtocol.init Failed to initialize end point associated with ProtocolHandler ["http-nio-443"] java.security.UnrecoverableKeyException: Cannot recover key
import javax.net.ssl.*; import java.io.*; import java.net.URL; import java.nio.charset.Charset; import java.security.KeyStore; /** * #2 * HTTPS 双向认证 - use truststore * 原生方式 * * @Author soft_xiang * @Date 7/11/2017 */ public class HttpsTruststoreNativeDemo { // 客户端证书路径,用了本地绝对路径,须要修改 调用方证书 private final static String CLIENT_CERT_FILE = "D:\\xxx\\https\\xxx-ip.p12"; // 客户端证书密码 private final static String CLIENT_PWD = "pwd"; // 信任库路径 (被调用方证书合集) private final static String TRUST_STRORE_FILE = "D:\\xxx\\https\\xxx.jks"; // 信任库密码 private final static String TRUST_STORE_PWD = "xxx"; private static String readResponseBody(InputStream inputStream) throws IOException { try { BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); StringBuffer sb = new StringBuffer(); String buff = null; while ((buff = br.readLine()) != null) { sb.append(buff + "\n"); } return sb.toString(); } finally { inputStream.close(); } } public static void httpsCall() throws Exception { // 初始化密钥库 KeyManagerFactory keyManagerFactory = KeyManagerFactory .getInstance("SunX509"); KeyStore keyStore = getKeyStore(CLIENT_CERT_FILE, CLIENT_PWD, "PKCS12"); keyManagerFactory.init(keyStore, CLIENT_PWD.toCharArray()); // 初始化信任库 TrustManagerFactory trustManagerFactory = TrustManagerFactory .getInstance("SunX509"); KeyStore trustkeyStore = getKeyStore(TRUST_STRORE_FILE, TRUST_STORE_PWD, "JKS"); trustManagerFactory.init(trustkeyStore); // 初始化SSL上下文 SSLContext ctx = SSLContext.getInstance("SSL"); ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory .getTrustManagers(), null); SSLSocketFactory sf = ctx.getSocketFactory(); HttpsURLConnection.setDefaultSSLSocketFactory(sf); String url = "post url"; URL urlObj = new URL(url); HttpsURLConnection con = (HttpsURLConnection) urlObj.openConnection(); con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"); con.setRequestProperty("Accept-Language", "zh-CN;en-US,en;q=0.5"); con.setRequestProperty("Content-Type", "text/xml"); con.setRequestMethod("POST"); con.setRequestProperty("Content-Length", "0"); con.setDoInput(true); con.setDoOutput(true); DataOutputStream os = new DataOutputStream(con.getOutputStream()); os.write("".getBytes("UTF-8"), 0, 0); os.flush(); os.close(); con.connect(); InputStream is = con.getInputStream(); Integer code = con.getResponseCode(); final String contentType = con.getContentType(); System.out.println(code + ":" + contentType); String response = readResponseBody(is); System.out.println(response); } /** * 得到KeyStore * * @param keyStorePath * @param password * @return * @throws Exception */ private static KeyStore getKeyStore(String keyStorePath, String password, String type) throws Exception { FileInputStream is = new FileInputStream(keyStorePath); KeyStore ks = KeyStore.getInstance(type); ks.load(is, password.toCharArray()); is.close(); return ks; } public static void main(String[] args) throws Exception { httpsCall(); } }
在容许调用的服务器,直接浏览器打开须要访问的地址,调用出现401,sap问题,协调sap解决网站
证书库密码错误
证书错误,确认sap加入信任库的证书和代码中调用的证书库中的证书是同一个
keytool -import -alias xxx -file "xxx.der" -keystore xxx.jks -storepass pass
将证书导入信任库
keytool -export -alias xxx -keystore abc.jks -storepass pass -file xxx.cer
将证书库abc.jks中别名为xxx的证书导出为xxx.cer,cer客户端证书
keytool -import -alias xxx -file xxx.cer -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit -trustcacerts
将客户端证书导入jdk的默认信任库(cacerts为jdk默认信任库,导入以前记得备份)