0

希望在这里得到一些帮助...

我正在尝试从未压缩的客户端公钥(0x04 |32 字节点 X|32 字节点 Y)和服务器私钥(openSSL 生成的 base64)生成共享密钥,但是在将服务器 EC 私钥转换为 EC 密钥对象时出错。我需要这些来生成共享秘密

我正在使用 - 1. 曲线是 BrainpoolP256r1 2. 服务器私钥是使用 openSSL 生成的(现在在代码中硬编码) 3. 客户端公共以未压缩的方式共享(格式 0x04 |32 字节点 X|32 字节点 Y)。

//Step 1 : Converting Client EC Public key (ephemeral) into actual EC Public key object (X509) 

byte[] clientECPublicKeybytes = DatatypeConverter.parseHexBinary("045894609CCECF9A92533F630DE713A958E96C97CCB8F5ABB5A688A238DEED6DC2D9D0C94EBFB7D526BA6A61764175B99CB6011E2047F9F067293F57F5");

 ECParameterSpec ecParameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolP256r1");

  ECCurve curve = ecParameterSpec.getCurve();  ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(curve.decodePoint(clientECPublicKeybytes), ecParameterSpec);


 KeyFactory kf = KeyFactory.getInstance("EC"); 


ECPublicKey ecClientPublicKey = (ECPublicKey)kf.generatePublic(pubKeySpec); 


 //Step 2 : Converting server EC private key ASN.1 to ECPrivateKey object  

byte[] serverECCSkBytes = DatatypeConverter.parseBase64Binary("MHgCAQEEIA27nM1klj9pVxOzJrO4aBLFvXTtOJnf+vMhiv3HA+3noAsGCSskAwMCCAEBB6FEA0IABG1erLtLyTbC5yN8gVY4a0JPO5eefKftWMbSQij2Ks5TaNNuj/tqigFqsk1g/l2UBBkIx2KdpeiY8nVddwMpzho=");

  KeyFactory keyFactory= KeyFactory.getInstance("EC","BC");

  PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(serverECCSkBytes);

  ECPrivateKey ecServerPrivateKey = (ECPrivateKey)keyFactory.generatePrivate(skSpec);

错误

2018-02-26 14:31:12.818[0;39m [31mERROR[0;39m [35m18096[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [36mo.a.c.c.C.[.[.[/].[dispatcherServlet]   [0;39m [2m:[0;39m Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.security.spec.InvalidKeySpecException: java.lang.IllegalArgumentException: wrong version for private key info] with root cause

java.security.spec.InvalidKeySpecException: java.lang.IllegalArgumentException: wrong version for private key info
    at org.bouncycastle.jce.provider.JDKKeyFactory$EC.engineGeneratePrivate(Unknown Source) ~[bcprov-jdk15-140.jar:1.40.0]
    at java.security.KeyFactory.generatePrivate(KeyFactory.java:366) ~[na:1.8.0_77]
4

1 回答 1

3

TLDR:该私钥不是 PKCS8

OpenSSL 支持每种算法四种不同的 PEM 私钥格式(总共大约十几种);其中只有一个是未加密的 PKCS8,这是 Java PKCS8EncodedKeySpec 所需的格式,而您使用了 Java 不支持的另一种格式。与如何将 .p12 转换为包含未加密 PKCS#1 私钥块的 .pem 进行比较?这是针对 RSA 的;对于 EC,两种“传统”格式由https://www.secg.org中的 SEC1 定义(而 PKCS1 用于 RSA),而 PKCS8 格式(对于所有算法,不仅仅是 EC 和 RSA)由 PKCS8 定义。OpenSSL 能够处理多种格式,主要是因为 PEM 文件有一个非常重要的 BEGIN 行(和不太重要的 END 行)来指定数据类型;您删除之前的文件具有类型EC PRIVATE KEY含义 SEC1不是类型PRIVATE KEY,表示 Java 使用的未加密的 PKCS8。

您有两个(或更多)选项:

  • 将 OpenSSL 密钥转换为 PKCS8 未加密的 PEMopenssl pkcs8 -topk8 -nocrypt或仅使用openssl pkey(在 1.0.0 版本中) - 或者首先使用genpkey或可能req -newkey(在 1.0.0 版本中)而不是ecparam -genkey. 在 PKCS8EncodedKeySpec 中使用blob de-base64-ed

  • 如果除了 bcprov 之外,您还拥有或获得 bcpkix,它可以处理许多(也许是全部?)普通 Java 无法处理的 OpenSSL 格式。重建或只是恢复到原始 PEM 格式(可以在内存中),您可以使用解析PEMReader然后转换JcaPEMKeyConverter(或只是 a KeyFactory)。

How to Load RSA Private Key From File for RSA涵盖了这两者,可以很容易地为 EC 修改。

  • 或者,由于您已经完成了 PEM 解析的一部分,您可以以同样的方式自己处理其余部分PEMParser,仅用于私有简化:
导入java.security.*;
导入 org.bouncycastle.asn1.ASN1Sequence;
导入 org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
导入 org.bouncycastle.asn1.x509.AlgorithmIdentifier;
导入 org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
...
静态无效 SO48996981BCparseECprivate () 抛出异常 {
    byte[] server_sec1 = DatatypeConverter.parseBase64Binary("MHgCAQEEIA27nM1klj9pVxOzJrO4aBLFvXTtOJnf+vMhiv3HA+3noA​​sGCSskAwMCCAEBB6FEA0IABG1erLtLyTbC5yN8gVY4a0JPO5eefKftWMbSQij2Ks5TaNNuj/tqigFqsk1g/l2UBBkIx2KdpeiY8nVddwMpzho=");
    ASN1Sequence seq = ASN1Sequence.getInstance(server_sec1);
    org.bouncycastle.asn1.sec.ECPrivateKey pKey = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(seq);
    AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters());
    byte[] server_pkcs8 = new PrivateKeyInfo(algId, pKey).getEncoded();
    KeyFactory 事实 = KeyFactory.getInstance("EC","BC");
    PrivateKey pkey = fact.generatePrivate (new PKCS8EncodedKeySpec(server_pkcs8));
    // 仅用于测试:
    System.out.println (pkey.getClass().getName() + " " + pkey.getAlgorithm());
}
  • 最后,您实际上并不需要 Java。OpenSSL 可以在库(从您编写的代码中调用)或命令行中进行 ECDH 密钥协商/派生openssl pkeyutl -derive——尽管对于后者,您需要 PEM文件中的密钥:OpenSSL 支持的任何 PEM 格式的私钥(您已经拥有)但是X.509 SubjectPublicKeyInfo 格式的对等公钥,根据给定的信息,您可能不需要也可能必须构建。
于 2018-02-27T06:43:34.250 回答