Мне нужно зашифровать данные с помощью симметричного ключа Triple-DES (TDEA) двойной длины в режиме CBC. Я использую Ruby OpenSSl::Cipher. Но это дает другой результат по сравнению с криптографическим калькулятором BP-tool и калькулятором EMV DES. Ниже приведен мой код
Может ли кто-нибудь помочь мне, почему мои результаты отличаются? У меня также есть онлайн-инструмент для проверки моего результата.
Это дает тот же результат, что и 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

Чтобы получить ожидаемые результаты, вам нужно настроить свой код так, чтобы 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.
c.key = keyвозвращаетсяArgumentError: key must be 24 bytes. Пожалуйста, приведите действительный пример.