java - 如何在java中将十六进制字符串转换为公钥?(java.security.InvalidKeyException)
问题描述
我正在使用 EC secp160r2 生成密钥。我的代码如下所示:
Security.insertProviderAt(new BouncyCastleProvider(), 1);
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(new ECGenParameterSpec("secp160r2"), new SecureRandom());
keyPair = keyGen.generateKeyPair();
此外,我得到如下公钥(用于交换):
byte [] publicKey = keyPair.getPublic().getEncoded(); // 64 bytes
ASN1Sequence sequence = DERSequence.getInstance(publicKey);
DERBitString subjectPublicKey = (DERBitString) sequence.getObjectAt(1);
byte[] ecPublicKeyBytes = subjectPublicKey.getBytes(); // 41 bytes
当我删除前缀时,我最终得到了一个 40 字节的公钥。
我遇到的问题是当我要做相反的事情时,从我收到的十六进制字符串中获取公钥。PubKey 是一个长度为 80 的十六进制字符串。我尝试像这样生成公钥:
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey generatedPublic = kf.generatePublic(new X509EncodedKeySpec(hexStringToByteArray(pubKey)));
但是我最终得到了一个 InvalidKeyException:
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: null
at jdk.crypto.ec/sun.security.ec.ECKeyFactory.engineGeneratePublic(ECKeyFactory.java:157)
at java.base/java.security.KeyFactory.generatePublic(KeyFactory.java:352)
at Main.main(Main.java:71)
Caused by: java.security.InvalidKeyException: IOException: null
at java.base/sun.security.x509.X509Key.decode(X509Key.java:397)
at java.base/sun.security.x509.X509Key.decode(X509Key.java:402)
at jdk.crypto.ec/sun.security.ec.ECPublicKeyImpl.<init>(ECPublicKeyImpl.java:71)
at jdk.crypto.ec/sun.security.ec.ECKeyFactory.implGeneratePublic(ECKeyFactory.java:219)
at jdk.crypto.ec/sun.security.ec.ECKeyFactory.engineGeneratePublic(ECKeyFactory.java:153)
... 2 more
此外,这里是十六进制转换方法,在我的情况下会产生一个 40 字节的数组:
public static byte[] parseHexBinary(String hexString) {
byte[] bytes = new byte[hexString.length() / 2];
for(int i = 0; i < hexString.length(); i += 2){
String sub = hexString.substring(i, i + 2);
Integer intVal = Integer.parseInt(sub, 16);
bytes[i / 2] = intVal.byteValue();
}
return bytes;
}
非常感谢任何解决问题的帮助!
解决方案
感谢评论中发布的解决方案,我想发布可能对某人有帮助的代码:
ECParameterSpec ecParameterSpec = ECNamedCurveTable.getParameterSpec("secp160r2");
ECNamedCurveSpec params = new ECNamedCurveSpec("secp160r2", ecParameterSpec.getCurve(), ecParameterSpec.getG(), ecParameterSpec.getN());
ECPoint publicPoint = ECPointUtil.decodePoint(params.getCurve(), parseHexBinary(pubKey));
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(publicPoint, params);
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey publicKey = kf.generatePublic(pubKeySpec);
上面的代码生成一个 64 字节的公钥。pubKey 是一个公钥十六进制字符串,带有附加前缀。请注意,02 和 03 前缀用于压缩密钥,04 用于未压缩密钥。椭圆曲线密码学主题公钥信息
推荐阅读
- django - 为什么 SQL 插入 (mariadb) 会随着表的增长而变慢
- flutter - Dart SDK 版本差异
- regex - 正则表达式删除句点,除非在数字之间
- javascript - 如何使用 javascript 将 HTML Treeview 下载为 PDF
- laravel - Laravel echo 只收听公共频道
- mysql - MySQL 从行值生成列
- java - 如何在 Spring Boot 嵌入式 tomcat 中自定义 ssl 配置?
- c# - 用于包含字母数字和特殊字符但不包含特殊字符的正则表达式
- mysql - MySQL查询计算两个坐标之间的距离,但HAVING子句太慢
- c - 使用 seccomp 过滤器获取“错误系统调用”