TripleDES в Perl /PHP /ColdFusion

Недавно возникла проблема, связанная с подключением API к процессору платежей, который запрашивал шифрование строки для использования в качестве токена с использованием стандарта TripleDES. Наши приложения выполняются с использованием ColdFusion, который имеет тег Encrypt, который поддерживает TripleDES, однако результат, который мы получили, оказался не таким, как ожидал обработчик платежей.

Прежде всего, вот полученный токен, который ожидал обработчик платежей.

AYOF+kRtg239Mnyc8QIarw==

А ниже приведен фрагмент ColdFusion, который мы использовали, и полученная строка.

<!--- Coldfusion Crypt (here be monsters) --->
<cfset theKey="123412341234123412341234">
<cfset theString = "username=test123">
<cfset strEncodedEnc = Encrypt(theString, theKey, "DESEDE", "Base64")>
<!---
 resulting string(strEncodedEnc): tc/Jb7E9w+HpU2Yvn5dA7ILGmyNTQM0h
--->

Как видите, это не возвращало строку, на которую мы надеялись. В поисках решения мы отказались от ColdFusion для этого процесса и попытались воспроизвести токен в PHP.

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

В любом случае, к исходному тексту PHP, который мы пробовали, и к полученной строке.

/* PHP Circus (here be Elephants) */
$theKey="123412341234123412341234";
$theString="username=test123";
$strEncodedEnc=base64_encode(mcrypt_ecb (MCRYPT_3DES, $theKey, $theString, MCRYPT_ENCRYPT));
/*
 resulting string(strEncodedEnc): sfiSu4mVggia8Ysw98x0uw==
*/

Как видите, у нас есть другая строка, которая отличается как от строки, ожидаемой обработчиком платежей, так и от строки, созданной ColdFusion. Методы интеграции головы к стене.

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

Он согласился, что наши попытки CF и PHP не привели к правильной строке. После быстрого поиска он также согласился, что это был не наш источник, а то, как эти два языка реализовали свое видение стандарта TripleDES.

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

#!/usr/bin/perl
# Perl Crypt Calamity (here be...something)
use strict;
use CGI;
use MIME::Base64;
use Crypt::TripleDES;

my $cgi = CGI->new();
my $param = $cgi->Vars();

$param->{key} = "123412341234123412341234";
$param->{string} = "username=test123";
my $des = Crypt::TripleDES->new();

my $enc = $des->encrypt3($param->{string}, $param->{key});
$enc = encode_base64($enc);
$enc =~ s/\n//gs;

# resulting string (enc): AYOF+kRtg239Mnyc8QIarw==

Итак, у нас это есть. Три языка, три реализации того, что они указывают в документации как стандартное шифрование TripleDES, и три совершенно разные результирующие строки.

Мой вопрос заключается в том, что, исходя из вашего опыта работы с этими тремя языками и их реализацией алгоритма TripleDES, вы смогли заставить любые два из них дать одинаковый ответ, и если да, то какие настройки для кода были вам необходимы? сделать для того, чтобы прийти к результату?

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

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

12 голосов | спросил Seidr 12 Maypm10 2010, 13:19:33

8 ответов


0

TripleDES в Perl никогда не следует использовать. Он делает так много странных вещей, и вам будет весело.

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

$theKey="123412341234123412341234";
$key = pack('H*', str_pad($theKey, 16*3, '0'));
$strEncodedEnc=base64_encode(mcrypt_ecb (MCRYPT_3DES, $key, $theString, MCRYPT_ENCRYPT));
echo $strEncodedEnc, "\n";

Результат

AYOF+kRtg239Mnyc8QIarw==

Тогда вы должны дополнить это странным образом. Я забыл детали. Вам повезло с этим примером (это 16 символов).

ответил ZZ Coder 12 Maypm10 2010, 14:25:13
0

Ответ Coldfusion:

Первая проблема заключается в том, что длина вашего ключа неверна для Triple DES. Кодер ZZ правильно сделал вывод, что его нужно дополнить до правильной длины нулями.

Следующим шагом является то, что ключ должен быть преобразован в шестнадцатеричный. Чтобы сделать это в CF, мы имеем:

<cfset theKey="123412341234123412341234000000000000000000000000">
<cfset encodedKey = ToBase64(BinaryDecode(theKey, "HEX"))>

Последний шаг заключается в том, что результат также не дополняется, поэтому нам нужно указать это в алгоритме шифрования в CF:

<cfset strEncodedEnc = Encrypt(theString, encodedKey, "DESEDE/ECB/NoPadding", "Base64")>

Полученный полный код:

<cfset theKey="123412341234123412341234000000000000000000000000">
<cfset encodedKey = ToBase64(BinaryDecode(theKey, "HEX"))>
<cfset theString = "username=test123">
<cfset strEncodedEnc = Encrypt(theString, encodedKey, "DESEDE/ECB/NoPadding", "Base64")>
<cfdump var="#strEncodedEnc#"><br>

Результаты:

AYOF+kRtg239Mnyc8QIarw==
ответил Edward M Smith 12 Maypm10 2010, 17:28:05
0

