Ruby OpenSSL::Cipher дает другой результат Тройной DES в режиме CBC

Мне нужно зашифровать данные с помощью симметричного ключа Triple-DES (TDEA) двойной длины в режиме CBC. Я использую Ruby OpenSSl::Cipher. Но это дает другой результат по сравнению с криптографическим калькулятором BP-tool и калькулятором EMV DES. Ниже приведен мой код

Может ли кто-нибудь помочь мне, почему мои результаты отличаются? У меня также есть онлайн-инструмент для проверки моего результата.

http://www.emvlab.org/descalc/?key=2315208C9110AD402315208C9110AD40&iv=0000000000000000&input=20205A4F534135366461746574696D653D32303138313032343130303332333B6578706972793D313232323B70616E3D343233363031373839303132333435362121212121212121&mode=cbc&action=Encrypt&output=20205A4F534135366461746574696D653D32303138313032343130303332333B6578706972793D313232323B70616E3D343233363031373839303132333435362121212121212121

Это дает тот же результат, что и BP-tool.

flavour = 'des-ede3-cbc'
# key and input text are in hexadecimal  
key = "2315208C9110AD402315208C9110AD40"
iv = "0000000000000000"
input_text = "2020205a4f534135366461746574696d653d32303138313032343130303332333b6578706972793d313232323b70616e3d343233363031373839303132333435362121212121212121"
begin
  c = OpenSSL::Cipher.new flavour
  c.encrypt
  c.key = key
  c.iv = iv
  str = input_text
  enc = c.update(str) + c.final
  puts "#{flavour} gives us #{enc.unpack('H*').first.upcase}"
rescue => e
    puts "#{flavour} didn't work because #{e.message}"
end
Result:
8AF0D655A844EC016E171F1892188DD5C77E6F76D79A4582313D2415D9AD6944E16C7CA680CCD0C251BA8921E7C35153F345126A89ECCD68E4B0485FFDCDA778DBBE32B38451A59AFA443A96C46E30BD7CE983078EA40F8F0196C607FF537E5A36B910A40C3B0DB8C42BD2AB135928B0BDDF6DC85CF516EACCD14E0C21B93CE428A9E4F78A13AE9834966B41C4C1835B1910BE5716D117F7

The expected result should be:
4A9E9B245BBDC16D76998143CB6FC1C2B8780539C1C9A100AEC3D745B8BF00DF43A4B51A29A6205845E510E18E26AB940152F90F12E86543A9E5239B30DFDBCD8D3FCDB65F603979
c.key = key возвращается ArgumentError: key must be 24 bytes. Пожалуйста, приведите действительный пример.
anothermh 16.07.2019 20:56
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
1
1
371
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы получить ожидаемые результаты, вам нужно настроить свой код так, чтобы OpenSSL действительно знал, что вы используете данные в шестнадцатеричном кодировании. Поскольку OpenSSL в любом случае имеет дело с простыми байтами, вам необходимо сначала закодировать шестнадцатеричную строку в необработанные байты:

key = ["2315208C9110AD402315208C9110AD40"].pack('H*')
iv = ["0000000000000000"].pack('H*')
input_text = ["2020205a4f534135366461746574696d653d32303138313032343130303332333b6578706972793d313232323b70616e3d343233363031373839303132333435362121212121212121"].pack('H*')

После этого нужно выбрать правильный алгоритм шифрования. Существует несколько различных разновидностей 3DES, которые (среди прочего) отличаются ожидаемой длиной ключа. Поскольку вы имеете дело с 16-байтовым ключом, похоже, вы используете то, что вызывает OpenSSL des-ede-cbc

flavour = 'des-ede-cbc'

Одних этих корректировок обычно должно быть достаточно, чтобы получить тот же результат, который вы увидите на веб-сайте, на который вы ссылаетесь. Однако по какой-то неизвестной мне причине они молча изменяют входные данные, прежде чем фактически их зашифровать.

Конкретные правила, которые они используют, мне неизвестны, но чтобы получить тот же вывод зашифрованного текста, что и на веб-сайте, вам нужно удалить все восклицательные знаки в конце и удалить начальный пробел из вашей входной строки.

input_text # packed as you provided it originally
# => "   ZOSA56datetime=20181024100323;expiry=1222;pan=4236017890123456!!!!!!!!"

# remove trailing exclamation marks
input_text = input_text.sub(/!*$/, '') 
# remove the first character
input_text = input_text[1..-1]

Наконец, теперь вы можете зашифровать свой «улучшенный» input_text:

begin
  c = OpenSSL::Cipher.new(flavour)
  c.encrypt
  c.key = key
  c.iv = iv
  enc = c.update(input_text) + c.final
  puts "#{flavour} gives us #{enc.unpack('H*').first.upcase}"
rescue => e
  puts "#{flavour} didn't work because #{e.message}"
end

Это должно привести к почти ожидаемому зашифрованному тексту.

Однако он лишь немного отличается в последнем блоке из 8 байтов. Я предполагаю, что на веб-сайте используется какое-то странное нестандартное заполнение (которое требуется для обеспечения возможности разделения входного текста на полные 8-байтовые фрагменты), что приводит к тому, что конечный блок отличается.

В любом случае, вы можете расшифровать результат (а также «ожидаемый» шифротекст с веб-сайта с помощью этого кода:

flavour = 'des-ede-cbc'
key = ["2315208C9110AD402315208C9110AD40"].pack('H*')
iv = ["0000000000000000"].pack('H*')
encrypted = ["4A9E9B245BBDC16D76998143CB6FC1C2B8780539C1C9A100AEC3D745B8BF00DF43A4B51A29A6205845E510E18E26AB940152F90F12E86543A9E5239B30DFDBCD8D3FCDB65F603979"].pack('H*')

c = OpenSSL::Cipher.new(flavour)
c.decrypt
c.key = key
c.iv = iv

decrypted = c.update(encrypted)

Как сделать отступы правильными, чтобы соответствовать результату с веб-сайта, я оставлю читателю в качестве упражнения :) К сожалению, я не нашел ни документации, ни исходного кода для их инструмента.

В любом случае обратите внимание, что 3DES — невероятно устаревший алгоритм шифрования, который больше не считается безопасным. Если вам действительно не нужно использовать 3DES, вам следует использовать более безопасный алгоритм.

Проект libsodium предоставляет усиленные реализации безопасных алгоритмов шифрования и подписи, которые могут стать надежной и безопасной основой для желаемого вами протокола. Существуют привязки Ruby, доступные с драгоценным камнем rbnacl.

Другие вопросы по теме

Как исправить конфликт Bundler при отправке в Heroku?
Преднамеренное создание 500 ошибок в Sinatra, чтобы проверить, как они обрабатываются
Исполнение классической синатры и модульной синатры? я делаю неправильно?
При запуске Rspec и Sinatra я продолжаю получать ArgumentError: неправильное количество аргументов (указано 2, ожидается 0)
Есть ли способ использовать другой тип пантомимы в моем приложении Sinatra?
PG::SyntaxError в /bookmarks — я не могу понять, почему SQL-запрос неверен
Как найти запись со всеми необходимыми ассоциациями has_many в таблице соединений вместо запроса ИЛИ/В
Как: оператор sql выбрать все из таблицы, где любая буква, введенная пользователем, соответствует любой букве, включенной в свойство имени
Каков процесс, с помощью которого send_file Sinatra решает, какой тип контента использовать?
Как создать приложение Sinatra со страницей, которая перезагружается с новым содержимым без кеша