【问题标题】:Get raw keys from Bouncy Castle Java从 Bouncy Castle Java 获取原始密钥
【发布时间】:2021-12-19 07:34:05
【问题描述】:

我正在使用 ECDSA 的 C 和 Java 实现。 C 实现使用原始格式 - 32 字节私钥、64 字节公钥、64 字节签名。

我正在使用 Bouncy Castle 在 Java 中创建密钥:

    public static KeyPair GenerateKeys() throws NoSuchAlgorithmException,
        NoSuchProviderException, InvalidAlgorithmParameterException {
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
        KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");

        g.initialize(ecSpec, new SecureRandom());

        return g.generateKeyPair();
    }

并希望将它们存储为字节数组,因为稍后我需要将它们导出到我的 C 应用程序。我注意到:

        KeyPair vendorKeys = GenerateKeys();
        for (byte byt : vendorKeys.getPublic().getEncoded()) {
            System.out.println("byt: " +  byt);
        }

将返回公钥字节,但长度错误。我想这与格式有关,X.509 用于 PublicKey,PKCS#8 用于私有。寻求帮助,我发现 Java 中有一些类,例如 PKCS8EncodedKeySpec,它们应该可以帮助我获取原始密钥,但无法弄清楚如何去做。

【问题讨论】:

  • 您应该能够将私钥和公钥分别转换为ECPrivateKeyECPublicKey,然后使用getD()getQ()获取所需的参数。
  • @Topaco:如果你使用org.bouncycastle.jce.interfaces中的那些;标准 JCE 不同。而BigInteger 并没有为您提供固定大小的编码,甚至来自 Bouncy ECPoint 的 X9.62 编码也不完全正确。但是,您可以使用 Bouncy '{hash}with{PLAIN-,CVC-}ECDSA' or SunEC 9+ '{hash}withECDSAinP1363format' 获得固定格式的 签名。 Michal:目前有几个关于将“原始”密钥进入 JCE的问题,但为了让它们最接近我能找到的是stackoverflow.com/questions/52299691stackoverflow.com/questions/51347513
  • @dave_thompson_085 - 我指的是 BouncyCastle(getD()getQ() 被 BC 使用),但感谢您的澄清。我在回答中说得更清楚了。

标签: java bouncycastle ecdsa


【解决方案1】:

可以使用 BouncyCastle 通过其实现 ECPrivateKeyECPublicKey 确定私钥或公钥的 x 和 y 坐标。使用getD() 可以确定私钥,使用getQ() 可以确定公钥,例如:

import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.encoders.Hex;
...
ECPublicKey publickKey = (ECPublicKey)vendorKeys.getPublic();
ECPrivateKey privateKey = (ECPrivateKey)vendorKeys.getPrivate();
byte[] rawPrivate = BigIntegers.asUnsignedByteArray(privateKey.getD());
byte[] x = publickKey.getQ().getAffineXCoord().getEncoded();
byte[] y = publickKey.getQ().getAffineYCoord().getEncoded();        
byte[] rawPublic = ByteBuffer.allocate(x.length + y.length).put(x).put(y).array();

System.out.println(Hex.toHexString(rawPrivate));
System.out.println(Hex.toHexString(rawPublic));

这里 secp256r1 的公钥长度始终为 64 字节。私钥大小为 32 字节,但也可以更小。


dave_thompson_085 在他的评论 (java.security.interfaces) 中指出了 ECPublicKeyECPrivateKey 的内置 Java 实现。这里私钥和公钥对应的方法分别是getS()getW()
在 x 或 y 小于 32 字节的情况下,此实现不会自动填充前导 0x00 值,因此必须手动完成。因此,BouncyCastle 方法对您来说可能更方便,也因为您已经在使用 BouncyCastle。

【讨论】:

  • 我不得不将 getD() 重命名为 getS()getQ() 重命名为 getW()getAffineXCoord() 重命名为 getAffineX() getAffineX()YCoord 相同,但现在效果很好!我猜API在版本之间发生了变化?任何提示如何做相反的事情,从原始密钥创建一个充气城堡?
  • @Michał - 通过您的更改,您可以应用内置的 Java 实现而不是 BC(我回答的最后一部分)。你可以这样做,但 x 和 y 不会自动填充。
  • @Michał - 关于其他方式,您可以在 SO 上找到各种帖子,例如herehere.
猜你喜欢
  • 2019-05-24
  • 1970-01-01
  • 2018-10-06
  • 1970-01-01
  • 2016-02-05
  • 2016-06-25
  • 1970-01-01
  • 2019-11-24
  • 2011-01-14
相关资源
最近更新 更多