サンプルプログラムでの実装


https://github.com/haruyama/ssh_client_sample/blob...

  private def negotiateAlgorithm(transportManager: TransportManager) = {
    //以後はSSHのパケットでやりとりされる

    //クライアントから KEXINIT メッセージを送る
    //この実装はアルゴリズムをそれぞれ1つのみサポートし,
    //アルゴリズムに依存した実装を行なう
    val clientKexinit = TransportMessageBuilder.buildKexinit(
      List("diffie-hellman-group1-sha1"), List("ssh-rsa"),
      List("aes128-ctr"), List("aes128-ctr"),
      List("hmac-sha1"), List("hmac-sha1"),
      List("none"), List("none"),
      List(), List(), false)
    transportManager.sendMessage(clientKexinit)

    //サーバから KEXINIT メッセージを受け取る
    val serverKexinit = transportManager.recvMessage().asInstanceOf[Kexinit]

    //アルゴリズムのネゴシエーションが本来は必要
    //この実装では, クライアント側が1つずつ指定したアルゴリズムが
    //受け入れられたと仮定して先に進む

    //クライアントとサーバのKEXINITメッセージが鍵の生成に必要なので
    //以下を返す
    (clientKexinit, serverKexinit)
  }

解説

KEXINIT メッセージを交換


以後はSSHのパケットでやりとりされます.

以後の通信で用いるアルゴリズムを交渉するために, クライアントとサーバはサポートしているアルゴリズムを KEXINIT メッセージで交換します.

KEXINIT メッセージは以下の形式です. name-list で各アルゴリズムを優先度の降順に列挙します.

      byte         SSH_MSG_KEXINIT
      byte[16]     cookie (random bytes)
      name-list    kex_algorithms
      name-list    server_host_key_algorithms
      name-list    encryption_algorithms_client_to_server
      name-list    encryption_algorithms_server_to_client
      name-list    mac_algorithms_client_to_server
      name-list    mac_algorithms_server_to_client
      name-list    compression_algorithms_client_to_server
      name-list    compression_algorithms_server_to_client
      name-list    languages_client_to_server
      name-list    languages_server_to_client
      boolean      first_kex_packet_follows
      uint32       0 (reserved for future extension)

SSH-2.0-OpenSSH_5.8p1 Debian-4 から送られる KEXINIT

サンプルプログラムで println(serverKexinit) とすると, サーバから送られる KEXINIT メッセージがダンプできます. 私の環境での出力を整形したものを示します.

Kexinit(
/* SSH_MSG_KEXINIT                         */ SSHByte(20),
/* cookie                                  */ (略),
/* kex_algorithms                          */ SSHNameList(WrappedArray(ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group-exchange-sha1, diffie-hellman-group14-sha1, diffie-hellman-group1-sha1)),
/* server_host_key_algorithms              */ SSHNameList(WrappedArray(ssh-rsa, ssh-dss)),
/* encryption_algorithms_client_to_server  */ SSHNameList(WrappedArray(aes128-ctr, aes192-ctr, aes256-ctr, arcfour256, arcfour128, aes128-cbc, 3des-cbc, blowfish-cbc, cast128-cbc, aes192-cbc, aes256-cbc, arcfour, rijndael-cbc@lysator.liu.se)),
/* encryption_algorithms_server_to_client  */ SSHNameList(WrappedArray(aes128-ctr, aes192-ctr, aes256-ctr, arcfour256, arcfour128, aes128-cbc, 3des-cbc, blowfish-cbc, cast128-cbc, aes192-cbc, aes256-cbc, arcfour, rijndael-cbc@lysator.liu.se)),
/* mac_algorithms_client_to_server         */ SSHNameList(WrappedArray(hmac-md5, hmac-sha1, umac-64@openssh.com, hmac-ripemd160, hmac-ripemd160@openssh.com, hmac-sha1-96, hmac-md5-96)),
/* mac_algorithms_server_to_client         */ SSHNameList(WrappedArray(hmac-md5, hmac-sha1, umac-64@openssh.com, hmac-ripemd160, hmac-ripemd160@openssh.com, hmac-sha1-96, hmac-md5-96)),
/* compression_algorithms_client_to_server */ SSHNameList(WrappedArray(none, zlib@openssh.com)),
/* compression_algorithms_server_to_client */ SSHNameList(WrappedArray(none, zlib@openssh.com)),
/* languages_client_to_server              */ SSHNameList(WrappedArray()),
/* languages_server_to_client              */ SSHNameList(WrappedArray()),
/* first_kex_packet_follows                */ SSHBoolean(false),
/* 0 (reserved for future extension)       */SSHUInt32(0)
)
kex_algorithms (鍵交換アルゴリズム)

このあとで行なう鍵交換のアルゴリズムを列挙します. 楕円曲線 Diffie-Hellman 鍵交換やDiffie-Hellman 鍵交換を用いるアルゴリズムを利用します.

サンプルプログラムでは, 「diffie-hellman-group1-sha1」 を指定しています.
server_host_key_algorithms (サーバホスト鍵アルゴリズム)

サーバのホスト鍵アルゴリズムを列挙します. 楕円曲線 DSA, RSA, DSA(DSS) を利用します.

サンプルプログラムでは, 「ssh-rsa」 を指定しています.
encryption_algorithms_client_to_server, encryption_algorithms_server_to_client (暗号アルゴリズム)

暗号アルゴリズムを列挙します. 通信の方向によって別のアルゴリズムを利用することができます. 同じアルゴリズムを利用する場合でも方向によって鍵とIVは異なります. AESや arcfour などを利用します.

サンプルプログラムでは, 「aes128-ctr」 を指定しています.
mac_algorithms_client_to_server, mac_algorithms_server_to_client (MACアルゴリズム)

MACアルゴリズムを列挙します. 通信の方向によって別のアルゴリズムを利用することができます. 同じアルゴリズムを利用する場合でも方向によって鍵は異なります. HMAC, UMACが利用できます.

サンプルプログラムでは, 「hmac-sha1」 を指定しています.
compression_algorithms_client_to_server, compression_algorithms_server_to_client (圧縮アルゴリズム)

圧縮アルゴリズムを列挙します. 通信の方向によって別のアルゴリズムを利用することができます.

サンプルプログラムでは, 「none」(圧縮なし) を指定しています.

交換した KEXINIT に基づき各アルゴリズムを決定


クライアントとサーバは交換した KEXINIT に基づき各アルゴリズムを決定します. 決定のための方法は, RFC4253に記述されています.

サンプルプログラムでは1つのアルゴリズムのみを指定しているので, そのアルゴリズムが利用できると仮定して先に進んでいます.

KEXINIT メッセージの返却


KEXINIT メッセージの内容は暗号やMACの鍵やIVの生成に利用します. サンプルプログラムでは, 2つの KEXINIT メッセージをタプルで返却しています.

メンバーのみ編集できます