Reputation: 814
Using Ruby 1.8.6.
I'm writing a basic server, that echo's an encrypted version a message sent from a client, to learn about the implementation of symmetric encryption in Ruby. The program is designed to accept a socket connection, share it's secret key then encrypt data it receives, before sending it back to the client program. The client then decrypts the message, using the shared secret key, revealing the echo'd message.
The problem I am running into is the return message causes a "wrong final block length (OpenSSL::CipherError)". Inspecting the issue further, removing the decrypted << chiper.final
allows my client program to decrypt the message, but adds additional characters or bank spaces at the end. I'm aware that this is because the final
keyword removes the additional padding to allow for 16 bit block encryption/decryption of the CBC mode, but I cannot figure out how to make the work correctly.
Here's simplified server code (I know this isn't secure, that not the point, this is merely a learning application)
require 'socket'
require 'thread'
require 'openssl'
require 'digest/sha1'
class Server
@@static_id = 1
@connection_no
@port
@server
@aes_cipher
@key
def initialize(p)
#setting up server connections
puts "Starting server"
@port = p
puts "connections on port #{@port} will be accepted"
@server = TCPServer.open(@port)
#generate a secret key
puts "creating secret key..."
@aes_cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
@aes_cipher.encrypt
@key = @aes_cipher.random_key
@aes_cipher.key = @key
puts "key: #{@key}"
#start server
start_server
end
def start_server
loop{
Thread.new(@server.accept) do |client|
#connection and request
sock_domain, remote_port, remote_hostname, remote_ip = client.peeraddr
client_ip = remote_ip.to_s
@@static_id += 1
@connection_no = @@static_id
puts "\nConnection ##{@connection_no} client #{client_ip} accepted"
#send client secret key
client.puts @key
#receive data from client
data = client.gets
puts "received: #{data}"
# you will need to store these for later, in order to decrypt your data
iv = @aes_cipher.random_iv
@aes_cipher.iv = iv
puts "generated IV: #{iv}"
encrypted = @aes_cipher.update(data)
encrypted << @aes_cipher.final
puts "Encrypted Msg: #{encrypted}"
#send back IV and data
client.puts encrypted
client.puts iv
#close connections
client.close
end
}
end
end
And my client...
require 'socket'
require 'thread'
require 'openssl'
require 'digest/sha1'
class aes_client
@port
@hostname
def initialize(p)
@hostname = 'localhost'
@port = p
connect
end
def connect
#establis connections
s = TCPSocket.new(@hostname, @port)
#get key on connection
key = s.gets
puts "Key to decrypt: #{key}"
#send data
data = $stdin.gets.chomp
s.puts data
#receive message and IV
message = s.gets
puts "Encrypted Message: #{message}"
iv = s.gets
puts "IV to decypt: #{iv}"
# now we create a sipher for decrypting
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.decrypt
cipher.key = key
cipher.iv = iv
# and decrypt it
decrypted = cipher.update(message)
#decrypted << cipher.final
puts "decrypted: #{decrypted}\n"
s.close
end
end
The client takes information from the keyboard and sends this to the server, before waiting for the encrypted message in return. As said before, I cannot think of a wait to get the decrypted << cipher.final
to work correctly, or to successfully remove the additional padding from the echo'd message.
Any help will be greatly appreciated. Thanks.
Upvotes: 4
Views: 17379
Reputation: 93968
puts
and gets
work on strings, while ciphertext is binary. So you run into problems if you just expect a string and then a newline during gets
(especially if you try to decrypt the final newline as well, this is probably what causes the error).
Instead you could first base 64 encode the ciphertext and IV (separately). Then gets.chomp
, decode and then decrypt the message.
If you just chomp
the newline then your encryption / decryption will likely work too... until you accidentally introduce a newline in the middle of the binary ciphertext.
So never forget, despite the name ciphertext isn't actually text (for modern ciphers), it's a random binary string of bytes.
Upvotes: 5