Simple CryptCat – jak napisać proste narzędzie skryptowe client-server do przesyłania przez sieć zaszyfrowanego tekstu

cryptcat

Jeśli chcesz błyskawicznie oprogramować swoje pomysły na kryptograficzne zabezpieczenia transmisji danych przez sieć, to czytaj dalej.

Kontynuując temat poruszony w poprzednim poście, na potrzeby edukacyjne opracowałem narzędzie skryptowe funkcjonujące na podobnej zasadzie jak kryptograficzna odmiana Netcata czyli Cryptcat. Narzędzie umożliwia przesyłanie przez sieć zaszyfrowanych informacji tekstowych. Składa się z dwóch komponentów funkcjonalnych: komponentu serwera i komponentu klienta. Komponent klienta odpowiedzialny jest za operacje szyfrowania i komunikowanie się z serwerem, natomiast komponent serwera za przyjmowanie i obsługę połączeń od klienta oraz rozszyfrowywane stamtąd otrzymywanych danych.

Oba komponenty działają w oparciu o gniazda (sokety) TCP. Biblioteka do programowania soketów jest częścią standardowej dystrybucji języka Ruby. Do celów szyfowania i deszyfrowania zastosowałem funkcje modułu ‘ezcrypto’, natomiast do  inteligentnej interpretacji opcji wywołania użyłem struktur danych i funkcji modułu ‘OptionsParser’. Pamiętajcie proszę, żeby te moduły zainstalować, zanim zaczniecie z nich korzystać w swoim kodzie:

gem install ezcrypto
gem install OptionsParser

Narzędzie jest prototypem typu Proof of Concept, a nie gotowym rozwiązaniem zastępującym na przykład SFTP czy VPN-y. By uczynić je w pełni użytecznym do zastosowań praktycznych, trzeba by wzbogacić je choćby o funkcjonalność autentykacji łączących się z sobą stron. Nie jest to zresztą trudne – przy odrobinie czasu i chęci można dopisać do skryptów kod do uwierzytelnień opartych o hasła (w lokalnych bazach, w RADIUS-ie czy w LDAP-ie), cyfrowe certyfikaty X.509 albo klucze kryptograficzne w jakiejkolwiek postaci. Jednak tego rodzaju zmiany w kodzie zostawiam moim czytelnikom i sympatykom bloga.

Serwer, póki co, działa w trybie jednowątkowym, co oznacza, że w swej aktualnej postaci potrafi na raz obsłużyć połączenie od jednego tylko klienta. Jeśli zależałoby nam na wielowątkowości należałoby lekko poprawić kod komponentu serwerowego, korzystając w tym celu z biblioteki Thread, w taki mniej więcej sposób:

server = TCPServer.open(port)                 # Socket to listen on particular port
loop {                                        # Server runs until manual break
   Thread.start(server.accept) do |client|
      client.puts “Something”                 # Send something to the client
      client.close                            # Disconnect from the client
   end
}

Zarówno component serwera, jaki i component klienta są uruchamiane z poziomu wiersza poleceń systemu operacyjnego. Pomocne na początek jest wywołanie obu komponentów z opcją podpowiedzi składni (-h lub –help):

C:\scripts>ruby crypto_client.rb -h
Crypto netcat client
Proper syntax: crypto_client.rb [options]
-p, --port port                  Port number
-d, --destination                Destination host (name or IP address)
-a, --algorithm                  Crypto algorithm
-k, --key                        Password to crypto key
-h, --help                       Displays Help

C:\scripts>ruby crypto_server.rb -h
Crypto netcat server
Proper syntax: crypto_server.rb [options]
-p, --port port                  Port number
-a, --alg alg                    Crypto algorithm
-k, --key key                    Password to crypto key
-h, --help                       Displays Help

Jeśli w wywołaniu klienta czy serwera nie podamy w linii poleceń któregoś z obligatoryjnych parametrów, to program zapyta o ten parametr natychmiast po uruchomieniu.

Oba komponenty działają następująco:

Klient pobiera interaktywnie tekst od użytkownika, po czym go szyfruje wybranym algorytmem kryptograficznym, prezentuje na konsoli tekst przed zaszyfrowaniem i po zaszyfrowaniu oraz wysyła zaszyfrowany tekst do serwera, wiersz po wierszu.

