Skip to content

Commit

Permalink
ed25519 fix
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeplotean committed Sep 15, 2023
1 parent f7d83f9 commit cf016f2
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {

Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<PrivateKey> {
class DefaultPublicKeyDeriver: PublicKeyDeriver<PrivateKey> {
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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)
Expand Down
34 changes: 19 additions & 15 deletions src/main/kotlin/id/walt/services/key/import/PemKeyImport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,7 +26,7 @@ import java.security.spec.X509EncodedKeySpec

class PemKeyImport(
private val keyString: String,
private val publicKeyDeriver: PublicKeyDeriver<PrivateKey>
private val publicKeyDeriver: PublicKeyDeriver<AsymmetricKeyParameter>
) : KeyImportStrategy {

private val log = KotlinLogging.logger {}
Expand Down Expand Up @@ -69,37 +71,39 @@ class PemKeyImport(
* Parses a keypair out of a one or multiple objects
*/
private fun getKeyPair(objs: List<Any>): 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) {
Expand Down
4 changes: 3 additions & 1 deletion src/test/kotlin/id/walt/services/key/KeyServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand All @@ -290,6 +291,7 @@ class KeyServiceTest : AnnotationSpec() {
}
0x01 -> {
keyService.export(kid.id, KeyFormat.PEM, KeyType.PRIVATE) shouldBe keyStr
shouldNotThrow<Exception> { keyService.export(kid.id, KeyFormat.PEM, KeyType.PUBLIC) }
}
0x10 -> {
keyService.export(kid.id, KeyFormat.PEM, KeyType.PUBLIC) shouldBe keyStr
Expand Down

0 comments on commit cf016f2

Please sign in to comment.