我在将一些代码从 python 移动到 java 时遇到了这个问题。 以下公钥:
MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC2X07fCab+nIPAWBb5eRlhdOfR0Bkrhx7TgM3cGbR31g=
可以从这个python代码中成功读取:
key = bytearray.fromhex(
"3039301306072a8648ce3d020106082a8648ce3d030107032200"
) + bytearray([0x03]) + bytearray.fromhex("d97d3b7c269bfa720f01605be5e46585d39f474064ae1c7b4e03377066d1df58")
device_public_key = load_der_public_key(
key
)
打印出 device_public_key 我看到:
当尝试在 Java 中执行相同操作时,我在所有可能的尝试中都失败了:
fun loadKey() {
val key = Hex.decode("3039301306072a8648ce3d020106082a8648ce3d03010703220003d97d3b7c269bfa720f01605be5e46585d39f474064ae1c7b4e03377066d1df58")
try {
read(key)
} catch (e: Exception) {
println("Failed to load for sign $sign: $e")
}
}
private fun read(publicKey: ByteArray) {
val spec: ECNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("prime256v1")
val kf = KeyFactory.getInstance("ECDSA", BouncyCastleProvider())
val params = ECNamedCurveSpec("prime256v1", spec.curve, spec.g, spec.n)
val point: ECPoint = ECPointUtil.decodePoint(params.curve, publicKey)
val pubKeySpec = ECPublicKeySpec(point, params)
val t = kf.generatePublic(pubKeySpec) as ECPublicKey
println(t)
}
我得到的例外是:
java.lang.IllegalArgumentException:无效点编码 0x30
我无法理解出了什么问题,因为密钥是正确的(在 python 中工作)。 我尝试进行 Byte64 编码/解码,但总是失败。
这篇文章链接到我之前的问题(下一步): 解析并加载EC私钥,曲线secp256r1
公钥不同,我尝试加载的公钥被定义为固定标头、符号字节和我收到的变量的串联:
用于带有读取器私钥的 ECDH 的压缩临时公钥,32 字节 X 坐标。注意:解压时Y坐标始终为偶数
该格式不是一个点,尝试将其用作点或将其解码是行不通的。另外,对于 y 坐标为偶数的点,X9 压缩点的第一个字节(即所谓的“符号字节”)是 0x02,而不是 0x03。而且您发布的语言不是 Java,尽管它显然是在调用 Java。
有两种方法:
$ xxd -r -p <<END | openssl asn1parse -inform der -i -dump
3039301306072a8648ce3d020106082a8648ce3d030107032200
02d97d3b7c269bfa720f01605be5e46585d39f474064ae1c7b4e03377066d1df58
END
0:d=0 hl=2 l= 57 cons: SEQUENCE
2:d=1 hl=2 l= 19 cons: SEQUENCE
4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1
23:d=1 hl=2 l= 34 prim: BIT STRING
0000 - 00 02 d9 7d 3b 7c 26 9b-fa 72 0f 01 60 5b e5 e4 ...};|&..r..`[..
0010 - 65 85 d3 9f 47 40 64 ae-1c 7b 4e 03 37 70 66 d1 e...G@d..{N.7pf.
0020 - df 58 .X
# observe that this contains an AlgorithmIdentifier for (X9-style) ECC with the desired curve
# and a BIT STRING _containing_ (after the first byte 0x00 which is actually part of
# the ASN.1 encoding but OpenSSL displays as part of the value) the point 0x02 + 32 bytes
这是(标准)Java 加密用于公钥编码的格式,以及 OpenSSL 和使用 OpenSSL 的东西(如 python 等)使用的格式,并且(因此)可以由合适的 JCA 按原样处理
KeyFactory
:
byte[] spki = unhex("3039301306072a8648ce3d020106082a8648ce3d030107032200"
+ "02d97d3b7c269bfa720f01605be5e46585d39f474064ae1c7b4e03377066d1df58");
KeyFactory kf1 = KeyFactory.getInstance("EC","BC");
PublicKey pub1 = kf1.generatePublic(new X509EncodedKeySpec(spki));
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint; // NOT the JCA one!
...
byte[] raw = unhex("02d97d3b7c269bfa720f01605be5e46585d39f474064ae1c7b4e03377066d1df58");
ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec ("prime256v1");
ECPoint point = ecSpec.getCurve().decodePoint (raw);
KeyFactory kf2 = KeyFactory.getInstance("EC", "BC");
PublicKey pub2 = kf2.generatePublic(new ECPublicKeySpec(point, ecSpec));