Pregunta

Recientemente surgió un problema con respecto conectar una API con un procesador de pagos que estaban solicitando una cadena a encriptar para ser utilizado como un símbolo, utilizando los TripleDES estándar. Nuestras aplicaciones se ejecutan utilizando ColdFusion, que tiene una etiqueta Cifrar - que apoya TripleDES - sin embargo, el resultado nos iban a dar la espalda no era lo que el procesador de pagos previsto.

En primer lugar, aquí es la resultante de contadores al procesador de pagos estaban esperando.

AYOF+kRtg239Mnyc8QIarw==

Y a continuación es el fragmento de ColdFusion que estábamos usando, y la cadena 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 se puede ver, esto no regresaba la cadena que se pueda desear. Buscando una solución, nos abandonamos ColdFusion para este proceso y se intentó reproducir el token en PHP.

Ahora estoy consciente de que varios lenguajes de implementar el cifrado de diferentes maneras - por ejemplo, en el pasado cifrado gestión entre una aplicación de C # y PHP back-end, he tenido que jugar con el acolchado sobre el fin de conseguir que los dos hablar, pero mi experiencia ha sido que PHP se comporta en general, cuando se trata de normas de cifrado.

De todos modos, a la fuente de PHP lo intentamos, y la cadena 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 se puede ver claramente, tenemos otra cadena que difiere tanto de la cadena esperada por el procesador de pago y el producido por ColdFusion. Cue cabeza-contra-pared técnicas de integración.

Después de muchas comunicaciones a ida y vuelta con el procesador de pagos (montones y montones de repeticiones indicando 'no podemos ayudar con problemas de codificación, que debemos estar haciendo de forma incorrecta, lea el manual') finalmente nos escalado a alguien con más de un par de células cerebrales se rocen entre sí, que fue capaz de volver a paso y en realidad ver y diagnosticar el problema.

Él estuvo de acuerdo, nuestros intentos CF y PHP no se traducen en la cadena correcta. Después de una búsqueda rápida, también acordó que no era neccesarily nuestra fuente, sino más bien cómo los dos idiomas en práctica su visión de los TripleDES estándar.

Al llegar a la oficina esta mañana, nos recibió un correo electrónico con un fragmento de código fuente, en Perl. Este es el código era que estaban usando directamente en su extremo para producir la esperada token.

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

Así que, ahí lo tenemos. Tres idiomas, tres implementaciones de lo que ellos citan en la documentación como TripleDES estándar de cifrado, y tres cadenas resultantes totalmente diferente.

Mi pregunta es, desde su experiencia de estos tres idiomas y sus implementaciones de los TripleDES algoritmo, ¿ha sido capaz de conseguir dos de ellos para dar la misma respuesta, y si es así lo retoques en el código ¿ha tenido que hacer el fin de llegar al resultado?

Yo entiendo que esta es una pregunta muy elaborado, pero yo quería dar configuración clara y precisa para cada etapa de las pruebas que hemos tenido que realizar.

También voy a estar realizando un trabajo de investigación más sobre este tema más adelante, y se publicará cualquier hallazgo que se me ocurren a esta pregunta, para que otros puedan evitar este dolor de cabeza.

¿Fue útil?

Solución

TripleDES del Perl nunca debería ser utilizado. Lo hace muchas cosas extrañas y se le va a divertirse.

Su primer problema es que las llaves en Perl son hexagonales y hay que convertirlos en binario. Prueba esto en 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";

El resultado es,

AYOF+kRtg239Mnyc8QIarw==

A continuación, hay que rellenarlo de una manera extraña. He olvidado los detalles. Tienes suerte con esta muestra (que es de 16 caracteres).

Otros consejos

La respuesta Coldfusion:

El primer problema es que su longitud de la clave no es correcta para Triple DES. ZZ Coder correctamente dedujo que necesita ser rellenado a la longitud correcta con 0 de.

El siguiente paso es que la clave debe ser convertido a hexadecimal. Para ello en la FQ, tenemos:

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

El paso final es que el resultado no está siendo rellenada o bien, por lo que tenemos que especificar esto en el algoritmo de cifrado en la FQ:

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

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

resultados en:

AYOF+kRtg239Mnyc8QIarw==

