Más contenido relacionado La actualidad más candente (20) Similar a SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File) (20) Más de Tetsuya Hasegawa (20) SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)1. SSH(Secure SHell)の勉強資料です。
SSHのパケットを復号することになり、その過程で調べたり作ったりしたものをサマった資料です。
● 使用ソフト
sshクライアント&サーバー libssh 0.8.6
復号 openssl 1.0.2q
間違っていたらコメントをお願いします。
SSHの勉強資料
Table of Contents
Table of Contents 1
SSH復号ツールを作ろう 3
1. デバッグ環境構築 3
1.1 libsshをビルド&インストール 3
1.2 sshクライアント 4
1.2.1 作成 4
1.2.2 ビルド 5
1.2.3 実行&トレース 5
1.3 sshサーバー 6
1.3.1 作成 6
1.3.2 ビルド 10
1.3.3 実行&トレース 10
1.4 libssh改造 10
1.4.1 処理の流れ 10
1.4.2 改造ソース 11
1.4.3 デバッグメモ 12
2. 復号プログラム作成 12
2.1 作成 12
2.2 ビルド 13
2.3 実行 13
2.3.1 パケット準備 13
2.3.2 復号プログラムの実行 14
2.4 考察 17
RFC編 18
SSH関連のRFCリスト 18
RFC 4251, The Secure Shell (SSH) Protocol Architecture 20
1. Introduction 20
4. Architecture 20
4.1. Host Keys 20
4.2. Extensibility 20
4.3. Policy Issues 21
4.4. Security Properties 21
5. Data Type Representations Used in the SSH Protocols 21
1
2. 7. Message Numbers 22
RFC 4253, The Secure Shell (SSH) Transport Layer Protocol 23
1. Introduction 23
4. Connection Setup 23
4.1. Use over TCP/IP 23
6. Binary Packet Protocol 23
6.1. Maximum Packet Length 24
6.2. Compression 24
6.3. Encryption 24
6.4. Data Integrity 25
6.5. Key Exchange Methods 26
6.6. Public Key Algorithms 26
7. Key Exchange 27
7.1. Algorithm Negotiation 28
7.2. Output from Key Exchange 29
7.3. Taking Keys Into Use 30
8. Diffie-Hellman Key Exchange 30
9. Key Re-Exchange 31
10. Service Request 31
11. Additional Messages 32
11.1. Disconnection Message 32
11.2. Ignored Data Message 33
11.3. Debug Message 33
11.4. Reserved Messages 33
12. Summary of Message Numbers 34
RFC 4344, The Secure Shell (SSH) Transport Layer Encryption Modes 35
1. Introduction 35
3. Rekeying 35
3.1. First Rekeying Recommendation 35
3.2. Second Rekeying Recommendation 35
4. Encryption Modes 35
2
4. 1.2 sshクライアント
1.2.1 作成
GDBでデバッグする用のsshクライアントを作成する。
localhost:2222のsshサーバーに接続して認証用のユーザー名、パスワードを送信してcatして切断する。
catしているファイルは2,000byteのテキストファイル。
詳細はlibsshのチュートリアルとAPIリファレンス参照。
http://api.libssh.org/master/libssh_tutorial.html
/* sshc.c */
#define LIBSSH_STATIC 1
#include <libssh/libssh.h>
#include <stdlib.h>
#include <stdio.h>
int main() {
ssh_session session;
ssh_channel channel;
int port = 2222;
char userpass[] = "hasegawa";
char buffer[4096];
unsigned int nbytes;
const char *pcap_file="debug.client.pcap";
ssh_pcap_file pcap;
int state;
session = ssh_new();
system("tcpdump -i any -w sshc.pcap &");
system("sleep 3");
ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
ssh_options_set(session, SSH_OPTIONS_PORT, &port);
ssh_options_set(session, SSH_OPTIONS_USER, userpass);
pcap=ssh_pcap_file_new();
ssh_pcap_file_open(pcap,pcap_file);
ssh_set_pcap_file(session,pcap);
ssh_connect(session);
ssh_userauth_password(session, NULL, userpass);
channel = ssh_channel_new(session);
ssh_channel_open_session(channel);
state = ssh_channel_request_exec(channel, "cat /hasegawa.txt");
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0) {
fwrite(buffer, 1, nbytes, stdout);
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
ssh_disconnect(session);
ssh_free(session);
ssh_pcap_file_free(pcap);
ssh_finalize();
system("sleep 3");
system("killall tcpdump");
return 0;
}
4
5. 1.2.2 ビルド
# gcc -DLIBSSH_STATIC sshc.c -lssh -g -O0 -o sshc
1.2.3 実行&トレース
# gdb ./sshc
(gdb) b main
Breakpoint 1 at 0xb65: file sshc.c, line 6.
(gdb) r
Breakpoint 1, main () at sshc.c:6
6 int main() {
(gdb) s
9 int port = 2222;
(gdb)
10 char userpass[] = "hasegawa";
(gdb)
13 session = ssh_new();
(gdb)
ssh_new () at /root/libssh/libssh-0.8.6/src/session.c:60
ちなみに「WITH_DEBUG_CRYPTO=ON」でlibsshをビルドすると、暗号化/復号のために必要なIV、Keyが表示される。
暗号化アルゴリズム種別は出力されないので「libssh-0.8.6srcdh.c::ssh_generate_session_keys()」に手を入
れて表示する。
実行例)クライアントのログ
クライアントからサーバー方向の通信(暗号化)はAES-256-CTRで暗号化されている。なのでIV, Keyが256 bit。圧縮は無
し。
サーバーからクライアント方向の通信(復号)はAES-256-CTRで暗号化されている。なのでIV, Keyが256 bit。圧縮は無
し。
Encrypt IV:
3b:1a:a7:66:20:a3:3b:de:71:ed:9c:3d:d3:25:39:44:bd:62:66:f0:cb:b5:e3:37:80:e5:db:47:fb:04:87
:02
Decrypt IV:
6e:57:fd:ee:d4:5e:dc:d0:9c:64:44:61:3b:5f:ef:a5:8b:da:35:e7:ed:6d:c8:14:38:d1:eb:ce:19:1b:d2
:eb
Encryption key:
d8:8f:39:02:4c:cc:46:43:6d:9e:b9:3e:13:21:ed:f9:e4:af:56:87:5d:5c:40:2c:42:85:09:a4:f6:ab:cc
:67
Decryption key:
ad:d0:6b:d0:12:17:0e:73:e3:40:90:e7:ec:05:57:3e:ea:98:bc:f9:cd:3e:9d:1b:bf:a2:d1:4d:e2:d3:a5
:98
Encryption MAC:
0f:b4:ae:91:af:af:e7:55:69:49:dd:0f:93:34:53:73:c6:ca:e4:31:08:a1:d3:48:9f:e3:4d:8e:c1:80:f0
:50
Decryption MAC:
19:9e:2c:da:33:ee:90:84:3d:31:8d:31:67:c8:d7:bd:e4:2d:36:77:54:bb:f6:1f:23:d3:03:a4:f1:44:bf
:39
Encrypt: aes256-ctr
Decryption: aes256-ctr
do_compress_out: 0
do_compress_in: 0
5
6. 1.3 sshサーバー
1.3.1 作成
GDBでデバッグする用のsshサーバーを作成する。
localhost:2222でリッスン、クライアントをパスワード認証してコマンドの結果を返す。
詳細はlibsshのチュートリアルとAPIリファレンス参照。
http://api.libssh.org/master/libssh_tutorial.html
/* sshs.c */
#include <libssh/callbacks.h>
#include <libssh/server.h>
#include <poll.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <stdio.h>
#define BUF_SIZE 4096
/* A userdata struct for channel. */
struct channel_data_struct {
pid_t pid;
socket_t pty_master;
socket_t pty_slave;
socket_t child_stdin;
socket_t child_stdout;
socket_t child_stderr;
ssh_event event;
struct winsize *winsize;
};
/* A userdata struct for session. */
struct session_data_struct {
ssh_channel channel;
int auth_attempts;
int authenticated;
};
static int exec_request(ssh_session session, ssh_channel channel,
const char *command, void *userdata) {
struct channel_data_struct *cdata = (struct channel_data_struct *) userdata;
int in[2], out[2], err[2];
pipe(in);
pipe(out);
pipe(err);
if((cdata->pid = fork()) == 0) {
close(in[1]);
close(out[0]);
close(err[0]);
dup2(in[0], STDIN_FILENO);
dup2(out[1], STDOUT_FILENO);
dup2(err[1], STDERR_FILENO);
close(in[0]);
close(out[1]);
close(err[1]);
execl("/bin/sh", "sh", "-c", command, NULL);
exit(0);
}
close(in[0]);
6
7. close(out[1]);
close(err[1]);
cdata->child_stdin = in[1];
cdata->child_stdout = out[0];
cdata->child_stderr = err[0];
return SSH_OK;
}
static int auth_password(ssh_session session, const char *user,
const char *pass, void *userdata) {
struct session_data_struct *sdata = (struct session_data_struct *) userdata;
if (strcmp(user, "hasegawa") == 0 && strcmp(pass, "hasegawa") == 0) {
sdata->authenticated = 1;
return SSH_AUTH_SUCCESS;
}
sdata->auth_attempts++;
return SSH_AUTH_DENIED;
}
static ssh_channel channel_open(ssh_session session, void *userdata) {
struct session_data_struct *sdata = (struct session_data_struct *) userdata;
sdata->channel = ssh_channel_new(session);
return sdata->channel;
}
static int process_stdout(socket_t fd, int revents, void *userdata) {
char buf[BUF_SIZE];
int n = -1;
ssh_channel channel = (ssh_channel) userdata;
if (channel != NULL && (revents & POLLIN) != 0) {
n = read(fd, buf, BUF_SIZE);
if (n > 0) {
ssh_channel_write(channel, buf, n);
}
}
return n;
}
static int process_stderr(socket_t fd, int revents, void *userdata) {
char buf[BUF_SIZE];
int n = -1;
ssh_channel channel = (ssh_channel) userdata;
if (channel != NULL && (revents & POLLIN) != 0) {
n = read(fd, buf, BUF_SIZE);
if (n > 0) {
ssh_channel_write_stderr(channel, buf, n);
}
}
return n;
}
static void handle_session(ssh_event event, ssh_session session) {
int n, rc;
struct winsize wsize = {
.ws_row = 0,
.ws_col = 0,
.ws_xpixel = 0,
7
8. .ws_ypixel = 0
};
struct channel_data_struct cdata = {
.pid = 0,
.pty_master = -1,
.pty_slave = -1,
.child_stdin = -1,
.child_stdout = -1,
.child_stderr = -1,
.event = NULL,
.winsize = &wsize
};
struct session_data_struct sdata = {
.channel = NULL,
.auth_attempts = 0,
.authenticated = 0
};
struct ssh_channel_callbacks_struct channel_cb = {
.userdata = &cdata,
.channel_exec_request_function = exec_request,
};
struct ssh_server_callbacks_struct server_cb = {
.userdata = &sdata,
.auth_password_function = auth_password,
.channel_open_request_session_function = channel_open,
};
ssh_callbacks_init(&server_cb);
ssh_callbacks_init(&channel_cb);
ssh_set_server_callbacks(session, &server_cb);
ssh_handle_key_exchange(session);
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);
ssh_event_add_session(event, session);
n = 0;
while (sdata.authenticated == 0 || sdata.channel == NULL) {
ssh_event_dopoll(event, 100);
n++;
}
ssh_set_channel_callbacks(sdata.channel, &channel_cb);
do {
ssh_event_dopoll(event, -1);
if (cdata.event != NULL || cdata.pid == 0) {
continue;
}
cdata.event = event;
if (cdata.child_stdout != -1) {
ssh_event_add_fd(event, cdata.child_stdout, POLLIN, process_stdout,
sdata.channel);
}
if (cdata.child_stderr != -1) {
ssh_event_add_fd(event, cdata.child_stderr, POLLIN, process_stderr,
sdata.channel);
}
} while(ssh_channel_is_open(sdata.channel) &&
(cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0));
close(cdata.pty_master);
close(cdata.child_stdin);
8
9. close(cdata.child_stdout);
close(cdata.child_stderr);
ssh_event_remove_fd(event, cdata.child_stdout);
ssh_event_remove_fd(event, cdata.child_stderr);
if (kill(cdata.pid, 0) < 0 && WIFEXITED(rc)) {
rc = WEXITSTATUS(rc);
ssh_channel_request_send_exit_status(sdata.channel, rc);
} else if (cdata.pid > 0) {
kill(cdata.pid, SIGKILL);
}
ssh_channel_send_eof(sdata.channel);
ssh_channel_close(sdata.channel);
for (n = 0; n < 50 && (ssh_get_status(session) & (SSH_CLOSED | SSH_CLOSED_ERROR)) == 0;
n++) {
ssh_event_dopoll(event, 100);
}
}
int main(int argc, char **argv) {
ssh_bind sshbind;
ssh_session session;
ssh_event event;
int port = 2222;
const char *pcap_file="debug.server.pcap";
ssh_pcap_file pcap;
ssh_init();
sshbind = ssh_bind_new();
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port);
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, "ssh_host_rsa_key");
ssh_bind_listen(sshbind);
while (1) {
session = ssh_new();
pcap = ssh_pcap_file_new();
ssh_pcap_file_open(pcap, pcap_file);
ssh_set_pcap_file(session, pcap);
ssh_bind_accept(sshbind, session);
if(fork() == 0) {
ssh_bind_free(sshbind);
event = ssh_event_new();
handle_session(event, session);
ssh_event_free(event);
ssh_disconnect(session);
ssh_free(session);
ssh_pcap_file_free(pcap);
exit(0);
}
ssh_disconnect(session);
ssh_free(session);
}
ssh_bind_free(sshbind);
ssh_finalize();
return 0;
}
Host認証用のRSA keyを生成する。
#ssh-keygen -t rsa -b 2048 -f ssh_host_rsa_key -N ''
9
10. 1.3.2 ビルド
gcc -DLIBSSH_STATIC -DWOTH_PCAP sshs.c -lssh -g -O0 -o sshs
1.3.3 実行&トレース
実行例)サーバーのログ
サーバーからクライアント方向の通信(暗号化)はAES-256-CTRで暗号化されている。なのでIV, Keyが256 bit。圧縮は無
し。
クライアントからサーバー方向の通信(復号)はAES-256-CTRで暗号化されている。なのでIV, Keyが256 bit。圧縮は無
し。
暗号化/復号のKey、IVがクライアントの例を逆になっている。
Encrypt IV:
6e:57:fd:ee:d4:5e:dc:d0:9c:64:44:61:3b:5f:ef:a5:8b:da:35:e7:ed:6d:c8:14:38:d1:eb:ce:19:1b:d2
:eb
Decrypt IV:
3b:1a:a7:66:20:a3:3b:de:71:ed:9c:3d:d3:25:39:44:bd:62:66:f0:cb:b5:e3:37:80:e5:db:47:fb:04:87
:02
Encryption key:
ad:d0:6b:d0:12:17:0e:73:e3:40:90:e7:ec:05:57:3e:ea:98:bc:f9:cd:3e:9d:1b:bf:a2:d1:4d:e2:d3:a5
:98
Decryption key:
d8:8f:39:02:4c:cc:46:43:6d:9e:b9:3e:13:21:ed:f9:e4:af:56:87:5d:5c:40:2c:42:85:09:a4:f6:ab:cc
:67
Encryption MAC:
19:9e:2c:da:33:ee:90:84:3d:31:8d:31:67:c8:d7:bd:e4:2d:36:77:54:bb:f6:1f:23:d3:03:a4:f1:44:bf
:39
Decryption MAC:
0f:b4:ae:91:af:af:e7:55:69:49:dd:0f:93:34:53:73:c6:ca:e4:31:08:a1:d3:48:9f:e3:4d:8e:c1:80:f0
:50
Encrypt: aes256-ctr
Decryption: aes256-ctr
do_compress_out: 0
do_compress_in: 0
1.4 libssh改造
SSH_MSG_NEWKEYSを受信し、Shared secret K、 Exchange hash HからKey、IVを生成する箇所にそれらをダンプす
る処理を追加する。
1.4.1 処理の流れ
処理内容 ソース
①Key, IVを生成、設定 srcpacket_cb.c::SSH_PACKET_CALLBACK(ssh_packet_newkeys)
SSH_MSG_NEWKEYS受信時のコールバック。主に以下の処理を実行する。
srcdh.c::ssh_generate_session_keys(ssh_session session)
Shared secret K、 Exchange hash HからKey、IVを生成。
両方向のIV、Key、MACをssh_crypto_structにセット。
srcpacket_cb.c::SSH_PACKET_CALLBACK(ssh_packet_newkeys)
set_decrypt_key()、set_encrypt_key()でKey、IVを設定
10
11. ②受信パケットの復号 srcpacket_crypt.c::ssh_packet_decrypt()
受信したパケットを復号する。アルゴリズム毎に復号関数は異なる。
srclibcrypto.c::struct ssh_ciphertab[]
アルゴリズム定義の構造体。名前、暗号化/復号の関数等が設定される。
(例)AES-256-CTRの場合は下記。
.name = "aes256-ctr",
.blocksize = 16,
.ciphertype = SSH_AES256_CTR,
.keysize = 256,
.set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key,
.encrypt = evp_cipher_encrypt,
.decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup
1.4.2 改造ソース
改造内容は下記。①のKey、IVを生成後に値をダンプする。
diff src/dh.c src/dh_org.c
1159,1198d1158
< {
< const char *filename_C[]= {"/EIV_C", "/DIV_C", "/EKEY_C", "/DKEY_C"};
< const char *filename_S[]= {"/EIV_S", "/DIV_S", "/EKEY_S", "/DKEY_S"};
< const char *ALGO_file_C="/ALGO_C";
< const char *ALGO_file_S="/ALGO_S";
< FILE *fp;
<
< for(int i=0; i<4; i++) {
< if(session->client)
< fp = fopen(filename_C[i], "wb");
< else
< fp = fopen(filename_S[i], "wb");
< switch(i) {
< case 0:
< fwrite ((const void *)crypto->encryptIV, crypto->digest_len, 1, fp);
< break;
< case 1:
< fwrite ((const void *)crypto->decryptIV, crypto->digest_len, 1, fp);
< break;
< case 2:
< fwrite ((const void *)crypto->encryptkey, crypto->out_cipher->keysize / 8, 1, fp);
< break;
< case 3:
< fwrite ((const void *)crypto->decryptkey, crypto->in_cipher->keysize / 8, 1, fp);
< break;
< }
< fclose(fp);
< }
< if(session->client)
< fp= fopen(ALGO_file_C, "w");
< else
< fp= fopen(ALGO_file_S, "w");
< fprintf(fp, "Encrypt: %sn", crypto->out_cipher->name);
< fprintf(fp, "Decryption: %sn", crypto->in_cipher->name);
< fprintf(fp, "do_compress_out: %dn", crypto->do_compress_out);
< fprintf(fp, "do_compress_in: %dn", crypto->do_compress_in);
< fclose(fp);
11
12. 1.4.3 デバッグメモ
デバッグしたときに改造したソースメモ。
受信した暗号化パケット+Decrption Key+復号したパケットの表示
srcpacket_crypt.c::ssh_packet_decrypt()
ssh_print_hexa("Decryption key", session->current_crypto->decryptkey, 256/8);//Dec Key
ssh_print_hexa("Source", source+start, 32);//受信した暗号化パケット
crypto->decrypt(crypto, source + start, destination, encrypted_size);
ssh_print_hexa("Destination", destination, 32);//復号したパケット
シーケンス番号の表示
srcpacket_crypt.c::ssh_packet_hmac_verify()
ssh_print_hexa("seq",(unsigned char *)&seq,sizeof(uint32_t));
2. 復号プログラム作成
2.1 作成
サーバーパケットの復号プログラムを作成する。
インプットは、サーバー→クライアント方向のSSHパケットのバイナリ、クライアントのDecryption keyとDecryption IV
。
前章のDecription Key、Decription IV、暗号化アルゴリズム名を使用して復号プログラムを作成する。
詳細はチュートリアルとAPIリファレンス参照。
https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption
/* ssl.c */
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <string.h>
EVP_CIPHER_CTX *ctx;
void setkey(unsigned char *key, char *iv)
{
EVP_CIPHER_CTX_init(ctx);
ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv);
}
int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned
char *plaintext, char *iv)
{
int len;
int plaintext_len;
EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len);
plaintext_len = len;
EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
plaintext_len += len;
return plaintext_len;
}
int main (void)
{
unsigned char sshkey[32], sshiv[32];
FILE *fp;
char filename[32];
unsigned char ciphertext[4096], decryptedtext[4096];
12
13. int decryptedtext_len, ciphertext_len;
unsigned int size;
fp = fopen("/DIV_C", "rb");
fread(sshiv, 32, 1, fp);
fclose(fp);
fp = fopen("/DKEY_C", "rb");
fread(sshkey, 32, 1, fp);
fclose(fp);
OpenSSL_add_all_algorithms();
setkey((unsigned char *)sshkey, (unsigned char *)sshiv);
for(int i=0; i<7; i++) {
sprintf(filename, "/hoge%d.bin", i);
fp = fopen(filename, "rb");
size = 4;
fread(ciphertext, size, 1, fp);
decryptedtext_len = decrypt(ciphertext, size, (unsigned char *)sshkey,
decryptedtext, (unsigned char *)sshiv);
printf("Decrypt Packet#%d Sizen", i);
BIO_dump_fp (stdout, (const char *)decryptedtext, decryptedtext_len);
size = (decryptedtext[2]<<8) + decryptedtext[3];
fread(ciphertext, size, 1, fp);
decryptedtext_len = decrypt(ciphertext, size, (unsigned char *)sshkey,
decryptedtext, (unsigned char *)sshiv);
printf("Decrypt Packet#%dn", i);
BIO_dump_fp (stdout, (const char *)decryptedtext, decryptedtext_len);
fclose(fp);
}
EVP_cleanup();
ERR_free_strings();
return 0;
}
2.2 ビルド
#gcc ssl.c -lssl -lcrypto -o decoder
2.3 実行
2.3.1 パケット準備
sshサーバープログラム、sshクライアントプログラムを実行する。Key(/DKEY_C)、IV(/DIV_C)、暗号化されたパケッ
トのキャプチャ(sshc.pcap)が出力される。
sshc.pcapからバイナリをエクスポートする。/配下にキャプチャした順番にhoge0.bin、hoge1.bin、、、をエクスポート
する。
13
14. 2.3.2 復号プログラムの実行
実行結果とサーバーが送信した平文パケットを以下に示す。復号されていることがわかる。
● パケット#0
復号プログラムの実行結果
Decrypt Packet#0 Size
0000 - 00 00 00 9c ....
Decrypt Packet#0
0000 - 0e 07 00 00 00 01 00 00-00 0f 73 65 72 76 65 72 ..........server
0010 - 2d 73 69 67 2d 61 6c 67-73 00 00 00 71 73 73 68 -sig-algs...qssh
0020 - 2d 65 64 32 35 35 31 39-2c 65 63 64 73 61 2d 73 -ed25519,ecdsa-s
0030 - 68 61 32 2d 6e 69 73 74-70 32 35 36 2c 65 63 64 ha2-nistp256,ecd
0040 - 73 61 2d 73 68 61 32 2d-6e 69 73 74 70 33 38 34 sa-sha2-nistp384
0050 - 2c 65 63 64 73 61 2d 73-68 61 32 2d 6e 69 73 74 ,ecdsa-sha2-nist
0060 - 70 35 32 31 2c 73 73 68-2d 72 73 61 2c 72 73 61 p521,ssh-rsa,rsa
0070 - 2d 73 68 61 32 2d 35 31-32 2c 72 73 61 2d 73 68 -sha2-512,rsa-sh
0080 - 61 32 2d 32 35 36 2c 73-73 68 2d 64 73 73 02 d1 a2-256,ssh-dss..
0090 - 94 54 8d 07 f6 0e f6 17-61 4b b8 fe .T......aK..
サーバーが送信した平文パケット
● パケット#1
復号プログラムの実行結果
Decrypt Packet#1 Size
0000 - 00 00 00 1c ....
14
15. Decrypt Packet#1
0000 - 0a 06 00 00 00 0c 73 73-68 2d 75 73 65 72 61 75 ......ssh-userau
0010 - 74 68 fc f1 ca ba 2b 29-e1 ac d7 4c th....+)...L
サーバーが送信した平文パケット
● パケット#2
復号プログラムの実行結果
Decrypt Packet#2 Size
0000 - 00 00 00 0c ....
Decrypt Packet#2
0000 - 0a 34 15 e0 72 88 91 1e-e1 06 dd 55 .4..r......U
サーバーが送信した平文パケット
● パケット#3
復号プログラムの実行結果
Decrypt Packet#3 Size
0000 - 00 00 00 1c ....
Decrypt Packet#3
0000 - 0a 5b 00 00 00 2b 00 00-00 2b 00 00 7d 00 00 00 .[...+...+..}...
0010 - 88 b8 48 2b ab da bc 05-56 57 c6 89 ..H+....VW..
サーバーが送信した平文パケット
● パケット#4
復号プログラムの実行結果
Decrypt Packet#4 Size
0000 - 00 00 00 0c ....
Decrypt Packet#4
0000 - 06 63 00 00 00 2b f4 f1-68 f7 90 5f .c...+..h.._
サーバーが送信した平文パケット
15
16. ● パケット#5
復号プログラムの実行結果
Decrypt Packet#5 Size
0000 - 00 00 07 ec ....
Decrypt Packet#5
0000 - 11 5e 00 00 00 2b 00 00-07 d1 30 31 32 33 34 35 .^...+....012345
0010 - 36 37 38 39 30 31 32 33-34 35 36 37 38 39 30 31 6789012345678901
0020 - 32 33 34 35 36 37 38 39-30 31 32 33 34 35 36 37 2345678901234567
~省略~
07c0 - 34 35 36 37 38 39 30 31-32 33 34 35 36 37 38 39 4567890123456789
07d0 - 30 31 32 33 34 35 36 37-38 39 0a 39 9d 4c 4e 67 0123456789.9.LNg
07e0 - b8 44 7c 1d 2d ba 5e 95-3e 02 78 b4 .D|.-.^.>.x.
サーバーが送信した平文パケット
● パケット#6
復号プログラムの実行結果
Decrypt Packet#6 Size
0000 - 00 00 00 2c ...,
Decrypt Packet#6
0000 - 12 62 00 00 00 2b 00 00-00 0b 65 78 69 74 2d 73 .b...+....exit-s
0010 - 74 61 74 75 73 00 00 00-00 00 1f df 3e 23 01 d3 tatus.......>#..
0020 - 72 eb c7 58 04 88 9c ed-93 75 17 05 r..X.....u..
サーバーが送信した平文パケット
16
18. RFC編
下線、ハイライトは個人的に重要そうなところ。斜体、#はメモ。
原文のMUST/REQUIRED/SHALL/SHOULD/MAY/OPTIONAL等のRFC2119用語は原文のまま残しています。
MUST、REQUIRED、SHALL:絶対的な要求事項
MUST NOT:絶対的な禁止事項
SHOULD、RECOMMENDED:慎重に重要性を判断するべき要求事項
SHOULD NOT、NOT RECOMMENDED:慎重に重要性を判断するべき禁止事項
MAY、OPTIONAL:オプション。
SSH関連のRFCリスト
RFC 概要
RFC 4250
The Secure Shell (SSH) Protocol Assigned Numbers
https://tools.ietf.org/html/rfc4250
SSHのプロトコルで使用される番号の定義。
RFC 4251
The Secure Shell (SSH) Protocol Architecture
https://tools.ietf.org/html/rfc4251
SSHのアーキテクチャーの概要。
3つの主要コンポーネントであるTransport Layer
Protocol、User Authentication Protocol、
Coonection Protocolの概要。
RFC 4252
The Secure Shell (SSH) Authentication Protocol
https://tools.ietf.org/html/rfc4252
Authentication Protocolの詳細。
Public key、Password、Host-based client
authentication method。
RFC 4253
The Secure Shell (SSH) Transport Layer Protocol
https://tools.ietf.org/html/rfc4253
Transport Layer Protocolの詳細。
RFC 4254
The Secure Shell (SSH) Connection Protocol
https://tools.ietf.org/html/rfc4254
Connection Protocolの詳細。
Interactive login、コマンドのリモート実行、TCP/IP接
続、X11接続を提供する。
RFC 4255
Using DNS to Securely Publish Secure Shell (SSH) Key
Fingerprints
https://tools.ietf.org/html/rfc4255
DNSSECでSSH host keyを検証する方法。
DNS resource recordに”SSHFP”を追加する。
RFC 4256
Generic Message Exchange Authentication for the Secure
Shell Protocol (SSH)
https://tools.ietf.org/html/rfc4256
認証データが手入力される場合に適したSSH用の認証方式。
RFC 4335
The Secure Shell (SSH) Session Channel Break Extension
https://tools.ietf.org/html/rfc4335
SSHでBREAK signalを送信するSession Channel Break
Extension。
RFC 4344
The Secure Shell (SSH) Transport Layer Encryption Modes
https://tools.ietf.org/html/rfc4344
SSHで推奨するsymmetric encription method、Rekey頻
度。
RFC 4345
Improved Arcfour Modes for the Secure Shell (SSH) Transport
Layer Protocol
https://tools.ietf.org/html/rfc4345
Arcfour cipherの使用方法。
18
19. RFC 4419
Diffie-Hellman Group Exchange for the Secure Shell (SSH)
Transport Layer Protocol
https://tools.ietf.org/html/rfc4419
DH group exchangeの新しいグループの追加。
RFC 4432
RSA Key Exchange for the Secure Shell (SSH) Transport Layer
Protocol
https://tools.ietf.org/html/rfc4432
RSA public key encryptionによる鍵交換。DH鍵交換より
CPUを使わない。
RFC 4462
Generic Security Service Application Program Interface
(GSS-API) Authentication and Key Exchange for the Secure
Shell (SSH) Protocol
https://tools.ietf.org/html/rfc4462
Generic Security Service Application Program
Interface (GSS-API)で認証、key exchangeする方法。
RFC 4716
The Secure Shell (SSH) Public Key File Format
https://tools.ietf.org/html/rfc4716
Public keyを交換するためのpublic key fileフォーマッ
トの定義。
RFC 4819
Secure Shell Public Key Subsystem
https://tools.ietf.org/html/rfc4819
Public Key Subsystemというクライアントがpublic key
を追加/削除したり、サーバーがpublic keyを管理するための
メカニズムの説明。
RFC 5647
AES Galois Counter Mode for the Secure Shell Transport
Layer Protocol
https://tools.ietf.org/html/rfc5647
Transport Layer Protocolに機密性とintegrityを提供
するAES Galois Counter Modeの説明。
RFC 5656
Elliptic Curve Algorithm Integration in the Secure Shell
Transport Layer
https://tools.ietf.org/html/rfc5656
Elliptic Curve Cryptography (ECC)(楕円曲線)に基づ
くアルゴリズムの説明。
RFC 6187
X.509v3 Certificates for Secure Shell Authentication
https://tools.ietf.org/html/rfc6187
X.509v3 public key certificatesを使用する方法。
RFC 6239
Suite B Cryptographic Suites for Secure Shell (SSH)
https://tools.ietf.org/html/rfc6239
Suite Bに対応するSSHのアーキテクチャー。ECDH key
agreement、ECDSA、AES-CGM、SHA-256/SHA-384、
X.509 certificatesを使用する。
RFC 6594
Use of the SHA-256 Algorithm with RSA, Digital Signature
Algorithm (DSA), and Elliptic Curve DSA (ECDSA) in SSHFP
Resource Records
https://tools.ietf.org/html/rfc6594
SSHFP、DNSSECでSSH host keyを検証する方法用のDNS
resource recordにSHA-256 Fingerprint、ECDSAを使
用する定義を追加する。
RFC 6668
SHA-2 Data Integrity Verification for the Secure Shell
(SSH) Transport Layer Protocol
https://tools.ietf.org/html/rfc6668
データのintegrityのためのアルゴリズムとパラメーターの定
義。
RFC 4253のupdate。
RFC 7478
Ed25519 SSHFP Resource Records
https://tools.ietf.org/html/rfc7479
Ed25519 signature algorithmのアルゴリズム番号の定
義。
Errata
https://www.rfc-editor.org/errata_search.php?rfc=XXXX
19
20. RFC 4251, The Secure Shell (SSH) Protocol Architecture
SSHは安全でないネットワーク上で安全なリモートログインおよびその他のネットワークサービスをするためのプロトコルであ
る。本ドキュメントではSSHのアーキテクチャー、SSHのドキュメントで使用される用語を説明する。
SSHは3つの主要コンポーネントで構成される。Transport Layer Protocolはサーバー認証、機密性、integrityを提供
する。User Authentication Protocolはサーバーによるクライアント認証を提供する。Connection Protocolは暗号化
されたトンネルを複数の論理チャネルに多重化する。
1. Introduction
SSHは安全なリモートログインおよびネットワークサービスのためのプロトコルである。以下の3つの主要なコンポーネントから
構成される。
● Transport Layer Protocol
サーバー認証、機密性、integrityを提供する。オプションで圧縮も提供する。Transport LayerはTCP/IPが使
用されるが、他を使用してもよい。
● User Authentication Protocol
クライアント側のユーザーをサーバー側で認証する。Transport Layer Protocolを介して実行される。
● Connection Protocol
暗号化されたトンネルを複数の論理チャネルに多重化うる。User Authentication Protocol上で実行される。
Transport Layerが確立されると、クライアントはサービス要求を送信する。ユーザー認証が完了すると、次のサービス要求
が送信される。これにより新しいサービスが開始する。Connection Protocolは様々な用途に使用できるチャネルを提供す
る。
4. Architecture
4.1. Host Keys
各サーバーはhost keyをもつことが推奨される(SHOULD)。サーバーは複数の異なるアルゴリズムを使用した複数のhost
keyを持ってもよい(MAY)。複数のサーバーが同じhost keyを共有してもよい(MAY)。
クライアントはサーバーの認証をするためにサーバーのpublic host keyについて知っておく必要がある。
二つの異なる信頼モデルを使用できる。
● クライアントは各サーバーに対応するpublic host keyに関連付くローカルデータベースをもつ。この方法では、集
中管理のインフラは必要としない。欠点は、ホスト名とkeyのデータベースのメンテが面倒なこと。
● サーバー名とkeyの関連付けはCAが認証する。クライアントはCA root keyのみ知っていればよい。CAによって承認
された全てのkeyを検証できる。
二つ目の方法は、クライアントに1つのCA root keyのみ格納すればよいので、メンテの問題が解消する。一方、認証前に各
サーバー keyがCAによって認証されており、CAの信頼性確保が必要になる。
SSHにはサーバーに接続するときにサーバー名とhost keyの関連付けをチェックしないオプションがある。これにより、host
keyや証明書を事前に確認しなくても通信ができる。Connectionは保護されるが、MITMに対して脆弱になる。実装は、デフォル
トではそのような接続を許可しないことが推奨される(SHOULD NOT)。
実装はhost keyの正当性を検証するための追加の方法、例えばpublic keyのSHA-1 hashのフィンガープリントを提供して
もよい(MAY)。そのようなフィンガープリントは、電話等の他の通信で容易に確認ができる。
4.2. Extensibility
SSHは独自の暗号化、認証、key exchangeによって拡張される。全ての拡張を一元管理するのは面倒でありが、一元管理しな
いと識別子の競合が発生し、相互運用が困難になる。
SSHではアルゴリズム、メソッド、フォーマット、拡張プロトコルを特定のフォーマットのテキストで識別する。
20
21. 設計の目標は、ベースプロトコルをできるだけシンプルにし、必須のアルゴリズムを最小限にすることである。
4.3. Policy Issues
SSHでは暗号化、integrity、鍵交換、圧縮、public keyアルゴリズムとフォーマットのネゴシエーションが可能である。
それらは方向毎に異なる。
以下のポリシーが実装の設定において可能であることが推奨される(SHOULD)。
● 暗号化、integrity、圧縮アルゴリズムは各方向で別々に設定できる。ポリシーには優先度が設定できること(MUST
)。例えば各カテゴリに設定された最初のアルゴリズムが優先される等。
● サーバー認証に使用されるpublic keyアルゴリズムと鍵交換の方法。
● 各ユーザーに対するサーバーの認証方法。サーバーのポリシーによってはユーザーに対して複数の認証方法をもってよい
(MAY)。例えば、ユーザーのアクセス場所によって認証方法を変える。
● ユーザーがconnection protocolを使用して実行できるオペレーション。例えば、サーバーがセッションを開始した
り、クライアントマシン上でコマンドを実行することは推奨されない(SHOULD NOT)。それらはファイアーウォール
の透過、迂回といったセキュリティの問題になる可能性がある。
4.4. Security Properties
SSHの主な目的はインターネット上のセキュリティを向上させることである。多少のセキュリティを無視したとしても、デプロ
イが容易な方法を採用する。
● 全ての暗号化、integrity、public keyは既知で確立されているアルゴリズムである。
● 全てのアルゴリズムが暗号解読攻撃に対して十分に強力なkey sizeで使用される。
● 全てのアルゴリズムはネゴシエーションされ、あるアルゴリズムが危殆化した場合でもベースのプロトコルを変更する
ことなく、アルゴリズムを切り替えられる。
5. Data Type Representations Used in the SSH Protocols
Data Type 説明
byte Byteは任意の8bit値を表す。固定長のデータはbyte[n]
(nは配列のbyte数)と表す。
boolean Booleanは1byteとして格納される。0はFALSE、1は
TRUEを表す。0以外の値は全てTRUEとして扱うこと(MUST
)。ただし、アプリケーションは0と1以外の値を格納しない
こと(MUST NOT)。
uint32 32bitの符号なし整数を表す。ネットワークバイトオー
ダー(ビッグエンディアン)で4byteで格納される。例えば
699921578(0x29b7f4aa)は29 b7 f4 aaである。
uint64 64bitの符号なし整数を表す。ネットワークバイトオー
ダー(ビッグエンディアン)で8byteで格納される。
string 任意長のバイナリ文字列。Stringには任意のバイナリデー
タを設定できる。Stringはstring長を示すuint32と
stringとして格納される。終端文字のnull文字は使用され
ない。
Stringはテキストを格納するためにも使用される。内部で
はUS-ASCIIが使用され、ユーザーに表示するときはUTF-8
が使用される。終端のnull文字はstringに格納しないこと
(SHOULD NOT)。
例)"testing" = 00 00 00 07 t e s t i n g
先頭の00000007はstring長=7。
21
23. RFC 4253, The Secure Shell (SSH) Transport Layer Protocol
SSH Transport layer protocolについて説明する。SSHは通常はTCP/IP上で動作し、暗号化、サーバー認証、
integrity protection、圧縮が可能である。
鍵交換方式、public keyアルゴリズム、symmetric暗号化アルゴリズム、メッセージ認証アルゴリズム、ハッシュアルゴリ
ズムは全てネゴシエーションされる。
1. Introduction
Transport protocolの認証はサーバーベースである。このプロトコルレベルではユーザー認証を実行しない。ユーザー認証
は上位のuser authentication protocolで実行される。
このプロトコルは、パラメーターのネゴシエーションを可能とし、ラウンドトリップ回数を最小限にするためにシンプルかつ柔
軟に設計されている。鍵交換方式、public keyアルゴリズム、symmetric暗号化アルゴリズム、メッセージ認証アルゴリズ
ム、ハッシュアルゴリズムは全てネゴシエーションされる。多くの環境では鍵交換、サーバー認証、Service request、
Service requestの許可通知に必要なラウンドトリップは2回である。最悪ケースでは3回になる。
4. Connection Setup
SSHは8bitのbinary-transparent transportで動作する。
クライアントがコネクションを開始する。
4.1. Use over TCP/IP
TCP/IPが使用される場合、サーバーは通常はport22で接続をlistenする。このポート番号はIANAにSSH用に登録されてい
る。
4.2. Protocol Version Exchange
コネクション確立後、双方でidentification stringを送信すること(MUST)。Identification stringは以下である
こと(MUST)。
SSH-protoversion-softwareversion SP comments CR LF
この文章で定義されているプロトコルはバージョン2.0のため、「protoversion」は「2.0」が設定されること(MUST)。
「comments」はオプションである。「comments」が含まれる場合、space(SP, ASCII:32)と「softwareversion」と「
comments」の文字列は分離されること(MUST)。Identificationは1つのCR(ASCII:13)と1つのLF(ASCII:10)で終
端されること(MUST)。Null文字を含めないこと(MUST)。CRとLFを含めて255文字が最大長である。
CRとLFの前のidentificationの一部はDH鍵交換で使用される。
サーバーはversion stringを送信する前に他の文字列を送信してよい(MAY)。その文字列はCRLFで終端されること
(SHOULD)。その文字列は"SSH-"で始まらず(MUST NOT)、UTF-8でエンコードされること(SHOULD)。クライアントはその
ような文字列を処理できること(MUST)。そのような文字列は無視してもよく(MAY)、クライアントに表示してもよい(MAY
)。この機能の主な用途は、TCPが切断する前のエラーメッセージ表示である。
6. Binary Packet Protocol
各パケットは以下のフォーマットである。
フィールド名 Data Type 説明
packet_length uint32 パケットのバイト数。mac、
packet_lengthは含まない。
23
24. padding_length byte random paddingのバイト数。
payload byte[n1]
n1 = packet_length - padding_length - 1
パケットのpayload。圧縮がネゴシ
エーションされている場合、このフィー
ルドは圧縮される。最初のパケットは圧
縮が"none"であること(MUST)。
random padding byte[n2]
n2 = padding_length
任意長のパディング。
packet_length~random
paddingまでのバイト数が8か暗号化ア
ルゴリズムのブロックサイズの大きい方
の倍数になるようにパディングされる。
最低でも4byteのパディングがあるこ
と(MUST)。パディングはランダム値
であることが推奨される(SHOULD)。
最大のパディングは255 byte。
mac byte[m]
m = mac_length
Message Authentication Code
。MACがネゴシエーションされる場合、
このフィールドにはMACが含まれる。最
初のパケットはMACアルゴリズムが
"none"であること(MUST)。
"packet_length"も暗号化されることに注意せよ。また、可変長の"random padding"が挿入されると、トラフィックの分
析が妨げられる可能性があることにも注意せよ。
6.1. Maximum Packet Length
実装は、32,768byte以下の非圧縮のpayloadおよび35,000byte以下のパケット長を処理できること(MUST)。実装はより
大きいパケット('packet_length'+'padding_length'+'payload'+'random padding'+'mac')をサポートすることが
推奨される(SHOULD)。実装はDoS攻撃やバッファオーバーフロー攻撃を回避するためにパケット長が妥当であるかチェックする
ことが推奨される(SHOULD)。
6.2. Compression
圧縮がネゴシエーションされる場合、'payload'のみが圧縮される。'packet_length'と'mac'は圧縮されたpayloadで計
算される。暗号化は圧縮後に実施される。圧縮アルゴリズムによっては、圧縮はステートフルになってもよい(MAY)。圧縮は双
方向に対して独立であり(MUST)、お互いに独立したアルゴリズムが選択できること(MUST)。ただし、圧縮アルゴリズムは双
方向で同一であることが推奨される(RECOMMENDED)。
以下の圧縮アルゴリズムが定義されている。
none REQUIRED 圧縮なし
zlib OPTIONAL ZLIB (LZ77)圧縮
6.3. Encryption
暗号化アルゴリズムとkeyは鍵交換時にネゴシエーションされる。暗号化が有効である場合、各パケットのpacket length,
padding length, payload, paddingは暗号化されること(MUST)。
一方向に送信される全てのパケットの暗号化されたデータは1つのデータストリームとして扱われる(SHOULD)。全ての暗号は
key lengthが128bit以上のkeyを使用することが推奨される(SHOULD)。
各方向の暗号はお互いに独立して動作すること(MUST)。複数のアルゴリズムの使用が許可されている場合、実装は各方向のア
ルゴリズムを独立して選択されることを許容すること(MUST)。ただし、暗号アルゴリズムは双方向で同一であることが推奨され
る(RECOMMENDED)。
24
25. 以下の暗号が現在定義されている。
3des-cbc REQUIRED 3DES CBC mode
blowfish-cbc OPTIONAL Blowfish CBC mode
twofish256-cbc OPTIONAL Twofish(256 bit key) CBC mode
twofish-cbc OPTIONAL twofish256-cbcと同じ。
twofish192-cbc OPTIONAL Twofish(192 bit key) CBC mode
twofish128-cbc OPTIONAL Twofish(128 bit key) CBC mode
aes256-cbc OPTIONAL AES(256 bit key) CBC mode
aes192-cbc OPTIONAL AES(192 bit key) CBC mode
aes128-cbc RECOMMENDED AES(128 bit key) CBC mode
serpent256-cbc OPTIONAL Serpent(256 bit key) CBC mode
serpent192-cbc OPTIONAL Serpent(192 bit key) CBC mode
serpent128-cbc OPTIONAL Serpent(128 bit key) CBC mode
arcfour OPTIONAL ARCFOUR(128 bit key)
idea-cbc OPTIONAL IDEA CBC mode
cast128-cbc OPTIONAL CAST-128 CBC mode
none OPTIONAL 暗号化無し, NOT RECOMMENDED
6.4. Data Integrity
Integrityはshared secret, パケットシーケンス番号、パケットから計算されるMACを各パケットに含めることで保護さ
れる。
MACアルゴリズムとkeyは鍵交換時にネゴシエーションされる。最初のパケットはMACは無効でサイズ0であること(MUST)。
鍵交換後、選択されたMACアルゴリズムの'mac'は暗号化前に計算される。
mac = MAC(key, sequence_number || unencrypted_packet)
# ||は連結。
unencrypted_packetは、'mac'を含まないパケット全体('length', 'payload', 'random padding')で
sequence_numberはuint32のパケットシーケンス番号である。sequence_numberは最初のパケットで0初期化され、パケッ
ト毎にインクリされる。Keyやアルゴリズムが再度ネゴシエーションされてもリセットされない。2^32で0に戻る。
sequence_numberはパケット自体には含まれない。
各方向のMACアルゴリズムは独立して処理され(MUST)、実装は各方向に独立してアルゴリズムを選択することを許容すること
(MUST)。だし、アルゴリズムは双方向で同一であることが推奨される(RECOMMENDED)。
'mac'はパケットの最後に暗号化無しで送信されること(MUST)。'mac'のサイズは選択したアルゴリズムによって異なる。
25
26. 以下のMACアルゴリズムが現在定義されている。
hmac-sha1 REQUIRED HMAC-SHA1
Digest Length = 20 byte
Key Length = 20 byte
hmac-sha1-96 RECOMMENDED HMAC-SHA1
Digest Length = 12 byte
Key Length = 20 byte
hmac-md5 OPTIONAL HMAC-MD5
Digest Length = 16 byte
Key Length = 16 byte
hmac-md5-96 OPTIONAL HMAC-MD5
Digest Length = 12 byte
Key Length = 16 byte
none OPTIONAL MAC無し。NOT RECOMMENDED
6.5. Key Exchange Methods
鍵交換は暗号化と認証のためのone-time session keyの生成方法と、サーバー認証方法を指定する。
2つの鍵交換方式が要求される(REQUIRED)。
diffie-hellman-group1-sha1 REQUIRED
diffie-hellman-group14-sha1 REQUIRED
6.6. Public Key Algorithms
SSHは様々なpublic keyフォーマット、エンコード、アルゴリズム(署名、暗号化)で動作できるように設計されている。
以下のpublic key、証明書のフォーマットが定義されている。
ssh-dss REQUIRED sign Raw DSS Key
ssh-rsa RECOMMENDED sign Raw RSA Key
pgp-sign-rsa OPTIONAL sign OpenPGP certificates (RSA key)
pgp-sign-dss OPTIONAL sign OpenPGP certificates (DSS key)
Public keyと証明書は以下のようにエンコードされる。
string certificate or public key format identifier
byte[n] key/certificate data
署名は以下のようにエンコードされる。
26
27. string signature format identifier
byte[n] 署名
"ssh-dss" key formatは以下である。p, q, g, yから署名を生成する。
string "ssh-dss"
mpint p
mpint q
mpint g
mpint y
署名(SHA-1 hash)は以下のようにエンコードされる。
string "ssh-dss"
string dss_signature_blob
"ssh-rsa"の場合は以下の通り。
key format。
string "ssh-rsa"
mpint e
mpint n
署名(SHA-1 hash)。
string "ssh-rsa"
string rsa_signature_blob
7. Key Exchange
鍵交換, Key exchange(Kex)はサポートしているアルゴリズム名のリストを送信することで双方から開始する。お互いが優
先するアルゴリズムをもっている。対向がどのアルゴリズムを使用するか推定してよく(MAY)、その結果に従って鍵交換をして
もよい(MAY)。
以下の場合、推測は誤っていると見なされる。
kexアルゴリズムまたはhost keyアルゴリズムがサーバーとクライアントで異なる。
一致するアルゴリズムが無い。
それ以外の場合、推測は正しく、鍵交換のパケットは正常に処理されること(MUST)。
鍵交換で署名またはサーバーの証明書を送信する場合は、明示的なサーバー認証である。鍵交換でサーバーとクライアントが
shared secret Kを使用したMACを送信することで認証する場合は暗黙のサーバー認証である。
27
28. 暗黙的なサーバー認証による鍵交換の場合、クライアントは鍵交換の後にデータを送信せず、service request messageの
応答を待つこと(MUST)。
7.1. Algorithm Negotiation
鍵交換では双方が以下のパケットを送信する。
アルゴリズム名のリストはアルゴリズム名のcsvである。アルゴリズムは優先順位の高い順になっていること(MUST)。各
name-listには最低限1つのアルゴリズムがあること(MUST)。
SSH_MSG_KEXINIT byte
cookie (random bytes) byte[16] 送信者が生成した乱数。Keyとsession identifierを使
用せずコネクションを識別するために使用する。
kex_algorithms name-list 最初のアルゴリズムが優先されるアルゴリズムであること(
MUST)。そのアルゴリズムが選択できない場合、以下の選択
をすること(MUST)。クライアントのKexアルゴリズムをイテ
レーションして、以下の条件を満たす最初のアルゴリズムを選
択する。
+サーバーがサポートしているアルゴリズム。
+アルゴリズムが暗号化host keyを必要とする場合、暗
号化可能なアルゴリズムで双方がサポートしていること。
つまり、サーバーのserver_host_key_algorithms
にもあること。
+アルゴリズムが署名host keyを必要とする場合、署名
可能なアルゴリズムで双方がサポートしていること。つま
り、サーバーのserver_host_key_algorithmsにも
あること。
上記を満たすアルゴリズムが無い場合、双方は切断すること
(MUST)。
server_host_key_algorithms name-list サーバー host keyでサポートされているアルゴリズムの
name-list。サーバーはhost keyのアルゴリズムを設定す
る。クライアントは許容するアルゴリズムを設定する。1つの
サーバーに対して複数のアルゴリズムが存在してもよい(MAY
)。
サーバーによってサポートされている、クライアントの
name-listの先頭のアルゴリズムを選択すること(MUST)。
そのようなアルゴリズムがない場合、双方は切断すること(
MUST)。
encryption_algorithms_client_to_server name-list 暗号化アルゴリズムの優先度順のname-list。選択される
アルゴリズムは、サーバー、クライアントの両方の
name-listにある最初のアルゴリズムであること(MUST)。
そのようなアルゴリズムがない場合、双方は切断すること(
MUST)。
encryption_algorithms_server_to_client name-list
mac_algorithms_client_to_server name-list MACアルゴリズムの優先度順のname-list。選択されるア
ルゴリズムは、サーバー、クライアントの両方のname-list
にある最初のアルゴリズムであること(MUST)。そのような
アルゴリズムがない場合、双方は切断すること(MUST)。
mac_algorithms_server_to_client name-list
compression_algorithms_client_to_server name-list 圧縮アルゴリズムの優先度順のname-list。選択されるアル
ゴリズムは、サーバー、クライアントの両方のname-listに
ある最初のアルゴリズムであること(MUST)。そのようなア
ルゴリズムがない場合、双方は切断すること(MUST)。
compression_algorithms_server_to_client name-list
languages_client_to_server name-list Language tagの優先度順のname-list。双方はこれを無
視してもよい(MAY)。Language設定が無い場合この
name-listは空であることが推奨される(SHOULD)。送信側languages_server_to_client name-list
28
29. が必要でない場合、Language tagを設定しないこと(
SHOULD NOT)。
first_kex_packet_follows boolean 鍵交換のパケットが以降送信するかを示す。送信される場合
はTRUE、送信されない場合はFALSEを設定すること(MUST
)。
対向からSSH_MSG_KEXINITパケットを受信したとき、ネゴ
シエーションの成功、失敗を知ることができる。失敗で本
フィールドがTRUEの場合、そのパケットは無視(silently
ignored)すること(MUST)。成功かつTRUEの場合はそのパ
ケットを使用する。
0 (reserved for future extension) uint32 Reserve。
SSH_MSG_KEXINITメッセージ交換の後、鍵交換アルゴリズムが実行される。
鍵交換または再鍵交換のためにSSH_MSG_KEXINITメッセージを送信した後は、SSH_MSG_NEWKEYSを送信するまでは、以下
のメッセージ以外のメッセージを送信しないこと(MUST)。
● Transport layer generic messages(1~19)
ただし、SSH_MSG_SERVICE_REQUEST、SSH_MSG_SERVICE_ACCEPTは送信しないこと(MUST NOT)。
● Algorithm negotiation messages(20~29)
ただし、追加でSSH_MSG_KEXINITを送信しないこと(MUST NOT)。
● Specific key exchange method messages(30~49)
7.2. Output from Key Exchange
鍵交換ではShared secret K、 Exchange hash Hが生成される。暗号化keyと認証keyはこれらから計算される。最初の
鍵交換で生成されたexchange hash Hはコネクションの一意の識別子(session_id)である。再鍵交換された場合でも変更さ
れない。
各鍵交換方式は、鍵交換で使用されるハッシュ関数(HASH)を指定する。同じハッシュアルゴリズムをkey生成で使用しないこ
と(MUST)。
以下のように暗号化keyはKとHASHから計算されること(MUST)。
Initial IV client to server HASH(K || H || "A" || session_id)
Kはmpint、"A"はASCII 65、session_idは生データでエ
ンコードされる。
#session_idは最初の鍵交換で生成されたexchange hash
H
Initial IV server to client HASH(K || H || "B" || session_id)
Encryption key client to server HASH(K || H || "C" || session_id)
Encryption key server to client HASH(K || H || "D" || session_id)
Integrity key client to server HASH(K || H || "E" || session_id)
Integrity key server to client HASH(K || H || "F" || session_id)
Keyはハッシュ値の先頭から必要なバイト数とること(MUST)。必要なKey長がハッシュ値より大きい場合、KとHの連結とKey
の全体のHASHを計算し、必要なだけkeyに追加して拡張する。
K1 = HASH(K || H || X || session_id) (X is e.g., "A")
K2 = HASH(K || H || K1)
K3 = HASH(K || H || K1 || K2)
29
30. ...
key = K1 || K2 || K3 || ...
使用されるメッセージ。
双方が送信する。
byte SSH_MSG_NEWKEYS
7.3. Taking Keys Into Use
鍵交換は双方がSSH_MSG_NEWKEYSメッセージを送信すると終了する。このメッセージは古いkeyとアルゴリズムで送信され
る。このメッセージの後に送信されるメッセージは新しいkeyとアルゴリズムを使用すること(MUST)。このメッセージを受信す
るときは、新しいkeyとアルゴリズムが使用されること(MUST)。
8. Diffie-Hellman Key Exchange
DH鍵交換はExchange hash Hを生成する。
p 素数。公開されている。
g GF(p)で生成されるサブグループ、生成元。公開されている。
#GF = Galois Field
q サブグループの任意の値。
V_C string クライアントの識別文字列。
V_S string サーバーの識別文字列。
I_C string クライアントが送信したSSH_MSG_KEXINITメッセージ。
I_S string サーバーが送信したSSH_MSG_KEXINITメッセージ。
K_S string サーバーの公開鍵。
e mpint DH鍵交換のA。
f mpint DH鍵交換のB。
K mpint Shared secret K。
1. クライアントは乱数x(1<x<q)を生成し、e=g^x mod pを計算。eをサーバーに送信する。
2. サーバーは乱数y(0<y<q)を生成し、f=g^y mod pを計算する。
K = e^y mod p、H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
を計算する。Hにprivate host keyで署名する(s)。サーバーはクライアントに(K_S || f || s)を送信する。
3. クライアントはK_Sがサーバーのhost keyであることを検証する(例:データベースや証明書を使って)。クライアン
トはベリファイ無しでhost keyを許容してもよい。
クライアントは
K = f^x mod p、H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
を計算し、Hに署名sをベリファイする。
上記の手順でShared secret K、Exchange hash Hが生成、ベリファイされた。
使用するメッセージ。
1でクライアントが送信する。
30
31. byte SSH_MSG_KEXDH_INIT
mpint e。(e=g^x mod p。DH鍵交換のA。)
2でサーバーが送信する。
byte SSH_MSG_KEXDH_REPLY
string サーバーの公開host keyと証明書。(K_S)
mpint f。(f=g^y mod p。DH鍵交換のB。)
string Hに署名したもの。(K_S || f || s)
9. Key Re-Exchange
鍵交換を開始していないときに、SSH_MSG_KEXINITを送信することによって再鍵交換が開始される。このメッセージが受信さ
れたとき、受信したSSH_MSG_KEXINITが応答だった場合を除き、SSH_MSG_KEXINITで応答すること(MUST)。どちらからで
も再鍵交換を開始してもよいが、サーバー、クライアントの役割は変わらない。
鍵交換を開始していないときに、SSH_MSG_KEXINITを送信することによって再鍵交換が開始される。このメッセージが受信さ
れたとき、受信したSSH_MSG_KEXINITが応答だった場合を除き、SSH_MSG_KEXINITで応答すること(MUST)。どちらからで
も再鍵交換を開始してもよいが、サーバー、クライアントの役割は変わらない。
再鍵交換は、交換前の暗号化で行われる。暗号化、圧縮、MACは再鍵交換後に新しいSSH_MSG_NEWKEYSが送信されるまで変更
されない。再鍵交換はsession idが変わらないことを除いて、最初の鍵交換と同様に処理される。全てのkeyとIVは鍵交換で再
計算される。
G byteのデータ送信後か接続して1時間経過の早い方でkey変更することが推奨される(RECOMMENDED)。ただし、再鍵交換
は処理能力が必要であるため、頻繁に実行すべきではない。
SSH_MSG_NEWKEYSの送信後にもアプリケーションデータが送信される可能性がある。鍵交換はSSH Transport layerより
上のプロトコルには影響しない。
10. Service Request
鍵交換後、クライアントはサービスを要求する。サービスは名前で識別される。
以下の名前が予約されている。ローカルサービスには"servicename@domain"のようなPRIVATE USE syntaxを用いる。
ssh-userauth
ssh-connection
サーバーは、サービス要求を拒否する場合、SSH_MSG_DISCONNECTメッセージを送信し(SHOULD)、接続を切断すること(
MUST)。
サービスが開始すると鍵交換中に生成されたsession idにアクセスできる。
暗黙のサーバー認証、鍵交換の後、クライアントはservice requestの応答を待つこと(MUST)。
使用するメッセージ。
クライアントが送信する。
31
32. byte SSH_MSG_SERVICE_REQUEST
string service name
サーバーが送信する。
byte SSH_MSG_SERVICE_ACCEPT
string service name
11. Additional Messages
クライアント、サーバーのどちらからでも下記のメッセージを送信してよい。
11.1. Disconnection Message
byte SSH_MSG_DISCONNECT
uint32 reason code
string description in ISO-10646 UTF-8 encoding
string language tag [RFC3066]
接続の終了をするメッセージ。全ての実装はこのメッセージを処理でき(MUST)、メッセージを送信することができること(
SHOULD)。
送信者はこのメッセージの後にデータの送信、受信をしてはならず(MUST NOT)、受信者はこのメッセージのの受信後に受信
したメッセージを許容しないこと(MUST NOT)。'description'は人がわかるstringである。'reason code'は下記の値
である。
Symbolic name reason code
SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1
SSH_DISCONNECT_PROTOCOL_ERROR 2
SSH_DISCONNECT_KEY_EXCHANGE_FAILED 3
SSH_DISCONNECT_RESERVED 4
SSH_DISCONNECT_MAC_ERROR 5
SSH_DISCONNECT_COMPRESSION_ERROR 6
SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7
SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8
SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9
SSH_DISCONNECT_CONNECTION_LOST 10
SSH_DISCONNECT_BY_APPLICATION 11
32
33. SSH_DISCONNECT_TOO_MANY_CONNECTIONS 12
SSH_DISCONNECT_AUTH_CANCELLED_BY_USER 13
SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
SSH_DISCONNECT_ILLEGAL_USER_NAME 15
11.2. Ignored Data Message
byte SSH_MSG_IGNORE
string data
全ての実装はこのメッセージを受信した場合、理解した上で無視すること(MUST)。送信処理を実装する必要は無い。このメッ
セージはトラフィック分析に使用できる。
#DPD的な死活監視用?
11.3. Debug Message
byte SSH_MSG_DEBUG
string always_display
string message in ISO-10646 UTF-8 encoding
string language tag [RFC3066]
全ての実装はこのメッセージを受信した場合、理解した上で(MUST)無視してよい。このメッセージはデバッグメッセージの送
信に使用される。'always_display'がTRUEの場合、メッセージは表示されることが推奨される(SHOULD)。FALSEの場合、
デバッグ情報の表示がユーザーから明示的に要求されていない限り、表示しないことが推奨される(SHOULD NOT)。
11.4. Reserved Messages
byte SSH_MSG_UNIMPLEMENTED
uint32 packet sequence number of rejected message
実装は受信したメッセージの順にSSH_MSG_UNIMPLEMENTEDで全ての認識できないメッセージに応答すること(MUST)。その
ようなメッセージはそれ以外の処理をせず無視すること(MUST)。
12. Summary of Message Numbers
メッセージ一覧。30-49はkex packetで使用される。異なるkexでは番号は再利用される。
SSH_MSG_DISCONNECT 1
SSH_MSG_IGNORE 2
33
35. RFC 4344, The Secure Shell (SSH) Transport Layer Encryption Modes
1. Introduction
SSH Transport Protocolはプライバシー保護とカプセル化されたデータのintegrityを提供するように設計されている。
しかし、RFC4253のSSH Transport Protocolの一部にセキュリティ問題が発見された。例えば、暗号化モードは
chosen-plaintext privacy attackに対して脆弱である。また、rekeyの頻度が不十分の場合、payloadが漏洩する可能
性がある。後者は暗号化モードによらず問題になる。
3. Rekeying
RFC4253 section 9ではSSHの実装が1GBのデータ送信毎のrekeyを提案している。しかし、rekeyが不十分な場合の問題に
ついては議論していない。このsectionではrekeyに関する推奨事項について説明する。
本sectionでは2つの推奨事項を示す。最初の推奨事項はMACタグの漏洩からの情報保護が目的であり、もう一つはブロック暗
号から発生する漏洩からの情報保護が目的である。ブロック暗号のブロック長と暗号化パケット長によっては最初の事項が優先さ
れる場合がある。
3.1. First Rekeying Recommendation
MACタグを介した情報漏洩の可能性があるため、SSHの実装は少なくとも2^32パケットの送信毎に1回 rekeyすることが推奨さ
れる(SHOULD)。別の言い方をすれば、鍵交換後、SSHの実装はrekey前に2^32パケット以上を送信しないことが推奨される(
SHOULD NOT)。
SSHの実装は最後のrekey後に2**32パケットを超えるパケットを受信する前にrekeyをすることが推奨される(SHOULD)。
推奨される方法は、最後のrekey後に2**31パケットを受信した後にrekeyすることである。
3.2. Second Rekeying Recommendation
ブロック暗号によっては、実装は多くのブロックを同一の暗号化keyで暗号化しないように注意すること。
ブロック暗号のブロック長をL(bit)とする。AESの場合はL=128。Lが128以上の場合、rekey前に2**(L/4)ブロックを超
えるブロックの暗号化をしないことが推奨される(SHOULD NOT)。そのような場合は実装はrekeyを試行することが推奨され
る。Lが128より小さい場合、2**(L/4)ブロック毎のrekeyはコストがかかる場合があるが、少なくとも1GB毎にrekeyすること
が推奨される。
4. Encryption Modes
SSH Transport Protocolで使用するための新しい暗号化方式を説明する。これらの暗号化方式はRFC4253 section 6.3
の暗号化方式に追加される。
aes128-ctr RECOMMENDED AES(128 bit key) SDCTR mode
aes192-ctr RECOMMENDED AES(192 bit key) SDCTR mode
aes256-ctr RECOMMENDED AES(256 bit key) SDCTR mode
3des-ctr RECOMMENDED 3DES SDCTR mode
blowfish-ctr OPTIONAL Blowfish SDCTR mode
twofish128-ctr OPTIONAL Twofish(128 bit key) SDCTR mode
twofish192-ctr OPTIONAL Twofish(192 bit key) SDCTR mode
35
36. twofish256-ctr OPTIONAL Twofish(256 bit key) SDCTR mode
serpent128-ctr OPTIONAL Serpent(128 bit key) SDCTR mode
serpent192-ctr OPTIONAL Serpent(192 bit key) SDCTR mode
serpent256-ctr OPTIONAL Serpent(256 bit key) SDCTR mode
idea-ctr OPTIONAL IDEA SDCTR mode
cast128-ctr OPTIONAL CAST-128 SDCTR mode
36