yokila
yokila
Published on 2022-10-05 / 8 Visits
0
0

Java 利用算法原理判定RSA密钥对是否匹配

0、前言

  • 本文仅局限于RSA密钥的相关内容,其他算法的密钥请自行参考研究

  • 本文基于java1.8 和 BC version 1.7(引入依赖包)

  • 如果你不知道BC库是什么,那么建议先行搜索了解一下

1、PKCS8标准的私钥转PCKS1标准的私钥字节数组

/**
     * PKCS8标准的私钥转PCKS1标准的私钥字节数组(或者可以说是从中提取出最原始的密钥算法的那一部分内容)
     * @param privateKey java默认的PKCS8标准的私钥对象
     * @return PCKS1标准的私钥字节数组
     * @throws Exception 异常
     */
    public static byte[] p8PrvKey2P1PrvKeyBytes(PrivateKey privateKey) throws Exception {
        PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(privateKey.getEncoded());
        ASN1Encodable privateKeyPKCS1ASN1Encodable = privateKeyInfo.parsePrivateKey();
        ASN1Primitive asn1Primitive = privateKeyPKCS1ASN1Encodable.toASN1Primitive();
        return asn1Primitive.getEncoded();
    }

2、将java.security.PrivateKey转换成org.bouncycastle.asn1.pkcs.RSAPrivateKey对象

/**
     * 将java.security.PrivateKey转换成org.bouncycastle.asn1.pkcs.RSAPrivateKey对象
     * @param privateKey java.security.PrivateKey对象
     * @return org.bouncycastle.asn1.pkcs.RSAPrivateKey对象
     */
    public static RSAPrivateKey prvKey2BCRSAPrvKey(PrivateKey privateKey) throws Exception {
        // 这里getInstance只接受PKCS1的私钥转换成的byte数组,否则会报错
        // 报错信息:org.bouncycastle.asn1.DLSequence cannot be cast to org.bouncycastle.asn1.ASN1Integer
        // 所以需要先转换成PKCS1的私钥信息,才能输入进来
        // 这里输入asn1Primitive也可以
        RSAPrivateKey rsaPrivateKey = RSAPrivateKey.getInstance(p8PrvKey2P1PrvKeyBytes(privateKey));
        System.out.println("RSA key version is: " + rsaPrivateKey.getVersion());
        return rsaPrivateKey;
    }

3、利用算法原理判定密钥对是否匹配

/**
     * 验证一个公钥和一个私钥是否匹配,一般就是加密一个数据,然后解密,看看解密后的结果与原始数据是否一致。 <br/>
     * 否则就像这样利用算法原理,去验证RSA公私钥是否匹配
     * @param publicKey 公钥
     * @param privateKey 私钥
     * @return true-匹配,false-不匹配
     * @throws Exception 异常
     */
    public static boolean validRSAKeyPairMatch(PublicKey publicKey, PrivateKey privateKey) throws Exception {
        // 必须把私钥转成BC库里的RSA私钥对象,才是PKCS1标准的私钥形式,此时才能依据私钥获取一些理论算法中提及的各种密钥参数
        RSAPrivateKey rsaPrivateKey = prvKey2BCRSAPrvKey(privateKey);
        // 从私钥中获取公钥的指数
        BigInteger prvE = rsaPrivateKey.getPublicExponent();
        // 从私钥中获取密钥对共用的模数
        BigInteger prvM = rsaPrivateKey.getModulus();
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        // 把公钥加载成RSA公钥spec对象,以此获取公钥的指数
        RSAPublicKeySpec pubKeySpec = keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);
        // 依据公钥获取公钥的指数
        BigInteger pubE = pubKeySpec.getPublicExponent();
        // 依据公钥获取密钥对共用的模数
        BigInteger pubM = pubKeySpec.getModulus();
        // 如果 利用私钥推算出的公钥的参数 和 公钥自身的参数 二者是一致的,说明这个公钥和这个私钥是匹配的,是一对的
        return prvE != null && prvM != null && prvE.equals(pubE) && prvM.equals(pubM);
    }

4、验证一个私钥与一本证书是否匹配

其实就是先从证书中提取公钥(利用证书对象就可以直接get到公钥对象),然后就是和上一环节一样的流程(目前没有找到更便捷的流程)

5、测试

N、补充

  • 本文使用代码基于我写的存放于github的公开代码(点击前往仓库),欢迎前去查看是否有遗漏或者bug或者复制下来检验

  • 如果你觉得本文对你有所收获,请点赞转发,让更多人看到这篇文章,谢谢!!!

  • 如果你觉得有哪里不对,也欢迎在评论区留言指教!!!


Comment