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

徘徊边缘 提交于 2020-04-30 07:09:07

问题


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:


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

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("create private key head for named curve");
        System.out.println("see https://stackoverflow.com/a/30471945/8166854");
        System.out.println("author: Maarten Bodewes\n");
        // input
        String curvename = "secp256k1";
        String privateKeyHeaderExpected = "303E020100301006072A8648CE3D020106052B8104000A042730250201010420";
        String privateKeyHeaderFromCurve = createPrivateKeyHeaderForNamedCurve(curvename);
        // output
        System.out.println("Curvename                    : " + curvename);
        System.out.println("expected private key header  : " + privateKeyHeaderExpected);
        System.out.println("calculated private key header: " + privateKeyHeaderFromCurve);
        System.out.println("private key headers matching : " + privateKeyHeaderExpected.contentEquals(privateKeyHeaderFromCurve));
    }

    private static String createPrivateKeyHeaderForNamedCurve(String name)
            throws NoSuchAlgorithmException,
            InvalidAlgorithmParameterException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
        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("%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;
    }
}

Enjoy !



来源:https://stackoverflow.com/questions/61069182/derive-ec-public-key-from-private-key-string-in-native-java-for-curve-secp256k1

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