从本机 Java 中的私钥字符串派生 EC public 密钥,用于曲线 secp256k1

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

我需要从没有任何第三方库的 "help" 的 EC 私钥字符串中派生出 EC Public 密钥。

私钥是外部生成和提供的,我需要获取Public密钥来生成比特币地址。由于我的项目正在运行 "offline" 我不需要像 Bouncy Castle 这样的库用于任何其他目的,所以我想删除它。

以下程序可以正常运行,并显示了(非常短的)使用 Bouncy Castle 获得解决方案的示例。第二部分是原生的Java解决方案,由so用户SkateScout的routines帮助,详情见他的回答.

请记住,此解决方案仅适用于椭圆曲线 "secp256k1"。您可以在 https://gobittest.appspot.com/Address.

上查看我的密钥对

我的问题:是否有任何其他解决方案可以避免标量运算的大量代码?

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

输出如下:

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

上面的代码已根据总统 James Moveon Polk 的评论进行了更正

我正在为我的问题提供最终解决方案,以防万一有人遇到同样的问题。感谢用户总裁 James Moveon Polk 和 dave_thompson_085 的指导。

我的 github-存储库 https://github.com/java-crypto/Whosebug/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/Whosebug/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
        // 
        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 
    // 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));
    }
}

这是输出:

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

如果有人需要另一条曲线的解决方案,这里是用户 Maarten Bodewes 的解决方案,用于生成 "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 ");
        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;
    }
}

尽情享受吧!