Pergunta

Recentemente, surgiu um problema em relação ligar uma API com um processador de pagamento que solicitavam uma seqüência de caracteres a ser encriptado para ser usado como um token, usando o TripleDES padrão.Nossos Aplicativos são executados usando ColdFusion, que tem um Criptografar tag - que suporta TripleDES - no entanto, o resultado, foram ficando para trás não era o que o processador de pagamento que o esperado.

Primeiro de tudo, aqui é a resultante de token o processador de pagamento que estavam esperando.

AYOF+kRtg239Mnyc8QIarw==

E abaixo está o trecho do ColdFusion que estávamos usando, e o string resultante.

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

Como você pode ver, este não estava retornando a string que esperávamos.Buscando uma solução, enterrámos ColdFusion para este processo e tentou reproduzir o token em PHP.

Agora, estou ciente de que vários idiomas implementar a criptografia de formas diferentes - por exemplo, no passado, a gestão de criptografia entre uma aplicação C# e PHP back-end, eu tive a brincar com preenchimento a fim de obter os dois para falar, mas a minha experiência tem sido que o PHP geralmente se comporta quando se trata de padrões de criptografia.

De qualquer maneira, para o código fonte em PHP, nós tentamos, e a seqüência de caracteres resultante.

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

Como você pode ver claramente, temos outra seqüência de caracteres que difere tanto a seqüência de caracteres esperados para o processador de pagamentos E aquele produzido pelo ColdFusion.Cue cabeça contra a parede técnicas de integração.