Я включу приведенный ниже код для всех, кто работает над обновлением CCBill (похоже на компанию, указанную в исходном посте). Функции PHP, приведенные ниже, будут соответствовать выходным данным внутреннего шифрования 3DES /TripleDES CCBill, как описано в документации здесь: http://www.ccbill.com/cs/manuals/CCBill_Subscription_Upgrade_Users_Guide.pdf/a

//Encrypt String using 3DES Key
function encrypt($str,$key){
    $hex_key = hexmod($key);
    $bin_hex_key = pack('H*', str_pad($hex_key, 16*3, '0'));
    //Pad string length to exact multiple of 8
    $str = $str. str_repeat(' ',8-(strlen($str)%8) );   
    $out = base64_encode( mcrypt_ecb(MCRYPT_3DES, $bin_hex_key, $str, MCRYPT_ENCRYPT) );
    //print_r('Key/Hex/Str: '.$key.' -> '.$hex_key.' -> '.$str.' -> '.$out,1);
    return $out;
}

//Hex Modulus: Converts G-Z/g-z to 0-f (See @Jinyo's Post)
//Necessary to match CCBill's Encryption
function hexmod($str){
    //Convert G-Z & g-z to 0-f
    $ascii_in  = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    $ascii_out = '0123456789ABCDEF0123456789ABCDEF0123abcdef0123456789abcdef0123';
    $hex_out = str_replace(str_split($ascii_in),str_split($ascii_out),$str);
    return $hex_out;
}

$triple_des_key = 'ABCDEFGHIJKLMNOPQRSTUVWX'; // <!-- 24char 3DES Key
$username_string = 'username=<username here>'; // Encrypt this string
$encrypted_username = encrypt($username_string,$triple_des_key); // <-- Output
ответил Eric Kigathi 12 PM000000110000003931 2012, 23:52:39
0

О, это весело!

> hex clear_text
0000  75 73 65 72 6e 61 6d 65  3d 74 65 73 74 31 32 33  username =test123

> openssl des3 -in clear_text -out crypt_text
enter des-ede3-cbc encryption password: 123412341234123412341234
Verifying - enter des-ede3-cbc encryption password: 123412341234123412341234

> hex crypt_text
0000  53 61 6c 74 65 64 5f 5f  d7 1b 37 a6 e0 c4 99 d1  Salted__ ..7.....
0010  ce 39 7f 87 5e 8b e8 8a  27 ca 39 41 58 01 38 16  .9..^... '.9AX.8.
0020  a5 2b c8 14 ed da b7 d5                           .+......

> base64 crypt_text
U2FsdGVkX1/XGzem4MSZ0c45f4dei+iKJ8o5QVgBOBalK8gU7dq31Q==

> openssl version
OpenSSL 0.9.8k 25 Mar 2009

> base64 --version | head -n 1
base64 (GNU coreutils) 7.1

Вам следует поговорить с экспертом по крипто, попробуйте, возможно, списки рассылки openssl-users или dev-tech-crypto @ mozilla, если здесь не появится кто-то полезный.

ответил daxim 12 Maypm10 2010, 13:56:45
0

ZZ Coder был почти у цели. Еще несколько предостережений о том, почему коды Perl и PHP возвращают разные шифры.

Во-первых, при наличии недопустимых шестнадцатеричных букв (букв после F) замените их в соответствии со следующим правилом:

    G- и при 0 Н- > 1 I- > 2 литий> J- > 3 литий>
  • ...
  • Р- > 9 литий> Q- > А литий> R- > Б
  • ...
  • V-& GT, F W- и при 0
  • ...
  • Z- > 3 литий>

При использовании этого метода ключом для AZ98AZ98AZ98AZ98AZ98AZ98 является A398A398A398A398A398A398000000000000000000000000 (после заполнения нулями).

Во-вторых, зашифрованный текст должен быть дополнен пробелами, чтобы число символов делилось на 8. В этом примере username = test123 делится на 8, поэтому его не нужно дополнять. Но если бы это было username = test12, то в конце ему нужен был бы один пробел.

Следующий код PHP возвращает шифрование, соответствующее шифрованию perl

$theKey="A398A398A398A398A398A398000000000000000000000000";
 $key = pack("H*", $theKey);
$input = "username=test123";

$strEncodedEnc=mcrypt_ecb (MCRYPT_3DES, $key, $input, MCRYPT_ENCRYPT);
$strEncodedEnc64=base64_encode($strEncodedEnc);
echo $strEncodedEnc . "<br />";
echo $strEncodedEnc64 . "<br />";
ответил jinyo 18 Jam1000000amWed, 18 Jan 2012 10:59:52 +040012 2012, 10:59:52
0

Это заняло у меня большую часть вечера, но именно так выглядит решение @Eric Kigathi в ruby ​​

def encoding(key, val)
  require "openssl"
  des = OpenSSL::Cipher::Cipher.new('des-ede3')
  des.encrypt
  des.key = convert_key_to_hex_bin key

  #ENCRYPTION
  des.padding = 0 #Tell Openssl not to pad
  val += " " until val.bytesize % 8 == 0 #Pad with zeros
  edata = des.update(val) + des.final 
  b64data = Base64.encode64(edata).gsub(/\n/,'')