C:\scripts>ruby crypto_client.rb -p 8000 -d localhost -a blowfish
Enter the password to crypto key: $ecYouR3
By trials and errors...
Plaintext: By trials and errors...
Ciphertext: ygk1ojpgM+NfktPk3PZBTd2WNlDHu/j4
You can be successful
Plaintext: You can be successful
Ciphertext: PE6R036ub7LT083JLmQyVEeE89WiGAis

Serwer deszyfruje otrzymane dane i prezentuje je w formie przed dekrypcją i po dekrypcji.

C:\scripts>ruby crypto_server.rb -p 8000 -a blowfish -k $ecYouR3
Encrypted: ygk1ojpgM+NfktPk3PZBTd2WNlDHu/j4
Decrypted: By trials and errors...
Encrypted: PE6R036ub7LT083JLmQyVEeE89WiGAis
Decrypted: You can be successful

Zachęcam do dalszego eksperymentowania z kodem skryptów. Miłej zabawy!

Oto kod źródłowy komponentu serwera:

# Author: Janusz Nawrat (2014) - for education purposes
require 'socket'
require 'ezcrypto'
require 'optparse'

$system_salt = ''      # Salt to strengthen encryption
$key = ""              # Password to crypto key

# ---------------------------------------------------------------------------------
# Decryption of ciphertext using the key dependent from the password provided
# Salt is provided by global variable $system_salt
# Crypto algorithm is the parameter provided to this function.
# ---------------------------------------------------------------------------------

def decrypt(alg, password, cipher)
   begin
      case alg
         when "3des" then key = EzCrypto::Key.with_password(password, $system_salt, :algorithm => 'des3')
         when "aes" then key = EzCrypto::Key.with_password(password, $system_salt, :algorithm => 'aes256')
         when "blowfish" then key = EzCrypto::Key.with_password(password, $system_salt, :algorithm => 'blowfish')
         when "plaintext" then return cipher
         else key = EzCrypto::Key.with_password(password, $system_salt, :algorithm => 'aes256')
      end
      decrypted_text = key.decrypt64(cipher)
   rescue => e
      p e.message
      # p e.backtrace
   end
   return decrypted_text
end

# Let's prepare the CLI parsing job rules
options = {:port => nil, :alg => nil, :key => nil}

parser = OptionParser.new do |opts|
   opts.banner = "Crypto netcat server\nProper syntax: #{File.basename($0)} [options]"
      opts.on('-p', '--port port', 'Port number') do |port|
      options[:port] = port;
   end

   opts.on('-a', '--alg alg', 'Crypto algorithm') do |algorithm|
      options[:alg] = algorithm;
   end

   opts.on('-k', '--key key', 'Password to crypto key') do |cryptkey|
      options[:key] = cryptkey;
   end

   opts.on('-h', '--help', 'Displays Help') do
      puts opts
      exit
   end
end

parser.parse!   # Performing CLI options parsing

# If not specified on CLI level, set the options now interactively

if options[:port] == nil
   print 'Enter the port number: '
   options[:port] = gets.chomp      # Read port number if not set already on CLI level
end

port_number = options[:port]

if options[:key] == nil
   print 'Enter the password to crypto key: '
   options[:key] = gets.chomp      # Read password to crypto key if not set already on CLI level
end

key = options[:key]

if options[:alg] == nil
   print 'Enter the crypto algorithm (3des/aes/blowfish/plaintext): '
   options[:alg] = gets.chomp      # Read the name of the crypto algorithm if not set already on CLI level
end

alg = options[:alg]

begin
   server = TCPServer.open(port_number)     # Run the server
   client = server.accept                   # Get connection from the client
rescue => e
   p e.message                              # Display diagnostics if something went wrong 
   p e.backtrace                            # with running server or getting connection from the client  
end

loop {
   line = client.gets.chomp                       # Get ciphertext from client
   puts "Encrypted: #{line}"                      # Display ciphertext
   puts "Decrypted: #{decrypt(alg, key, line)}"   # Decrypt ciphertext end display plaintext
}

server.close    # Shut-down the server

Oto kod źródłowy komponentu klienta:

