Skip to content

Commit

Permalink
fix: 340 cant generate a did with an existing key (#344)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeplotean authored Sep 15, 2023
1 parent fcd2bf1 commit adf5bfe
Show file tree
Hide file tree
Showing 27 changed files with 456 additions and 249 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ ARG SKIP_TESTS
FROM docker.io/rkimf1/dos2unix@sha256:60f78cd8bf42641afdeae3f947190f98ae293994c0443741a2b3f3034998a6ed as dos2unix-env
WORKDIR /convert
COPY gradlew .
COPY src/test/resources/key/*.pem ./
COPY src/test/resources/key/pem/*/*.pem ./
RUN dos2unix ./gradlew *.pem

# --- build-env # build the SSI Kit
Expand Down
12 changes: 7 additions & 5 deletions src/main/kotlin/id/walt/cli/did/DidCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ class CreateDidCommand : CliktCommand(
echo("Creating did:${method.method} (key: ${keyId})")

val did = when (method) {
is WebMethodOption -> DidService.create(web, keyId, DidWebCreateOptions((method as WebMethodOption).domain, (method as WebMethodOption).path))
is EbsiMethodOption -> DidService.create(ebsi, keyId, DidEbsiCreateOptions((method as EbsiMethodOption).version))
is CheqdMethodOption -> DidService.create(cheqd, keyId, DidCheqdCreateOptions((method as CheqdMethodOption).network))
is KeyMethodOption -> DidService.create(key, keyId, DidKeyCreateOptions((method as KeyMethodOption).useJwkJcsPubMulticodec))
else -> DidService.create(DidMethod.valueOf(method.method), keyId)
is WebMethodOption -> DidWebCreateOptions((method as WebMethodOption).domain, (method as WebMethodOption).path)
is EbsiMethodOption -> DidEbsiCreateOptions((method as EbsiMethodOption).version)
is CheqdMethodOption -> DidCheqdCreateOptions((method as CheqdMethodOption).network)
is KeyMethodOption -> DidKeyCreateOptions((method as KeyMethodOption).useJwkJcsPubMulticodec)
else -> null
}.let{
DidService.create(DidMethod.valueOf(method.method), keyId, it)
}

echo("\nResults:\n")
Expand Down
17 changes: 11 additions & 6 deletions src/main/kotlin/id/walt/crypto/CryptFun.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ fun java.security.Key.toPEM(): String = when (this) {
else -> throw IllegalArgumentException()
}

fun java.security.Key.toBase64(): String = when (this) {
is PublicKey -> this.toBase64()
is PrivateKey -> this.toBase64()
else -> throw IllegalArgumentException()
}

fun PrivateKey.toPEM(): String =
"-----BEGIN PRIVATE KEY-----" +
System.lineSeparator() +
Expand All @@ -104,6 +110,8 @@ fun PrivateKey.toPEM(): String =

fun PrivateKey.toBase64(): String = String(Base64.getEncoder().encode(PKCS8EncodedKeySpec(this.encoded).encoded))

fun PublicKey.toBase64(): String = encBase64(X509EncodedKeySpec(this.encoded).encoded)

fun PublicKey.toPEM(): String =
"-----BEGIN PUBLIC KEY-----" +
System.lineSeparator() +
Expand All @@ -124,9 +132,6 @@ fun decBase64(base64: String): ByteArray = Base64.getDecoder().decode(base64)

fun toBase64Url(base64: String) = base64.replace("+", "-").replace("/", "_").replace("=", "")


fun PublicKey.toBase64(): String = encBase64(X509EncodedKeySpec(this.encoded).encoded)

fun decodePubKeyBase64(base64: String, kf: KeyFactory): PublicKey =
kf.generatePublic(X509EncodedKeySpec(decBase64(base64)))

Expand Down Expand Up @@ -175,15 +180,15 @@ fun buildKey(
val keyPair = when (format) {
KeyFormat.PEM -> KeyPair(
decodePubKeyPem(publicPart, keyFactory),
privatePart?.let { decodePrivKeyPem(privatePart, keyFactory) })
privatePart?.let { decodePrivKeyPem(it, keyFactory) })

KeyFormat.BASE64_DER -> KeyPair(
decodePubKeyBase64(publicPart, keyFactory),
privatePart?.let { decodePrivKeyBase64(privatePart, keyFactory) })
privatePart?.let { decodePrivKeyBase64(it, keyFactory) })

KeyFormat.BASE64_RAW -> KeyPair(
decodeRawPubKeyBase64(publicPart, keyFactory),
privatePart?.let { decodeRawPrivKey(privatePart, keyFactory) })
privatePart?.let { decodeRawPrivKey(it, keyFactory) })
}

