我想使用 ECDSA 私钥和 P-256 (secp256k1) 曲线对十六进制数据进行签名。但Android中的大多数方法都使用PKCS#8编码的私钥来生成签名。如何将具有 P-256 (secp256k1) 曲线的 ECDSA 私钥转换为 Android Kotlin 中编码的 PKCS#8?
当前私钥格式:-----BEGIN EC PRIVATE KEY----- MHQCAQEEIKUpeefDapsFwkh3nvxEtDkkh3eA......-----结束 EC 私钥-----
所需的私钥格式:-----开始私钥----- MHQCAQEEIKUpeefDapsFwkh3nvxEtDkkh3eA......-----结束私钥-----
我无法使用 Bouncy Castle,因为我的项目的 targetSdkVersion 是 33。 我还尝试了以下方法来解析私钥和签名生成,但收到“java.security.spec.InvalidKeySpecException:com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException:解析私钥时出错”
fun parseECPrivateKey(pem: String): PrivateKey {
// Remove the header and footer from the PEM string
val privateKeyPEM = pem
.replace("-----BEGIN EC PRIVATE KEY-----", "")
.replace("-----END EC PRIVATE KEY-----", "")
.replace("\\s".toRegex(), "")
// Decode the Base64 encoded string
val encoded = Base64.getDecoder().decode(privateKeyPEM)
// Use KeyFactory to convert the PKCS8 encoded key into a PrivateKey object
val keyFactory = KeyFactory.getInstance("EC")
val keySpec = PKCS8EncodedKeySpec(encoded)
return keyFactory.generatePrivate(keySpec)
}
fun signData(privateKey: PrivateKey, dataHex: String): ByteArray {
val data = hexStringToByteArray(dataHex)
val signature = Signature.getInstance("SHA256withECDSA")
signature.initSign(privateKey)
signature.update(data)
return signature.sign()
}
fun hexStringToByteArray(hex: String): ByteArray {
val len = hex.length
val data = ByteArray(len / 2)
for (i in 0 until len step 2) {
data[i / 2] = ((Character.digit(hex[i], 16) shl 4)
+ Character.digit(hex[i + 1], 16)).toByte()
}
return data
}
fun main() {
val privateKeyPem = """
-----BEGIN EC PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0m4yLz+sdzZtBG9Q
HQ9++wcfq1O4hOWgSBMb/A6eijyhRANCAAQeB0fBl2D7HZOKVBjpPiU2jabzNxQU
ZYrJ+MSA3LpzZxmRk2JaFHNujjkJghQT19HHjg3Fnkb8Y9oIhB9neXBI
-----END EC PRIVATE KEY-----
""".trimIndent()
val dataHex = "48656c6c6f2c20576f726c6421" // Example data
try {
val privateKey = parseECPrivateKey(privateKeyPem)
val hash = hexStringToByteArray(dataHex)
val signature = signData(privateKey, hash)
println("Signature: ${Base64.getEncoder().encodeToString(signature)}")
} catch (e: Exception) {
e.printStackTrace()
}
}```
可以使用 BouncyCastle 完成 SEC1 格式的 PEM 编码密钥的导入。为此,必须首先在 app/gradle 中引用所需的 BouncyCastle 依赖项:
implementation("org.bouncycastle:bcpkix-jdk15to18:1.78.1")
implementation("org.bouncycastle:bcprov-jdk15to18:1.78.1")
删除预装的BC版本并导入当前版本:
import org.bouncycastle.jce.provider.BouncyCastleProvider
...
Security.removeProvider("BC")
Security.addProvider(BouncyCastleProvider())
然后可以导入 SEC1 格式的 PEM 编码的 EC 密钥,如下所示:
import org.bouncycastle.openssl.PEMKeyPair
import org.bouncycastle.openssl.PEMParser
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
...
val sec1Pem = """
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIK1vV4iLOPym9KvJJU5hd6CMEp+DTt8QI7NPBdJSf+VDoAoGCCqGSM49
AwEHoUQDQgAEMpHT+HNKM7zjhx0jZDHyzQlkbLV0xk0H/TFo6gfT23ish58blPNh
YrFI51Q/czvkAwCtLZz/6s1n/M8aA9L1Vg==
-----END EC PRIVATE KEY-----
""".trimIndent()
val pemParser = PEMParser(StringReader(sec1Pem))
val pemKeyPair = pemParser.readObject() as PEMKeyPair
val privateKey = JcaPEMKeyConverter().getKeyPair(pemKeyPair).private
以这种方式生成的
privateKey
可以直接传递给您的 signData()
方法。