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地址

相关推荐
Fcy6481 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满1 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠2 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
主机哥哥2 小时前
阿里云OpenClaw部署全攻略,五种方案助你快速部署!
服务器·阿里云·负载均衡
Harvey9032 小时前
通过 Helm 部署 Nginx 应用的完整标准化步骤
linux·运维·nginx·k8s
珠海西格电力科技3 小时前
微电网能量平衡理论的实现条件在不同场景下有哪些差异?
运维·服务器·网络·人工智能·云计算·智慧城市
释怀不想释怀3 小时前
Linux环境变量
linux·运维·服务器
zzzsde3 小时前
【Linux】进程(4):进程优先级&&调度队列
linux·运维·服务器
qq_297574674 小时前
Linux 服务器 Java 开发环境搭建保姆级教程
java·linux·服务器
聆风吟º5 小时前
CANN开源项目实战指南:使用oam-tools构建自动化故障诊断与运维可观测性体系
运维·开源·自动化·cann