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

最近项目用到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;
        }
    }

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SM2算法的pem格式公钥可以通过Bouncy Castle库转换为Java的`PublicKey`对象。以下是一个简单的示例代码: ```java import java.io.BufferedReader; import java.io.FileReader; import java.security.PublicKey; import java.security.Security; import org.bouncycastle.asn1.pkcs.RSAPublicKey; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemReader; public class SM2PublicKeyConverter { public static void main(String[] args) { try { // 加载Bouncy Castle Provider Security.addProvider(new BouncyCastleProvider()); // 读取pem格式公钥 BufferedReader reader = new BufferedReader(new FileReader("path/to/your/public_key.pem")); PemReader pemReader = new PemReader(reader); PemObject pemObject = pemReader.readPemObject(); byte[] publicKeyBytes = pemObject.getContent(); // 解析公钥 SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKeyBytes); ECPublicKeyParameters ecPublicKeyParameters = (ECPublicKeyParameters) PublicKeyFactory.createKey(subjectPublicKeyInfo); PublicKey publicKey = ECKeyUtil.publicKeyFromParameters(ecPublicKeyParameters); // 输出公钥信息 System.out.println("Algorithm: " + publicKey.getAlgorithm()); System.out.println("Format: " + publicKey.getFormat()); System.out.println("Encoded: " + Base64.getEncoder().encodeToString(publicKey.getEncoded())); } catch (Exception e) { e.printStackTrace(); } } } ``` 其中,`path/to/your/public_key.pem`是pem格式公钥文件的路径。`publicKey`是SM2公钥,可以通过它获取公钥的各种属性。注意,`ECKeyUtil.publicKeyFromParameters()`方法是自定义的,用于从`ECPublicKeyParameters`对象创建`PublicKey`对象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

晒干的老咸鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值