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

后端 未结 4 879
天涯浪人
天涯浪人 2020-12-29 17:34

I need to implement ECC (Elliptic Curve Cryptography) algorithm using jdk 1.7. I tried using bouncy castle, sunEC, but all of them gave errors and errors. My target is to ge

相关标签:
4条回答
  • 2020-12-29 17:57

    I wrote a sample program that outputs the following:

    FL261:java jvah$ javac -cp bcprov-ext-jdk15on-149.jar ECTest.java
    FL261:java jvah$ java -cp bcprov-ext-jdk15on-149.jar:. ECTest
    Private key: 7ba78909571fbc336b2b94054dfb745a6b0776ff36a8fa98a598dc32cb83cc8e
    Public key: 035b9e4a6148c9f9b08b573871ac66a832e6e9f63cf117545523a45b8017b7c43f
    Calculated public key: 035b9e4a6148c9f9b08b573871ac66a832e6e9f63cf117545523a45b8017b7c43f
    Congratulations, public keys match!
    FL261:java jvah$
    

    The code should be clear enough so that you can understand what is done here. Please note that you really have to know which curve your private key is generated for, otherwise it is impossible to generate the matching public key. The sample code uses secp256r1 curve, which is quite commonly used.

    import java.math.BigInteger;
    import java.security.SecureRandom;
    
    import org.bouncycastle.asn1.x9.X9ECParameters;
    import org.bouncycastle.asn1.sec.SECNamedCurves;
    import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
    import org.bouncycastle.crypto.params.ECDomainParameters;
    import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
    import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
    import org.bouncycastle.crypto.params.ECPublicKeyParameters;
    import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
    import org.bouncycastle.math.ec.ECPoint;
    
    class ECTest {
      public static String toHex(byte[] data) {
        StringBuilder sb = new StringBuilder();
        for (byte b: data) {
          sb.append(String.format("%02x", b&0xff));
        }
        return sb.toString();
      }
    
      public static void main(String[] argv) {
        // Get domain parameters for example curve secp256r1
        X9ECParameters ecp = SECNamedCurves.getByName("secp256r1");
        ECDomainParameters domainParams = new ECDomainParameters(ecp.getCurve(),
                                                                 ecp.getG(), ecp.getN(), ecp.getH(),
                                                                 ecp.getSeed());
    
        // Generate a private key and a public key
        AsymmetricCipherKeyPair keyPair;
        ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, new SecureRandom());
        ECKeyPairGenerator generator = new ECKeyPairGenerator();
        generator.init(keyGenParams);
        keyPair = generator.generateKeyPair();
    
        ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();
        ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();
        byte[] privateKeyBytes = privateKey.getD().toByteArray();
    
        // First print our generated private key and public key
        System.out.println("Private key: " + toHex(privateKeyBytes));
        System.out.println("Public key: " + toHex(publicKey.getQ().getEncoded(true)));
    
        // Then calculate the public key only using domainParams.getG() and private key
        ECPoint Q = domainParams.getG().multiply(new BigInteger(privateKeyBytes));
        System.out.println("Calculated public key: " + toHex(Q.getEncoded(true)));
    
        // The calculated public key and generated public key should always match
        if (!toHex(publicKey.getQ().getEncoded(true)).equals(toHex(Q.getEncoded(true)))) {
          System.out.println("ERROR: Public keys do not match!");
        } else {
          System.out.println("Congratulations, public keys match!");
        }
      }
    }
    
    0 讨论(0)
  • 2020-12-29 18:00

    This is an improvement to the answer from @SkateScout. I used the scalar routines in another context, published them here on SO (Derive EC public key from private key string in native Java for curve secp256k1) and got an "error" comment from @President James Moveon Polk:

    "That code is incorrect, as is the code you got it from. This line BigInteger k = kin.mod(p); is wrong, wrong, wrong. The scalar multiple should be taken mod the order of the group, which is not the prime over which the field is defined. The only reason you haven't been bitten by that bug is because you never tested it with a kin that was >= p. That's why you don't do crypto by hand. But anyway, the answer to your question is no. If you were exclusively using Java 8 (or maybe 7) you could find an internal sun. class and method that does scalar multiplication, but not beyond that.* "

    I changed the source code a little and this is the final code (complete example):

    import org.bouncycastle.jce.ECNamedCurveTable;
    import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
    import java.math.BigInteger;
    import java.security.GeneralSecurityException;
    import java.security.KeyFactory;
    import java.security.PrivateKey;
    import java.security.interfaces.ECPrivateKey;
    import java.security.interfaces.ECPublicKey;
    import java.security.spec.*;
    
    public class DerivePublicKeyFromPrivateKeyCurveSecp256k1Final {
        // sourcecode available https://github.com/java-crypto/Stackoverflow/tree/master/DervicePublicKeyFromPrivateKeyCurveSecp256k1
        // get bouncycastle here: https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on/1.65
        // tested with version 15 1.65
        // java open jdk 11.0.5
        final static BigInteger FieldP_2 = BigInteger.TWO; // constant for scalar operations
        final static BigInteger FieldP_3 = BigInteger.valueOf(3); // constant for scalar operations
    
        public static void main(String[] args) throws GeneralSecurityException {
            System.out.println("Generate ECPublicKey from PrivateKey (String) for curve secp256k1 (final)");
            System.out.println("Check keys with https://gobittest.appspot.com/Address");
            // https://gobittest.appspot.com/Address
            String privateKey = "D12D2FACA9AD92828D89683778CB8DFCCDBD6C9E92F6AB7D6065E8AACC1FF6D6";
            String publicKeyExpected = "04661BA57FED0D115222E30FE7E9509325EE30E7E284D3641E6FB5E67368C2DB185ADA8EFC5DC43AF6BF474A41ED6237573DC4ED693D49102C42FFC88510500799";
            System.out.println("\nprivatekey given : " + privateKey);
            System.out.println("publicKeyExpected: " + publicKeyExpected);
            // routine with bouncy castle
            System.out.println("\nGenerate PublicKey from PrivateKey with BouncyCastle");
            ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); // this ec curve is used for bitcoin operations
            org.bouncycastle.math.ec.ECPoint pointQ = spec.getG().multiply(new BigInteger(1, hexStringToByteArray(privateKey)));
            byte[] publickKeyByte = pointQ.getEncoded(false);
            String publicKeyBc = byteArrayToHexString(publickKeyByte);
            System.out.println("publicKeyExpected: " + publicKeyExpected);
            System.out.println("publicKey BC     : " + publicKeyBc);
            System.out.println("publicKeys match : " + publicKeyBc.contentEquals(publicKeyExpected));
    
            // regeneration of ECPublicKey with java native starts here
            System.out.println("\nGenerate PublicKey from PrivateKey with Java native routines");
            // the preset "303E.." only works for elliptic curve secp256k1
            // see answer by user dave_thompson_085
            // https://stackoverflow.com/questions/48832170/generate-ec-public-key-from-byte-array-private-key-in-native-java-7
            String privateKeyFull = "303E020100301006072A8648CE3D020106052B8104000A042730250201010420" +
                    privateKey;
            byte[] privateKeyFullByte = hexStringToByteArray(privateKeyFull);
            System.out.println("privateKey full  : " + privateKeyFull);
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            PrivateKey privateKeyNative = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyFullByte));
            ECPrivateKey ecPrivateKeyNative = (ECPrivateKey) privateKeyNative;
            ECPublicKey ecPublicKeyNative = getPublicKey(ecPrivateKeyNative);
            byte[] ecPublicKeyNativeByte = ecPublicKeyNative.getEncoded();
            String publicKeyNativeFull = byteArrayToHexString(ecPublicKeyNativeByte);
            String publicKeyNativeHeader = publicKeyNativeFull.substring(0, 46);
            String publicKeyNativeKey = publicKeyNativeFull.substring(46, 176);
            System.out.println("ecPublicKeyFull  : " + publicKeyNativeFull);
            System.out.println("ecPublicKeyHeader: " + publicKeyNativeHeader);
            System.out.println("ecPublicKeyKey   : " + publicKeyNativeKey);
            System.out.println("publicKeyExpected: " + publicKeyExpected);
            System.out.println("publicKeys match : " + publicKeyNativeKey.contentEquals(publicKeyExpected));
        }
    
        private static String byteArrayToHexString(byte[] a) {
            StringBuilder sb = new StringBuilder(a.length * 2);
            for (byte b : a)
                sb.append(String.format("%02X", b));
            return sb.toString();
        }
    
        public static byte[] hexStringToByteArray(String s) {
            int len = s.length();
            byte[] data = new byte[len / 2];
            for (int i = 0; i < len; i += 2) {
                data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                        + Character.digit(s.charAt(i + 1), 16));
            }
            return data;
        }
    
        // scalar operations for native java
        // see https://stackoverflow.com/a/42797410/8166854
        // written by author: SkateScout
        private static ECPoint doublePoint(final BigInteger p, final BigInteger a, final ECPoint R) {
            if (R.equals(ECPoint.POINT_INFINITY)) return R;
            BigInteger slope = (R.getAffineX().pow(2)).multiply(FieldP_3);
            slope = slope.add(a);
            slope = slope.multiply((R.getAffineY().multiply(FieldP_2)).modInverse(p));
            final BigInteger Xout = slope.pow(2).subtract(R.getAffineX().multiply(FieldP_2)).mod(p);
            final BigInteger Yout = (R.getAffineY().negate()).add(slope.multiply(R.getAffineX().subtract(Xout))).mod(p);
            return new ECPoint(Xout, Yout);
        }
    
        private static ECPoint addPoint(final BigInteger p, final BigInteger a, final ECPoint r, final ECPoint g) {
            if (r.equals(ECPoint.POINT_INFINITY)) return g;
            if (g.equals(ECPoint.POINT_INFINITY)) return r;
            if (r == g || r.equals(g)) return doublePoint(p, a, r);
            final BigInteger gX = g.getAffineX();
            final BigInteger sY = g.getAffineY();
            final BigInteger rX = r.getAffineX();
            final BigInteger rY = r.getAffineY();
            final BigInteger slope = (rY.subtract(sY)).multiply(rX.subtract(gX).modInverse(p)).mod(p);
            final BigInteger Xout = (slope.modPow(FieldP_2, p).subtract(rX)).subtract(gX).mod(p);
            BigInteger Yout = sY.negate().mod(p);
            Yout = Yout.add(slope.multiply(gX.subtract(Xout))).mod(p);
            return new ECPoint(Xout, Yout);
        }
    
        public static ECPoint scalmultNew(final ECParameterSpec params, final ECPoint g, final BigInteger kin) {
            EllipticCurve curve = params.getCurve();
            final ECField field = curve.getField();
            if (!(field instanceof ECFieldFp)) throw new UnsupportedOperationException(field.getClass().getCanonicalName());
            final BigInteger p = ((ECFieldFp) field).getP();
            final BigInteger a = curve.getA();
            ECPoint R = ECPoint.POINT_INFINITY;
            // value only valid for curve secp256k1, code taken from https://www.secg.org/sec2-v2.pdf,
            // see "Finally the order n of G and the cofactor are: n = "FF.."
            BigInteger SECP256K1_Q = params.getOrder();
            //BigInteger SECP256K1_Q = new BigInteger("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16);
            BigInteger k = kin.mod(SECP256K1_Q); // uses this !
            // BigInteger k = kin.mod(p); // do not use this ! wrong as per comment from President James Moveon Polk
            final int length = k.bitLength();
            final byte[] binarray = new byte[length];
            for (int i = 0; i <= length - 1; i++) {
                binarray[i] = k.mod(FieldP_2).byteValue();
                k = k.shiftRight(1);
            }
            for (int i = length - 1; i >= 0; i--) {
                R = doublePoint(p, a, R);
                if (binarray[i] == 1) R = addPoint(p, a, R, g);
            }
            return R;
        }
    
        public static ECPoint scalmultOrg(final EllipticCurve curve, final ECPoint g, final BigInteger kin) {
            final ECField field = curve.getField();
            if (!(field instanceof ECFieldFp)) throw new UnsupportedOperationException(field.getClass().getCanonicalName());
            final BigInteger p = ((ECFieldFp) field).getP();
            final BigInteger a = curve.getA();
            ECPoint R = ECPoint.POINT_INFINITY;
            // value only valid for curve secp256k1, code taken from https://www.secg.org/sec2-v2.pdf,
            // see "Finally the order n of G and the cofactor are: n = "FF.."
            BigInteger SECP256K1_Q = new BigInteger("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16);
            BigInteger k = kin.mod(SECP256K1_Q); // uses this !
            // wrong as per comment from President James Moveon Polk
            // BigInteger k = kin.mod(p); // do not use this !
            System.out.println(" SECP256K1_Q: " + SECP256K1_Q);
            System.out.println("           p: " + p);
            System.out.println("curve: " + curve.toString());
            final int length = k.bitLength();
            final byte[] binarray = new byte[length];
            for (int i = 0; i <= length - 1; i++) {
                binarray[i] = k.mod(FieldP_2).byteValue();
                k = k.shiftRight(1);
            }
            for (int i = length - 1; i >= 0; i--) {
                R = doublePoint(p, a, R);
                if (binarray[i] == 1) R = addPoint(p, a, R, g);
            }
            return R;
        }
    
        public static ECPublicKey getPublicKey(final ECPrivateKey pk) throws GeneralSecurityException {
            final ECParameterSpec params = pk.getParams();
            final ECPoint w = scalmultNew(params, pk.getParams().getGenerator(), pk.getS());
            //final ECPoint w = scalmult(params.getCurve(), pk.getParams().getGenerator(), pk.getS());
            final KeyFactory kg = KeyFactory.getInstance("EC");
            return (ECPublicKey) kg.generatePublic(new ECPublicKeySpec(w, params));
        }
    }
    

    Enjoy!

    0 讨论(0)
  • 2020-12-29 18:01

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

    public static ECPublicKey publicFromPrivate(final ECPrivateKey privateKey) throws Exception {
        ECParameterSpec params = privateKey.getParams();
        org.bouncycastle.jce.spec.ECParameterSpec bcSpec = org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util
            .convertSpec(params, false);
        org.bouncycastle.math.ec.ECPoint q = bcSpec.getG().multiply(privateKey.getS());
        org.bouncycastle.math.ec.ECPoint bcW = bcSpec.getCurve().decodePoint(q.getEncoded(false));
        ECPoint w = new ECPoint(
            bcW.getAffineXCoord().toBigInteger(),
            bcW.getAffineYCoord().toBigInteger());
        ECPublicKeySpec keySpec = new ECPublicKeySpec(w, tryFindNamedCurveSpec(params));
        return (ECPublicKey) KeyFactory
            .getInstance("EC", org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME)
            .generatePublic(keySpec);
    }
    
    @SuppressWarnings("unchecked")
    public static ECParameterSpec tryFindNamedCurveSpec(ECParameterSpec params) {
        org.bouncycastle.jce.spec.ECParameterSpec bcSpec
            = org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util.convertSpec(params, false);
        for (Object name : Collections.list(org.bouncycastle.jce.ECNamedCurveTable.getNames())) {
            org.bouncycastle.jce.spec.ECNamedCurveParameterSpec bcNamedSpec
                = org.bouncycastle.jce.ECNamedCurveTable.getParameterSpec((String) name);
            if (bcNamedSpec.getN().equals(bcSpec.getN())
                && bcNamedSpec.getH().equals(bcSpec.getH())
                && bcNamedSpec.getCurve().equals(bcSpec.getCurve())
                && bcNamedSpec.getG().equals(bcSpec.getG())) {
                return new org.bouncycastle.jce.spec.ECNamedCurveSpec(
                    bcNamedSpec.getName(),
                    bcNamedSpec.getCurve(),
                    bcNamedSpec.getG(),
                    bcNamedSpec.getN(),
                    bcNamedSpec.getH(),
                    bcNamedSpec.getSeed());
            }
        }
        return params;
    }
    
    0 讨论(0)
  • 2020-12-29 18:06

    i use this method to recover ECPublicKey from ECPrivateKey. At the end the main Routine prove that it works. It is plain java without any external library.

    public final class MULT {
    private static ECPoint doublePoint(final BigInteger p, final BigInteger a, final ECPoint R) {
        if (R.equals(ECPoint.POINT_INFINITY)) return R;
        BigInteger slope = (R.getAffineX().pow(2)).multiply(FieldP._3);
        slope = slope.add(a);
        slope = slope.multiply((R.getAffineY().multiply(FieldP._2)).modInverse(p));
        final BigInteger Xout = slope.pow(2).subtract(R.getAffineX().multiply(FieldP._2)).mod(p);
        final BigInteger Yout = (R.getAffineY().negate()).add(slope.multiply(R.getAffineX().subtract(Xout))).mod(p);
        return new ECPoint(Xout, Yout);
    }
    
    private static ECPoint addPoint   (final BigInteger p, final BigInteger a, final ECPoint r, final ECPoint g) {
        if (r.equals(ECPoint.POINT_INFINITY)) return g;
        if (g.equals(ECPoint.POINT_INFINITY)) return r;
        if (r==g || r.equals(g)) return doublePoint(p, a, r);
        final BigInteger gX    = g.getAffineX();
        final BigInteger sY    = g.getAffineY();
        final BigInteger rX    = r.getAffineX();
        final BigInteger rY    = r.getAffineY();
        final BigInteger slope = (rY.subtract(sY)).multiply(rX.subtract(gX).modInverse(p)).mod(p);
        final BigInteger Xout  = (slope.modPow(FieldP._2, p).subtract(rX)).subtract(gX).mod(p);
        BigInteger Yout =   sY.negate().mod(p);
        Yout = Yout.add(slope.multiply(gX.subtract(Xout))).mod(p);
        return new ECPoint(Xout, Yout);
    }
    
    public static ECPoint scalmult   (final EllipticCurve curve, final ECPoint g, final BigInteger kin) {
        final ECField         field    = curve.getField();
        if(!(field instanceof ECFieldFp)) throw new UnsupportedOperationException(field.getClass().getCanonicalName());
        final BigInteger p = ((ECFieldFp)field).getP();
        final BigInteger a = curve.getA();
        ECPoint R = ECPoint.POINT_INFINITY;
        BigInteger k = kin.mod(p);
        final int length = k.bitLength();
        final byte[] binarray = new byte[length];
        for(int i=0;i<=length-1;i++){
            binarray[i] = k.mod(FieldP._2).byteValue();
            k = k.shiftRight(1);
        }
        for(int i = length-1;i >= 0;i--){
            R = doublePoint(p, a, R);
            if(binarray[i]== 1) R = addPoint(p, a, R, g);
        }
        return R;
    }
    
    public static ECPublicKey getPublicKey(final ECPrivateKey pk) throws GeneralSecurityException {
        final ECParameterSpec params = pk.getParams();
        final ECPoint w = scalmult(params.getCurve(), pk.getParams().getGenerator(), pk.getS());
        final KeyFactory kg = KeyFactory.getInstance("EC");
        return (ECPublicKey)kg.generatePublic (new ECPublicKeySpec (w, params));
    }
    

    }

    0 讨论(0)
提交回复
热议问题