Codes to generate a public key in an elliptic curve algorithm using a given private key
Asked Answered
C

4

5

I need to implement ECC (Elliptic Curve Cryptography) algorithm using jdk 1.7. I tried using bouncy castle, sunEC, but all of them gave errors and errors. My target is to generate an elliptic curve using the private key, I will be given to the system.

Thus, I need to get a accurate code to generate a public key using a given private key using jdk1.7. The IDE I use is ecllipse. And I need to know, what are the other parameters I should be given other than a private key? Is it enough to provide just a curve point and the private key?

Can someone help me to generate public key from private key?? I can manage the rest of my implementation.

Anyone who knows to implement keys of Elliptic Curve Cryptography using java, please tell me whether this code is correct?

public class ECCrypt {

    private ECPoint curve_point;

      public ECCrypt(ECPoint curve_point) {
        this.curve_point = curve_point;
      }

public BigInteger makePublicKey(BigInteger privateKey) {
        ECPoint ecPublicKey = new ECPoint(curve_point);
        ecPublicKey.mult(privateKey);
        return ecPublicKey.pack();
}


public static void main(String[] argv) throws Exception {
        java.util.Random rnd = new java.util.Random();
        ECPoint cp = new ECPoint();
        cp.random(rnd);
        ECCrypt ec = new ECCrypt(cp);
        BigInteger priv = new BigInteger(255,rnd);
        BigInteger pub = ec.makePublicKey(priv);

}

Thanks!

Custommade answered 30/10, 2013 at 4:38 Comment(11)
Recently worked on bouncycastle with java. See my recent question's matched to your exception.. Yes got many exceptions and solved one by one. Post the exceptions you faced. For sure someone will help.Mamba
you generated both keys automatically.but I need to generate public key using a given/specific private key.Custommade
Judging from your question, you don't seem to be an expert in cryptography. So you probably should not do what you are trying. Otherwise, which algorithm do you try to implement? Getting the public key from the private key is in some algorithms just a matter of multiplication in the group of the elliptic curve.Jorrie
Plz don't say no, I started the work, to proceed I need to generate public key. Can't I get a maching elliptic curve for my private key using bouncy castle?Custommade
@Jorrie well I am generating user specific private key using matlab. Actually I've finished that work. Now I need to find a way to generate a public key by giving a private key. Thus, I need some guidance for that. Since it's hard to understand the mathematical concept of Elliptic Curve it's hard for me to code alone. I use ECC algorithm for key generation.Custommade
"ECC" does not tell too much. It just stands for Elliptic Curve Cryptography in general and does not identify a specific algorithm. This page may give some insight: en.wikipedia.org/wiki/Elliptic_curve_cryptography Basically, if the private key is k you have to calculate kG where G is the generator of the used group.Jorrie
Please please PLEASE don't try to roll your own crypto solution. It's extraordinarily risky - I'd recommend extensive attempts to solve the issues you're having with libraries first.Buchanan
@thegrinner: Is there a way to map my private key to an elliptic curve to generate public key using bouncycastle? I've tried to do so, but still couldn't find a way.Custommade
no solution for my question yetCustommade
You still did not explain which algorithm you try to use.Jorrie
What format is the private key in? If you are trying to generate your own private key...why? As @Buchanan said, you should not try to do your own crypto. Sony made that mistake a few years ago and their EC private key was compromised. Use a known Java Provider to create your private and public keypair.Audiology
A
8

I wrote a sample program that outputs the following:

FL261:java jvah$ javac -cp bcprov-ext-jdk15on-149.jar ECTest.java
FL261:java jvah$ java -cp bcprov-ext-jdk15on-149.jar:. ECTest
Private key: 7ba78909571fbc336b2b94054dfb745a6b0776ff36a8fa98a598dc32cb83cc8e
Public key: 035b9e4a6148c9f9b08b573871ac66a832e6e9f63cf117545523a45b8017b7c43f
Calculated public key: 035b9e4a6148c9f9b08b573871ac66a832e6e9f63cf117545523a45b8017b7c43f
Congratulations, public keys match!
FL261:java jvah$

The code should be clear enough so that you can understand what is done here. Please note that you really have to know which curve your private key is generated for, otherwise it is impossible to generate the matching public key. The sample code uses secp256r1 curve, which is quite commonly used.

import java.math.BigInteger;
import java.security.SecureRandom;

import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.sec.SECNamedCurves;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.math.ec.ECPoint;

class ECTest {
  public static String toHex(byte[] data) {
    StringBuilder sb = new StringBuilder();
    for (byte b: data) {
      sb.append(String.format("%02x", b&0xff));
    }
    return sb.toString();
  }

