I'm trying to get the public key of EC from a private key.
I already do it using Python, Go which works fine, but I have not been able to do it in Java/Kotlin.
here is the python version:
import base64
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
private_key_b64 = "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAMM3sUprvk/VVF3vXhYcY0Vi9Ay1UPd4GLoBV/htAf2w=="
private_key_der = base64.b64decode(private_key_b64)
private_key = serialization.load_der_private_key(
private_key_der,
password=None,
backend=default_backend()
)
public_key = private_key.public_key()
public_key_der = public_key.public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
public_key_base64 = base64.b64encode(public_key_der).decode('utf-8')
print(public_key_base64)
here is the Go version:
privKeyBase64 := "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAMM3sUprvk/VVF3vXhYcY0Vi9Ay1UPd4GLoBV/htAf2w=="
privKeyDER, err := base64.StdEncoding.DecodeString(privKeyBase64)
if err != nil {
log.Fatalf("Error decoding base64 private key: %v", err)
}
privateKey, err := x509.ParsePKCS8PrivateKey(privKeyDER)
if err != nil {
log.Fatalf("Error parsing EC private key: %v", err)
}
k, ok := privateKey.(*ecdsa.PrivateKey)
if !ok {
panic("not ecdsa")
}
publicKeyDer, err := x509.MarshalPKIXPublicKey(k.Public())
if err != nil {
fmt.Println("Error marshaling public key to DER:", err)
return
}
fmt.Printf("Public Key: %s\n", base64.StdEncoding.EncodeToString(publicKeyDer))
here is my Kotlin version:
val privateKey64 = "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAMM3sUprvk/VVF3vXhYcY0Vi9Ay1UPd4GLoBV/htAf2w=="
val decodedKey = Base64.getDecoder().decode(privateKey64)
val keySpec = PKCS8EncodedKeySpec(decodedKey)
val keyFactory = KeyFactory.getInstance("EC")
val privateKey: PrivateKey = keyFactory.generatePrivate(keySpec)
val publicKey = keyFactory.generatePublic(keySpec)
println("Private Key: ${privateKey.encoded.encodeBase64()}")
println("Public Key: ${publicKey.encoded.encodeBase64()}")
but I have an exception:
java.security.spec.InvalidKeySpecException: Only ECPublicKeySpec and X509EncodedKeySpec supported for EC public keys
how to do it without bouncycastle?
I end up using a version of https://github.com/Archerxy/ecdsa_java code.
val curve = Ecdsa(Curve.SECP_256_R1)
val keystore = buildKeyStore {
certificate("key") {
hash = HashAlgorithm.SHA256
sign = SignatureAlgorithm.ECDSA
keySizeInBits = 256
password = "changeit"
}
}
val privateKey = keystore.getKey("key", "changeit".toCharArray()) as ECPrivateKey
val publicKeyReal = keystore.getCertificate("key").publicKey as ECPublicKey
// create public key from private
val publicKey = curve.privateKeyToPublicKey(privateKey)
println("Public Key Match: ${publicKey == publicKeyReal}")
and modified privateKeyToPublicKey
to:
fun privateKeyToPublicKey(privateKey: ECPrivateKey): ECPublicKey {
val priv = privateKey.s
if (priv > N) throw RuntimeException("Invalid private key.")
val p: Array<BigInteger> = fastMultiply(Gx, Gy, NUM[1], priv)
val z: BigInteger = inv(p[2], P)
val x = z.pow(2).multiply(p[0]).mod(P)
val y = z.pow(3).multiply(p[1]).mod(P)
val w = ECPoint(x, y)
return KeyFactory.getInstance("EC")
.generatePublic(ECPublicKeySpec(w, privateKey.params)) as ECPublicKey
}
note it might include security vulnerabilities and performance issues