Depois de muitos-e-vir de comunicação com o processador de pagamento (muitas e muitas repetições, afirmando que "nós não podemos ajudar com problemas de codificação, você deve fazê-lo incorretamente, leia o manual') finalmente estávamos escalados para alguém com mais do que um par de cérebro-células a esfregar juntos, que foi capaz de dar um passo para trás e, na verdade, analisar e diagnosticar o problema.

Ele concordou, a nossa CF e PHP tentativas não foram resultando na seqüência correta.Depois de uma rápida pesquisa, ele também concordou que ela não era neccesarily a nossa fonte, mas, sim, de como os dois idiomas implementados sua visão do TripleDES padrão.

Vindo para o escritório esta manhã, fomos atendidos por um e-mail com um snippet de código-fonte, em Perl.Este é o código que eles estavam usando diretamente em seu final para produzir o token esperado.

#!/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==

Assim, não temos.Três línguas, três implementações do que eles citam na documentação como TripleDES Padrão de Criptografia, e três totalmente diferentes, resultando cadeias de caracteres.

A minha pergunta é, a partir de sua experiência desses três idiomas e suas implementações do TripleDES algoritmo, você foi capaz de obter quaisquer dois deles para dar a mesma resposta, e se, de modo que ajustes para o código que você tem que fazer para chegar ao resultado?

Eu entendo que isso é muito puxado pergunta, mas eu queria dar clara e precisa definição para cada estágio do teste que tivemos para realizar.

Eu também vou ser a realização de mais algum trabalho de investigação sobre este assunto mais tarde, e vou postar os resultados que eu venha com essa questão, de modo que outros possam evitar essa dor de cabeça.

Foi útil?

Solução

Os triplicados do Perl nunca devem ser usados. Faz tantas coisas estranhas e você vai se divertir.

Seu primeiro problema é que as chaves em Perl são hexadecimais e você precisa convertê -las em binário. Tente isso em 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";

O resultado é,

AYOF+kRtg239Mnyc8QIarw==

Então você tem que prendê -lo de uma maneira estranha. Esqueci os detalhes. Você tem sorte com esta amostra (são 16 chars).

Outras dicas

A resposta do Coldfusion:

O primeiro problema é que o seu comprimento principal não está correto para o Triple des. O ZZ Coder deduziu corretamente que precisa ser acolchoado para o comprimento correto com 0.

O próximo passo é que a chave precisa ser convertida em hexadecimal. Para fazer isso no CF, temos:

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

A etapa final é que o resultado também não está sendo acolchoado, então precisamos especificar isso no algoritmo de criptografia em CF:

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

O código completo resultante:

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

resulta em:

AYOF+kRtg239Mnyc8QIarw==

Incluirei o código abaixo para qualquer pessoa que esteja trabalhando na atualização do CCBILL (que soa como a empresa mencionada na postagem original). As funções PHP abaixo corresponderão à saída da criptografia interna 3DES/Tripledes da CCBILL, conforme descrito na documentação aqui:http://www.ccbill.com/cs/manuals/ccbill_subscription_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

Oh, isso é divertido!

> 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

Você deve conversar com um especialista em criptografia, tente talvez as listas de discussão OpenSSL-Users ou Dev-Tech-Crypto@Mozilla, a menos que alguém útil apareça aqui.

O ZZ Coder estava quase lá. Há apenas algumas advertências no motivo pelo qual os códigos PERL e PHP retornaram criptografias diferentes.

Em primeiro lugar, sempre que houver cartas hexadecimais inválidas (cartas após f), substitua -as de acordo com a seguinte regra:

  • G-> 0
  • H-> 1
  • I-> 2
  • J-> 3
  • ...
  • P-> 9
  • Q-> a
  • R-> b
  • ...
  • V-> f
  • W-> 0
  • ...
  • Z-> 3

Usando esse método, a chave para AZ98AZ98AZ98AZ98AZ98AZ98 é A398A398A398A398A398A3988000000000000000000000000 (Após o preenchimento com zeros).

Em segundo lugar, o texto a ser criptografado deve ser acolchoado com espaços em branco para que o número de caracteres seja divisível por 8. Neste exemplo, nome de usuário = test123 é divisível por 8, portanto não precisa ser acolchoado. Mas, se fosse nome de usuário = test12, precisará de um espaço em branco no final.

O código PHP a seguir retorna uma criptografia que corresponde à criptografia 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 />";

Levei -me a maior parte da noite, mas é assim que a solução @eric kigathi parece em rubi

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

Tenha cuidado, no entanto. Não tenho muita certeza do que o + e / convertem no final. Eu imaginei aos 4 e 5, mas não posso dizer se isso é verdade.

Gorjeta de chapéu para http://opensourcester.co.uk/2012/11/29/zeros-padding-3des-ruby-openssl/ o código de criptografia e o comentário.

Falta a resposta do Coldfusion, modificando a chave do CCBILL para funcionar (como na resposta de Eric) ... Modifiquei a resposta de Eric ao código Lucee. Não deve levar muito trabalho para levá -lo de volta ao código compatível com ACF (alterando a estrutura na replacenocase com os individuais).

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

Há dois problemas (ou não), com Crypt::TripleDES:

  1. O fato de que as chaves para a Cripta::TripleDES são HEX (explicado anteriormente pela ZZ Coder).Você pode hex sua chave usando descompactar ou usando ord/sprintf ou um monte de outros métodos:

    • $pass = descompactar("H", "SUA SENHA");#pack/unpack versão

    • $pass = join(", mapa de { sprintf("%x",$)} mapa { ord($) } split(//, "O PASSE"));

    Cripta::TripleDES almofadas a frase-passe com espaços (que estava ok para mim)

  2. Cripta::TripleDES não preenchimento de espaços em branco apenas do texto sem formatação.Existem inúmeras preenchimento métodos que são usados em Java ou PHP mcrypt_encrypt:

    • (ie.PKCS5, PKCS7, CMS) - pad com bytes de o mesmo valor que indica o número de bytes acolchoado por exemplo:"andrei" -> hex:61 6e 64 72 65 69 -> colar acolchoado:61 6e 64 72 65 69 02 02
    • almofada com caracteres nulos por exemplo:61 6e 64 72 65 69 00 00
    • preencher com espaços (Cripta::TripleDES já faz isso)
    • preencher com zeros (null chars), exceto para o último byte que será o número de acolchoado bytes por exemplo:61 6e 64 72 65 69 00 02
    • almofada com 0x80 seguido por nulo caracteres, por exemplo:61 6e 64 72 65 69 80 00

Preste atenção ao seu texto cifrado, se corresponder a até algum ponto, mas o final é diferente, então você tem um texto simples preenchimento problema.Caso contrário, você pode ter uma frase-passe problema, uma cifra de bloco em modo de problema (EBC,CBC,..) http://www.tools4noobs.com/online_tools/encrypt/help_modes.php ou um algoritmo de problema.

Então o que eu fiz em Perl para ser capaz de corresponder à codificação de texto a partir do Java (que costumava nulo caracteres de preenchimento):

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

Espero que isso ajude

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top