  public static void main(String[] argv) {
    // Get domain parameters for example curve secp256r1
    X9ECParameters ecp = SECNamedCurves.getByName("secp256r1");
    ECDomainParameters domainParams = new ECDomainParameters(ecp.getCurve(),
                                                             ecp.getG(), ecp.getN(), ecp.getH(),
                                                             ecp.getSeed());

    // Generate a private key and a public key
    AsymmetricCipherKeyPair keyPair;
    ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, new SecureRandom());
    ECKeyPairGenerator generator = new ECKeyPairGenerator();
    generator.init(keyGenParams);
    keyPair = generator.generateKeyPair();

    ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();
    ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();
    byte[] privateKeyBytes = privateKey.getD().toByteArray();

    // First print our generated private key and public key
    System.out.println("Private key: " + toHex(privateKeyBytes));
    System.out.println("Public key: " + toHex(publicKey.getQ().getEncoded(true)));

    // Then calculate the public key only using domainParams.getG() and private key
    ECPoint Q = domainParams.getG().multiply(new BigInteger(privateKeyBytes));
    System.out.println("Calculated public key: " + toHex(Q.getEncoded(true)));

    // The calculated public key and generated public key should always match
    if (!toHex(publicKey.getQ().getEncoded(true)).equals(toHex(Q.getEncoded(true)))) {
      System.out.println("ERROR: Public keys do not match!");
    } else {
      System.out.println("Congratulations, public keys match!");
    }
  }
}
Aseity answered 18/11, 2013 at 21:42 Comment(0)
R
3

i use this method to recover ECPublicKey from ECPrivateKey. At the end the main Routine prove that it works. It is plain java without any external library.

public final class MULT {
private static ECPoint doublePoint(final BigInteger p, final BigInteger a, final ECPoint R) {
    if (R.equals(ECPoint.POINT_INFINITY)) return R;
    BigInteger slope = (R.getAffineX().pow(2)).multiply(FieldP._3);
    slope = slope.add(a);
    slope = slope.multiply((R.getAffineY().multiply(FieldP._2)).modInverse(p));
    final BigInteger Xout = slope.pow(2).subtract(R.getAffineX().multiply(FieldP._2)).mod(p);
    final BigInteger Yout = (R.getAffineY().negate()).add(slope.multiply(R.getAffineX().subtract(Xout))).mod(p);
    return new ECPoint(Xout, Yout);
}

private static ECPoint addPoint   (final BigInteger p, final BigInteger a, final ECPoint r, final ECPoint g) {
    if (r.equals(ECPoint.POINT_INFINITY)) return g;
    if (g.equals(ECPoint.POINT_INFINITY)) return r;
    if (r==g || r.equals(g)) return doublePoint(p, a, r);
    final BigInteger gX    = g.getAffineX();
    final BigInteger sY    = g.getAffineY();
    final BigInteger rX    = r.getAffineX();
    final BigInteger rY    = r.getAffineY();
    final BigInteger slope = (rY.subtract(sY)).multiply(rX.subtract(gX).modInverse(p)).mod(p);
    final BigInteger Xout  = (slope.modPow(FieldP._2, p).subtract(rX)).subtract(gX).mod(p);
    BigInteger Yout =   sY.negate().mod(p);
    Yout = Yout.add(slope.multiply(gX.subtract(Xout))).mod(p);
    return new ECPoint(Xout, Yout);
}

public static ECPoint scalmult   (final EllipticCurve curve, final ECPoint g, final BigInteger kin) {
    final ECField         field    = curve.getField();
    if(!(field instanceof ECFieldFp)) throw new UnsupportedOperationException(field.getClass().getCanonicalName());
    final BigInteger p = ((ECFieldFp)field).getP();
    final BigInteger a = curve.getA();
    ECPoint R = ECPoint.POINT_INFINITY;
    BigInteger k = kin.mod(p);
    final int length = k.bitLength();
    final byte[] binarray = new byte[length];
    for(int i=0;i<=length-1;i++){
        binarray[i] = k.mod(FieldP._2).byteValue();
        k = k.shiftRight(1);
    }
    for(int i = length-1;i >= 0;i--){
        R = doublePoint(p, a, R);
        if(binarray[i]== 1) R = addPoint(p, a, R, g);
    }
    return R;
}

public static ECPublicKey getPublicKey(final ECPrivateKey pk) throws GeneralSecurityException {
    final ECParameterSpec params = pk.getParams();
    final ECPoint w = scalmult(params.getCurve(), pk.getParams().getGenerator(), pk.getS());
    final KeyFactory kg = KeyFactory.getInstance("EC");
    return (ECPublicKey)kg.generatePublic (new ECPublicKeySpec (w, params));
}

}

