【问题标题】:How to make a Bouncy Castle ECPublicKey如何制作充气城堡 ECPublicKey
【发布时间】:2015-10-19 15:44:31
【问题描述】:

我知道曲线名称 (secp256k1) 以及 EC 公钥的 X 和 Y 坐标。

我如何用它们制作org.bouncycastle.jce.interfaces.ECPublicKey?

我读过https://stackoverflow.com/a/29355749/5453873,但那里的代码使用java.security... 而不是org.bouncycastle...,并且ECPublicKey 是org.bouncycastle... 中的一个接口,而不是一个可实例化的类。

【问题讨论】:

  • 你真的需要使用 org.bouncycastle.* 类吗?我希望您最好尝试将 JCE API 与 BC 一起用作提供者。或者,如果您不介意对 BC 的直接依赖,那么如果您直接使用轻量级(即非 JCE)API,事情可能会更简单。
  • @PeterDettman 有用于 bouncycastle 的非 JCE API?我正在使用 BC 的 ECIES - 这是否意味着我应该坚持使用 JCE API?
  • 请不要忘记指明语言/运行时 (Java) 以及其他标签,例如密码学。关注bouncycastle的人并不多。

标签: java cryptography bouncycastle


【解决方案1】:

使用 Bouncy Castle 生成 ECPublicKey

​​>

这会生成在 JCE/JCA 中使用的 EC 公钥。 Bouncy Castle 提供商可以直接使用这些软件密钥。否则 Bouncy 仅用于生成生成公钥所需的参数。

package nl.owlstead.stackoverflow;

import static java.nio.charset.StandardCharsets.US_ASCII;

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;

import javax.crypto.Cipher;

import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.util.encoders.Hex;

public class ECPublicKeyFactory {

    public static void main(String[] args) throws Exception {

        String name = "secp256r1";

        Security.addProvider(new BouncyCastleProvider());

        // === NOT PART OF THE CODE, JUST GETTING TEST VECTOR ===
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
        ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec(name);
        kpg.initialize(ecGenParameterSpec);
        ECPublicKey key = (ECPublicKey) kpg.generateKeyPair().getPublic();
        byte[] x = key.getW().getAffineX().toByteArray();
        byte[] y = key.getW().getAffineY().toByteArray();

        // === here the magic happens ===
        KeyFactory eckf = KeyFactory.getInstance("EC");
        ECPoint point = new ECPoint(new BigInteger(1, x), new BigInteger(1, y));
        ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec(name);
        ECParameterSpec spec = new ECNamedCurveSpec(name, parameterSpec.getCurve(), parameterSpec.getG(), parameterSpec.getN(), parameterSpec.getH(), parameterSpec.getSeed());
        ECPublicKey ecPublicKey = (ECPublicKey) eckf.generatePublic(new ECPublicKeySpec(point, spec));
        System.out.println(ecPublicKey.getClass().getName());

        // === test 123 ===
        Cipher ecies = Cipher.getInstance("ECIESwithAES", "BC");
        ecies.init(Cipher.ENCRYPT_MODE, ecPublicKey);
        byte[] ct = ecies.doFinal("owlstead".getBytes(US_ASCII));
        System.out.println(Hex.toHexString(ct));
    }
}

生成充气城堡 ECPublicKeyParameters

最初我认为需要 Bouncy Castle 特定密钥,因此以下代码生成了用于 Bouncy Castle 轻量级 API 的 EC 公钥。

package nl.owlstead.stackoverflow;

import java.math.BigInteger;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECNamedDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

public class BC_EC_KeyCreator {

    public static void main(String[] args) throws Exception {

        String name = "secp256r1";

        // === NOT PART OF THE CODE, JUST GETTING TEST VECTOR ===
        Security.addProvider(new BouncyCastleProvider());
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
        kpg.initialize(new ECGenParameterSpec(name));
        ECPublicKey key = (ECPublicKey) kpg.generateKeyPair().getPublic();
        byte[] x = key.getW().getAffineX().toByteArray();
        byte[] y = key.getW().getAffineY().toByteArray();

        // assumes that x and y are (unsigned) big endian encoded
        BigInteger xbi = new BigInteger(1, x);
        BigInteger ybi = new BigInteger(1, y);
        X9ECParameters x9 = ECNamedCurveTable.getByName(name);
        ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
        ECCurve curve = x9.getCurve();
        ECPoint point = curve.createPoint(xbi, ybi);
        ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
                x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
        ECPublicKeyParameters pubKey = new ECPublicKeyParameters(point, dParams);
        System.out.println(pubKey);

        // some additional encoding tricks
        byte[] compressed = point.getEncoded(true);
        System.out.println(Hex.toHexString(compressed));
        byte[] uncompressed = point.getEncoded(false);
        System.out.println(Hex.toHexString(uncompressed));
    }
}

这很棘手,因为我不想包含任何 JCE 特定代码,而且X9ECParameters 不是ECDomainParameters 的子类。所以我使用了从 Bouncy Castle 代码库中其他地方复制的 ECNamedDomainParameters 的转换。

【讨论】:

  • 感谢您的工作。使用 bcpkix-jdk15on-152.jar 和 bcprov-jdk15on-152.jar org.bouncycastle.jce.ECNameCurveTable 没有方法 getByName 也没有 getOID,所以代码不会编译。
  • 另外,org.bouncycastle.crypto.params.ECPublicKeyParameters 不是 org.bouncycastle.jce.interfaces.ECPublicKey,我认为我需要在“ECIESwithAES”javax.crypto.Cipher 上调用 init。
  • 感谢您的工作 - 我还没有足够的代表来支持您的答案,但我会的。我找到了一种更简单的方法来满足我的需要。
【解决方案2】:

在下面的代码中,encoded 包含0x04,后跟 32 个字节的 X,然后是 32 个字节的 Y。

或者,它可以包含0x02 或0x03(取决于 Y 的符号)后跟 32 个字节的 X。

public static ECPublicKey decodeKey(byte[] encoded) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException{
    ECNamedCurveParameterSpec params = ECNamedCurveTable.getParameterSpec("secp256k1");
    KeyFactory fact = KeyFactory.getInstance("ECDSA", "BC");
    ECCurve curve = params.getCurve();
    java.security.spec.EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, params.getSeed());
    java.security.spec.ECPoint point = ECPointUtil.decodePoint(ellipticCurve, encoded);
    java.security.spec.ECParameterSpec params2 =EC5Util.convertSpec(ellipticCurve, params);
    java.security.spec.ECPublicKeySpec keySpec = new java.security.spec.ECPublicKeySpec(point,params2);
    return (ECPublicKey) fact.generatePublic(keySpec);
}

【讨论】:

  • @MaartenBodewes 我接受了你的回答,因为它更普遍有用。
【解决方案3】:

不使用 KeyFactory 的 Thomas 答案的较短变体是:

public static ECPublicKey decodeKey(byte[] encoded) {
  ECNamedCurveParameterSpec params = ECNamedCurveTable.getParameterSpec("secp256k1");
  ECPublicKeySpec keySpec = new ECPublicKeySpec(params.getCurve().decodePoint(encoded), params);
  return new BCECPublicKey("ECDSA", keySpec, BouncyCastleProvider.CONFIGURATION);
}

我假设encoded是由BCECPublicKey.getQ().getEncoded(bool)获得的,即它是一个字节0x2-0x4后跟secp256k1上公共点的一两个仿射坐标。

【讨论】:

    【解决方案4】:

    ECUtil 已经提供

    ECPublicKeyParameters bcecPublicKey =(ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(kp.getPublic());
    ECPrivateKeyParameters bcecPrivateKey = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(kp.getPrivate());
    

    【讨论】:

      猜你喜欢
      • 2013-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-19
      • 2019-12-29
      • 1970-01-01
      • 1970-01-01
      • 2011-03-13
      相关资源
      最近更新 更多