2

2 つのプラットフォーム間で ECDH を実行して、共有シークレットを取得しようとしています。名前付き曲線 (まだ決定されていない曲線) を使用する予定です。フローは次のようになります。

  • アリスは曲線を選ぶ
  • アリスは自分の曲線のためにランダムな鍵のペアを生成します
  • アリスは自分の曲線に関するいくつかのデータをシリアル化します
  • アリスは自分の公開鍵と曲線データをボブに送信します
  • ボブはアリスのデータで曲線を初期化します
  • ボブはアリスのデータに基づいて鍵ペアを作成します
  • Bob は ECDH を実行して共有秘密を導出します
  • ボブは自分の公開鍵でアリスに応答します
  • Alice は ECDH を実行して共有秘密を導出します

弾む城を使用して、これを達成する最もクリーンな方法は何ですか?

私が見たほとんどすべての例 (この例: https://gist.github.com/wuyongzheng/0e2ed6d8a075153efcd3など) は、共有シークレットに到達するプロセスを示していますが、実際に情報をシリアル化するプロセスを考慮しているようには見えません。曲線/開始点 (G) を「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

必ずしもカーブを送信する必要はありません。事前に修正できます。重要な例として、ECDH ではなく ECDSA を使用する Bitcoin は、secp256k1 を指定します。

ただし、回答のコードは、Java によって返されたエンコーディングを使用し、PublicKey.getEncoded()それぞれPrivateKey.getEncoded()「X.509」(より正確にはSubjectPublicKeyInfoX.509 の構造) と「PKCS8」です。javadoc を参照してください。これらは両方ともAlgorithmIdentifier、ECC の場合、ASN.1 OBJECT IDENTIFIER aka OID または基礎となるフィールドの詳細な仕様、ワイエルシュトラス曲線方程式の係数、基点、次数、および補因子。実際には、誰もが OID を持つ標準の名前付き曲線を使用します。これが、すべてのキーの出力でオフセット 2 から始まる 21 バイトが同一である理由です。これは、アルゴリズムの OID (id-ecPublicKey) と選択された曲線の OID (prime256v1) を含む ASN.1 SEQUENCE です。

他のスキームも可能です。TLS ECDHE は、 rfc4492で定義されているように、曲線の詳細または標準曲線を識別する小さな整数のいずれかを送信します。後者はほとんど常に使用されます。static-ECDH は X.509 証明書を使用するため、X.509 形式を使用します。SSH ECDH ephemeral または ECMQV は、名前または OID を含む文字列を送信します。 rfc5656を参照してください。CMS および S/MIME はAlgorithmIdentifier、OID 形式のnamedCurveパラメータを含む ASN.1 構造を使用します。rfc 5753を参照してください。

于 2015-12-05T00:25:12.523 に答える