【问题标题】:Java / Erlang: Diffie Hellman Key Exchange not WorkingJava / Erlang:Diffie Hellman 密钥交换不起作用
【发布时间】:2018-12-29 23:16:01
【问题描述】:

我想为我的应用程序实施我自己的加密。我对这篇文章进行了大修。直到今天我真的没有太多时间来解决它。希望这比我原来的更有用。在这个问题上花了很多时间。希望可以节省其他人的时间。

我在执行此操作时遇到了几个问题。直到最后我才意识到发生了什么。我得到了不同的共享秘密和后来的一些例外。

这是我尝试过的:

  • 使用了两种语言提供的内置设施。无法弄清楚如何将原始公钥转换为 Java 可以使用的形式。
  • 草草解决了这个问题,并使用简单的公式来计算每一方的公钥和私钥。 (从统计上看,这可能在大约 25% 的时间内有效……幸运的是,它没有。)
  • 深入了解 ITU 的 ASN.1 文档,并发送以与 Java 密钥类似的方式编码的 Erlang 公钥。通过将 Java 密钥保存到文件并使用十六进制编辑器来确定这一点。我没有回去进行长时间的测试。它确实摆脱了java.security.spec.InvalidKeySpecException: Inappropriate key specification。认为统计数据在这里也对我不利。秘密仍然不匹配。
  • 将所有数字从 Java 发送到 Erlang 端以计算密钥,使用 Java 数字共享密钥...相同的数字。有希望!!!
  • 开始仔细检查他们正在交流的数据。这有点耗时,因为 Erlang 将数据组织成无符号字节。 Eclipse IDE(可能需要更改某个设置)在字节数组中使用有符号字节,在 BigInteger 中使用有符号整数数组。

这就是我开始看到事物的地方。这一切都是通过多次迭代手动输入的,以确保我找到了正确的事件模式。在 Erlang 中,我看到我的公钥以 <<215, 101, 208, 153, 开头。 Java 端BigInteger 的第一个元素是681193318。字节数据读入的缓冲区为:[-41, 101, -48, -103。 (与 Erlang 的相同)。但是花时间将二进制字符串的前四个元素转换为整数...

<<I:32/signed-integer>> = <<215,101,208,153>>.

这产生-681193319 与大整数的681193318

我使用的代码很简单:

二郎“服务器”:

-module(echo).
-export([start/0]).

start() ->
    crypto:start(),
    spawn(fun () -> {ok, Sock} = gen_tcp:listen(12321, [binary, {packet, raw}]),
    echo_loop(Sock)
    end).

echo_loop(Sock) ->
    {ok, Conn} = gen_tcp:accept(Sock),
    io:format("Got connection: ~p~n", [Conn]),
    Handler = spawn(fun () -> handle(Conn) end),
    gen_tcp:controlling_process(Conn, Handler),
    echo_loop(Sock).

p() ->
    16#ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff.

g() ->
    2.

handle(Conn) ->
    receive
        {tcp, Conn, Yc} ->
            Xs = crypto:strong_rand_bytes(64),
            Ys = crypto:mod_pow(g(),Xs,p()),
            S = crypto:mod_pow(Yc, Xs, p()),

            AESKey = crypto:hash(sha256, S),

            gen_tcp:send(Conn, Ys),%KeyCert),
            handle(Conn);
        {tcp_closed, Conn} ->
            io:format("Connection closed: ~p~n", [Conn])
    end.

Java“客户端”:

public class MyProgram {
    private static Socket s;
    private static OutputStream out;
    private static InputStream in;
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        MessageDigest hash;
        byte buffer[] = new byte[1024];
        byte buf2[];
        int len = 0;
        byte[] aeskey;

