Deriving ECDSA Public Key from Private Key

I came here with the same problem in Kotlin, so, in case it helps anyone, here's how I derived the PublicKey from an ECDSA PrivateKey in Kotlin. This code is based on Lev Knoblock's Java answer and then trial-and-errored until it worked.

In my case I knew that my key used the secp256k1 curve, so I could hard-code that part. I didn't bother to learn how to generalize it to other curves.

import java.security.KeyFactory
import java.security.KeyPair
import java.security.PrivateKey
import java.security.PublicKey
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECPrivateKey
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
import org.bouncycastle.jce.spec.ECPublicKeySpec
import org.bouncycastle.math.ec.ECPoint
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
import org.bouncycastle.openssl.PEMParser

fun getECPrivateKeyFromPEM(privatePem: String): ECPrivateKey {
    val pemParser = PEMParser(privatePem.reader())
    val privateKeyInfo = pemParser.readObject() as PrivateKeyInfo
    return JcaPEMKeyConverter().getPrivateKey(privateKeyInfo) as ECPrivateKey
}

fun getKeyPairFromECPrivateKey(privateKey: ECPrivateKey): KeyPair {
    val keyFactory: KeyFactory = KeyFactory.getInstance("ECDSA", "BC")
    val spec: ECNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("secp256k1")
    val Q: ECPoint = spec.getG().multiply(privateKey.getD())
    val publicKey: PublicKey = keyFactory.generatePublic(ECPublicKeySpec(Q, spec))
    return KeyPair(publicKey, privateKey)
}

And here's my test harness:

import java.io.StringWriter
import org.bouncycastle.openssl.jcajce.JcaPEMWriter

fun main() {
    val privatePem = """
       |-----BEGIN PRIVATE KEY-----
       |MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg55EMdhNJX+YN/bjN
       |Eof91oKqEqD0QidEsRMhHBwSRjShRANCAARnSFpE0LDugORBWlSJz0Zf9e0mR9s6
       |tlxSeo1Nbd2vv9LDedm+l/CfZpbyYvPm49DAKDhkUHFIVDd2SsiPrRa7
       |-----END PRIVATE KEY-----
    """.trimMargin()
    val privateKey: ECPrivateKey = getECPrivateKeyFromPEM(privatePem)
    val pair: KeyPair = getKeyPairFromECPrivateKey(privateKey)
    val pems: String = StringWriter().use {
        val pemWriter = JcaPEMWriter(it)
        pemWriter.writeObject(pair.getPublic())
        pemWriter.writeObject(pair.getPrivate())
        pemWriter.flush()
        it.toString()
    }
    println(pems)
}

This prints out:

-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEcE4HMAHLDvPr6xHKsjhPXJzTdxLlRRR8
BfYnI2TGb0QLTFyyXm13CeYiLnxbkGhSvz9ZRo0zGQygKPVpgiThSw==
-----END PUBLIC KEY-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOeRDHYTSV/mDf24zRKH/daCqhKg9EInRLETIRwcEkY0oAoGCCqGSM49
AwEHoUQDQgAEZ0haRNCw7oDkQVpUic9GX/XtJkfbOrZcUnqNTW3dr7/Sw3nZvpfw
n2aW8mLz5uPQwCg4ZFBxSFQ3dkrIj60Wuw==
-----END EC PRIVATE KEY-----

Building on the previous answer one can extend this to the case that all one is given is a private key satisfying BCECPrivateKey interface:

DerivePubKeyFromPrivKey(BCECPrivateKey definingKey, Provider provider) {

    KeyFactory keyFactory = KeyFactory.getInstance("EC", provider);

    BigInteger d = definingKey.getD();
    org.bouncycastle.jce.spec.ECParameterSpec ecSpec = 
    definingKey.getParameters();
    ECPoint Q = definingKey.getParameters().getG().multiply(d);

    org.bouncycastle.jce.spec.ECPublicKeySpec pubSpec = new 
    org.bouncycastle.jce.spec.ECPublicKeySpec(Q, ecSpec);
    PublicKey publicKeyGenerated = keyFactory.generatePublic(pubSpec);
    return publicKeyGenerated;
}

So after a while, I figured out a solution and decided to post it in case anyone else has the same issue as me:

KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", "BC");
    ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");

    ECPoint Q = ecSpec.getG().multiply(((org.bouncycastle.jce.interfaces.ECPrivateKey) this.privateKey).getD());

    ECPublicKeySpec pubSpec = new ECPublicKeySpec(Q, ecSpec);
    PublicKey publicKeyGenerated = keyFactory.generatePublic(pubSpec);
    this.publicKey = publicKeyGenerated;

EDIT: Removed the code decoding the ECPoint as per @MaartenBodewes comment.