公司有个域名要升级到https,找运维要.crt及.key文件,运维说没有,丢给我一个JKS格式证书,于是我按下方方法导出.crt及.key文件,放到nginx上验证一波,Chrome告诉我没问题,然鹅,却埋下了隐患。
上线后发现Java应用访问CAS爆出证书验证错误。
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: validity check failed
...
这就很奇怪了,Chrome访问证书合法,但是Java却不认这个证书,难道是JDK版本低导致的?换成1.8最新版也是这个问题,同样验证同一个签发机构证书却没有问题。
由此可以证明JDK是没问题的,问题应该还在证书上。
image 如图所示,证书由CA证书,中级证书,根证书组成的证书链。缺少一层证书都不能通过验证。
什么是中级证书?
中级证书相当于是我们的根证书的替身。我们之所以使用中级证书,是因为我们必须在根证书上建立许多安全层,从而确保根证书的密钥绝对不会被任何人访问。
不过,由于根证书自身签署了中级证书,因此中级证书就可以用于签署我们的客户安装的 SSL 并维持“信任链”
追根到底,查看crt内容发现只有一段CERTIFICATE,对比另一个域名(同一家证书机构购买),发现有三段CERTIFICATE(CA证书,中级证书,根证书)。
原因比较明显了,浏览器缓存的中级和根证书,所以在chrome上看上去是合法的,然鹅由于证书链不完整,客户端校验不是合法的。
问题解决办法就是crt里补充上该域名的中级证书即可。
JKS格式导出crt和key 先导出.der格式
keytool -export -alias sample -file sample.der -keystore my.jks
将.der转为crt
openssl x509 -inform der -in sample.der -out sample.crt
导出.p12格式
keytool -importkeystore -srckeystore my.jks -destkeystore keystore.p12 -deststoretype PKCS12
将.p12转为key
openssl pkcs12 -in keystore.p12 -nodes -nocerts -out server.key
Java验证证书合法性
package Cert;
import java.net.URL;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
public class CheckCert {
public static void main(String[] args) throws Exception {
URL url = new URL("https://www.mango.im");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.connect();
Certificate[] certs = conn.getServerCertificates(); //会拿到完整的证书链
System.out.println(certs.length);
for (int i = 0; i< certs.length;i++){
X509Certificate cert = (X509Certificate)certs[i]; //cert[0]是证书链的最下层
System.out.println("序号:" + cert.getSerialNumber());
System.out.println("颁发给:" + cert.getSubjectDN().getName());
System.out.println("颁发者:" + cert.getIssuerDN().getName());
System.out.println("起始:" + cert.getNotBefore());
System.out.println("过期:" + cert.getNotAfter());
System.out.println("算法:" + cert.getSigAlgName());
System.out.println("指纹:" + getThumbPrint(cert));
}
conn.disconnect();
}
private static String getThumbPrint(X509Certificate cert) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] der = cert.getEncoded();
md.update(der);
byte[] digest = md.digest();
return bytesToHexString(digest);
}
private static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
}