Skip to content

Commit

Permalink
added public key derivers for eddsa and ecdsa
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeplotean committed Sep 14, 2023
1 parent b8099fc commit 6bf5cc9
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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<AsymmetricKeyParameter> {

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)
}
}
Original file line number Diff line number Diff line change
@@ -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<PrivateKey> {
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)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package id.walt.services.key.deriver

import java.security.PublicKey

interface PublicKeyDeriver<T> {
fun derive(key: T): PublicKey?
}
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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)
}

Expand Down
37 changes: 6 additions & 31 deletions src/main/kotlin/id/walt/services/key/import/PemKeyImport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<PrivateKey>
) : KeyImportStrategy {

private val log = KotlinLogging.logger {}

Expand Down Expand Up @@ -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))
Expand Down

0 comments on commit 6bf5cc9

Please sign in to comment.