Encryption

(EC-)Diffie-Hellman 的驗證結果

  • August 27, 2021

我收到了 JSON 的公鑰。

例如,我有 4 個密鑰:2 個公鑰和 2 個私鑰。

public A : co2D0pNxZJIeQ4RZlCRJYBDzNXSLluETdztid0M+HGzN1uGJ4JWZsenjWgRrmkLh3yqHQqzOBMl/wHVH97A6+g==

private A : TXxii5Ka8LMvuc9arHu63qTmNKxGlgti+wpR3YhBGew=

public B : nUblC+OKdl94iBiWk0941wmYBiMt7C90CjOJPI2BPr8K7xGuC1XsR5DtwFCoM3Iew2BjBG+5SqrYwAPTJF7gdA==

private B : sm6V7+hChvkFSeLNoR+5tItiX8gH5tT47xBkFaV6SDU=

我無法從我在 JSON 中收到的公鑰 A私鑰 B進行****ECDH 交換。

如何驗證:(公鑰 A + 私鑰 B)==(公鑰 B + 私鑰 A)

所以這就是我解決問題的方法:

我在字元串中的鍵:

public A : co2D0pNxZJIeQ4RZlCRJYBDzNXSLluETdztid0M+HGzN1uGJ4JWZsenjWgRrmkLh3yqHQqzOBMl/wHVH97A6+g==

private A : TXxii5Ka8LMvuc9arHu63qTmNKxGlgti+wpR3YhBGew=

public B : nUblC+OKdl94iBiWk0941wmYBiMt7C90CjOJPI2BPr8K7xGuC1XsR5DtwFCoM3Iew2BjBG+5SqrYwAPTJF7gdA==

private B : sm6V7+hChvkFSeLNoR+5tItiX8gH5tT47xBkFaV6SDU=

獲取私鑰的函式:

public static PrivateKey getPrivateKey(byte[] encodedPrivateKey) {
  BigInteger s = new BigInteger(1,encodedPrivateKey);
  ECNamedCurveParameterSpec ecCurve = ECNamedCurveTable.getParameterSpec("secp256r1");
  ECParameterSpec ecParameterSpec = new ECNamedCurveSpec("secp256r1", ecCurve.getCurve(), ecCurve.getG(), ecCurve.getN(), ecCurve.getH(), ecCurve.getSeed());
  ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(s, ecParameterSpec);
  try {
      KeyFactory keyFactory = KeyFactory.getInstance("EC");
      return keyFactory.generatePrivate(privateKeySpec);
   } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
       e.printStackTrace();
       return null;
  }
}

public static PublicKey rawToEncodedECPublicKey(String curveName, byte[] rawBytes) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidParameterSpecException {
  KeyFactory kf = KeyFactory.getInstance("EC");
  byte[] x = Arrays.copyOfRange(rawBytes, 0, rawBytes.length/2);
  byte[] y = Arrays.copyOfRange(rawBytes, rawBytes.length/2, rawBytes.length);
  ECPoint w = new ECPoint(new BigInteger(1,x), new BigInteger(1,y));
  return kf.generatePublic(new ECPublicKeySpec(w, ecParameterSpecForCurve(curveName)));
}

public static java.security.spec.ECParameterSpec ecParameterSpecForCurve(String curveName) throws NoSuchAlgorithmException, InvalidParameterSpecException {
  AlgorithmParameters params = AlgorithmParameters.getInstance("EC");
  params.init(new ECGenParameterSpec(curveName));
  return params.getParameterSpec(ECParameterSpec.class);
}

我們需要通過字元串中的公鑰/私鑰創建 2 對密鑰,並檢查兩者是否相等:

byte [] cle_publique_a_decode = Base64.getDecoder().decode(cle_publique_a);
byte [] cle_privee_a_decode = Base64.getDecoder().decode(cle_privee_a);
byte [] cle_publique_b_decode = Base64.getDecoder().decode(cle_publique_b);
byte [] cle_privee_b_decode = Base64.getDecoder().decode(cle_privee_b);

try {
    PublicKey PublicKeyA = rawToEncodedECPublicKey("secp256r1",cle_publique_a_decode);
    PublicKey PublicKeyB = rawToEncodedECPublicKey("secp256r1",cle_publique_b_decode);

    PrivateKey PrivateKeyA =  getPrivateKey(cle_privee_a_decode);
    PrivateKey PrivateKeyB =  getPrivateKey(cle_privee_b_decode);

    // Secret #1
    // PrivateKeyA + PublicKeyB = generateSecret
    KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
    keyAgreement.init(PrivateKeyA);
    keyAgreement.doPhase(PublicKeyB, true);
    byte[] generateSecret = keyAgreement.generateSecret();
    String base64_generateSecret = Base64.getEncoder().encodeToString(generateSecret);

    // Secret #2
    // PrivateKeyB + PublicKeyA = generateSecret2
    KeyAgreement keyAgreement2 = KeyAgreement.getInstance("ECDH");
    keyAgreement.init(PrivateKeyB);
    keyAgreement.doPhase(PublicKeyA, true);
    byte[] generateSecret2 = keyAgreement.generateSecret();
    String base64_generateSecret2 = Base64.getEncoder().encodeToString(generateSecret);

    // compare 2 secrets
    // (public key A + private key B) == (public key B + private key A)
    if(base64_generateSecret.equals(base64_generateSecret2)){
         // Good : Secrets are same
         // continue..
    }
    else{
         // Not good : Secrets are differents
    }
}
catch{
    throw new IllegalArgumentException(e.getMessage(), e);
}

密鑰相等,我可以開始加密了。

通常(橢圓曲線)Diffie-Hellman 計算的結果稱為主機密。此主密鑰通常用作密鑰派生函式 (KDF) 的輸入密鑰材料 (IKM)。使用 KDF 可以從主密鑰派生多個密鑰。您正在尋找的東西稱為“關鍵構象”。

有多種確認對稱密鑰的方法:

  1. 這些密鑰之一可以用作質詢響應協議的輸入。質詢響應協議是一種對稱方法,用於驗證實體是否有權訪問正確的密鑰。
  2. 另一種方法是通過雙方都知道的字元串將密鑰用作 MAC 的輸入。這樣,MAC 就表明該實體可以訪問正確的密鑰。例如,TL​​S (1.3) 在握手的最終“Finished”消息中的整個握手過程中使用 MAC:

完成:整個握手過程中的 MAC(消息驗證碼)。此消息提供密鑰確認,將端點的身份綁定到交換的密鑰,並且在 PSK 模式下還驗證握手。

$$ Section 4.4.4 $$

  1. 您還可以想像,一方可以簽署生成的秘密並將簽名發送給另一方。
  2. 最後,當第一個經過身份驗證的消息從一方發送到另一方時,也可以隱式地確認結果密鑰和秘密相同。然而,這通常是可以避免的,因為這意味著您無法區分數據傳輸和握手錯誤;因此,它可以在協議實現中玩得開心。

筆記:

  • 如果您確認一個密鑰,那麼密鑰和任何其他派生密鑰的有效性也會得到確認(除非實現錯誤)。
  • DH 的(靜態)公鑰可能可以通過其他方式被信任,在這種情況下,被認證方可能不需要對生成的秘密進行額外的驗證;只有非認證方需要發送質詢-響應、MAC 或簽名值。

引用自:https://crypto.stackexchange.com/questions/93637