JayJay
JayJay

Reputation: 814

OpenSSL AES-256-CBC Encryption Error, "wrong final block length" Ruby

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

Answers (1)

Maarten Bodewes
Maarten Bodewes

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

Related Questions