From cf016f2e2fe6f85dc4189f66c90a4d12e091aa20 Mon Sep 17 00:00:00 2001 From: mikeplotean Date: Fri, 15 Sep 2023 17:59:55 +0300 Subject: [PATCH] ed25519 fix --- .../key/AsymmetricPublicKeyConverter.kt | 7 +++- ...yDeriver.kt => DefaultPublicKeyDeriver.kt} | 13 ++----- .../services/key/import/KeyImportStrategy.kt | 5 +-- .../walt/services/key/import/PemKeyImport.kt | 34 +++++++++++-------- .../id/walt/services/key/KeyServiceTest.kt | 4 ++- 5 files changed, 33 insertions(+), 30 deletions(-) rename src/main/kotlin/id/walt/services/key/deriver/{SunPublicKeyDeriver.kt => DefaultPublicKeyDeriver.kt} (62%) diff --git a/src/main/kotlin/id/walt/services/key/AsymmetricPublicKeyConverter.kt b/src/main/kotlin/id/walt/services/key/AsymmetricPublicKeyConverter.kt index 05755930..8f1973e0 100644 --- a/src/main/kotlin/id/walt/services/key/AsymmetricPublicKeyConverter.kt +++ b/src/main/kotlin/id/walt/services/key/AsymmetricPublicKeyConverter.kt @@ -1,5 +1,8 @@ package id.walt.services.key +import org.bouncycastle.asn1.edec.EdECObjectIdentifiers +import org.bouncycastle.asn1.x509.AlgorithmIdentifier +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.crypto.params.AsymmetricKeyParameter import org.bouncycastle.crypto.params.ECPublicKeyParameters import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters @@ -10,6 +13,7 @@ import java.security.PublicKey import java.security.spec.ECPoint import java.security.spec.ECPublicKeySpec import java.security.spec.RSAPublicKeySpec +import java.security.spec.X509EncodedKeySpec class AsymmetricPublicKeyConverter { @@ -28,7 +32,8 @@ class AsymmetricPublicKeyConverter { } private fun edAsymmetricKeyParameterToPublicKey(key: Ed25519PublicKeyParameters): PublicKey = let { - TODO() + val pubKeyInfo = SubjectPublicKeyInfo(AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), key.encoded) + KeyFactory.getInstance("Ed25519").generatePublic(X509EncodedKeySpec(pubKeyInfo.encoded)) } private fun rsaAsymmetricKeyParameterToPublicKey(key: RSAKeyParameters): PublicKey = let { diff --git a/src/main/kotlin/id/walt/services/key/deriver/SunPublicKeyDeriver.kt b/src/main/kotlin/id/walt/services/key/deriver/DefaultPublicKeyDeriver.kt similarity index 62% rename from src/main/kotlin/id/walt/services/key/deriver/SunPublicKeyDeriver.kt rename to src/main/kotlin/id/walt/services/key/deriver/DefaultPublicKeyDeriver.kt index 04664eeb..a7ff04de 100644 --- a/src/main/kotlin/id/walt/services/key/deriver/SunPublicKeyDeriver.kt +++ b/src/main/kotlin/id/walt/services/key/deriver/DefaultPublicKeyDeriver.kt @@ -2,29 +2,20 @@ package id.walt.services.key.deriver import id.walt.crypto.KeyAlgorithm import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey -import sun.security.ec.ed.EdDSAOperations -import sun.security.ec.ed.EdDSAParameters -import java.security.InvalidAlgorithmParameterException import java.security.KeyFactory import java.security.PrivateKey import java.security.PublicKey import java.security.interfaces.RSAPrivateCrtKey -import java.security.spec.EdECPublicKeySpec -import java.security.spec.NamedParameterSpec import java.security.spec.RSAPublicKeySpec -class SunPublicKeyDeriver: PublicKeyDeriver { +class DefaultPublicKeyDeriver: PublicKeyDeriver { override fun derive(key: PrivateKey): PublicKey? = when (KeyAlgorithm.fromString(key.algorithm)) { KeyAlgorithm.RSA -> (key as? RSAPrivateCrtKey)?.let { KeyFactory.getInstance("RSA").generatePublic(RSAPublicKeySpec(it.modulus, it.publicExponent)) } KeyAlgorithm.EdDSA_Ed25519 -> { - val edDsaOperations = - EdDSAOperations(EdDSAParameters.get({ InvalidAlgorithmParameterException() }, NamedParameterSpec.ED25519)) - val edecPublicKeyPoint = edDsaOperations.computePublic(key.encoded) - val publicSpec = EdECPublicKeySpec(NamedParameterSpec.ED25519, edecPublicKeyPoint) - KeyFactory.getInstance("Ed25519").generatePublic(publicSpec) + null } //TODO: remove BC dependency, rely purely on java.security diff --git a/src/main/kotlin/id/walt/services/key/import/KeyImportStrategy.kt b/src/main/kotlin/id/walt/services/key/import/KeyImportStrategy.kt index 7d7dd463..80620cd4 100644 --- a/src/main/kotlin/id/walt/services/key/import/KeyImportStrategy.kt +++ b/src/main/kotlin/id/walt/services/key/import/KeyImportStrategy.kt @@ -1,7 +1,8 @@ package id.walt.services.key.import import id.walt.crypto.KeyId -import id.walt.services.key.deriver.SunPublicKeyDeriver +import id.walt.services.key.AsymmetricPublicKeyConverter +import id.walt.services.key.deriver.AsymmetricPublicKeyDeriver import id.walt.services.keystore.KeyStoreService interface KeyImportStrategy { @@ -10,7 +11,7 @@ interface KeyImportStrategy { abstract class KeyImportFactory { companion object { - private val publicKeyDeriver = SunPublicKeyDeriver() + private val publicKeyDeriver = AsymmetricPublicKeyDeriver(AsymmetricPublicKeyConverter()) fun create(keyString: String) = when (isPEM(keyString)) { true -> PemKeyImport(keyString, publicKeyDeriver) false -> JwkKeyImport(keyString) diff --git a/src/main/kotlin/id/walt/services/key/import/PemKeyImport.kt b/src/main/kotlin/id/walt/services/key/import/PemKeyImport.kt index 0377a1cc..8508a28b 100644 --- a/src/main/kotlin/id/walt/services/key/import/PemKeyImport.kt +++ b/src/main/kotlin/id/walt/services/key/import/PemKeyImport.kt @@ -12,6 +12,8 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers import org.bouncycastle.asn1.pkcs.PrivateKeyInfo import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo +import org.bouncycastle.crypto.params.AsymmetricKeyParameter +import org.bouncycastle.crypto.util.PrivateKeyFactory import org.bouncycastle.openssl.PEMKeyPair import org.bouncycastle.openssl.PEMParser import java.io.StringReader @@ -24,7 +26,7 @@ import java.security.spec.X509EncodedKeySpec class PemKeyImport( private val keyString: String, - private val publicKeyDeriver: PublicKeyDeriver + private val publicKeyDeriver: PublicKeyDeriver ) : KeyImportStrategy { private val log = KotlinLogging.logger {} @@ -69,37 +71,39 @@ class PemKeyImport( * Parses a keypair out of a one or multiple objects */ private fun getKeyPair(objs: List): KeyPair { - var pubKey: PublicKey? = null - var privKey: PrivateKey? = null + var pubKey: SubjectPublicKeyInfo? = null + var privKey: PrivateKeyInfo? = null objs.toList() log.debug { "Searching key pair in: $objs" } for (obj in objs) { if (obj is SubjectPublicKeyInfo) { - pubKey = getPublicKey(obj) + pubKey = obj } if (obj is PrivateKeyInfo) { - privKey = getPrivateKey(obj) + privKey = obj } if (obj is PEMKeyPair) { - pubKey = getPublicKey(obj.publicKeyInfo) - privKey = getPrivateKey(obj.privateKeyInfo) + pubKey = obj.publicKeyInfo + privKey = obj.privateKeyInfo break } } - pubKey = pubKey ?: publicKeyDeriver.derive(privKey!!) - return KeyPair(pubKey, privKey) + return KeyPair( + getPublicKey(pubKey) ?: publicKeyDeriver.derive(PrivateKeyFactory.createKey(privKey)), + getPrivateKey(privKey) + ) } - private fun getPublicKey(key: SubjectPublicKeyInfo): PublicKey { - val kf = getKeyFactory(key.algorithm.algorithm) - return kf.generatePublic(X509EncodedKeySpec(key.encoded)) + private fun getPublicKey(key: SubjectPublicKeyInfo?): PublicKey? = key?.let { + val kf = getKeyFactory(it.algorithm.algorithm) + return kf.generatePublic(X509EncodedKeySpec(it.encoded)) } - private fun getPrivateKey(key: PrivateKeyInfo): PrivateKey { - val kf = getKeyFactory(key.privateKeyAlgorithm.algorithm) - return kf.generatePrivate(PKCS8EncodedKeySpec(key.encoded)) + private fun getPrivateKey(key: PrivateKeyInfo?): PrivateKey? = key?.let { + val kf = getKeyFactory(it.privateKeyAlgorithm.algorithm) + return kf.generatePrivate(PKCS8EncodedKeySpec(it.encoded)) } private fun getKeyFactory(alg: ASN1ObjectIdentifier): KeyFactory = when (alg) { diff --git a/src/test/kotlin/id/walt/services/key/KeyServiceTest.kt b/src/test/kotlin/id/walt/services/key/KeyServiceTest.kt index f0a36b90..7db3867b 100644 --- a/src/test/kotlin/id/walt/services/key/KeyServiceTest.kt +++ b/src/test/kotlin/id/walt/services/key/KeyServiceTest.kt @@ -12,6 +12,7 @@ import id.walt.services.crypto.SunCryptoService import id.walt.services.keystore.InMemoryKeyStoreService import id.walt.services.keystore.KeyType import id.walt.test.RESOURCES_PATH +import io.kotest.assertions.throwables.shouldNotThrow import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.AnnotationSpec import io.kotest.data.blocking.forAll @@ -276,7 +277,7 @@ class KeyServiceTest : AnnotationSpec() { // Ed25519 row(File("src/test/resources/key/pem/ed25519/ed25519.pem").readText(), 0x11), row(File("src/test/resources/key/pem/ed25519/ed25519.public.pem").readText(), 0x10), -// row(File("src/test/resources/key/pem/ed25519/ed25519.private.pem").readText(), 0x01), + row(File("src/test/resources/key/pem/ed25519/ed25519.private.pem").readText(), 0x01), // Secp256k1 row(File("src/test/resources/key/pem/ecdsa/secp256k1.pem").readText(), 0x11), row(File("src/test/resources/key/pem/ecdsa/secp256k1.public.pem").readText(), 0x10), @@ -290,6 +291,7 @@ class KeyServiceTest : AnnotationSpec() { } 0x01 -> { keyService.export(kid.id, KeyFormat.PEM, KeyType.PRIVATE) shouldBe keyStr + shouldNotThrow { keyService.export(kid.id, KeyFormat.PEM, KeyType.PUBLIC) } } 0x10 -> { keyService.export(kid.id, KeyFormat.PEM, KeyType.PUBLIC) shouldBe keyStr