简体   繁体   English

如何在Java中序列化和使用ECDH参数

[英]How to serialize and consume ECDH parameters in Java

I am looking to perform ECDH between 2 platforms to derive a shared secret. 我希望在2个平台之间执行ECDH,以得出一个共享的秘密。 I am planning to use a named curve (which curve not yet determined). 我打算使用命名曲线(尚未确定该曲线)。 The flow would look something like this: 流看起来像这样:

  • Alice picks a curve 爱丽丝挑曲线
  • Alice generates a random key pair for her curve 爱丽丝为其曲线生成随机密钥对
  • Alice serializes some data about her curve 爱丽丝序列化了一些关于她曲线的数据
  • Alice sends her public key and curve data to Bob 爱丽丝将她的公钥和曲线数据发送给鲍勃
  • Bob initializes a curve with Alice's data 鲍勃用爱丽丝的数据初始化一条曲线
  • Bob creates a key pair based on Alice's data 鲍勃根据爱丽丝的数据创建密钥对
  • Bob performs ECDH to derive shared secret 鲍勃执行ECDH以获取共享机密
  • Bob responds to Alice with his public key 鲍勃用他的公钥回应爱丽丝
  • Alice performs ECDH to derive shared secret 爱丽丝执行ECDH以获取共享秘密

Using bouncycastle, what is the cleanest way to accomplish this? 使用bouncycastle,最干净的方法是什么?

Almost all examples I've seen (like this one: https://gist.github.com/wuyongzheng/0e2ed6d8a075153efcd3 ) illustrate the process of arriving at the shared secret, but none seem to factor in the process of actually serializing information about the curve / starting point (G) to "Bob" and how to use that data on Bob's side to rebuild the curve and generate corresponding keys. 我见过的几乎所有示例(例如这个示例: https : //gist.github.com/wuyongzheng/0e2ed6d8a075153efcd3 )都说明了达到共享机密的过程,但似乎没有一个因素真正影响了有关序列化信息的序列化过程曲线/起点(G)到“鲍勃”,以及如何使用鲍勃一侧的数据重建曲线并生成相应的关键点。 What data would you need to send to Bob? 您需要发送什么数据给鲍勃?

I believe i found a relatively succinct way of demonstrating the problem / solution for this. 我相信我发现了一种相对简洁的方式来说明问题/解决方案。 I was originally missing the fact that named curves include a common starting point, so no need to serialize that data if you have an agreed-upon curve. 我本来就没有这样一个事实,即命名曲线包含一个公共起点,因此,如果您同意曲线,则无需序列化该数据。

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()));

And on first run, this yielded: 在第一次运行时,结果如下:

Alice public: 3059301306072A8648CE3D020106082A8648CE3D03010703420004D8FF8DAB9683C7D6C798FE381775AE3BCC25260E2B270C57584F684BFBF59A73221480040E70993F2F4DEBE25A19E772896F5C98DFAE6865C31830BBD876E8DF
Alice private: 308193020100301306072A8648CE3D020106082A8648CE3D030107047930770201010420A08DEC852618FA6BF0CA8B67DFFCC72AA39BE7402978CA456F73660337837DE1A00A06082A8648CE3D030107A14403420004D8FF8DAB9683C7D6C798FE381775AE3BCC25260E2B270C57584F684BFBF59A73221480040E70993F2F4DEBE25A19E772896F5C98DFAE6865C31830BBD876E8DF
Bob public: 3059301306072A8648CE3D020106082A8648CE3D03010703420004E4343FD573F117446925BBFE0DEF591098AA300066AB4F51DC2736736C8CE18BA72EA67AE4D0D6DD5E22007BA45BAA9DCE473002D17D6A29207AA15A1E97C596
Bob private: 308193020100301306072A8648CE3D020106082A8648CE3D030107047930770201010420D272E7BD59F7EA2AA3710910073AFE58082BC460B347A3782981CCCABA452538A00A06082A8648CE3D030107A14403420004E4343FD573F117446925BBFE0DEF591098AA300066AB4F51DC2736736C8CE18BA72EA67AE4D0D6DD5E22007BA45BAA9DCE473002D17D6A29207AA15A1E97C596
Bob secret: B65B4C8A1C797B867CE39F26DC97A0241A407FC79CF0D3CBA061A4A907CF3E1B
Alice secret: B65B4C8A1C797B867CE39F26DC97A0241A407FC79CF0D3CBA061A4A907CF3E1B

You don't necessarily need to send the curve, you can fix it in advance. 您不一定需要发送曲线,您可以提前对其进行修复。 As an important example Bitcoin, which uses ECDSA rather than ECDH, specifies secp256k1. 作为一个重要的例子,使用ECDSA而不是ECDH的比特币指定secp256k1。

However, the code in your answer uses the encodings returned by Java PublicKey.getEncoded() and PrivateKey.getEncoded() which are 'X.509' (more precisely the SubjectPublicKeyInfo structure in X.509) and 'PKCS8' respectively; 但是,答案中的代码使用Java PublicKey.getEncoded()PrivateKey.getEncoded()返回的编码,分别为“ X.509”(更确切地说是X.509中的SubjectPublicKeyInfo结构)和“ PKCS8”; see the javadoc. 参见javadoc。 These are both ASN.1 encodings that include an AlgorithmIdentifier containing parameters which for ECC define the curve either by an ASN.1 OBJECT IDENTIFIER aka OID or by a detailed specification of the underlying field, coefficients for the Weierstrass curve equation, base point, order and cofactor. 这两种都是ASN.1编码,其中包含AlgorithmIdentifier其中包含用于ECC的参数,这些参数用于通过ASN.1对象标识符(又称为OID)或基础字段的详细说明来定义曲线 ,Weierstrass曲线方程的系数,基点,阶数和辅因子。 In practice everyone uses the standard named curves which have OIDs. 实际上,每个人都使用具有OID的标准命名曲线。 That is why 21 bytes beginning at offset 2 in all of your key printouts are identical; 这就是为什么所有键打印输出中从偏移量2开始的21个字节都相同的原因; it's an ASN.1 SEQUENCE containing an OID for the algorithm (id-ecPublicKey) and an OID for the selected curve (prime256v1). 它是一个ASN.1 SEQUENCE,包含用于算法的OID(id-ecPublicKey)和用于所选曲线的OID(prime256v1)。

Other schemes are possible. 其他方案也是可能的。 TLS ECDHE sends either the curve details or a small integer identifying a standard curve as defined in rfc4492 of which the latter is almost always used; TLS ECDHE发送曲线详细信息或小整数,以标识标准曲线,该标准曲线在rfc4492中定义,几乎总是使用后者。 static-ECDH uses X.509 certs, and thus the X.509 format. static-ECDH使用X.509证书,因此使用X.509格式。 SSH ECDH ephemeral or ECMQV sends a string containing the name or the OID, see rfc5656 . SSH ECDH临时或ECMQV发送包含名称或OID的字符串,请参阅rfc5656 CMS and thus S/MIME uses ASN.1 structures containing the AlgorithmIdentifier with OID-form namedCurve parameters, see rfc 5753 . CMS以及S / MIME使用ASN.1结构,其中包含带有OID形式namedCurve参数的AlgorithmIdentifier ,请参见rfc 5753

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM