繁体   English   中英

使用给定私钥在椭圆曲线算法中生成公钥的代码

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

我需要使用 jdk 1.7 实现 ECC(椭圆曲线加密)算法。 我尝试使用充气城堡,sunEC,但它们都给出了错误和错误。 我的目标是使用私钥生成一条椭圆曲线,我将提供给系统。

因此,我需要使用 jdk1.7 获取使用给定私钥生成公钥的准确代码。 我使用的 IDE 是 eclipse。 我需要知道,除了私钥之外,我还应该获得哪些其他参数? 只提供一个曲线点和私钥就足够了吗?

有人可以帮我从私钥生成公钥吗? 我可以管理其余的实现。

有谁知道用java实现椭圆曲线密码的keys,请告诉我这段代码是否正确?

public class ECCrypt {

    private ECPoint curve_point;

      public ECCrypt(ECPoint curve_point) {
        this.curve_point = curve_point;
      }

public BigInteger makePublicKey(BigInteger privateKey) {
        ECPoint ecPublicKey = new ECPoint(curve_point);
        ecPublicKey.mult(privateKey);
        return ecPublicKey.pack();
}


public static void main(String[] argv) throws Exception {
        java.util.Random rnd = new java.util.Random();
        ECPoint cp = new ECPoint();
        cp.random(rnd);
        ECCrypt ec = new ECCrypt(cp);
        BigInteger priv = new BigInteger(255,rnd);
        BigInteger pub = ec.makePublicKey(priv);

}

谢谢!

我写了一个示例程序,输出如下:

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$

代码应该足够清晰,以便您可以理解这里做了什么。 请注意,您确实必须知道您的私钥是针对哪条曲线生成的,否则无法生成匹配的公钥。 示例代码使用了相当常用的secp256r1曲线。

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

我使用这种方法从 ECPrivateKey 恢复 ECPublicKey。 最后,主程序证明它有效。 它是没有任何外部库的纯 java。

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

}

@markwGenerate ECPublicKey from ECPrivateKey上的回答的更多改进,通过@bas-goossen从这里扩展了曲线名称检测: 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;
}

这是对@SkateScout 答案的改进。 我在另一个上下文中使用了标量例程,在 SO 上发布了它们( 从本地 Java 中的私钥字符串派生 EC 公钥用于曲线 secp256k1 ),并从@President James Moveon Polk 得到了“错误”评论:

"那个代码不正确,就像你从中得到的代码一样。这一行 BigInteger k = kin.mod(p); 是错误的,错误的,错误的。标量倍数应该取模组的顺序,这不是定义字段的素数。你没有被那个错误咬到的唯一原因是因为你从未用 >= p 的亲属测试过它。这就是为什么你不手动进行加密。但无论如何,您的问题的答案是否定的。如果您只使用 Java 8(或者可能是 7),您可以找到一个内部 sun.类和方法进行标量乘法,但不能超出此范围。*

我稍微更改了源代码,这是最终代码(完整示例):

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

享受!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM