使用BouncyCastle从文件中读取椭圆曲线私钥

7

BouncyCastle密码学API允许使用常规的java.security包对象创建和验证数字签名,例如java.security.PublicKeyjava.security.PrivateKey及其容器java.security.KeyPair

假设我使用OpenSSL来创建一个包含我想在应用程序中使用的椭圆曲线私钥的.pem文件(或者更简单地说,.der文件)。例如,它看起来像这样:

-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIDzESrZFmTaOozu2NyiS8LMZGqkHfpSOoI/qA9Lw+d4NoAcGBSuBBAAK
oUQDQgAE7kIqoSQzC/UUXdFdQ9Xvu1Lri7pFfd7xDbQWhSqHaDtj+XY36Z1Cznun
GDxlA0AavdVDuoGXxNQPIed3FxPE3Q==
-----END EC PRIVATE KEY-----

如何使用BouncyCastle APIs获取包含私钥和相应公钥的java.security.KeyPair
请注意,我想使用BouncyCastle 1.50中提供的API(在撰写本文时为最新版本),而不使用任何已弃用的API。这不幸地排除了其他SO答案中使用的PEMReader类。此外,此问题特定于椭圆曲线的格式;与RSA或DSA密钥文件相比,它们包含其他参数。

您的文件中的EC PARAMETERS块是由于openssl ecparam -genkey默认工作方式而发生的意外,并且不是实际密钥的必要部分,您可以通过指定-noout来省略它,尽管这有些不太明显。 EC(DSA / DH)的实际密钥结构(在base64 / DER数据中“隐藏”)确实包含一些RSA所没有但DSA却有的参数信息。 - dave_thompson_085
3个回答

19
除了标准的JCE方法(由divanov展示)外,只要您提供正确的输入(请参见我的评论),或者像您的自我回答一样首先使用JCE,BouncyCastle 1.48及更高版本仍包含旧的PEMReader功能,只是组织方式有些不同,在这种情况下,您可以使用类似于以下内容的东西:
static void SO22963581BCPEMPrivateEC () throws Exception {
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    Reader rdr = new StringReader ("-----BEGIN EC PRIVATE KEY-----\n"
            +"MHQCAQEEIDzESrZFmTaOozu2NyiS8LMZGqkHfpSOoI/qA9Lw+d4NoAcGBSuBBAAK\n"
            +"oUQDQgAE7kIqoSQzC/UUXdFdQ9Xvu1Lri7pFfd7xDbQWhSqHaDtj+XY36Z1Cznun\n"
            +"GDxlA0AavdVDuoGXxNQPIed3FxPE3Q==\n"+"-----END EC PRIVATE KEY-----\n");
    Object parsed = new org.bouncycastle.openssl.PEMParser(rdr).readObject();
    KeyPair pair = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter().getKeyPair((org.bouncycastle.openssl.PEMKeyPair)parsed);
    System.out.println (pair.getPrivate().getAlgorithm());
}

我有一个填充:-----BEGIN EC PARAMETERS-----。必须剥离EC参数,然后使用PEMParser进行读取。然后正确地获得了KeyPair对象。否则,如果密钥中存在EC参数,则PEMParser会将其读取为ASN1Object。我希望剥离EC参数不会使目标失去。 - AnirbanDebnath
1
Anirban:是的,您不需要参数块。请参阅我大约3年前在_question_上的评论。 - dave_thompson_085
哈哈。--3年前-- 我相信软件行业是一个小世界!! - AnirbanDebnath

12

在Java中,这段代码基本上是相同的。 在剥离保护字符串并解码Base64数据后,将其传递给此实用方法:

public static PrivateKey keyToValue(byte[] pkcs8key)
    throws GeneralSecurityException {

    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8key);
    KeyFactory factory = KeyFactory.getInstance("ECDSA");
    PrivateKey privateKey = factory.generatePrivate(spec);
    return privateKey;
}

4
阅读JCA文档使我相信,传递给KeyFactory.getInstance()的参数应该是EC而不是ECDSA。 我错了吗? - Adam Mackler
2
这取决于所使用的提供程序。对于BouncyCastle,它可以是EC和ECDSA两者。对于Sun提供程序,它只能是EC,我很少使用它。 - divanov
嗨,什么是保护字符串?当我去掉开头的 **---- 时,我收到了“输入字节数组具有不正确的结尾字节”异常。 - zhouxiang
守卫字符串是以 ----- 开头和结尾的字符串。请使用示例数据和异常跟踪来提出问题。 - divanov
16
这对于此问题中有关的数据不起作用,因为它采用OpenSSL的“传统”格式,而不是JCE使用的PKCS8格式;请注意PEM标题 BEGIN / END EC PRIVATE KEY 而不是用于未加密PKCS8的 BEGIN / END PRIVATE KEY。您可以使用现代版本(1.0.0以上)中的 openssl pkey -in oldfile -out newfile 或在所有版本中使用 openssl pkcs8 -topk8 -nocrypt -in oldfile -out newfile 将其转换,然后将其解码为 PKCS8EncodedKeySpec - dave_thompson_085
显示剩余2条评论

4

由于我只需要一个快速而简单的演示,我用以下方式解决了这个问题(使用Scala)。首先,在REPL中生成公私钥对并打印出其数据:

Security.addProvider(new BouncyCastleProvider)

val SignatureScheme = "some signature scheme, eg ECDSA"
val RandomAlgorithm = "some random algorithm, eg SHA1PRNG"

val keygen = KeyPairGenerator.getInstance(SignatureScheme)
val rng = SecureRandom.getInstance(RandomAlgorithm)
rng.setSeed(seed)
keygen.initialize(KeySize, rng)

val kp = keygen.generateKeyPair()
println(kp.getPublic.getEncoded.toSeq) // toSeq so that Scala actually prints it
println(kp.getPrivate.getEncoded.toSeq)

然后使用生成的数据,
val hardcodedPublic = Array[Byte]( /* data */ )
val hardcodedPrivate = Array[Byte]( /* data */ )

val factory = KeyFactory.getInstance(SignatureScheme)

val publicSpec = new X509EncodedKeySpec(hardcodedPublic)
val publicKey = factory.generatePublic(publicSpec)

val privateSpec = new PKCS8EncodedKeySpec(hardcodedPrivate)
val privateKey = factory.generatePrivate(privateSpec)

您需要知道的关键是,默认情况下公钥数据使用X509编码,私钥数据使用PKCS8编码。可以通过OpenSSL输出这些格式并手动解析,但我没有检查过。
我在这篇博客文章中找到了有关SpongyCastle(Android的BouncyCastle别名)的信息,非常有帮助。不幸的是,文档分散如此,而BouncyCastle的维基在提问时已经关闭。 更新: BouncyCastle维基已经上线,您可以在这里找到文档。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接