如何在Kotlin中利用ECDSA密钥生成ECPrivateKey?
我有如下格式的密钥字符串,处理代码如下:
val keyBase64Str = "BAqKWv7ac4kVFXSKcbCSJNQcZEWhoDMBVx+H1sUXZBh9gQcdiHlNJprY51aPf5yXRlwY0smXYNs7oYIBBqBoPUWkJZEO05O+Jb463/d1x7qHT1T3V7XL2nN3XHkq06jvuw==" val keyArray = keyBase64Str.base64DecodetoByteArray() //keyArray size is 97 byte,65byte publickey + 32byte privatekey if(keyArray.size >= 97){ keyPairArray.copyInto(publicKeyArray, startIndex = 0, endIndex = 65) keyPairArray.copyInto(privateKeyArray, startIndex = 65) }
需要生成ECPrivateKey用于签名,尝试了以下代码但抛出异常:
val ecParameterSpec: ECParameterSpec = ECNamedCurveTable.getParameterSpec("secp256r1") val privateKeySpec:ECPrivateKeySpec = ECPrivateKeySpec(BigInteger(privateKeyArray), ecParameterSpec) var privateKey:PrivateKey? = null try { val keyFactory = KeyFactory.getInstance("EC") privateKey = keyFactory.generatePrivate(privateKeySpec) } catch (e:Exception){ Log.e("test",e.toString()) //exception is below //java.security.spec.InvalidKeySpecException: Must use ECPrivateKeySpec or PKCS8EncodedKeySpec; was org.bouncycastle.jce.spec.ECPrivateKeySpec }
错误日志
2024-01-23 18:20:01.136 16430-16430 System.err
com.dlink.mydlinkunified W
java.security.spec.InvalidKeySpecException: Must use ECPrivateKeySpec
or PKCS8EncodedKeySpec; was org.bouncycastle.jce.spec.ECPrivateKeySpec
2024-01-23 18:20:01.154 16430-16430 System.err
com.dlink.mydlinkunified W at
com.android.org.conscrypt.OpenSSLECKeyFactory.engineGeneratePrivate(OpenSSLECKeyFactory.java:74)
2024-01-23 18:20:01.155 16430-16430 System.err
com.dlink.mydlinkunified W at
java.security.KeyFactory.generatePrivate(KeyFactory.java:399)
2024-01-23 18:20:01.156 16430-16430 System.err
com.dlink.mydlinkunified W at
com.google.chip.chiptool.DeviceControllerInitData.getPrivateKeyFromRaw(DeviceControllerInitData.kt:120)
2024-01-23 18:20:01.157 16430-16430 System.err
com.dlink.mydlinkunified W at
com.google.chip.chiptool.DeviceControllerInitData.init(DeviceControllerInitData.kt:87)
2024-01-23 18:20:01.158 16430-16430 System.err
com.dlink.mydlinkunified W at
com.dlink.unify.ui.main.wifi.wizard.matter.page.MatterStartPage.initView(MatterStartPage.kt:42)
2024-01-23 18:20:01.159 16430-16430 System.err
com.dlink.mydlinkunified W at
com.dlink.unify.ui.base.BaseFragment.onViewCreated(BaseFragment.kt:131)
2024-01-23 18:20:01.160 16430-16430 System.err
com.dlink.mydlinkunified W at
androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2974)
2024-01-23 18:20:01.162 16430-16430 System.err
com.dlink.mydlinkunified W at
androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:543)
2024-01-23 18:20:01.163 16430-16430 System.err
com.dlink.mydlinkunified W at
androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
2024-01-23 18:20:01.164 16430-16430 System.err
com.dlink.mydlinkunified W at
androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2177)
2024-01-23 18:20:01.165 16430-16430 System.err
com.dlink.mydlinkunified W at
androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2088)
2024-01-23 18:20:01.166 16430-16430 System.err
com.dlink.mydlinkunified W at
androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1990)
2024-01-23 18:20:01.167 16430-16430 System.err
com.dlink.mydlinkunified W at
androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:524)
2024-01-23 18:20:01.168 16430-16430 System.err
com.dlink.mydlinkunified W at
android.os.Handler.handleCallback(Handler.java:883) 2024-01-23
18:20:01.169 16430-16430 System.err
com.dlink.mydlinkunified W at
android.os.Handler.dispatchMessage(Handler.java:100) 2024-01-23
18:20:01.170 16430-16430 System.err
com.dlink.mydlinkunified W at
android.os.Looper.loop(Looper.java:214) 2024-01-23 18:20:01.171
16430-16430 System.err com.dlink.mydlinkunified
W at android.app.ActivityThread.main(ActivityThread.java:7397)
2024-01-23 18:20:01.171 16430-16430 System.err
com.dlink.mydlinkunified W at
java.lang.reflect.Method.invoke(Native Method) 2024-01-23 18:20:01.173
16430-16430 System.err com.dlink.mydlinkunified
W at
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
2024-01-23 18:20:01.174 16430-16430 System.err
com.dlink.mydlinkunified W at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)
解决方案示例代码
错误原因是使用了BouncyCastle库的org.bouncycastle.jce.spec.ECPrivateKeySpec,但系统默认的KeyFactory(Conscrypt实现)只支持JDK标准的java.security.spec.ECPrivateKeySpec。以下两种方式可解决问题:
方式一:使用JDK标准的EC参数规范
import java.security.spec.ECParameterSpec import java.security.spec.ECPrivateKeySpec import java.security.KeyFactory import java.security.PrivateKey import java.math.BigInteger import java.security.spec.EllipticCurve import java.security.spec.ECFieldFp import android.util.Log fun getPrivateKeyFromRaw(privateKeyArray: ByteArray): PrivateKey? { // secp256r1的标准参数定义 val p = BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951") val a = BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948") val b = BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291") val gx = BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286") val gy = BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109") val n = BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369") val h = BigInteger("1") val ecField = ECFieldFp(p) val ellipticCurve = EllipticCurve(ecField, a, b) val g = java.security.spec.ECPoint(gx, gy) val ecParameterSpec = ECParameterSpec(ellipticCurve, g, n, h.toInt()) // 注意这里要传入BigInteger(1, privateKeyArray),确保是正整数 val privateKeySpec = ECPrivateKeySpec(BigInteger(1, privateKeyArray), ecParameterSpec) return try { val keyFactory = KeyFactory.getInstance("EC") keyFactory.generatePrivate(privateKeySpec) } catch (e: Exception) { Log.e("test", e.toString()) null } }
方式二:指定BouncyCastle作为KeyFactory的Provider
确保已添加BouncyCastle依赖,然后修改代码:
import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.spec.ECParameterSpec import org.bouncycastle.jce.spec.ECPrivateKeySpec import java.security.KeyFactory import java.security.PrivateKey import java.math.BigInteger import org.bouncycastle.jce.provider.BouncyCastleProvider import android.util.Log fun getPrivateKeyFromRaw(privateKeyArray: ByteArray): PrivateKey? { val ecParameterSpec: ECParameterSpec = ECNamedCurveTable.getParameterSpec("secp256r1") // 注意这里要传入BigInteger(1, privateKeyArray),确保是正整数 val privateKeySpec = ECPrivateKeySpec(BigInteger(1, privateKeyArray), ecParameterSpec) return try { // 指定使用BouncyCastle的Provider val keyFactory = KeyFactory.getInstance("EC", BouncyCastleProvider()) keyFactory.generatePrivate(privateKeySpec) } catch (e: Exception) { Log.e("test", e.toString()) null } }
注意:方式二中需要在项目中添加BouncyCastle依赖,比如在gradle中添加:
implementation 'org.bouncycastle:bcprov-jdk15to18:1.78.1'
内容的提问来源于stack exchange,提问作者space freeman

