尝试将Bouncy Castle生成的密钥放入AndroidKeyStore时出现“不支持的密钥算法:ECDSA”

问题描述

我正在尝试将各种私钥导入AndroidKeyStore。我正在使用 BouncyCastle解码原始密钥数据。虽然我可以获得可用的KeyPair, 由于以下异常,我无法将EC键放入AndroidKeyStore:

java.security.KeyStoreException: Unsupported key algorithm: ECDSA
    at android.security.keystore.AndroidKeyStoreSpi.getLegacyKeyProtectionParameter(AndroidKeyStoreSpi.java:348)
    at android.security.keystore.AndroidKeyStoreSpi.setPrivateKeyEntry(AndroidKeyStoreSpi.java:360)
    at android.security.keystore.AndroidKeyStoreSpi.engineSetKeyEntry(AndroidKeyStoreSpi.java:294)
    at java.security.KeyStore.setKeyEntry(KeyStore.java:1179)

将其扔到这里:

androidKeyStore.setKeyEntry(alias,keyPair.getPrivate(),null,new Certificate[]{ generateCertificate(keyPair) });

(证书生成为 shown here除了我添加的 case "ECDSA":下面的case "EC":

问题似乎出在算法名称上,它是ECDSA而不是EC; 当我使用其他方式获取密钥对并且其算法为EC时, 它可以正常工作。

这是怎么回事?我可以将ECDSA键转换为EC键吗?我可以做 BouncyCastle首先创建EC键吗?

为了记录,这就是我从字节数组和密码创建KeyPair的方式: 编辑:请参见下面答案中的固定代码

解决方法

一个窍门是使用Bouncy Castle转换PCKS1密钥。在Android 10上,内置的BC提供程序可以做到这一点。要获取密钥对,请对仅由getKeyPair()而不是JcaPEMKeyConverter()返回的对象调用JcaPEMKeyConverter().setProvider(bouncyCastleProvider)

完整代码:

private val bouncyCastleProvider = BouncyCastleProvider()
private val pkcs8DecryptorProvider = JceOpenSSLPKCS8DecryptorProviderBuilder()
        .setProvider(bouncyCastleProvider)
private val pemDecryptorProviderBuilder = JcePEMDecryptorProviderBuilder()
        .setProvider(bouncyCastleProvider)

private val pkcs8pemKeyConverter = JcaPEMKeyConverter().setProvider(bouncyCastleProvider)

// do NOT use BC for converting pkcs1 as for ECDSA it creates keys with algorithm "ECDSA"
// instead of "EC",and trying to put the former inside AndroidKeyStore results in
// exception “Unsupported key algorithm: ECDSA”
private val pkcs1pemKeyConverter = JcaPEMKeyConverter()


@Throws(Exception::class)   // this method throws just too many exceptions
fun makeKeyPair(keyReader: Reader,passphrase: CharArray): KeyPair {
    var obj = PEMParser(keyReader).readObject()

    // encrypted pkcs8 file,header: -----BEGIN ENCRYPTED PRIVATE KEY-----
    //   $ openssl genpkey -aes256 -pass pass:password -algorithm EC -out ecdsa.aes256.pkcs8 \
    //     -pkeyopt ec_paramgen_curve:P-384
    //   $ pkcs8 -topk8 -v2 des3 -passout pass:password -in rsa.pem -out rsa.des3.pkcs8
    if (obj is PKCS8EncryptedPrivateKeyInfo) {
        val decryptorProvider = pkcs8DecryptorProvider.build(passphrase)
        obj = /* PrivateKeyInfo */ obj.decryptPrivateKeyInfo(decryptorProvider)
    }

    // unencrypted pkcs8 file,header: -----BEGIN PRIVATE KEY-----
    //   $ openssl genpkey -algorithm RSA -out rsa.pkcs8 -pkeyopt rsa_keygen_bits:3072
    //   $ openssl genpkey -algorithm Ed25519 -out ed25519.pkcs8
    //   $ pkcs8 -topk8 -in rsa.pem -out rsa.pkcs8
    if (obj is PrivateKeyInfo) {
        val privateKey = pkcs8pemKeyConverter.getPrivateKey(obj)

        // Ed25519 can't be converted to PEM,so generate public key manually
        // the actual type here is probably BCEdDSAPrivateKey
        if (privateKey is EdDSAKey) {
            return KeyPair(genEd25519publicKey(privateKey,obj),privateKey)
        }

        // there is no direct way of extracting the public key from the private key.
        // to simplify things,we convert the key back to PEM and read it; this gets us a key pair
        obj = /* PEMKeyPair */ PEMParser(privateKey.toPem().toReader()).readObject()
    }

    // encrypted pkcs1 file,header like: -----BEGIN RSA PRIVATE KEY-----
    //                                    Proc-Type: 4,ENCRYPTED
    //                                    DEK-Info: AES-256-CBC,3B162E06B12794EA105855E7942D5A1A
    //   $ ssh-keygen -t ecdsa -m pem -N "password" -f ecdsa.aes128.pem
    //   $ openssl genrsa -aes256 -passout pass:password 4096 -out rsa.aes256.pem
    if (obj is PEMEncryptedKeyPair) {
        val decryptorProvider = pemDecryptorProviderBuilder.build(passphrase)
        obj = /* PEMKeyPair */ obj.decryptKeyPair(decryptorProvider)
    }

    // unencrypted pkcs1 file,header like: -----BEGIN RSA PRIVATE KEY-----
    //   $ ssh-keygen -t rsa -m pem -f rsa.pem
    //   $ openssl ecparam -name secp521r1 -genkey -noout -out ecdsa.pem
    if (obj is PEMKeyPair) {
        return pkcs1pemKeyConverter.getKeyPair(obj)
    }

    if (obj == null) throw IllegalArgumentException("File does not contain PEM objects")

    throw IllegalArgumentException("Don't know how to decode " + obj.javaClass.simpleName)
}


private fun PrivateKey.toPem(): String {
    val privateKeyPemObject = JcaMiscPEMGenerator(this,null).generate()
    val stringWriter = StringWriter()
    PemWriter(stringWriter).use { it.writeObject(privateKeyPemObject) }
    return stringWriter.toString()
}


private fun genEd25519publicKey(privateKey: EdDSAKey,keyInfo: PrivateKeyInfo): PublicKey {
    val privateKeyParameters = Ed25519PrivateKeyParameters(privateKey.encoded,0)
    val publicKeyParameters = privateKeyParameters.generatePublicKey()
    val spi = SubjectPublicKeyInfo(keyInfo.privateKeyAlgorithm,publicKeyParameters.encoded)
    val factory = KeyFactory.getInstance(privateKey.algorithm,bouncyCastleProvider)
    return factory.generatePublic(X509EncodedKeySpec(spi.encoded))
}


fun ByteArray.toReader() = InputStreamReader(ByteArrayInputStream(this))
fun String.toReader() = StringReader(this)

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...