1. ホーム
  2. ruby

[解決済み] OpenSSL AES-256-CBC 暗号化エラー、"間違った最終ブロック長" Ruby

2022-02-16 23:07:18

質問

Ruby 1.8.6を使用しています。

私は、Ruby における対称型暗号の実装について学ぶために、クライアントから送信されたメッセージを暗号化して返す、基本的なサーバを書いています。このプログラムは、ソケット接続を受け入れ、秘密鍵を共有し、受信したデータを暗号化して、クライアントプログラムに送り返すように設計されています。クライアントは、共有された秘密鍵を使ってメッセージを復号化し、エコーされたメッセージを明らかにします。

私が遭遇している問題は、リターンメッセージが "間違った最終ブロック長 (OpenSSL::CipherError)" を引き起こすということです。この問題をさらに調査し decrypted << chiper.final 私のクライアントプログラムはメッセージを復号化することができますが、最後に追加の文字またはバンクスペースが追加されます。私は、これは final キーワードは、CBCモードの16ビットブロック暗号化/復号化を可能にするために追加のパディングを削除しますが、どうすれば正しく動作するのかが分かりません。

以下は簡略化したサーバーコードです(これが安全でないことは分かっていますが、それは重要ではなく、これは単なる学習用アプリケーションです)。

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

そして、私のクライアントは...

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

クライアントはキーボードから情報を取得し、これをサーバーに送信した後、暗号化されたメッセージの返信を待ちます。前にも言ったように、私は decrypted << cipher.final を正しく動作させたり、echoされたメッセージから追加のパディングをうまく削除することができます。

何かお手伝いいただけると幸いです。ありがとうございます。

解決方法は?

putsgets は文字列に対して動作しますが、暗号文はバイナリです。ですから、文字列を期待して gets (特に最後の改行も復号化しようとすると、おそらくこれがエラーの原因になります)。

その代わりに、まず ベース64エンコード 暗号文の IV(別々に)。次に gets.chomp は、デコードし では を復号化する。

もし、あなたがただ chomp バイナリ暗号文の途中に誤って改行を入れてしまうまでは、暗号化も復号もうまくいくでしょう。


つまり、暗号文は名前に反して実際にはテキストではなく(最近の暗号では)、ランダムなバイナリバイト列であることを決して忘れないでください。