Java中的DH密钥协议:为什么生成的密钥对于双方来说大多相同?

时间:2013-11-01 05:01:47

标签: java cryptography

我正在尝试用Java创建一个非常简单的密钥交换。在代码和输出之后有问题:

 public class Blergh {

    public static KeyPair genKeyPair512() {
        try {
            AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator
                    .getInstance("DH");
            paramGen.init(512);

            BigInteger g = new BigInteger(
                    "7961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20",
                    16);
            BigInteger p = new BigInteger(
                    "00AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC5",
                    16);
            final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
            final DHParameterSpec dhSpec = new DHParameterSpec(p, g, 511);
            keyGen.initialize(dhSpec);
            return keyGen.generateKeyPair();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static byte[] genSharedSecretKey(KeyPair keyPair,
            byte[] bytesPeerPublicKey) {
        PrivateKey privateKey = keyPair.getPrivate();

        try {
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(
                    bytesPeerPublicKey);
            KeyFactory keyFact = KeyFactory.getInstance("DH");
            PublicKey peerPublicKey = keyFact.generatePublic(x509KeySpec);

            KeyAgreement ka;

            ka = KeyAgreement.getInstance("DH");
            ka.init(privateKey);
            ka.doPhase(peerPublicKey, true);
            String algorithm = "AES";

            SecretKey secretKey = ka.generateSecret(algorithm);

            return secretKey.getEncoded();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static void main(String argv[]) {
            KeyPair akp = genKeyPair512();
            KeyPair bkp = genKeyPair512();

        System.out.println("Ali pub key: "
                + toRawHex(akp.getPublic().getEncoded()));
        System.out.println("Bob pub key: "
                + toRawHex(bkp.getPublic().getEncoded()));

            System.out.println("Ali pri key: "
                + toRawHex(akp.getPrivate().getEncoded()));
        System.out.println("Bob pri key: "
                + toRawHex(bkp.getPrivate().getEncoded()));

        byte[] apk = akp.getPublic().getEncoded();
        byte[] bpk = bkp.getPublic().getEncoded();

        byte[] as = genSharedSecretKey(akp, bpk);
        byte[] bs = genSharedSecretKey(bkp, apk);

    }
}

例如,它会生成输出:

Ali pub key: 3081DF30819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF03430002403BBCBF4052CD1CEF7A580A919AF75186CE0A624BC93AA47922C3822CE60A8CD10CE98550ABCA2D39DA2F09903C3D761B9A1C4AED185934FE5D08AD0CD097AA86
Bob pub key: 3081DF30819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF03430002400F119BC06E53F8C33D3F7C16473D1F9E001FABF4D619930C34945AA2C6D0A00CB9B332CEAF2C0C2FB61D3F568B9263B69A152410237F4D793F8B571C34AB37B7
Ali pri key: 3081E102010030819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF0442024043BA0B3C73EB7482B80DE98FA81A7E50B0DC2F5786CA62285655BD36CE012C056545DE5EED65736D9135EC9CD5148F8D68FF3C7B5CC62B2A1F7649698B26D1BE
Bob pri key: 3081E102010030819706092A864886F70D010301308189024100AC86AB9A1F921B251027BD10B93D0A8D9A260364974648E2543E8CD5C48DB4FFBEF0C3843465BA8DE20FFA36FFAF840B8CF26C9EB865BA184642A5F84606AEC502407961C6D7913FDF8A034593294FA52D6F8354E9EDFE3EDC8EF082D36662D69DFE8CA7DC7480121C98B9774DFF915FB710D79E1BCBA68C0D429CD6B9AD73C0EF20020201FF04420240485DDD7F5BDECA92FEE30D9D15211D274BC0FF7838B8B51E7894263CA65DB4E394033CE3E2146C0CD0CA74E2DB0EF95D01EE0DC4011A3EC6A8EC61CC2FDC5A44

所以,我有一个主要问题和另外两个问题:

  1. 为什么Alice和Bob之间的密钥(私有和公共)的一半以上字节是相等的?例如,Alice的私人信息以3081E102010030819706092A...开头,而Bob私人信息也以3081E102010030819706092A...开头。
  2. 为什么使用511创建DHParameterSpec而不是512(至少在网络上的大多数示例中)?
  3. 假设生成的密钥没有任何问题,是否还有其他我在这里缺少的东西,或者这个代码在通过互联网交换公钥时应该是安全的?
  4. 欢迎任何帮助。提前谢谢。

    编辑:第三个问题也适用于genSharedSecretKey()(也就是整个代码),虽然我没有调用/显示输出,因为它不相关。

2 个答案:

答案 0 :(得分:6)

  

为什么Alice和Bob之间的密钥(私有和公共)的一半以上字节是相等的?例如,Alice的私人信息以3081E102010030819706092A...开头,而Bob私信开始为3081E102010030819706092A...

当您致电getEncoded()时,您会收到ASN.1 format中的关键数据副本。取决于所表示的对象,在任何两个结构中通常存在一定量的重复数据。结构中的某个位置将是关键数据,与Alice和Bob不同。

实际上,正如CodesInChaos所述in the comments,ASN.1结构的第一部分包含了双方都相同的组参数。

答案 1 :(得分:4)

你收到重复密钥的原因 - 正如Duncan建议的那样 - 它是以ASN.1格式编码的。这确实解释了重复数据的数量,因为它包含您自己指定的参数pg - 查找十六进制字符串中的值。如果您想查看内容,只需将十六进制输出粘贴到在线ASN.1解码器中,例如找到here的解码器。

关于DH和(关键)尺寸,有很多要说的。可以在security.stackexchange.comcrypto.stackexchange.com上找到一些有趣的讨论。但最终,我们不知道;你输入了代码,所以我们应该问你如何以及为什么选择Diffie-Hellman参数。

至于第三个问题;要在互联网上分发公钥,您需要建立信任。你需要信任对方。 Diffie Hellman密钥协议本身并不建立信任;只与某些其他方的共享秘密。因此,您需要具有受信任证书的安全协议(例如TLS)或包含身份验证组件的密钥。然而,创建这样的协议需要很多知识;如果您不完全理解您刚刚向我们展示的代码,您将无法自己创建此类协议

请注意,如果双方都同意了一组参数,则可能没有必要发送整个编码的公钥;您可能只需要发送另一方不知道的值,以便另一方可以重建公钥。