Repletion answered 14/3, 2017 at 22:2 Comment(2)
What is it FieldP._2?Potent
FieldP._2 is nothing more than an BigInteger with the value 2.Repletion
V
0

Some more improvement to the answer of @markw on Generate ECPublicKey from ECPrivateKey, extended with curve name detection by @bas-goossen from here: How to find the matching curve name from an ECPublicKey

public static ECPublicKey publicFromPrivate(final ECPrivateKey privateKey) throws Exception {
    ECParameterSpec params = privateKey.getParams();
    org.bouncycastle.jce.spec.ECParameterSpec bcSpec = org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util
        .convertSpec(params, false);
    org.bouncycastle.math.ec.ECPoint q = bcSpec.getG().multiply(privateKey.getS());
    org.bouncycastle.math.ec.ECPoint bcW = bcSpec.getCurve().decodePoint(q.getEncoded(false));
    ECPoint w = new ECPoint(
        bcW.getAffineXCoord().toBigInteger(),
        bcW.getAffineYCoord().toBigInteger());
    ECPublicKeySpec keySpec = new ECPublicKeySpec(w, tryFindNamedCurveSpec(params));
    return (ECPublicKey) KeyFactory
        .getInstance("EC", org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME)
        .generatePublic(keySpec);
}

@SuppressWarnings("unchecked")
public static ECParameterSpec tryFindNamedCurveSpec(ECParameterSpec params) {
    org.bouncycastle.jce.spec.ECParameterSpec bcSpec
        = org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util.convertSpec(params, false);
    for (Object name : Collections.list(org.bouncycastle.jce.ECNamedCurveTable.getNames())) {
        org.bouncycastle.jce.spec.ECNamedCurveParameterSpec bcNamedSpec
            = org.bouncycastle.jce.ECNamedCurveTable.getParameterSpec((String) name);
        if (bcNamedSpec.getN().equals(bcSpec.getN())
            && bcNamedSpec.getH().equals(bcSpec.getH())
            && bcNamedSpec.getCurve().equals(bcSpec.getCurve())
            && bcNamedSpec.getG().equals(bcSpec.getG())) {
            return new org.bouncycastle.jce.spec.ECNamedCurveSpec(
                bcNamedSpec.getName(),
                bcNamedSpec.getCurve(),
                bcNamedSpec.getG(),
                bcNamedSpec.getN(),
                bcNamedSpec.getH(),
                bcNamedSpec.getSeed());
        }
    }
    return params;
}
Valuate answered 26/8, 2018 at 19:31 Comment(0)
C
0

This is an improvement to the answer from @SkateScout. I used the scalar routines in another context, published them here on SO (Derive EC public key from private key string in native Java for curve secp256k1) and got an "error" comment from @President James Moveon Polk:

"That code is incorrect, as is the code you got it from. This line BigInteger k = kin.mod(p); is wrong, wrong, wrong. The scalar multiple should be taken mod the order of the group, which is not the prime over which the field is defined. The only reason you haven't been bitten by that bug is because you never tested it with a kin that was >= p. That's why you don't do crypto by hand. But anyway, the answer to your question is no. If you were exclusively using Java 8 (or maybe 7) you could find an internal sun. class and method that does scalar multiplication, but not beyond that.* "

I changed the source code a little and this is the final code (complete example):

import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.*;

public class DerivePublicKeyFromPrivateKeyCurveSecp256k1Final {
    // sourcecode available https://github.com/java-crypto/Stackoverflow/tree/master/DervicePublicKeyFromPrivateKeyCurveSecp256k1
    // get bouncycastle here: https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on/1.65
    // tested with version 15 1.65
    // java open jdk 11.0.5
    final static BigInteger FieldP_2 = BigInteger.TWO; // constant for scalar operations
    final static BigInteger FieldP_3 = BigInteger.valueOf(3); // constant for scalar operations

