Press "Enter" to skip to content

由证书链不完整所引起的…

公司有个域名要升级到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();
    }

}

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注