TLS(TCP) with Ruby

# 何をするのか？
Rubyを用いてTCP, TLS通信を行います。
Wiresharkを使って、パケットを確認し、その挙動の違いを観察します。
また、RSAのprivate keyを用いてdecryptionしたりして、理解を深めます。

# なぜやろうと思ったのか？
「HTTPS(HTTP over SSL/TLS)をWiresharkで確認する」
みたいな記事はよく拝見しましたが、TLS単体を確認する感じのものは比較的少ないようだったのでやってみました。

コードは以下に置いています。

https://github.com/kataokatsuki/socket-connection-with-ruby

流れとしては以下の形で進めます。
1. Directoryの確認
2. TCP
3. TLS(over TCP)

# Directory
GitHubにあるように、構造は以下です。
![Screen Shot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404277/b74ec20e-18e3-4dd2-94b3-8b49b2a3c4d5.png)

# TCP
使用するコードを紹介します。

```ruby:./tcp/client.rb
require "socket"

# open 8081
socket = TCPSocket.open("127.0.0.1", 8081)

# send strings
socket.write("Hello from client")

# close socket
socket.close
```

```ruby:./tcp/server.rb
require "socket"

# open 8081
server = TCPServer.open(8081)

# connect to client
socket = server.accept

# show strings send by client
while line = socket.gets
  puts line
end

# close socket
socket.close

# close server
server.close
```

clientからserverに文字列を送るプログラムです。
これを実行してみましょう。
以下の順で実行してみてください。

```
ruby tcp/server.rb
ruby tcp/client.rb
```

この時、Wiresharkはこんな感じになります。
![Screen Shot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404277/c6999dcf-5243-1ec3-3fc1-f88e66d8f778.png)

ここでNo.5のパケットを見るとデータが生で送られていることが分かります。
![Screen Shot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404277/1346a8b0-6fda-e350-01b6-64777677bbe6.png)

# TLS(over TCP)
それでは、TLSに移りましょう。

使用するコードを紹介します。

```ruby:./tls/create_key.rb
require 'openssl'

key = OpenSSL::PKey::RSA.new(2048)

# output private key
File.open(__dir__ + "/keys/private_key.pem", "w+") do |f|
  f.write(key.export)
end

# output public key
public_key = key.public_key
File.open(__dir__ + "/keys/public_key.pem", "w+") do |f|
  f.write(public_key.export)
end
```

```ruby:./tls/client.rb
require 'socket'
require 'openssl'

# open 4433
tcp_socket = TCPSocket.new('127.0.0.1', 4433)
ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket)
ssl_socket.connect

# send strings
ssl_socket.puts("Hello from client")

# show strings send by server
puts ssl_socket.gets

# close socket
ssl_socket.close
```

```ruby:./tls/server.rb
require "socket"
require 'openssl'

# read key
key = OpenSSL::PKey::RSA.new(File.open(__dir__ + "/keys/private_key.pem"))

# create stuff used for TLS
digest = OpenSSL::Digest::SHA1.new()
issuer = subject = OpenSSL::X509::Name.new()

# subject example
subject.add_entry('C', 'JP')
subject.add_entry('ST', 'Tokyo')
subject.add_entry('CN', 'kataokatsuki')

# sign 
certification = OpenSSL::X509::Certificate.new()
certification.not_before = Time.at(0)
certification.not_after = Time.at(0)
certification.public_key = key 
certification.serial = 1
certification.issuer = issuer
certification.subject = subject
certification.sign(key, digest)

# specify a cipher which use RSA for Kx(Key Exchange) and Au(Authentication)
# you may not decrypt in Wireshark easily if you use ECDHE for Kx
ssl_context = OpenSSL::SSL::SSLContext.new()
ssl_context.cert = certification
ssl_context.key = key
ssl_context.ssl_version = :TLSv1_2
ssl_context.ciphers = ["AES256-GCM-SHA384"]

# open 4433
tcp_server = TCPServer.new('127.0.0.1', 4433)
ssl_server = OpenSSL::SSL::SSLServer.new(tcp_server, ssl_context)

# show strings send by client
socket = ssl_server.accept
text = socket.gets
puts text

# send strings
socket.puts("Server receives a message: " + text)

# close socket
socket.close

# close server
ssl_server.close
```

clientからserverに文字列を送り, 文字列を送り返すプログラムです。
これを実行してみましょう。
以下の順で実行してみてください。
./tls/keys にpemが作成されます。

```
ruby tls/create_key.rb
ruby tls/server.rb
ruby tls/client.rb
```

Wiresharkで確認しましょう。
結構パケットが増えましたね。
きちんとClient Hello出来ていています。

![Screen Shot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404277/fbc28577-3598-00cd-6dd2-48d71edeee43.png)


また、「Applicatio Data」を見ると、encryptionされています。
![Screen Shot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404277/fc660c22-0ae0-6ea6-dfba-714bbc66582c.png)

さあ、これからdecriptionしてみましょう。

Preferences > Protocols > TLS
を開いて、「Edit」を押してください。
![Screen Shot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404277/d1f07514-2b37-9478-a0c1-2b61aca82c71.png)

ここで、「＋」を押して各種情報を登録してください。
登録例はこんな形です。
![Screen Shot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404277/6308933b-ff06-fb69-10e6-b00d8660aa51.png)

OKを押して反映させましょう。
そうすると、ページ下部に
「Decrypted TLS」
が表示されますので、そちらを表示します。
きちんとdecryptionされていることが分かります。
![Screen Shot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404277/f85825a0-4c35-788f-40e4-93492c2c2c64.png)

これにて完了です。お疲れ様でした。

長くなりすぎるので、こちらでは記述をしないのですが、他にもやってて面白かったのはこんなことです。

- session keyを用いてECDHE方式でもdecryptionする
- HTTPを乗せる
   - DockerのNGINXイメージを用いてHTTP, HTTPS時のパケットを比較する。
- 様々な暗号方式を比較する

いろいろガチャガチャ触るのは楽しいです。