从私钥字符串中在Java中原生生成secp256k1曲线的EC公钥。

huangapple go评论68阅读模式
英文:

Derive EC public key from private key string in native Java for curve secp256k1

问题

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

public class DerivePublicKeyFromPrivateKeyCurveSecp256k1 {
    // ... (imports and constants)

    public static void main(String[] args) throws GeneralSecurityException {
        // ... (main method content)

        // ... (bouncy castle code)

        // ... (native java routines)

        System.out.println("publicKeys match : " + publicKeyNativeKey.contentEquals(publicKeyExpected));
    }

    // ... (byteArrayToHexString, hexStringToByteArray, scalar operations)

    private static ECPoint scalmult(final EllipticCurve curve, final ECPoint g, final BigInteger kin) {
        // ... (scalar operations content)
    }

    public static ECPublicKey getPublicKey(final ECPrivateKey pk) throws GeneralSecurityException {
        // ... (getPublicKey method content)
    }
}

Please note that the provided code is quite extensive, and the translated code snippet might not contain all the details. If you encounter any issues or need further assistance, feel free to ask.

英文:

I need to derive an EC Public Key from an EC private key string without the "help" of any third party library.

The Private key is externally produced and provided and I need to get the Public Key to generate a Bitcoin address. As my project is working "offline" I do not need a library like Bouncy Castle for any other purpose, so I would like to eliminate it.

The following program is fully working and shows the (very short) example when working with Bouncy Castle to get a solution. The second part is the native Java solution with the kindly help from the routines by the so user SkateScout, for details see his answer https://stackoverflow.com/a/42797410/8166854.

Please keep in mind that this solution is working only for the Elliptic curve "secp256k1". You can check my keypair on https://gobittest.appspot.com/Address.

My question: is there any other solution available to avoid the mass of code for scalar operations?

import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.*;
public class DerivePublicKeyFromPrivateKeyCurveSecp256k1 {
// get bouncycastle here: https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on/1.65
// tested with version 15 1.65
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");
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 kecFactory = KeyFactory.getInstance("EC");
PrivateKey privateKeyNative = kecFactory.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 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;
// 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 !
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));
}
}

The output looks like:

Generate ECPublicKey from PrivateKey (String) for curve secp256k1
Check keys with https://gobittest.appspot.com/Address
privatekey given : D12D2FACA9AD92828D89683778CB8DFCCDBD6C9E92F6AB7D6065E8AACC1FF6D6
publicKeyExpected: 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
Generate PublicKey from PrivateKey with BouncyCastle
publicKeyExpected: 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
publicKey BC     : 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
publicKeys match : true
Generate PublicKey from PrivateKey with Java native routines
privateKey full  : 303E020100301006072A8648CE3D020106052B8104000A042730250201010420D12D2FACA9AD92828D89683778CB8DFCCDBD6C9E92F6AB7D6065E8AACC1FF6D6
ecPublicKeyFull  : 3056301006072A8648CE3D020106052B8104000A03420004661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
ecPublicKeyHeader: 3056301006072A8648CE3D020106052B8104000A034200
ecPublicKeyKey   : 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
publicKeyExpected: 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
publicKeys match : true

Code above corrected as commented from President James Moveon Polk

答案1

得分: 2

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 {
    final static BigInteger FieldP_2 = BigInteger.TWO;
    final static BigInteger FieldP_3 = BigInteger.valueOf(3);

    public static void main(String[] args) throws GeneralSecurityException {
        String privateKey = "D12D2FACA9AD92828D89683778CB8DFCCDBD6C9E92F6AB7D6065E8AACC1FF6D6";
        String publicKeyExpected = "04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799";

        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
        org.bouncycastle.math.ec.ECPoint pointQ = spec.getG().multiply(new BigInteger(1, hexStringToByteArray(privateKey)));
        byte[] publickKeyByte = pointQ.getEncoded(false);
        String publicKeyBc = byteArrayToHexString(publickKeyByte);

        String privateKeyFull = "303E020100301006072A8648CE3D020106052B8104000A042730250201010420" +
                privateKey;
        byte[] privateKeyFullByte = hexStringToByteArray(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 publicKeyNativeKey = publicKeyNativeFull.substring(46, 176);

        System.out.println("publicKeyExpected: " + publicKeyExpected);
        System.out.println("publicKey BC     : " + publicKeyBc);
        System.out.println("publicKeys match : " + publicKeyBc.contentEquals(publicKeyExpected));
        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;
    }

    private static ECPoint doublePoint(final BigInteger p, final BigInteger a, final ECPoint R) {
        // ... (omitted for brevity)
    }

    private static ECPoint addPoint(final BigInteger p, final BigInteger a, final ECPoint r, final ECPoint g) {
        // ... (omitted for brevity)
    }

    public static ECPoint scalmultNew(final ECParameterSpec params, final ECPoint g, final BigInteger kin) {
        // ... (omitted for brevity)
    }

    public static ECPoint scalmultOrg(final EllipticCurve curve, final ECPoint g, final BigInteger kin) {
        // ... (omitted for brevity)
    }

    public static ECPublicKey getPublicKey(final ECPrivateKey pk) throws GeneralSecurityException {
        // ... (omitted for brevity)
    }
}
英文:

I'm providing the final solution for my problem, just in case anybody encounters the same problem. Thanks to user President James Moveon Polk and dave_thompson_085 for guiding.

The source code is additionally available on my github-repository https://github.com/java-crypto/Stackoverflow/tree/master/DervicePublicKeyFromPrivateKeyCurveSecp256k1.

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(&quot;Generate ECPublicKey from PrivateKey (String) for curve secp256k1 (final)&quot;);
System.out.println(&quot;Check keys with https://gobittest.appspot.com/Address&quot;);
// https://gobittest.appspot.com/Address
String privateKey = &quot;D12D2FACA9AD92828D89683778CB8DFCCDBD6C9E92F6AB7D6065E8AACC1FF6D6&quot;;
String publicKeyExpected = &quot;04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799&quot;;
System.out.println(&quot;\nprivatekey given : &quot; + privateKey);
System.out.println(&quot;publicKeyExpected: &quot; + publicKeyExpected);
// routine with bouncy castle
System.out.println(&quot;\nGenerate PublicKey from PrivateKey with BouncyCastle&quot;);
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(&quot;secp256k1&quot;); // 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(&quot;publicKeyExpected: &quot; + publicKeyExpected);
System.out.println(&quot;publicKey BC     : &quot; + publicKeyBc);
System.out.println(&quot;publicKeys match : &quot; + publicKeyBc.contentEquals(publicKeyExpected));
// regeneration of ECPublicKey with java native starts here
System.out.println(&quot;\nGenerate PublicKey from PrivateKey with Java native routines&quot;);
// the preset &quot;303E..&quot; 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 = &quot;303E020100301006072A8648CE3D020106052B8104000A042730250201010420&quot; +
privateKey;
byte[] privateKeyFullByte = hexStringToByteArray(privateKeyFull);
System.out.println(&quot;privateKey full  : &quot; + privateKeyFull);
KeyFactory keyFactory = KeyFactory.getInstance(&quot;EC&quot;);
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(&quot;ecPublicKeyFull  : &quot; + publicKeyNativeFull);
System.out.println(&quot;ecPublicKeyHeader: &quot; + publicKeyNativeHeader);
System.out.println(&quot;ecPublicKeyKey   : &quot; + publicKeyNativeKey);
System.out.println(&quot;publicKeyExpected: &quot; + publicKeyExpected);
System.out.println(&quot;publicKeys match : &quot; + publicKeyNativeKey.contentEquals(publicKeyExpected));
}
private static String byteArrayToHexString(byte[] a) {
StringBuilder sb = new StringBuilder(a.length * 2);
for (byte b : a)
sb.append(String.format(&quot;%02X&quot;, 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 &lt; len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) &lt;&lt; 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 &quot;Finally the order n of G and the cofactor are: n = &quot;FF..&quot;
BigInteger SECP256K1_Q = params.getOrder();
//BigInteger SECP256K1_Q = new BigInteger(&quot;00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141&quot;,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 &lt;= length - 1; i++) {
binarray[i] = k.mod(FieldP_2).byteValue();
k = k.shiftRight(1);
}
for (int i = length - 1; i &gt;= 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 &quot;Finally the order n of G and the cofactor are: n = &quot;FF..&quot;
BigInteger SECP256K1_Q = new BigInteger(&quot;00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141&quot;,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(&quot; SECP256K1_Q: &quot; + SECP256K1_Q);
System.out.println(&quot;           p: &quot; + p);
System.out.println(&quot;curve: &quot; + curve.toString());
final int length = k.bitLength();
final byte[] binarray = new byte[length];
for (int i = 0; i &lt;= length - 1; i++) {
binarray[i] = k.mod(FieldP_2).byteValue();
k = k.shiftRight(1);
}
for (int i = length - 1; i &gt;= 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(&quot;EC&quot;);
return (ECPublicKey) kg.generatePublic(new ECPublicKeySpec(w, params));
}
}

This is the output:

Generate ECPublicKey from PrivateKey (String) for curve secp256k1 (final)
Check keys with https://gobittest.appspot.com/Address
privatekey given : D12D2FACA9AD92828D89683778CB8DFCCDBD6C9E92F6AB7D6065E8AACC1FF6D6
publicKeyExpected: 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
Generate PublicKey from PrivateKey with BouncyCastle
publicKeyExpected: 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
publicKey BC     : 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
publicKeys match : true
Generate PublicKey from PrivateKey with Java native routines
privateKey full  : 303E020100301006072A8648CE3D020106052B8104000A042730250201010420D12D2FACA9AD92828D89683778CB8DFCCDBD6C9E92F6AB7D6065E8AACC1FF6D6
ecPublicKeyFull  : 3056301006072A8648CE3D020106052B8104000A03420004661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
ecPublicKeyHeader: 3056301006072A8648CE3D020106052B8104000A034200
ecPublicKeyKey   : 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
publicKeyExpected: 04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799
publicKeys match : true

If anyone needs this solution for another curve here is a solution from user Maarten Bodewes to generate the "magic string":

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
public class CreateHeadForNamedCurveSo {
public static void main(String[] args) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
System.out.println(&quot;create private key head for named curve&quot;);
System.out.println(&quot;see https://stackoverflow.com/a/30471945/8166854&quot;);
System.out.println(&quot;author: Maarten Bodewes\n&quot;);
// input
String curvename = &quot;secp256k1&quot;;
String privateKeyHeaderExpected = &quot;303E020100301006072A8648CE3D020106052B8104000A042730250201010420&quot;;
String privateKeyHeaderFromCurve = createPrivateKeyHeaderForNamedCurve(curvename);
// output
System.out.println(&quot;Curvename                    : &quot; + curvename);
System.out.println(&quot;expected private key header  : &quot; + privateKeyHeaderExpected);
System.out.println(&quot;calculated private key header: &quot; + privateKeyHeaderFromCurve);
System.out.println(&quot;private key headers matching : &quot; + privateKeyHeaderExpected.contentEquals(privateKeyHeaderFromCurve));
}
private static String createPrivateKeyHeaderForNamedCurve(String name)
throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(&quot;EC&quot;);
ECGenParameterSpec m = new ECGenParameterSpec(name);
kpg.initialize(m);
ECPrivateKey privateKey = (ECPrivateKey) kpg.generateKeyPair().getPrivate();
PKCS8EncodedKeySpec pKCS8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
String pKCS8EncodedKeySpecString = byteArrayToHexString(pKCS8EncodedKeySpec.getEncoded());
return pKCS8EncodedKeySpecString.substring(0,64);
}
private static String byteArrayToHexString(byte[] a) {
StringBuilder sb = new StringBuilder(a.length * 2);
for (byte b : a)
sb.append(String.format(&quot;%02X&quot;, 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 &lt; len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) &lt;&lt; 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
}

Enjoy !

huangapple
  • 本文由 发表于 2020年4月7日 05:21:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/61069182.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定