        try {
            hash = MessageDigest.getInstance("SHA-256");
            byte    keybuffer[] = new byte[64];
            SecureRandom srnd = SecureRandom.getInstance("SHA1PRNG");
            BigInteger Xc, Yc, Sc, Ys;

            srnd.nextBytes(keybuffer);
            Xc = new BigInteger(keybuffer);
            Yc = new BigInteger("2").modPow(Xc, DiffieHellman.Group2.P);

            s = new Socket("localhost",12321);
            out = s.getOutputStream();
            in = s.getInputStream();

            out.write(Yc.toByteArray());
            out.flush();

            len = in.read(buffer);
            buf2 = new byte[len];
            System.arraycopy(buffer, 0, buf2, 0, len);

            Ys = new BigInteger(buf2);          
            Sc = Ys.modPow(Xc, DiffieHellman.Group2.P);
            aeskey = hash.digest(Sc.toByteArray());

            out.close();
            in.close();
            s.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }       
    }
}

出了什么问题?

【问题讨论】:

  • 当您通过套接字发送数据时,您无法知道它将被分成多少块。当您指定 {packet, raw} (或等效的 {packet, 0} )时,您是在告诉 erlang 您将负责将不确定数量的块组装成完整的数据,因此 erlang 只是将每个块放入单独的消息中。见这里:stackoverflow.com/questions/43957164/…。我不知道以上是否导致您的任何问题,但这是您需要解决的问题。
  • @7stud 谢谢。很容易改变。我认为虽然 TCP 会处理这个问题。我相信 TCP 应该是在操作系统中实现的最高级别。然而,我注意到两件事。 1) 密钥正确传输(仅 128 字节)2) 当我使用 {packet, 2} 时,我认为 erlang 可能期待我在 Java 应用程序中没有提供的东西并无限期挂起。我使用了1,这导致传输成功,但在 Java 端,密钥长了 1 个字节。管理更多内容。
  • 我认为 TCP 会处理这个问题。我认为 TCP 处理将一个块拆分为要发送的数据包,然后在接收端将数据包重新组装成一个块——但是您的数据被拆分成的块数由 网络缓冲区的状态决定>。阅读此:docs.python.org/3/howto/sockets.html 和此:stackoverflow.com/questions/17667903/…

标签: java erlang x509 diffie-hellman


【解决方案1】:

问题在于阅读但不理解文档。我花了很多时间在参考页面上,因为我不经常编码。在BigInteger 的文档中,我没有考虑太多这个特定的细节:

所有操作的行为都好像 Biginteger 表示在 补码符号...

我的原始代码中有两个地方出现了问题:

       Ys = new BigInteger(buf2);          
       Sc = Ys.modPow(Xc, DiffieHellman.Group2.P);

第一行的问题是,如果在第一个字节中设置了第 8 位,则整个buf2 数组需要在前面加上0x00 字节。第二行也有问题......直到执行以下行时才会变得明显:aeskey = hash.digest(Sc.toByteArray());

这里的问题是如果在结果的第一个字节中设置了第 8 位...0x00 被添加到它前面。这被转发到digest() 函数,但需要省略。

我的代码更改为以下内容并且可以正常工作::)

    len = in.read(buffer);
    buf2 = new byte[len+1];
    System.arraycopy(buffer, 0, buf2, 1, len);
    buf2[0] = 0;

    if(buf2[1] < 0)
        Ys = new BigInteger(buf2);
    else
        Ys = new BigInteger(Arrays.copyOfRange(buf2, 1, buf2.length));

    Sc = Ys.modPow(Xc, DiffieHellman.Group2.P);
    buffer = Sc.toByteArray();
    if(buffer[0] == 0)
        aeskey = hash.digest(Arrays.copyOfRange(buffer, 1, buffer.length));
    else
        aeskey = hash.digest(buffer);

这两行保持原样:

    Xc = new BigInteger(keybuffer);
    Yc = new BigInteger("2").modPow(Xc, DiffieHellman.Group2.P);

这是因为私钥可以是“任意随机数”。如有必要,0x00 字节将添加到第二行中客户端的公钥之前。然而,Erlang 将整数解释为 big-endian,任何前导的 0x00 字节最终都无关紧要,因为它不会影响数值,因此在执行 crypto:mod_pow() 时会影响结果。

欢迎评论如何改进代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2014-05-13
    • 1970-01-01
    • 2017-10-08
    相关资源
    最近更新 更多