Me va a incluir el código de abajo para cualquier persona que pasa a estar trabajando en la actualización CCBill (que suena como la empresa se hace referencia en el post original). Las funciones de PHP a continuación coincidirán con la salida de de CCBill 3DES / TripleDES cifrado interno como se describe en la documentación aquí: 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, esto es 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

Se debe hablar con un experto en criptografía, quizá tratar las listas de correo de los usuarios o openssl-dev-tecnología-cripto @ Mozilla menos que alguien se presenta útiles aquí.

ZZ Coder estaba casi allí. Hay sólo unos pocos más advertencias a por qué los códigos Perl y PHP devueltos diferentes codificaciones.

En primer lugar, cada vez que hay cartas hexagonales no válidos (letras después de F), sustituirlos de acuerdo a la siguiente regla:

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

Usando este método, la clave para AZ98AZ98AZ98AZ98AZ98AZ98 es A398A398A398A398A398A398000000000000000000000000 (después de relleno con ceros).

En segundo lugar, el texto para ser encriptado debe ser rellenado con espacios en blanco de modo que el número de caracteres es divisible por 8. En este ejemplo, nombre de usuario = test123 es divisible por 8 por lo que no necesita ser acolchada. Sin embargo, si se tratara de usuario = prueba12, entonces se necesita un espacio en blanco al final.

El siguiente código PHP devuelve un cifrado que coincida con el Perl cifrado

$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 />";

Me llevó más de una noche, pero así es como se ve la solución de @Eric Kigathi en 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

Ten mucho cuidado, sin embargo. No estoy muy seguro de lo que los signos + y / convertir a al final. Supuse a las 4 y 5, pero no puedo decir si eso es cierto.

punta

http: // opensourcetester. co.uk/2012/11/29/zeros-padding-3des-ruby-openssl/ el código de cifrado y el comentario.

La respuesta ColdFusion falta modificar la clave de CCBill al trabajo (como en la respuesta de Eric) ... He modificado respuesta de Eric para Código Lucee. No debe tomar mucho trabajo para llevarlo de vuelta a la ACF Código compatible (cambio de la estructura en ReplaceNoCase con las individuales).

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

Hay dos problemas (o no) con Crypt :: TripleDES:

  1. El hecho de que las claves para Crypt :: TripleDES son HEX (explicada anteriormente por ZZ Coder). Puede hexadecimal su clave, ya sea usando deshacer las maletas o utilizando ord / sprintf o un montón de otros métodos:

    • $ pass = unpack ( "H *", "la frase de contraseña"); # Paquete / versión de desempaquetado

    • $ = pass unen ( '', la correlación {sprintf ( "% x", $ )} {mapa ord ($ )} dividida (//, "tu pase") );

    Crypt :: TripleDES almohadillas de la frase de paso con espacios (que estaba bien para mí)

  2. Cripta :: TripleDES tiene espacios en blanco relleno única del texto plano. Existen numerosos métodos de relleno que se utilizan en Java o PHP MCRYPT_ENCRYPT:

    • (es decir PKCS5, PKCS7, CMS.) - almohadilla con bytes del mismo valor que indica el número de bytes acolchada por ejemplo: "Andrei" -> hex: 61 6e 64 72 65 69 -> acolchada: 61 6e 64 72 65 69 02 02
    • almohadilla con caracteres nulos por ejemplo: 61 6e 64 72 65 69 00 00
    • rellenar con espacios (Crypt :: TripleDES ya lo hace)
    • almohadilla con ceros (CHARS null), excepto para el último byte que será el número de acolchado bytes por ejemplo: 61 6e 64 72 65 69 00 02
    • almohadilla con 0x80 seguido de caracteres nulos por ejemplo: 61 6e 64 72 65 69 80 00

Preste atención a su texto cifrado, si coincide hasta cierto punto, pero el final es diferente, entonces usted tiene un problema de relleno de texto sin formato. De lo contrario, es posible que tenga un problema frase de paso, un problema de modo de cifrado de bloques (EBC, CBC, ..) http://www.tools4noobs.com/online_tools/encrypt/help_modes.php o un problema algoritmo.

Así que lo que hice en Perl para poder coincidir con el texto cifrado desde Java (que solía caracteres nulos relleno):

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top