Java 使用给定私钥在椭圆曲线算法中生成公钥的代码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19673962/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 19:17:49  来源:igfitidea点击:

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

javabouncycastleelliptic-curve

提问by user2934766

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.

我需要使用 jdk 1.7 实现 ECC(椭圆曲线加密)算法。我尝试使用充气城堡,sunEC,但它们都给出了错误和错误。我的目标是使用私钥生成一条椭圆曲线,我将提供给系统。

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?

因此,我需要使用 jdk1.7 获取使用给定私钥生成公钥的准确代码。我使用的 IDE 是 eclipse。我需要知道,除了私钥之外,我还应该获得哪些其他参数?只提供一个曲线点和私钥就足够了吗?

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?

有谁知道用java实现椭圆曲线密码的keys,请告诉我这段代码是否正确?

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!

谢谢!

回答by juhovh

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 secp256r1curve, which is quite commonly used.

代码应该足够清晰,以便您可以理解这里做了什么。请注意,您确实必须知道您的私钥是针对哪条曲线生成的,否则无法生成匹配的公钥。示例代码使用了secp256r1非常常用的曲线。

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

回答by SkateScout

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.

我使用这种方法从 ECPrivateKey 恢复 ECPublicKey。最后,主程序证明它有效。它是没有任何外部库的纯 java。

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

}

}

回答by plajko

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

一些改进的答案@markw从ECPrivateKey生成ECPublicKey,用曲线名检测扩展@浅古森从这里:如何从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;
}

回答by Michael Fehr

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:

这是对@SkateScout 答案的改进。我在另一个上下文中使用了标量例程,在 SO 上发布了它们(从本地 Java 中的私钥字符串派生 EC 公钥用于曲线 secp256k1),并从@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.* "

"那个代码不正确,就像你从中得到的代码一样。这一行 BigInteger k = kin.mod(p); 是错误的,错误的,错误的。标量倍数应该取模组的顺序,这不是定义字段的素数。你没有被那个错误咬到的唯一原因是因为你从未用 >= p 的亲属测试过它。这就是为什么你不手动进行加密。但无论如何,您的问题的答案是否定的。如果您只使用 Java 8(或者可能是 7),您可以找到一个内部 sun.类和方法进行标量乘法,但不能超出此范围。*

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://stackoverflow.com/questions/48832170/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://stackoverflow.com/a/42797410/8166854
    // 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!

享受!