2

我希望在 2 个平台之间执行 ECDH 以获取共享密钥。我打算使用命名曲线(尚未确定哪条曲线)。流程看起来像这样:

  • 爱丽丝选择了一条曲线
  • Alice 为她的曲线生成一个随机密钥对
  • Alice 序列化一些关于她的曲线的数据
  • Alice 将她的公钥和曲线数据发送给 Bob
  • Bob 用 Alice 的数据初始化一条曲线
  • Bob 根据 Alice 的数据创建密钥对
  • Bob 执行 ECDH 以导出共享密钥
  • Bob 用他的公钥回应 Alice
  • Alice 执行 ECDH 以导出共享密钥

使用 bouncycastle,最干净的方法是什么?

我见过的几乎所有示例(例如:https://gist.github.com/wuyongzheng/0e2ed6d8a075153efcd3)都说明了获得共享密钥的过程,但似乎没有一个实际序列化有关曲线/起点 (G) 到“Bob”,以及如何使用 Bob 一侧的数据来重建曲线并生成相应的密钥。您需要向 Bob 发送什么数据?

4

2 回答 2

4

我相信我找到了一种相对简洁的方式来证明这个问题/解决方案。我最初错过了命名曲线包含一个共同起点的事实,因此如果您有一个商定的曲线,则无需序列化该数据。

Security.addProvider(new BouncyCastleProvider());

// Alice sets up the exchange
KeyPairGenerator aliceKeyGen = KeyPairGenerator.getInstance("ECDH", "BC");
aliceKeyGen.initialize(new ECGenParameterSpec("prime256v1"), new SecureRandom());

KeyPair alicePair = aliceKeyGen.generateKeyPair();
ECPublicKey alicePub = (ECPublicKey)alicePair.getPublic();
ECPrivateKey alicePvt = (ECPrivateKey)alicePair.getPrivate();

byte[] alicePubEncoded = alicePub.getEncoded();
byte[] alicePvtEncoded = alicePvt.getEncoded();

System.out.println("Alice public: " + DatatypeConverter.printHexBinary(alicePubEncoded));
System.out.println("Alice private: " + DatatypeConverter.printHexBinary(alicePvtEncoded));


// POST hex(alicePubEncoded)

// Bob receives Alice's public key

KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey remoteAlicePub = kf.generatePublic(new X509EncodedKeySpec(alicePubEncoded));

KeyPairGenerator bobKeyGen = KeyPairGenerator.getInstance("ECDH", "BC");
bobKeyGen.initialize(new ECGenParameterSpec("prime256v1"), new SecureRandom());

KeyPair bobPair = bobKeyGen.generateKeyPair();
ECPublicKey bobPub = (ECPublicKey)bobPair.getPublic();
ECPrivateKey bobPvt = (ECPrivateKey)bobPair.getPrivate();

byte[] bobPubEncoded = bobPub.getEncoded();
byte[] bobPvtEncoded = bobPvt.getEncoded();

System.out.println("Bob public: " + DatatypeConverter.printHexBinary(bobPubEncoded));
System.out.println("Bob private: " + DatatypeConverter.printHexBinary(bobPvtEncoded));

KeyAgreement bobKeyAgree = KeyAgreement.getInstance("ECDH");
bobKeyAgree.init(bobPvt);
bobKeyAgree.doPhase(remoteAlicePub, true);

System.out.println("Bob secret: " + DatatypeConverter.printHexBinary(bobKeyAgree.generateSecret()));


// RESPOND hex(bobPubEncoded)

// Alice derives secret

KeyFactory aliceKf = KeyFactory.getInstance("EC");
PublicKey remoteBobPub = aliceKf.generatePublic(new X509EncodedKeySpec(bobPubEncoded));

KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("ECDH");
aliceKeyAgree.init(alicePvt);
aliceKeyAgree.doPhase(remoteBobPub, true);

System.out.println("Alice secret: " + DatatypeConverter.printHexBinary(aliceKeyAgree.generateSecret()));

在第一次运行时,这产生了:

Alice public: 3059301306072A8648CE3D020106082A8648CE3D03010703420004D8FF8DAB9683C7D6C798FE381775AE3BCC25260E2B270C57584F684BFBF59A73221480040E70993F2F4DEBE25A19E772896F5C98DFAE6865C31830BBD876E8DF
Alice private: 308193020100301306072A8648CE3D020106082A8648CE3D030107047930770201010420A08DEC852618FA6BF0CA8B67DFFCC72AA39BE7402978CA456F73660337837DE1A00A06082A8648CE3D030107A14403420004D8FF8DAB9683C7D6C798FE381775AE3BCC25260E2B270C57584F684BFBF59A73221480040E70993F2F4DEBE25A19E772896F5C98DFAE6865C31830BBD876E8DF
Bob public: 3059301306072A8648CE3D020106082A8648CE3D03010703420004E4343FD573F117446925BBFE0DEF591098AA300066AB4F51DC2736736C8CE18BA72EA67AE4D0D6DD5E22007BA45BAA9DCE473002D17D6A29207AA15A1E97C596
Bob private: 308193020100301306072A8648CE3D020106082A8648CE3D030107047930770201010420D272E7BD59F7EA2AA3710910073AFE58082BC460B347A3782981CCCABA452538A00A06082A8648CE3D030107A14403420004E4343FD573F117446925BBFE0DEF591098AA300066AB4F51DC2736736C8CE18BA72EA67AE4D0D6DD5E22007BA45BAA9DCE473002D17D6A29207AA15A1E97C596
Bob secret: B65B4C8A1C797B867CE39F26DC97A0241A407FC79CF0D3CBA061A4A907CF3E1B
Alice secret: B65B4C8A1C797B867CE39F26DC97A0241A407FC79CF0D3CBA061A4A907CF3E1B
于 2015-12-04T22:07:49.617 回答
2

您不一定需要发送曲线,您可以提前修复它。作为一个重要的例子,比特币使用 ECDSA 而不是 ECDH,它指定了 secp256k1。

但是,您的答案中的代码使用 Java 返回的编码PublicKey.getEncoded()PrivateKey.getEncoded()它们分别是“X.509”(更准确地说是SubjectPublicKeyInfoX.509 中的结构)和“PKCS8”;请参阅 javadoc。这些都是 ASN.1 编码,包括AlgorithmIdentifier包含参数,ECC通过 ASN.1 OBJECT IDENTIFIER aka OID 或通过基础字段的详细规范、Weierstrass 曲线方程的系数、基点、阶数和辅因子。在实践中,每个人都使用具有 OID 的标准命名曲线。这就是为什么在所有关键打印输出中从偏移量 2 开始的 21 个字节都是相同的;它是一个 ASN.1 序列,包含算法的 OID (id-ecPublicKey) 和所选曲线的 OID (prime256v1)。

其他方案是可能的。TLS ECDHE 发送曲线详细信息或标识rfc4492中定义的标准曲线的小整数,后者几乎总是使用;static-ECDH 使用 X.509 证书,因此使用 X.509 格式。SSH ECDH ephemeral 或 ECMQV 发送包含名称或 OID 的字符串,请参阅rfc5656AlgorithmIdentifierCMS 和因此 S/MIME 使用包含带有 OID 形式参数的 ASN.1 结构namedCurve,请参阅 rfc 5753

于 2015-12-05T00:25:12.523 回答