DNS校验,SSL证书校验

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.http.HttpUtil;
import com.faraway.pdemo.exception.DefinedException;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.xbill.DNS.CNAMERecord;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Record;
import org.xbill.DNS.Type;

import java.io.*;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.util.Date;

/**
 * <dependency>
 * <groupId>cn.hutool</groupId>
 * <artifactId>hutool-all</artifactId>
 * <version>5.4.5</version>
 * </dependency>
 *
 * <dependency>
 * <groupId>dnsjava</groupId>
 * <artifactId>dnsjava</artifactId>
 * <version>3.3.1</version>
 * </dependency>
 *
 * <dependency>
 * <groupId>org.bouncycastle</groupId>
 * <artifactId>bcprov-jdk15on</artifactId>
 * <version>1.67</version>
 * </dependency>
 *
 * <dependency>
 * <groupId>org.projectlombok</groupId>
 * <artifactId>lombok</artifactId>
 * <version>1.18.12</version>
 * <scope>provided</scope>
 * </dependency>
 */
@Slf4j
public class DNSAndSSLUtil {

    /**
     * 校验域名和记录值是否匹配
     * 作用:甲方域名指向乙方域名,乙方域名指向自己的服务,确保甲方填写的记录值是乙方提供的,这样才能正常提供服务
     *
     * @param domainFromA 域名      甲方提供
     * @param valueFromB  记录值    乙方提供
     * @throws DefinedException 自定义异常
     * @throws Exception
     * @Description 流程:用阿里云举例
     * 1.(甲方)登录阿里云的云解析DNS/域名解析/解析设置
     * 2.(甲方)添加记录 > 记录类型:CNAME;主机记录:起一个二级域名;记录值:乙方提供(例如七牛云:image.langflying.com.qiniudns.com)
     * 3.(乙方)通过第三方工具(dnsjava),甲方将二级域名给乙方,通过下方代码反解析出记录值,将反解析的记录值与我们提供的对比,看是否一致,一致则校验通过
     */
    public static void checkDNS(String domainFromA, String valueFromB) throws DefinedException, Exception {
        // 提供域名给第三方
        Lookup lookup = new Lookup(domainFromA, Type.CNAME);
        lookup.run();
        if (lookup.getResult() != Lookup.SUCCESSFUL) {
            throw new DefinedException("未查询到该域名");
        }
        Record[] records = lookup.getAnswers();
        for (int i = 0; i < records.length; i++) {
            CNAMERecord cnameRecord = (CNAMERecord) records[i];
            if (cnameRecord == null || cnameRecord.getTarget() == null || StrUtil.isBlank(cnameRecord.getTarget().toString())) {
                throw new DefinedException("域名的记录值为空");
            }
            // 通过域名查询到记录值
            String parseValue = cnameRecord.getTarget().toString();
            // 获取到的记录值最后会多一个".",例如(image.langflying.com.qiniudns.com.),这里处理一下,移除掉
            String valueWithRemoveLastPoint = parseValue.substring(0, parseValue.length() - 1);
            // 将反解析的记录值与我们提供的记录值对比
            if (!valueWithRemoveLastPoint.trim().equals(valueFromB.trim())) {
                throw new DefinedException("域名与记录值不匹配");
            }
        }
    }

