问题
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