Python可正常读取的EC公钥,在Kotlin(Android)加载时报错求助
Python转Android Kotlin时EC公钥加载问题及解决
问题概述
将代码从Python迁移到Android平台Kotlin时,遇到EC公钥加载失败的问题。目标公钥为:
MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC2X07fCab+nIPAWBb5eRlhdOfR0Bkrhx7TgM3cGbR31g=
Python端可行实现
该公钥可通过以下Python代码成功读取:
key = bytearray.fromhex( "3039301306072a8648ce3d020106082a8648ce3d030107032200" ) + bytearray([0x03]) + bytearray.fromhex("d97d3b7c269bfa720f01605be5e46585d39f474064ae1c7b4e03377066d1df58") device_public_key = load_der_public_key( key )
打印结果为:
<cryptography.hazmat.backends.openssl.ec._EllipticCurvePublicKey object at 0x102372cd0>
Kotlin端初始失败实现
尝试用Kotlin实现相同功能时抛出异常,代码如下:
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: Invalid point encoding 0x30
该公钥由固定头部、标识字节和32字节X坐标拼接而成:
Compressed ephemeral public key used for ECDH w/ reader private key, 32 byte X coordinate. Note: when uncompressing, the Y coordinate is always even
解决方案(Android平台JVM兼容Kotlin)
方案1:从私钥关联曲线加载压缩公钥
仅使用标识字节(0x02)+ X坐标的压缩公钥,通过私钥关联的曲线解析:
fun load_compressed_public_key(privateKey: ECPrivateKey, compressedKey: ByteArray): ECPublicKey { val decodePoint = org.bouncycastle.jce.ECPointUtil.decodePoint(privateKey.params.curve, compressedKey) val spec = ECNamedCurveTable.getParameterSpec("secp256r1") val kf = java.security.KeyFactory.getInstance("ECDSA", BouncyCastleProvider()) val params = ECNamedCurveSpec("secp256r1", spec.curve, spec.g, spec.n) val pubKeySpec = java.security.spec.ECPublicKeySpec(decodePoint, params) val uncompressed = kf.generatePublic(pubKeySpec) as ECPublicKey return uncompressed }
方案2:直接加载完整X.509格式公钥
最初尝试失败的原因是创建KeyFactory时未指定BouncyCastleProvider,添加后即可正常加载完整的X.509格式公钥:
@Test fun `test load ephemeral x509`(){ val spki: ByteArray = Hex.decode( "3039301306072a8648ce3d020106082a8648ce3d030107032200" + "02d97d3b7c269bfa720f01605be5e46585d39f474064ae1c7b4e03377066d1df58" ) val kf1: KeyFactory = KeyFactory.getInstance("EC", BouncyCastleProvider()) val pub1: PublicKey = kf1.generatePublic(X509EncodedKeySpec(spki)) println(pub1) }
内容的提问来源于stack exchange,提问作者N Dorigatti
相关产品推荐
相关产品推荐

