2016-01-28 3 views
6

Я использую Ruby's Open SSL bindings для шифрования AES-256. Я могу зашифровать непустую строку. Однако, пытаясь зашифровать пустую строку, Ruby вызывает исключение, жалующееся на то, что данные не должны быть пустыми. Как я могу зашифровать пустую строку с помощью привязок OpenSSL Ruby?Шифровать пустую строку

код, чтобы воспроизвести проблему

require "openssl" 

KEY = OpenSSL::Cipher::Cipher.new("aes-256-cbc").random_key 

def encrypt(plaintext) 
    cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    cipher.encrypt 
    iv = cipher.random_iv 
    cipher.iv = iv 
    cipher.key = KEY 
    ciphertext = cipher.update(plaintext) # <- ArgumentError here 
    ciphertext << cipher.final 
    [iv, ciphertext] 
end 

def decrypt(iv, ciphertext) 
    cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    cipher.decrypt 
    cipher.iv = iv 
    cipher.key = KEY 
    plaintext = cipher.update(ciphertext) 
    plaintext << cipher.final 
    plaintext 
end 

p decrypt(*encrypt("foo")) # "foo" 

p decrypt(*encrypt("")) 
# /tmp/foo.rb:11:in `update': data must not be empty (ArgumentError) 
#   from /tmp/foo.rb:11:in `encrypt' 
#   from /tmp/foo.rb:27:in `<main>' 

Версии

  • рубинового 2.2.2p95
  • OpenSSL :: версия "1.1.0"
  • Microsoft SQL Server 2014 (12.0.2000.8)

Почему я хочу зашифровать пустые строки?

Я пишу программу ETL для переноса данных из одной базы данных в базу данных SqlServer. Некоторые столбцы из исходной базы данных должны быть зашифрованы перед записью их в целевую базу данных. Исходные столбцы могут содержать любые данные, включая пустые строки. Столбцы назначения обычно не имеют значения NULL. Столбцы назначения будут дешифрованы кодом .net.

Цель № 1: Никакая информация о зашифрованном поле, в том числе и не существует, должна быть восстановлена ​​без надлежащего ее дешифрования. Зашифрованная пустая строка должна быть неотличима от любых других зашифрованных данных.

Цель №2: код .net, который расшифровывает эти значения, не должен обрабатывать пустые строки специально.

Если я могу получить openssl для шифрования пустых строк, я добьюсь обоих этих целей.

Обход - Не шифровать пустые строки

Я мог бы просто не шифровать пустые строки, пропуская их через.

def encrypt(plaintext) 
    return plaintext if plaintext.empty? 
    ... 
end 

def decrypt(iv, ciphertext) 
    return ciphertext if ciphertext.empty? 
    ... 
end 

Это имеет недостатки обнажая информации, а также требовать, взаимодействующий код должен быть записан на стороне .net.

Обход - Добавьте константу к открытому тексту

Я мог бы добавить некоторую постоянную строку в незашифрованном перед шифрованием, и удалить его после расшифровки:

PLAINTEXT_SUFFIX = " " 

def encrypt(plaintext) 
    plaintext += PLAINTEXT_SUFFIX 
    ... 
end 

def decrypt(iv, ciphertext) 
    ... 
    plaintext.chomp(PLAINTEXT_SUFFIX) 
end 

Это скрывает, существуют ли данные или нет, но по-прежнему требует сотрудничества .net-кода.

+1

OpenSSL поддерживает заполнение PKCS # 7, поэтому это должно работать. Это, вероятно, лучше подходит для отчета об ошибке. Кстати, вы пробовали просто пропустить 'cipher.update', если открытый текст пуст? 'cipher.final' может быть достаточно в этом случае. –

+0

@ArtjomB. Я попытался отказаться от вызова обновления Cipher #. Это не сработало, когда я попробовал это раньше из-за ошибки ID10T, чем в то время не заметил. Похоже, это решение. Не могли бы вы добавить его в качестве ответа? –

+0

Вы можете добавить его сами. Я понятия не имею о рубине, и я не хочу начинать учиться в это конкретное время. :) –

ответ

7

As suggested by @ArtjomB, это так же просто, как не звонить Cipher#update с пустой строкой. Значение, возвращаемое Cipher#final, затем правильно шифрует пустую строку.

require "openssl" 

KEY = OpenSSL::Cipher::Cipher.new("aes-256-cbc").random_key 

def encrypt(plaintext) 
    cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    cipher.encrypt 
    iv = cipher.random_iv 
    cipher.iv = iv 
    cipher.key = KEY 
    ciphertext = "" 
    ciphertext << cipher.update(plaintext) unless plaintext.empty? 
    ciphertext << cipher.final 
    [iv, ciphertext] 
end 

def decrypt(iv, ciphertext) 
    cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    cipher.decrypt 
    cipher.iv = iv 
    cipher.key = KEY 
    plaintext = cipher.update(ciphertext) 
    plaintext << cipher.final 
end 

p decrypt(*encrypt("foo")) # "foo" 
p decrypt(*encrypt(""))  # "" 
2

Если вы можете использовать функции шифрования, предоставляемые СУБД, то MySQL AES_ENCRYPT, похоже, способен шифровать пустую строку.

Например:

UPDATE some_table 
    SET some_column = AES_ENCRYPT('',UNHEX('F3229A0B371ED2D9441B830D21A390C3')); 

Это AES-128 по умолчанию, я предполагаю, что будет проблемой, как вам нужно AES-256. Кроме того, не знаете, какую СУБД вы используете и имеет ли эта СУБД функции шифрования.

+0

Я не думал использовать базу данных для шифрования. Бит назначения - SqlServer 2014. –

+1

@WayneConrad Не знаю много об этом сервере, но он, похоже, поддерживает AES-256. Возможно, вам придется протестировать его для шифрования пустой строки. –

+0

@WayneConrad На стороне примечания, из-за вашей страницы профиля SO - я обнаружил, что было что-то, называемое [Ruby Extensions] (http://extensions.rubyforge.org/rdoc/index.html), в котором были первые версии 'Symbol # to_proc', который был ассимилирован в Rails, а затем в ядре Ruby. Я делал некоторое исследование о том, действительно ли вы использовали '[день, доллар] .each (&: другое)' в 2003 году: D –

Смежные вопросы