Certificate数字证书的有效性验证

1.证书相关概念

在讲证书有效性验证的逻辑之前,先了解几个概念。

  1. 证书颁发机构:一般为运营数字证书的机构,该机构负责证书的签发、吊销等生命周期管理。
  2. 证书链:证书颁发机构一般会由多个组成,为树状层级,第一级的机构证书叫根证书、第二级叫二级子CA证书,多个层级的机构证书组成一个文件叫证书链(后缀为:.p7b)。
  3. 吊销列表:由证书颁发机构发布,若证书过期或因某些原因(密钥泄漏)吊销,则会将该证书添加至吊销列表。吊销列表为一个文件,包含已吊销的证书序列号(后缀为:.crl)。

2.证书相关概念

数字证书的有效性验证分为三个步骤:

1.验证证书的有效期,证书的有效期是一个时间短,需验证当前使用证书的时间不早于开始时间,不晚于结束时间。

2.证书链的验证:验证证书是否由合法的机构签发的。

2.验证证书签发机构发布的吊销列表是否包含当前证书,若包含,则当前证书已不可用,为无效证书。

3.使用场景

一般我们在调用https的服务接口时,会做证书的有效性校验。避免证书过期或因密钥泄漏后仍在使用。造成不安全的因素。

4.代码案例

依赖的jar包坐标如下:

XML 复制代码
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15to18</artifactId>
            <version>1.68</version>
        </dependency>

        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15to18</artifactId>
            <version>1.68</version>
        </dependency>

以下为代码实现