# Author: Janusz Nawrat (2014) - for education purposes
require 'socket'        # If needed please remember to install
require 'ezcrypto'      # appropriate gem:
require 'optparse'      # gem install <name_of_gem>

$system_salt = ''      # Salt to strengthen encryption. Please set it if needed.
$key = ""              # Password to crypto key

# ---------------------------------------------------------------------------------
# Encryption of plaintext using the key dependent from the password provided
# Salt is provided by global variable $system_salt
# Crypto algorithm is the parameter provided to the function.
# ---------------------------------------------------------------------------------
def encrypt(alg, password, string)
   begin
      case alg
         when "3des" then key = EzCrypto::Key.with_password(password, $system_salt, :algorithm => 'des3')
         when "aes" then key = EzCrypto::Key.with_password(password, $system_salt, :algorithm => 'aes256')
         when "blowfish" then key = EzCrypto::Key.with_password(password, $system_salt, :algorithm => 'blowfish')
         when "plaintext" then return string
         else key = EzCrypto::Key.with_password(password, $system_salt, :algorithm => 'aes256')
      end
      encrypted_text = key.encrypt64(string)
   rescue => e
      p e.message
      # p e.backtrace
   end
   return encrypted_text
end

# Let's prepare the CLI parsing job rules
options = {:port => nil, :host => nil, :alg => nil, :key => nil}

parser = OptionParser.new do |opts|
   opts.banner = "Crypto netcat client\nProper syntax: #{File.basename($0)} [options]"
   opts.on('-p', '--port port', 'Port number') do |port|
      options[:port] = port;
   end

   opts.on('-d', '--destination destination', 'Destination host (name or IP address)') do |host|
      options[:host] = host;
   end

   opts.on('-a', '--algorithm algorithm', 'Crypto algorithm') do |alg|
      options[:alg] = alg;
   end

   opts.on('-k', '--key key', 'Password to crypto key') do |key|
      options[:key] = key;
   end

   opts.on('-h', '--help', 'Displays Help') do
      puts opts
      exit
   end
end

parser.parse! # Performing CLI options parsing

# If not specified on CLI level, set the options now interactively
if options[:port] == nil
   print 'Enter the port number: '
   options[:port] = gets.chomp      # Read port number if not set already on CLI level
end

port_number = options[:port]

if options[:host] == nil
   print 'Enter destination host name or IP address: '
   options[:host] = gets.chomp      # Read server host name or IP address if not set already on CLI level
end

destination = options[:host]

if options[:key] == nil
   print 'Enter the password to crypto key: '
   options[:key] = gets.chomp      # Read password to crypto key if not set already on CLI level
end

key = options[:key]

if options[:alg] == nil
   print 'Enter the crypto algorithm (3des/aes/blowfish/plaintext): '
   options[:alg] = gets.chomp      # Read the name of the crypto algorithm if not set already on CLI level
end

alg = options[:alg]

hostname = destination
port = port_number

begin
   server = TCPSocket.open(hostname, port) # Connect to the server
rescue => e
   p e.message      # Display diagnostics if something went wrong with connection to the server
   p e.backtrace
end

while line = gets.chomp
   puts "Plaintext: #{line}"              # Display text in the raw (plaintext) form
   ciphertext = encrypt(alg, key, line)   # Encrypt it
   puts "Ciphertext: #{ciphertext}"       # Display Encrypted text
   server.puts ciphertext                 # Send encrypted text to the server
end

server.close   # Close connection to the server

Informacje Janusz Nawrat
Just ordinary man who likes thinking...

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

TOMASZ WEŁNA

artysta grafik | wykładowca

PRACOWNIA OKO

Szkoła Rysunku Malarstwa i Grafiki DR TOMASZA WEŁNY | KRAKÓW | Plac Matejki 10 | tel 691 81 75 74

Piękno neurobiologii

Blog Jerzego Vetulaniego

Teoria muzyki, zasady muzyki, podstawy muzyki

Teoria muzyki, zasady muzyki, podstawy muzyki - czyli to co każdy amator muzyki wiedzieć powinien :)

Personal Development & Inspirations

Przemyślenia i refleksje, którymi warto się podzielić (blog by Janusz Nawrat)

Business IT Cooperation Platform

Biznes i IT - dwa światy, które muszą współdziałać

%d bloggers like this: