【问题标题】:Writing SSH server over TCP通过 TCP 编写 SSH 服务器
【发布时间】:2016-04-17 01:00:35
【问题描述】:

出于学习目的,我试图弄清楚 SSH 协议是如何工作的。不幸的是,互联网上有一些信息(例如与 WebSocket 协议的比较),所以我关注RFC 4253 - The Secure Shell (SSH) Transport Layer Protocol


在客户端,我使用 Git Bash(仅用于测试)和一个通过 SSH 协议工作的简单克隆命令:

git clone git@localhost:test/test.git

在服务器端,我在端口 22 上打开了 TCP 套接字,并使用以下代码 sn-p 来解析传入的数据:

public class TestSSH {
    public static void main(String[] args) throws IOException{
        ServerSocket server = new ServerSocket(22);
        Socket client = server.accept();
        InputStream inputStream = client.getInputStream();
        OutputStream outputStream = client.getOutputStream();
        
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        PrintWriter writer = new PrintWriter(outputStream);
        String versionExchange = reader.readLine();
        System.out.println("VERSION EXCHANGE: " + versionExchange);
        writer.println("SSH-2.0-TestSSH_1.0");
        writer.flush();

        int packetLength = fetchPacketLength(inputStream);
        System.out.println("PACKET LENGTH: " + packetLength);

        int paddingLength = inputStream.read();
        System.out.println("PADDING LENGTH: " + paddingLength);

        int payloadLength = packetLength - paddingLength - 1;
        byte[] payload = fetch(inputStream, payloadLength);
        System.out.println("PAYLOAD: " + payload.length + " bytes");
        System.out.println(new String(payload, "UTF-8"));

        byte[] randomPadding = fetch(inputStream, paddingLength);
        System.out.println("RANDOM PADDING: " + randomPadding.length);
        server.close();
    }

    private static int fetchPacketLength(InputStream inputStream) throws IOException{
        int length = ((inputStream.read() & 0xFF) << 24);
        length |= ((inputStream.read() & 0xFF) << 16);
        length |= ((inputStream.read() & 0xFF) << 8);
        length |= (inputStream.read() & 0xFF);
        return length;
    }

    private static byte[] fetch(InputStream inputStream, int length) throws IOException{
        byte[] buffer = new byte[length];
        for (int i = 0; i < length; i++) {
            buffer[i] = (byte) inputStream.read();
        }
        return buffer;
    }
}

我在这里尝试实现的是在服务器端读取实际的 SSH 密钥以进行身份​​验证

此时我有以下输出:

VERSION EXCHANGE: SSH-2.0-OpenSSH_7.1
PACKET LENGTH: 1844
PADDING LENGTH: 10
PAYLOAD: 1833 bytes
D^?>?$??   ?curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384...
RANDOM PADDING: 10

如果我理解正确 - 有效负载应该包含解密 MAC(目标 SSH 密钥)的密码,但有效负载本身是一个长字符串,它的头部已损坏,带有一些不可解码的字节,这让我觉得我做错了什么.此外,我不知道在 MAC 结束之前我应该​​读取多少字节。

任何帮助表示赞赏。 谢谢。

【问题讨论】:

    标签: java sockets authentication encryption ssh


    【解决方案1】:

    如果我理解正确 - 有效负载应该包含解密 MAC(目标 SSH 密钥)的密码

    第一个数据包包含SSH_MSG_KEXINIT 数据包(RFC 中的7.1. Algorithm Negotiation)。

    但有效载荷本身是一个长字符串,它的头部已损坏,带有一些无法解码的字节,这让我觉得我做错了什么。

    来自 RFC,。它不是文本协议,而是二进制。第一个字节的含义如下:

    byte         SSH_MSG_KEXINIT
    byte[16]     cookie (random bytes)
    name-list    kex_algorithms
    name-list    server_host_key_algorithms
    

    此外,我不知道在 MAC 结束之前我应该​​读取多少字节。

    后面几行会描述:

    每个算法名称列表必须是逗号分隔的算法名称列表(请参阅 [SSH-ARCH] 中的算法命名和 [SSH-NUMBERS] 中的其他信息)。每个受支持(允许)的算法必须按优先顺序从高到低列出。

    从我对tcpdump 的运行来看,这些字段似乎被两个 NULL 字节分隔:

    [...]
    0x0110:  656c 6c6d 616e 2d67 726f 7570 312d 7368  ellman-group1-sh
    0x0120:  6131 0000 0167 6563 6473 612d 7368 6132  a1...gecdsa-sha2
    0x0130:  2d6e 6973 7470 3235 362d 6365 7274 2d76  -nistp256-cert-v
    [...]
    

    【讨论】:

      猜你喜欢
      • 2013-01-17
      • 1970-01-01
      • 2013-12-07
      • 1970-01-01
      • 2016-04-24
      • 2021-12-29
      • 2013-07-01
      • 2021-03-09
      • 2019-10-19
      相关资源
      最近更新 更多