    public static void main(String[] args) throws GeneralSecurityException {
        System.out.println("Generate ECPublicKey from PrivateKey (String) for curve secp256k1 (final)");
        System.out.println("Check keys with https://gobittest.appspot.com/Address");
        // https://gobittest.appspot.com/Address
        String privateKey = "D12D2FACA9AD92828D89683778CB8DFCCDBD6C9E92F6AB7D6065E8AACC1FF6D6";
        String publicKeyExpected = "04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799";
        System.out.println("\nprivatekey given : " + privateKey);
        System.out.println("publicKeyExpected: " + publicKeyExpected);
        // routine with bouncy castle
        System.out.println("\nGenerate PublicKey from PrivateKey with BouncyCastle");
        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); // this ec curve is used for bitcoin operations
        org.bouncycastle.math.ec.ECPoint pointQ = spec.getG().multiply(new BigInteger(1, hexStringToByteArray(privateKey)));
        byte[] publickKeyByte = pointQ.getEncoded(false);
        String publicKeyBc = byteArrayToHexString(publickKeyByte);
        System.out.println("publicKeyExpected: " + publicKeyExpected);
        System.out.println("publicKey BC     : " + publicKeyBc);
        System.out.println("publicKeys match : " + publicKeyBc.contentEquals(publicKeyExpected));