end

def convert_key_to_hex_bin(str)
  decoder_ring = Hash['0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/'.split(//).zip('0123456789ABCDEF0123456789ABCDEF0123ABCDEF0123456789ABCDEF012345'.split(//))]
  str.gsub!(/./, decoder_ring)
  [str.ljust(16*3, '0')].pack("H*")
end

Будьте осторожны, хотя. Я не совсем уверен, что + и /конвертировать в конце. Я догадался на 4 и 5, но я не могу сказать вам, если это правда.

Шляпа для http: //opensourcetester. co.uk/2012/11/29/zeros-padding-3des-ruby-openssl/ код шифрования и комментарий.

ответил kayakyakr 2 J0000006Europe/Moscow 2013, 10:29:07
0

В ответе ColdFusion отсутствует изменение работающего ключа ccbill (как в ответе Эрика) ... Я изменил ответ Эрика на код Люси. Не нужно много работать, чтобы вернуть его в совместимый с ACF код (изменение структуры в ReplaceNoCase на отдельные).

public function ccbillupgrade(string key = "XXXXXXXXXXXXXXXXXXXXXXXX", string username){

    var remote_user = padUserName("username=#arguments.username#");
    var padded_key = 
        Ucase(
            Replace(
                LJustify(
                    hexmod(arguments.key)
                , 48), // Pad key to 48 bytes (hex) 
                " ", '0', 'all'
            )
        );

    var encodedKey = ToBase64(BinaryDecode(padded_key, "HEX"));

    return Encrypt(remote_user, encodedKey, "DESEDE/ECB/NoPadding", "Base64");
}

private string function hexmod(string input) {
    return ReplaceNoCase( arguments.input,
        {
            'G' = '0', 'H' = '1',
            'I' = '2', 'J' = '3',
            'K' = '4', 'L' = '5',
            'M' = '6', 'N' = '7',
            'O' = '8', 'P' = '9',
            'Q' = 'A', 'R' = 'B',
            'S' = 'C', 'T' = 'D',
            'U' = 'E', 'V' = 'F',
            'W' = '0', 'X' = '1',
            'Y' = '2', 'Z' = '3'

        }
    );
}
private string function padUserName(string username) {
    var neededLength = Len(arguments.username) + ( 8 - Len(username) % 8 );
    return LJustify(arguments.username, neededLength);
}
ответил Ben 16 J0000006Europe/Moscow 2016, 15:23:30
0

Есть две проблемы (или нет) с Crypt :: TripleDES:

  1. Тот факт, что ключи для Crypt :: TripleDES являются HEX (объяснено ранее ZZ Coder). Вы можете зашифровать свой ключ, используя unpack или ord /sprintf, или несколько других методов:

    • $ pass = unpack ("H *", "YOUR PASSPHRASE"); # упаковать /распаковать версию

    • $ pass = join ('', map {sprintf ("% x", $ )} map {ord ($ )} split (//, "YOUR PASS") );

    Crypt :: TripleDES дополняет парольную фразу пробелами (что было хорошо для меня)

  2. Crypt :: TripleDES выполняет заполнение пробелами только простого текста. Существует множество методов заполнения, которые используются в Java или PHP. Mcrypt_encrypt:

    • (т. е. PKCS5, PKCS7, CMS) - панель с байтами с одинаковым значением, указывающим количество заполненных байтов, например: "andrei" -> гекс: 61 6e 64 72 65 69 -> дополнено: 61 6e 64 72 65 69 02 02
    • панель с нулевыми символами, например: 61 6e 64 72 65 69 00 00
    • панель с пробелами (Crypt :: TripleDES уже делает это)
    • блок с нулями (нулевыми символами), за исключением последнего байта, который будет числом дополненных байтов, например: 61 6e 64 72 65 69 00 02
    • pad с 0x80, за которым следуют нулевые символы, например: 61 6e 64 72 65 69 80 00

Обратите внимание на ваш зашифрованный текст, если он совпадает до некоторой точки, но окончание отличается, тогда у вас есть проблема с заполнением простым текстом. В противном случае у вас может возникнуть проблема с парольной фразой, проблема с режимом блока шифрования (EBC, CBC, ..) "> http://www.tools4noobs.com/online_tools/encrypt/help_modes.php или проблема с алгоритмом.

Итак, что я сделал в Perl, чтобы иметь возможность сопоставлять зашифрованный текст из Java (который использовал заполнение нулевыми символами):

my $pass = unpack("H*", "MY PASS");
my $text = "bla bla bla";
my $pad = 8 - (length $text % 8);
$pad = 0 if ( $pad > 7 );
$text .= chr(00) x $pad;

my $des = new Crypt::TripleDES;
my $cipher = $des->encrypt3( $text, $pass );

Надеюсь, это поможет

ответил andrei 26 PMpThu, 26 Apr 2012 20:45:12 +040045Thursday 2012, 20:45:12

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132