java 复制代码
    //以下静态块不能省略
    static {
        if(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    /**
     * 验证用户证书的有效性,包括验证有效期、证书信任列表、吊销状态等
     *
     * @param certFile       用户证书
     * @param certChainFile  证书链文件
     * @param crlFile        证书吊销列表文件
     * @throws CertificateNotYetValidException 证书未生效
     * @throws CertificateExpiredException     证书已过期
     * @throws SignatureException              证书不被信任
     * @throws IsRevokedException 证书被吊销
     */
    public static void verifyCertificate(File certFile, File certChainFile, File crlFile) throws CertificateNotYetValidException, CertificateExpiredException,IsRevokedException, CertificateEncodingException {

        X509Certificate certificate = CertFileUtil.getCertificate(certFile);
        certificate.checkValidity();

        CertPath certPath = null;
        try {
            certPath = CertFileUtil.getCertPath(certChainFile);
        } catch (CertificateException e) {
            e.printStackTrace();
            throw new CommonException("证书链读取异常,原因:"+e.getMessage());
        }catch (FileNotFoundException e){
            throw new CommonException("未找到证书链文件,原因:"+e.getMessage());
        }
        List<X509Certificate> certList = (List<X509Certificate>) certPath.getCertificates();
        boolean flag = false;
        for(X509Certificate caCert : certList) {
            if(certificate.getIssuerX500Principal().equals(caCert.getSubjectX500Principal())) {
                //使用机构证书验签用户证书
                flag = verify(caCert.getPublicKey(),certificate);
                if(flag) {
                    break;
                }
            }
        }

        if(!flag) {
            throw new CommonException("证书链验证证书签名失败");
        }
        X509CRL crlObj = CertFileUtil.getCrl(crlFile);
        if(crlObj.isRevoked(certificate)){
            X509CRLEntry crlEntry = crlObj.getRevokedCertificate(certificate);
            throw new IsRevokedException(crlEntry.getRevocationReason());
        }
    }



   private static boolean verify(PublicKey publicKey,X509Certificate certObj) {

        byte[] signValue = certObj.getSignature();
        byte[] data = null;
        try {
            data = certObj.getTBSCertificate();
        } catch (CertificateEncodingException e) {
            throw new CommonException("证书格式错误,未获取到证书内容,原因:"+e.getMessage(),e);
        }
        //获取签名算法的OID
        AlgorithmIdentifier oid = new AlgorithmIdentifier(new ASN1ObjectIdentifier(certObj.getSigAlgOID()), DERNull.INSTANCE);
        String signName = new DefaultAlgorithmNameFinder().getAlgorithmName(oid);
        return verificationSignature(publicKey.getEncoded(), signName, signValue,data);
    }


    /**
     * 数字签名验签
     * @param publicKey 公钥(PKCS8编码的私钥字节数组,可参考非对称密钥对的生成接口返回结果。)
     * @param signAlg 签名算法
     * @param signValue 签名值
     * @param data 签名值的原文
     * @return true为验签通过,表示签名者是合法的。false为验证未通过,签名者为非法。
     */
    private static boolean verificationSignature(byte[] publicKey, String signAlg, byte[] signValue,byte[] data)throws CryptoException{

        PublicKey key = null;

        if(signAlg.endsWith("RSA")) {

            key = SoftwareKeyUtil.bytesToPublicKey(publicKey, "RSA");
        }else{
            key = SoftwareKeyUtil.bytesToPublicKey(publicKey, "SM2");
        }

        Signature inSignatue = null;
        try {
            inSignatue = Signature.getInstance(signAlg, BouncyCastleProvider.PROVIDER_NAME);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new CryptoException("未找到算法",e);
        }catch (NoSuchProviderException e){
            throw new CryptoException("未找到安全程序提供商,原因:"+e.getMessage(),e);
        }


        try {
            inSignatue.initVerify(key);
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            throw new CryptoException("无效的公钥",e);
        }
        try {
            inSignatue.update(data);
        } catch (SignatureException e) {
            e.printStackTrace();
            throw new CryptoException("签名更新数据异常",e);
        }
        try {
            return inSignatue.verify(signValue);
        } catch (SignatureException e) {
            e.printStackTrace();
            throw new CryptoException("签名异常",e);
        }
    }

测试代码:以下代码只要未出现异常,则为证书有效性验证通过。

java 复制代码
public static void main(String[] args) {
        try {
            CertificateUtil.verifyCertificate(new File("/home/zhangzz/Downloads/_.csdn.crt"),new File("/home/zhangzz/Downloads/GeoTrust CN RSA CA G1.p7c"));
            System.out.println("证书有效");
        } catch (CertificateNotYetValidException e) {
            System.out.println("证书有效期未开始,请待进入有效期后开始使用:" + e.getMessage());
        } catch (CertificateExpiredException e) {
            System.out.println("证书已过期:" + e.getMessage());
        }

    }

在数字证书里,是可以看到证书链的http下载地址及吊销列表的下载地址的。如下图:

证书链地址:已CSDN的证书为例,它的上一级就是当前证书的颁发机构证书,可以导出为证书链。

点击*.csdn.net这个证书,它的证书属性里有授权信息访问,该属性为颁发机构的信息,其中CA颁发者就是该机构的CA证书,也可以用该证书直接作为证书链去验证。

具体如何使用代码从证书里获取证书链和吊销列表文件,无需手动更新。这个问题我们下次再分解。

以上的代码可以在git中获取(若未找到代码,请切换dev分支),代码路径:com.zhangzz.crypto.utils.CertificateUtil.java

加密工具starter组件git地址

相关推荐
Arenaschi16 分钟前
在Tomcat中部署应用时,如何通过域名访问而不加端口号
运维·服务器
小张认为的测试16 分钟前
Linux性能监控命令_nmon 安装与使用以及生成分析Excel图表
linux·服务器·测试工具·自动化·php·excel·压力测试
waicsdn_haha23 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
良许Linux38 分钟前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
蜜獾云1 小时前
docker 安装雷池WAF防火墙 守护Web服务器
linux·运维·服务器·网络·网络安全·docker·容器
小屁不止是运维1 小时前
麒麟操作系统服务架构保姆级教程(五)NGINX中间件详解
linux·运维·服务器·nginx·中间件·架构
Hacker_Oldv1 小时前
WPS 认证机制
运维·服务器·wps
bitcsljl1 小时前
Linux 命令行快捷键
linux·运维·服务器
ac.char1 小时前
在 Ubuntu 下使用 Tauri 打包 EXE 应用
linux·运维·ubuntu
Youkiup2 小时前
【linux 常用命令】
linux·运维·服务器