    /**
     * 证书校验:是否过期,拥有者是否正确
     *
     * @param domain         域名
     * @param certificateUrl https证书
     * @throws DefinedException 自定义异常
     */
    public static void checkCert(String domain, String certificateUrl) throws DefinedException {
        try {
            // 拆分域名并一级域名
            String[] split = domain.split("\\.");
            String domainName = split[split.length - 2];
            // 从远程地址读取证书文件(本地更好,直接读文件)
            File certificateFile = HttpUtil.downloadFileFromUrl(certificateUrl, "");
            InputStream is = new FileInputStream(certificateFile);
            // 创建X509工厂类
            CertificateFactory factory = CertificateFactory.getInstance("X.509");
            // 创建证书对象
            X509Certificate cert = (X509Certificate) factory.generateCertificate(is);
            is.close();
            // 获取证书过期时间
            Date afterDate = cert.getNotAfter();
            Date now = new Date();
            if (now.compareTo(afterDate) == 1) {
                throw new DefinedException("证书已过期");
            }
            //获得证书主体信息(即一级域名)
            String owner = cert.getSubjectDN().getName();
            if (!owner.contains(domainName)) {
                throw new DefinedException("该证书不属于[" + domain + "]");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new DefinedException("解析证书出错");
        }
    }

    /*public static void showCertInfo() {
        try {
            //读取证书文件
            File file = new File("e://ICINFOCERTS//天谷证书.cer");
            InputStream inStream = new FileInputStream(file);
            //创建X509工厂类
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            //创建证书对象
            X509Certificate oCert = (X509Certificate) cf.generateCertificate(inStream);
            inStream.close();
            SimpleDateFormat dateformat = new SimpleDateFormat("yyyy/MM/dd");
            String info;
            //获得证书版本
            info = String.valueOf(oCert.getVersion());
            System.out.println("证书版本:" + info);
            //获得证书序列号
            info = oCert.getSerialNumber().toString(16);
            System.out.println("证书序列号:" + info);
            //获得证书有效期
            Date beforeDate = oCert.getNotBefore();
            info = dateformat.format(beforeDate);
            System.out.println("证书生效日期:" + info);
            Date afterDate = oCert.getNotAfter();
            info = dateformat.format(afterDate);
            System.out.println("证书失效日期:" + info);
            //获得证书主体信息
            info = oCert.getSubjectDN().getName();
            System.out.println("证书拥有者:" + info);
            //获得证书颁发者信息
            info = oCert.getIssuerDN().getName();
            System.out.println("证书颁发者:" + info);
            //获得证书签名算法名称
            info = oCert.getSigAlgName();
            System.out.println("证书签名算法:" + info);
            // 下方一般不需要
            *//*byte[] byt = oCert.getExtensionValue("1.2.86.11.7.9");
            String strExt = new String(byt);
            System.out.println("证书扩展域:" + strExt);
            byt = oCert.getExtensionValue("1.2.86.11.7.1.8");
            String strExt2 = new String(byt);
            System.out.println("证书扩展域2:" + strExt2);*//*
        } catch (Exception e) {
            System.out.println("解析证书出错!");
        }
    }*/

    /**
     * 验证证书私钥是否匹配(即文件通过公钥加密私钥解密后不变)
     *
     * @param publicKeyFileUrl  公钥文件远程地址(不是远程更好,直接获取文件)
     * @param privateKeyFileUrl 私钥文件远程地址(不是远程更好,直接获取文件)
     * @return
     */
    public static void certValid(String publicKeyFileUrl, String privateKeyFileUrl) throws DefinedException {

        try {
            File publicKeyFile = HttpUtil.downloadFileFromUrl(publicKeyFileUrl, "");
            File privateKeyFile = HttpUtil.downloadFileFromUrl(privateKeyFileUrl, "");

            PublicKey publicKey = getPublicKey(publicKeyFile);
            if (publicKey == null) {
                throw new DefinedException("获取公钥失败");
            }
            PrivateKey privateKey = getPrivateKey(privateKeyFile);
            if (privateKey == null) {
                throw new DefinedException("获取私钥失败");
            }
            // tutool工具包
            RSA rsa = new RSA(privateKey, publicKey);
            String sourceStr = "HelloWorld";
            // 公钥加密
            String encryptStr = rsa.encryptBase64(sourceStr, KeyType.PublicKey);
            // 私钥解密
            String decryptStr = rsa.decryptStr(encryptStr, KeyType.PrivateKey);
            // 字符串根据证书公钥加密,私钥解密后不能还原说明证书与私钥不匹配
            if (!sourceStr.equals(decryptStr)) {
                throw new DefinedException("证书私钥不匹配");
            }
        } catch (Exception e) {
            log.error("验证证书私钥是否匹配失败!", e);
            throw new DefinedException("验证证书私钥是否匹配失败");
        }
    }

    /**
     * 获取Https私钥
     *
     * @param privateKeyFile 私钥文件
     * @return
     */
    public static PrivateKey getPrivateKey(File privateKeyFile) {
        PemReader pemReader = null;
        PrivateKey privateKey = null;
        try {
            pemReader = new PemReader(new FileReader(privateKeyFile));
            PemObject pemObject = pemReader.readPemObject();
            byte[] pemContent = pemObject.getContent();
            //支持从PKCS#1或PKCS#8 格式的私钥文件中提取私钥
            if (pemObject.getType().endsWith("RSA PRIVATE KEY")) {
                // 取得私钥 for PKCS#1
                RSAPrivateKey rsaPrivateKey = RSAPrivateKey.getInstance(pemContent);
                RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(rsaPrivateKey.getModulus(), rsaPrivateKey.getPrivateExponent());
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                privateKey = keyFactory.generatePrivate(rsaPrivateKeySpec);
            } else if (pemObject.getType().endsWith("PRIVATE KEY")) {
                // 取得私钥 for PKCS#8
                PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(pemContent);
                KeyFactory kf = KeyFactory.getInstance("RSA");
                privateKey = kf.generatePrivate(pkcs8EncodedKeySpec);
            }
        } catch (Exception e) {
            log.error("获取私钥失败!", e);
        } finally {
            try {
                if (pemReader != null) {
                    pemReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return privateKey;
    }

    /**
     * 获取Https公钥
     *
     * @param publicKeyFile 公钥文件
     * @return
     */
    public static PublicKey getPublicKey(File publicKeyFile) {
        PublicKey publicKey = null;
        try {
            CertificateFactory factory = CertificateFactory.getInstance("X.509");
            FileInputStream in = new FileInputStream(publicKeyFile);
            Certificate cert = factory.generateCertificate(in);
            publicKey = cert.getPublicKey();
        } catch (Exception e) {
            log.error("获取公钥文件失败!", e);
        }
        return publicKey;
    }
}

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值