问题描述
我正在尝试将各种私钥导入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)