diff --git a/src/main/kotlin/id/walt/services/key/AsymmetricPublicKeyConverter.kt b/src/main/kotlin/id/walt/services/key/AsymmetricPublicKeyConverter.kt new file mode 100644 index 00000000..05755930 --- /dev/null +++ b/src/main/kotlin/id/walt/services/key/AsymmetricPublicKeyConverter.kt @@ -0,0 +1,38 @@ +package id.walt.services.key + +import org.bouncycastle.crypto.params.AsymmetricKeyParameter +import org.bouncycastle.crypto.params.ECPublicKeyParameters +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters +import org.bouncycastle.crypto.params.RSAKeyParameters +import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util +import java.security.KeyFactory +import java.security.PublicKey +import java.security.spec.ECPoint +import java.security.spec.ECPublicKeySpec +import java.security.spec.RSAPublicKeySpec + +class AsymmetricPublicKeyConverter { + + fun convert(key: AsymmetricKeyParameter) = when (key) { + is ECPublicKeyParameters -> ecAsymmetricKeyParameterToPublicKey(key) + is Ed25519PublicKeyParameters -> edAsymmetricKeyParameterToPublicKey(key) + is RSAKeyParameters -> rsaAsymmetricKeyParameterToPublicKey(key) + else -> null + } + + private fun ecAsymmetricKeyParameterToPublicKey(key: ECPublicKeyParameters): PublicKey = let { + val ecParameterSpec = EC5Util.convertToSpec(key.parameters) + val ecPoint: ECPoint = EC5Util.convertPoint(key.q) + val ecPublicKeySpec = ECPublicKeySpec(ecPoint, ecParameterSpec) + KeyFactory.getInstance("EC").generatePublic(ecPublicKeySpec) + } + + private fun edAsymmetricKeyParameterToPublicKey(key: Ed25519PublicKeyParameters): PublicKey = let { + TODO() + } + + private fun rsaAsymmetricKeyParameterToPublicKey(key: RSAKeyParameters): PublicKey = let { + val rsaPublicKeySpec = RSAPublicKeySpec(key.modulus, key.exponent) + KeyFactory.getInstance("RSA").generatePublic(rsaPublicKeySpec) + } +} diff --git a/src/main/kotlin/id/walt/services/key/deriver/AsymmetricPublicKeyDeriver.kt b/src/main/kotlin/id/walt/services/key/deriver/AsymmetricPublicKeyDeriver.kt new file mode 100644 index 00000000..23cf4392 --- /dev/null +++ b/src/main/kotlin/id/walt/services/key/deriver/AsymmetricPublicKeyDeriver.kt @@ -0,0 +1,26 @@ +package id.walt.services.key.deriver + +import id.walt.services.key.AsymmetricPublicKeyConverter +import org.bouncycastle.crypto.params.* +import java.security.PublicKey + +class AsymmetricPublicKeyDeriver( + private val keyConverter: AsymmetricPublicKeyConverter +) : PublicKeyDeriver { + + override fun derive(key: AsymmetricKeyParameter): PublicKey? = when (key) { + is RSAPrivateCrtKeyParameters -> { + RSAKeyParameters(false, key.modulus, key.publicExponent) + } + is Ed25519PrivateKeyParameters -> { + key.generatePublicKey()//TODO + } + is ECPrivateKeyParameters -> { + val q = key.parameters.g.multiply(key.d) + ECPublicKeyParameters(q, key.parameters) + } + else -> null + }?.let{ + keyConverter.convert(it) + } +} diff --git a/src/main/kotlin/id/walt/services/key/deriver/DefaultPublicKeyDeriver.kt b/src/main/kotlin/id/walt/services/key/deriver/DefaultPublicKeyDeriver.kt new file mode 100644 index 00000000..87bfc09b --- /dev/null +++ b/src/main/kotlin/id/walt/services/key/deriver/DefaultPublicKeyDeriver.kt @@ -0,0 +1,42 @@ +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.math.BigInteger +import java.security.InvalidAlgorithmParameterException +import java.security.KeyFactory +import java.security.PrivateKey +import java.security.PublicKey +import java.security.spec.EdECPublicKeySpec +import java.security.spec.NamedParameterSpec +import java.security.spec.RSAPrivateKeySpec +import java.security.spec.RSAPublicKeySpec + +class DefaultPublicKeyDeriver: PublicKeyDeriver { + override fun derive(key: PrivateKey): PublicKey? = when (KeyAlgorithm.fromString(key.algorithm)) { + KeyAlgorithm.RSA -> { + val kf = KeyFactory.getInstance("RSA") + val privateSpec = kf.getKeySpec(key, RSAPrivateKeySpec::class.java) + val publicSpec = RSAPublicKeySpec(privateSpec.modulus, BigInteger.valueOf(65537)) + kf.generatePublic(publicSpec) + } + 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) + } + //TODO: remove BC dependency, rely purely on java.security + KeyAlgorithm.ECDSA_Secp256k1, KeyAlgorithm.ECDSA_Secp256r1 -> { + val definingKey = key as BCECPrivateKey + val d = definingKey.d + val ecSpec = definingKey.parameters + val q = definingKey.parameters.g.multiply(d) + val pubSpec = org.bouncycastle.jce.spec.ECPublicKeySpec(q, ecSpec) + KeyFactory.getInstance("ECDSA").generatePublic(pubSpec) + } + } +} diff --git a/src/main/kotlin/id/walt/services/key/deriver/PublicKeyDeriver.kt b/src/main/kotlin/id/walt/services/key/deriver/PublicKeyDeriver.kt new file mode 100644 index 00000000..749c1de3 --- /dev/null +++ b/src/main/kotlin/id/walt/services/key/deriver/PublicKeyDeriver.kt @@ -0,0 +1,7 @@ +package id.walt.services.key.deriver + +import java.security.PublicKey + +interface PublicKeyDeriver { + fun derive(key: T): PublicKey? +} 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 7ede9969..7c3d88ca 100644 --- a/src/main/kotlin/id/walt/services/key/import/KeyImportStrategy.kt +++ b/src/main/kotlin/id/walt/services/key/import/KeyImportStrategy.kt @@ -1,6 +1,7 @@ package id.walt.services.key.import import id.walt.crypto.KeyId +import id.walt.services.key.deriver.DefaultPublicKeyDeriver import id.walt.services.keystore.KeyStoreService interface KeyImportStrategy { @@ -9,8 +10,9 @@ interface KeyImportStrategy { abstract class KeyImportFactory { companion object { + private val publicKeyDeriver = DefaultPublicKeyDeriver() fun create(keyString: String) = when (isPEM(keyString)) { - true -> PemKeyImport(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 81004c2f..0377a1cc 100644 --- a/src/main/kotlin/id/walt/services/key/import/PemKeyImport.kt +++ b/src/main/kotlin/id/walt/services/key/import/PemKeyImport.kt @@ -5,27 +5,27 @@ import id.walt.crypto.KeyAlgorithm import id.walt.crypto.KeyId import id.walt.crypto.newKeyId import id.walt.services.CryptoProvider +import id.walt.services.key.deriver.PublicKeyDeriver import id.walt.services.keystore.KeyStoreService import mu.KotlinLogging 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.* import org.bouncycastle.openssl.PEMKeyPair import org.bouncycastle.openssl.PEMParser import java.io.StringReader -import java.math.BigInteger import java.security.KeyFactory import java.security.KeyPair import java.security.PrivateKey import java.security.PublicKey import java.security.spec.PKCS8EncodedKeySpec -import java.security.spec.RSAPrivateKeySpec -import java.security.spec.RSAPublicKeySpec import java.security.spec.X509EncodedKeySpec -class PemKeyImport(private val keyString: String) : KeyImportStrategy { +class PemKeyImport( + private val keyString: String, + private val publicKeyDeriver: PublicKeyDeriver +) : KeyImportStrategy { private val log = KotlinLogging.logger {} @@ -88,35 +88,10 @@ class PemKeyImport(private val keyString: String) : KeyImportStrategy { break } } -// pubKey = pubKey ?: computePublicKeyFromPrivate(privKey as AsymmetricKeyParameter) - pubKey = pubKey ?: computeRSAPublicKeyFromPrivate(privKey!!) + pubKey = pubKey ?: publicKeyDeriver.derive(privKey!!) return KeyPair(pubKey, privKey) } - private fun computeRSAPublicKeyFromPrivate(privateKey: PrivateKey): PublicKey? = when(KeyAlgorithm.fromString(privateKey.algorithm)) { - KeyAlgorithm.RSA -> { - val kf = KeyFactory.getInstance("RSA") - val privateSpec = kf.getKeySpec(privateKey, RSAPrivateKeySpec::class.java) - val publicSpec = RSAPublicKeySpec(privateSpec.modulus, BigInteger.valueOf(65537)) - kf.generatePublic(publicSpec) - } - else -> null - } - - private fun computePublicKeyFromPrivate(privateKey: AsymmetricKeyParameter): AsymmetricKeyParameter = when (privateKey) { - is RSAPrivateCrtKeyParameters -> { - RSAKeyParameters(false, privateKey.modulus, privateKey.publicExponent) - } - is Ed25519PrivateKeyParameters -> { - privateKey.generatePublicKey() - } - is ECPrivateKeyParameters -> { - val q = privateKey.parameters.g.multiply(privateKey.d) - ECPublicKeyParameters(q, privateKey.parameters) - } - else -> throw IllegalArgumentException("Key type not supported.") - } - private fun getPublicKey(key: SubjectPublicKeyInfo): PublicKey { val kf = getKeyFactory(key.algorithm.algorithm) return kf.generatePublic(X509EncodedKeySpec(key.encoded))