return Key(KeyId(keyId), KeyAlgorithm.valueOf(algorithm), CryptoProvider.valueOf(provider), keyPair)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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
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
import java.security.spec.X509EncodedKeySpec

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("ECDSA").generatePublic(ecPublicKeySpec)
}

private fun edAsymmetricKeyParameterToPublicKey(key: Ed25519PublicKeyParameters): PublicKey = let {
val pubKeyInfo = SubjectPublicKeyInfo(AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), key.encoded)
KeyFactory.getInstance("Ed25519").generatePublic(X509EncodedKeySpec(pubKeyInfo.encoded))
}

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()
}
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,31 @@
package id.walt.services.key.deriver

import id.walt.crypto.KeyAlgorithm
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import java.security.KeyFactory
import java.security.PrivateKey
import java.security.PublicKey
import java.security.interfaces.RSAPrivateCrtKey
import java.security.spec.RSAPublicKeySpec


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 -> {

null
}
//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?
}
63 changes: 63 additions & 0 deletions src/main/kotlin/id/walt/services/key/import/JwkKeyImport.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package id.walt.services.key.import

import com.nimbusds.jose.jwk.Curve
import com.nimbusds.jose.jwk.JWK
import com.nimbusds.jose.jwk.KeyType
import id.walt.crypto.*
import id.walt.services.CryptoProvider
import id.walt.services.keystore.KeyStoreService

class JwkKeyImport(private val keyString: String) : KeyImportStrategy {

override fun import(keyStore: KeyStoreService): KeyId {
val key = parseJwkKey(keyString)
keyStore.store(key)
return key.keyId
}

private fun parseJwkKey(jwkKeyStr: String): Key {
val jwk = JWK.parse(jwkKeyStr)

val key = when (jwk.keyType) {
KeyType.RSA -> Key(
keyId = KeyId(jwk.keyID ?: newKeyId().id),
algorithm = KeyAlgorithm.RSA,
cryptoProvider = CryptoProvider.SUN,
keyPair = jwk.toRSAKey().toKeyPair()
)

KeyType.EC -> {
val alg = when (jwk.toECKey().curve) {
Curve.P_256 -> KeyAlgorithm.ECDSA_Secp256r1
Curve.SECP256K1 -> KeyAlgorithm.ECDSA_Secp256k1
else -> throw IllegalArgumentException("EC key with curve ${jwk.toECKey().curve} not suppoerted")
}
Key(
keyId = KeyId(jwk.keyID ?: newKeyId().id),
algorithm = alg,
cryptoProvider = CryptoProvider.SUN,
keyPair = jwk.toECKey().toKeyPair()
)
}

KeyType.OKP -> {
val alg = when (jwk.toOctetKeyPair().curve) {
Curve.Ed25519 -> KeyAlgorithm.EdDSA_Ed25519
else -> throw IllegalArgumentException("OKP key with curve ${jwk.toOctetKeyPair().curve} not supported")
}
buildKey(
keyId = jwk.keyID ?: newKeyId().id,
algorithm = alg.name,
provider = CryptoProvider.SUN.name,
publicPart = jwk.toOctetKeyPair().x.toString(),
privatePart = jwk.toOctetKeyPair().d?.let { jwk.toOctetKeyPair().d.toString() },
format = KeyFormat.BASE64_RAW
)
}

else -> throw IllegalArgumentException("KeyType ${jwk.keyType} / Algorithm ${jwk.algorithm} not supported")
}
return key
}

}
Loading

0 comments on commit adf5bfe

Please sign in to comment.