如何在 Android Kotlin 中使用 ECDSA 私钥和 P-256 (secp256k1) 曲线对数据进行签名?

问题描述 投票:0回答:1

我想使用 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()
    }
}```
android kotlin private-key ecdsa secp256k1
1个回答
0
投票

可以使用 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()
 方法。

© www.soinside.com 2019 - 2024. All rights reserved.