        // regeneration of ECPublicKey with java native starts here
        System.out.println("\nGenerate PublicKey from PrivateKey with Java native routines");
        // the preset "303E.." only works for elliptic curve secp256k1
        // see answer by user dave_thompson_085
        // https://mcmap.net/q/2029845/-generate-ec-public-key-from-byte-array-private-key-in-native-java-7
        String privateKeyFull = "303E020100301006072A8648CE3D020106052B8104000A042730250201010420" +
                privateKey;
        byte[] privateKeyFullByte = hexStringToByteArray(privateKeyFull);
        System.out.println("privateKey full  : " + privateKeyFull);
        KeyFactory keyFactory = KeyFactory.getInstance("EC");
        PrivateKey privateKeyNative = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyFullByte));
        ECPrivateKey ecPrivateKeyNative = (ECPrivateKey) privateKeyNative;
        ECPublicKey ecPublicKeyNative = getPublicKey(ecPrivateKeyNative);
        byte[] ecPublicKeyNativeByte = ecPublicKeyNative.getEncoded();
        String publicKeyNativeFull = byteArrayToHexString(ecPublicKeyNativeByte);
        String publicKeyNativeHeader = publicKeyNativeFull.substring(0, 46);
        String publicKeyNativeKey = publicKeyNativeFull.substring(46, 176);
        System.out.println("ecPublicKeyFull  : " + publicKeyNativeFull);
        System.out.println("ecPublicKeyHeader: " + publicKeyNativeHeader);
        System.out.println("ecPublicKeyKey   : " + publicKeyNativeKey);
        System.out.println("publicKeyExpected: " + publicKeyExpected);
        System.out.println("publicKeys match : " + publicKeyNativeKey.contentEquals(publicKeyExpected));
    }

    private static String byteArrayToHexString(byte[] a) {
        StringBuilder sb = new StringBuilder(a.length * 2);
        for (byte b : a)
            sb.append(String.format("%02X", b));
        return sb.toString();
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

    // scalar operations for native java
    // see https://mcmap.net/q/1935487/-codes-to-generate-a-public-key-in-an-elliptic-curve-algorithm-using-a-given-private-key
    // written by author: SkateScout
    private static ECPoint doublePoint(final BigInteger p, final BigInteger a, final ECPoint R) {
        if (R.equals(ECPoint.POINT_INFINITY)) return R;
        BigInteger slope = (R.getAffineX().pow(2)).multiply(FieldP_3);
        slope = slope.add(a);
        slope = slope.multiply((R.getAffineY().multiply(FieldP_2)).modInverse(p));
        final BigInteger Xout = slope.pow(2).subtract(R.getAffineX().multiply(FieldP_2)).mod(p);
        final BigInteger Yout = (R.getAffineY().negate()).add(slope.multiply(R.getAffineX().subtract(Xout))).mod(p);
        return new ECPoint(Xout, Yout);
    }

    private static ECPoint addPoint(final BigInteger p, final BigInteger a, final ECPoint r, final ECPoint g) {
        if (r.equals(ECPoint.POINT_INFINITY)) return g;
        if (g.equals(ECPoint.POINT_INFINITY)) return r;
        if (r == g || r.equals(g)) return doublePoint(p, a, r);
        final BigInteger gX = g.getAffineX();
        final BigInteger sY = g.getAffineY();
        final BigInteger rX = r.getAffineX();
        final BigInteger rY = r.getAffineY();
        final BigInteger slope = (rY.subtract(sY)).multiply(rX.subtract(gX).modInverse(p)).mod(p);
        final BigInteger Xout = (slope.modPow(FieldP_2, p).subtract(rX)).subtract(gX).mod(p);
        BigInteger Yout = sY.negate().mod(p);
        Yout = Yout.add(slope.multiply(gX.subtract(Xout))).mod(p);
        return new ECPoint(Xout, Yout);
    }

    public static ECPoint scalmultNew(final ECParameterSpec params, final ECPoint g, final BigInteger kin) {
        EllipticCurve curve = params.getCurve();
        final ECField field = curve.getField();
        if (!(field instanceof ECFieldFp)) throw new UnsupportedOperationException(field.getClass().getCanonicalName());
        final BigInteger p = ((ECFieldFp) field).getP();
        final BigInteger a = curve.getA();
        ECPoint R = ECPoint.POINT_INFINITY;
        // value only valid for curve secp256k1, code taken from https://www.secg.org/sec2-v2.pdf,
        // see "Finally the order n of G and the cofactor are: n = "FF.."
        BigInteger SECP256K1_Q = params.getOrder();
        //BigInteger SECP256K1_Q = new BigInteger("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16);
        BigInteger k = kin.mod(SECP256K1_Q); // uses this !
        // BigInteger k = kin.mod(p); // do not use this ! wrong as per comment from President James Moveon Polk
        final int length = k.bitLength();
        final byte[] binarray = new byte[length];
        for (int i = 0; i <= length - 1; i++) {
            binarray[i] = k.mod(FieldP_2).byteValue();
            k = k.shiftRight(1);
        }
        for (int i = length - 1; i >= 0; i--) {
            R = doublePoint(p, a, R);
            if (binarray[i] == 1) R = addPoint(p, a, R, g);
        }
        return R;
    }

    public static ECPoint scalmultOrg(final EllipticCurve curve, final ECPoint g, final BigInteger kin) {
        final ECField field = curve.getField();
        if (!(field instanceof ECFieldFp)) throw new UnsupportedOperationException(field.getClass().getCanonicalName());
        final BigInteger p = ((ECFieldFp) field).getP();
        final BigInteger a = curve.getA();
        ECPoint R = ECPoint.POINT_INFINITY;
        // value only valid for curve secp256k1, code taken from https://www.secg.org/sec2-v2.pdf,
        // see "Finally the order n of G and the cofactor are: n = "FF.."
        BigInteger SECP256K1_Q = new BigInteger("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16);
        BigInteger k = kin.mod(SECP256K1_Q); // uses this !
        // wrong as per comment from President James Moveon Polk
        // BigInteger k = kin.mod(p); // do not use this !
        System.out.println(" SECP256K1_Q: " + SECP256K1_Q);
        System.out.println("           p: " + p);
        System.out.println("curve: " + curve.toString());
        final int length = k.bitLength();
        final byte[] binarray = new byte[length];
        for (int i = 0; i <= length - 1; i++) {
            binarray[i] = k.mod(FieldP_2).byteValue();
            k = k.shiftRight(1);
        }
        for (int i = length - 1; i >= 0; i--) {
            R = doublePoint(p, a, R);
            if (binarray[i] == 1) R = addPoint(p, a, R, g);
        }
        return R;
    }

    public static ECPublicKey getPublicKey(final ECPrivateKey pk) throws GeneralSecurityException {
        final ECParameterSpec params = pk.getParams();
        final ECPoint w = scalmultNew(params, pk.getParams().getGenerator(), pk.getS());
        //final ECPoint w = scalmult(params.getCurve(), pk.getParams().getGenerator(), pk.getS());
        final KeyFactory kg = KeyFactory.getInstance("EC");
        return (ECPublicKey) kg.generatePublic(new ECPublicKeySpec(w, params));
    }
}

Enjoy!

Cucurbit answered 17/4, 2020 at 10:39 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.