Вопрос

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

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

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 # и Back-End 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 не привели к правильной строке. После быстрого поиска он также согласился с тем, что он не был обязательно нашим источником, а скорее, как два языка реализовали свое видение встроенного стандарта.

Приходя в офис сегодня утром, мы встречались по электронной почте с фрагментом исходного кода, в 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==

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

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

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

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

Это было полезно?

Решение

Уривок 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 символов).

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

Ответ ColdFusion:

Первая проблема в том, что ваша клавиша неверна для Triple Des. CZZ Coder правильно выводил, что его необходимо дополнить до правильной длины с 0.

Следующим шагом является то, что ключ должен быть преобразован в Hex. Для этого в 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==

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

//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

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

> 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

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

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

Во-первых, всякий раз, когда есть недопустимые шестигранные буквы (буквы после f), замените их в соответствии со следующим правилом:

  • G-> 0.
  • H-> 1.
  • I-> 2.
  • J-> 3.
  • ...
  • P-> 9.
  • Q-> A.
  • R-> B.
  • ...
  • V-> F.
  • W-> 0.
  • ...
  • Z-> 3.

Во-вторых, текст, который должен быть зашифрован, должен быть дополнен пробелами, так что количество символов делится на 8. В этом примере имя пользователя = Test123 делится на 8, поэтому его не нужно быть дополненным. Но, если это было имя пользователя = 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 />";

Взял у меня большую часть вечера, но вот как выглядит решение @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/ код шифрования и комментарии.

Ответ ColdFusion отсутствует изменение ключа CCBILL для работы (например, в ответе Эрика) ... Я изменил ответ ERIC на код Lucee. Не следует принимать много работы, чтобы вернуть его обратно к совместимому коду ACF (изменение структуры в рефазенокозе с отдельными).

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);
}

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

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

    • $ pass = распаковать ("h *", "ваша пароль"); # Pack / распаковывать версию

    • $ pass = join ('', карта {sprintf ("% x", $)} Карта {ORD ($)} Сплит (//, "ваш проход"));

    Crypt :: Уривок прокладки прокладки проход с пробелами (которая была в порядке для меня)

  2. Crypt :: Tripledes делает пробел пробел только из простого текста. Есть многочисленные методы прокладки, которые используются на Java или PHP Mcrypt_encrypt:

    • (т.е. pkcs5, pkcs7, cms) - pad с байтами одинакового значения, указывающие количество байтов, например: "Andrei" -> Hex: 61 6e 64 72 65 69 -> Мягкий: 61 6E 64 72 65 69 02 02
    • Pad с нулевыми символами, например: 61 6e 64 72 65 69 00 00
    • Pad с пробелами (Crypt :: Tripledes уже это делает)
    • PAD с нулями (нулевые символы), за исключением последнего байта, который будет количеством мягких байтов, например: 61 6E 64 72 65 69 00 02
    • PAD с 0x80, а затем NULL CHARS, например: 61 6E 64 72 65 69 80 00

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

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

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 );

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top