You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin中利用ECDSA密钥生成ECPrivateKey?

生成ECPrivateKey时遭遇InvalidKeySpecException问题

我有如下格式的密钥字符串,处理代码如下:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 12:10:54