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

这一生的挚爱 提交于 2019-12-03 01:47:24

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!");
    }
  }
}

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));
}

}

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;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!