使用der格式公钥生成publicKey报错

本文讲述了如何处理在项目中遇到的RSA公钥从DER格式转换为PublicKey时的异常,涉及ASN.1到X509编码转换,并提供了关键代码示例。通过解析DER编码、重构X509公钥和适配不同格式的KeyUtil,确保了公钥的正确解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近项目用到RSA公私钥对进行加解密,遇到将der格式公钥转换为PublicKey报错的问题,异常信息如下:

cn.hutool.crypto.CryptoException: InvalidKeySpecException: encoded key spec not recognized: unknown object in getInstance: org.bouncycastle.asn1.ASN1Integer
        at cn.hutool.crypto.KeyUtil.generatePublicKey(KeyUtil.java:355)
        at cn.hutool.crypto.KeyUtil.generatePublicKey(KeyUtil.java:335)
        at com.unionpay.trust.crypto.util.HsmUtilTest.testGenerateRsaAndPubKeyEncrypt(HsmUtilTest.java:95)
        at com.unionpay.trust.crypto.util.HsmUtilTest.main(HsmUtilTest.java:61)
Caused by: java.security.spec.InvalidKeySpecException: encoded key spec not recognized: unknown object in getInstance: org.bouncycastle.asn1.ASN1Integer
        at org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi.engineGeneratePublic(Unknown Source)
        at org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi.engineGeneratePublic(Unknown Source)
        at java.security.KeyFactory.generatePublic(KeyFactory.java:328)
        at cn.hutool.crypto.KeyUtil.generatePublicKey(KeyUtil.java:353)

使用der格式公钥转换为PublicKey的方式如下:

//这里无法正常生成publickey
            // PublicKey publicKey = KeyUtil.generatePublicKey("RSA", Forms.hexStringToByte(publicKeyDerHex));
            // PublicKey publicKey = KeyUtil.generatePublicKey("RSA/ECB/PKCS1Padding", Forms.hexStringToByte(publicKeyDerHex));
            // PublicKey publicKey = KeyUtil.generatePublicKey("RSA/ECB/NoPadding", Forms.hexStringToByte(publicKeyDerHex));
            // PublicKey publicKey = KeyUtil.generatePublicKey("RSA/None/NoPadding", Forms.hexStringToByte(publicKeyDerHex));
            // PublicKey publicKey = KeyUtil.generateRSAPublicKey(Forms.hexStringToByte(publicKeyDerHex));

以上方式均无法正常生成PublicKey,报的错误都是上面的异常信息

出现错误的原因如下:

加密机产生的RSA公钥是ASN.1编码的,这里进行转换的使用需要先将ANS.1编码转换为X509编码,具体转换方式如下:

/**
     * RSA 公钥 格式转换 ANS1转X509
     * @param encodedKey   公钥
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static byte[] ansOneToX509(byte[] encodedKey) throws NoSuchAlgorithmException, InvalidKeySpecException {

        // 调用ASN1Sequence,对ASN1的RSADER编码进行解码
        ASN1Sequence asn1 = ASN1Sequence.getInstance(encodedKey);
        Enumeration<?> e = asn1.getObjects();
        BigInteger modulus = ASN1Integer.getInstance(e.nextElement()).getPositiveValue();
        BigInteger publicExponent = ASN1Integer.getInstance(e.nextElement()).getPositiveValue();

        // 重新构造一个x509对象,并获取编码后的数据
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubkey = keyFactory.generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
        byte[] x509Encoded = pubkey.getEncoded();
        return x509Encoded;
    }

经过转换之后,再讲der格式转换为PublicKey就正常了。

RSA公钥将X509编码转换为加密机可用的ASN.1编码方式如下:

/**
     * 将x509的证书公钥转换为ASN1编码
     * @param pkcs8PublicKeyByte
     * @return
     */
    public static byte[] formatPublicKeyPKCS8ToPKCS1(byte[] pkcs8PublicKeyByte) {
        ASN1Sequence publicKeyASN1Object = ASN1Sequence.getInstance(pkcs8PublicKeyByte);
        ASN1Encodable derBitStringASN1Encodable = publicKeyASN1Object.getObjectAt(1);
        DERBitString derBitStringObject = DERBitString.getInstance(derBitStringASN1Encodable);
        return derBitStringObject.getBytes();

    }


    public static String changeX509ToAns1(String publicKeyBase64){
        try {
            log.debug("changeX509ToAns1 publicKeyBase64:" + publicKeyBase64);
            //公钥加密
            String hexStr = Base64Util.base64Decode(publicKeyBase64);
            byte[] decode = hexStringToByte(hexStr);
            // //将内容转成流的方式
            ByteArrayInputStream bis = new ByteArrayInputStream(decode);
            CertificateFactory cf = null;
            cf = CertificateFactory.getInstance("X.509");
            Certificate certificate = cf.generateCertificate(bis);
            //取出公钥--这里的公钥是pkcs8的那种结构型--待核实
            PublicKey publicKey = certificate.getPublicKey();
            byte[] data = publicKey.getEncoded();
            byte[] result = formatPublicKeyPKCS8ToPKCS1(data);
            String ansEncode = Forms.byteToHexString(result);
            log.debug("changeX509ToAns1 data:" + ansEncode);
            return ansEncode;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

/**
* Base64为hutool工具类
*/
public static String base64Decode(String str) {
        return byteToHexString(Base64.decode(str));
    }

public static String byteToHexString(byte[] b) {
        if (b == null) {
            return null;
        } else {
            StringBuffer sb = new StringBuffer(b.length * 2);

            for(int i = 0; i < b.length; ++i) {
                sb.append("0123456789ABCDEF".charAt((b[i] & 240) >> 4));
                sb.append("0123456789ABCDEF".charAt((b[i] & 15) >> 0));
            }

            return sb.toString();
        }
    }


public static byte[] hexStringToByte(String hex) {
        if (hex == null) {
            return null;
        } else {
            int len = hex.length() / 2;
            hex = hex.toUpperCase();
            byte[] result = new byte[len];
            char[] achar = hex.toCharArray();

            for(int i = 0; i < len; ++i) {
                int pos = i * 2;
                result[i] = (byte)(toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
            }

            return result;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晒干的老咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值