Domanda

Recentemente è emerso un problema relativo al collegamento di un'API con un processore di pagamento che richiedeva la crittografia di una stringa da utilizzare come token, utilizzando lo standard TripleDES.Le nostre applicazioni funzionano utilizzando ColdFusion, che ha un tag Encrypt - che supporta TripleDES - tuttavia il risultato che stavamo ottenendo non era quello che il processore di pagamento si aspettava.

Prima di tutto, ecco il token risultante che il processore di pagamento si aspettava.

AYOF+kRtg239Mnyc8QIarw==

E sotto c'è lo snippet di ColdFusion che stavamo utilizzando e la stringa risultante.

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

Come puoi vedere, questo non restituiva la stringa che speravamo.Alla ricerca di una soluzione, abbiamo abbandonato ColdFusion per questo processo e abbiamo tentato di riprodurre il token in PHP.

Ora sono consapevole che vari linguaggi implementano la crittografia in modi diversi: ad esempio in passato, gestendo la crittografia tra un'applicazione C# e il back-end PHP, ho dovuto giocare con il riempimento per far parlare i due, ma la mia esperienza è stata che PHP generalmente si comporta bene quando si tratta di standard di crittografia.

Comunque, passiamo al sorgente PHP che abbiamo provato e alla stringa risultante.

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

Come puoi chiaramente vedere, abbiamo un'altra stringa che differisce sia da quella prevista dal processore di pagamento SIA da quella prodotta da ColdFusion.Tecniche di integrazione testa a testa.

Dopo molte comunicazioni avanti e indietro con l'elaboratore dei pagamenti (tantissimi rappresentanti che affermavano "non possiamo aiutarti con i problemi di codifica, devi farlo in modo errato, leggi il manuale") siamo stati finalmente inoltrati a qualcuno con più di un paio di cellule cerebrali da strofinare insieme, che è stato in grado di fare un passo indietro, osservare e diagnosticare il problema.

Ha acconsentito, i nostri tentativi CF e PHP non hanno prodotto la stringa corretta.Dopo una rapida ricerca ha anche convenuto che non si tratta necessariamente della nostra fonte, ma piuttosto del modo in cui i due linguaggi hanno implementato la loro visione dello standard TripleDES.

Entrando in ufficio stamattina, siamo stati accolti da un'e-mail con uno snippet di codice sorgente, in Perl.Questo era il codice che stavano utilizzando direttamente per produrre il token previsto.

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

Quindi eccoci qua.Tre linguaggi, tre implementazioni di ciò che citano nella documentazione come TripleDES Standard Encryption e tre stringhe risultanti totalmente diverse.

La mia domanda è, dalla tua esperienza con questi tre linguaggi e le loro implementazioni dell'algoritmo TripleDES, sei riuscito a far sì che due di essi diano la stessa risposta e, in caso affermativo, quali modifiche al codice hai dovuto apportare in ordine arrivare al risultato?

Capisco che sia una domanda molto complessa, ma volevo dare un'impostazione chiara e precisa per ogni fase di test che dovevamo eseguire.

In seguito effettuerò anche un ulteriore lavoro di indagine su questo argomento e pubblicherò tutti i risultati che avrò a questa domanda, in modo che altri possano evitare questo mal di testa.

È stato utile?

Soluzione

Il TripleDES di Perl non dovrebbe mai essere utilizzato.Fa così tante cose strane e ti divertirai.

Il tuo primo problema è che le chiavi in ​​Perl sono esadecimali e devi convertirle in binario.Prova questo in 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";

Il risultato è,

AYOF+kRtg239Mnyc8QIarw==

Quindi devi riempirlo in un modo strano.Ho dimenticato i dettagli.Sei fortunato con questo campione (è 16 caratteri).

Altri suggerimenti

Il Coldfusion risposta:

Il primo problema è che la lunghezza della chiave non è corretta per Triple DES. ZZ Coder correttamente dedotto che ha bisogno di essere riempito alla giusta lunghezza con 0 di.

Il passo successivo è che la chiave deve essere convertito in esadecimale. Per fare questo in CF, abbiamo:

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

Il passo finale è che il risultato non è stato imbottito o, così abbiamo bisogno di specificarlo nella algoritmo di cifratura a CF:

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

Il codice completo risultante:

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

Risultati in:

AYOF+kRtg239Mnyc8QIarw==

io includono il codice qui sotto per tutti coloro che capita di essere al lavoro su di aggiornamento CCBill (che suona come la società di cui al post originale). Le funzioni PHP di seguito corrisponderanno l'output di CCBill 3DES / TripleDES crittografia interna, come descritto nella documentazione qui: 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, questo è divertente!

> 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

Si dovrebbe parlare con un esperto di crittografia, provare forse le mailing list openssl-utenti o dev-tech-crypto @ mozilla a meno che qualcuno spettacoli utili qui.

ZZ Coder era quasi lì. C'è solo un paio di avvertimenti per questo che i codici di Perl e PHP restituiti diverse crittografie.

In primo luogo, quando ci sono lettere esadecimali validi (lettere dopo F), sostituirli secondo la seguente regola:

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

Con questo metodo, la chiave per AZ98AZ98AZ98AZ98AZ98AZ98 è A398A398A398A398A398A398000000000000000000000000 (dopo il riempimento con zeri).

In secondo luogo, il testo da cifrare deve essere riempita con spazi bianchi in modo che il numero di caratteri è divisibile per 8. In questo esempio, username = test123 è divisibile per 8 quindi non ha bisogno di essere riempito. Ma, se si trattasse di username = test12, quindi ha bisogno di uno spazio bianco alla fine.

Il seguente codice PHP restituisce un crittografia che corrisponde a perl crittografia

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

Mi ha portato la maggior parte di una serata, ma questo è come sembra la soluzione di @Eric Kigathi in rubino

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

Do attenzione, però. Io non sono abbastanza sicuro che cosa il + e / convertito alla fine. Ho indovinato a 4 e 5, ma non posso dire se questo è vero.

Punta di cappello a http: // opensourcetester. co.uk/2012/11/29/zeros-padding-3des-ruby-openssl/ il codice di crittografia e il commento.

La risposta ColdFusion manca modificando la chiave CCBill al lavoro (come nella risposta di Eric) ... Ho modificato risposta di Eric al Codice Lucee. Non dovrebbe prendere molto lavoro per prendere di nuovo a ACF codice compatibile (modificare la struttura in ReplaceNoCase con quelle individuali).

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

Ci sono due problemi (o non) con Crypt :: TripleDES:

  1. Il fatto che le chiavi per cripta :: TripleDES sono HEX (spiegato in precedenza da ZZ Coder). È possibile esadecimale la chiave sia usando unpack o utilizzando ord / sprintf o di un gruppo di altri metodi:

    • $ passaggio = unpack ( "H *", "la propria passphrase"); # Pacchetto / versione unpack

    • $ pass = uniscono ( '', {mappa sprintf ( "% x", $ )} mappa {ord ($ )} split (//, "il pass") );

    Cripta :: TripleDES pastiglie il pass-frase con spazi (che era ok per me)

  2. Cripta :: TripleDES fa spazio bianco imbottitura solo del testo in chiaro. Ci sono numerosi metodi di riempimento che vengono utilizzati sul mcrypt_encrypt Java o PHP:

    • (cioè PKCS5, PKCS7, CMS.) - pad con byte dello stesso valore che indica il numero di byte imbottita es: "andrei" -> hex: 61 6e 64 72 65 69 -> imbottito: 61 6e 64 72 65 69 02 02
    • pad con caratteri nulli es: 61 6e 64 72 65 69 00 00
    • pad con spazi (cripta :: TripleDES già fa questo)
    • pad con zeri (chars null) ad eccezione dell'ultimo byte che sarà il numero di byte imbottita es: 61 6e 64 72 65 69 00 02
    • pad con 0x80 seguita da caratteri nulli es: 61 6e 64 72 65 69 80 00

Prestate attenzione alla vostra cipher-text, se corrisponde fino a un certo punto, ma il finale è diverso, allora hai un problema imbottitura testo semplice. In caso contrario, si potrebbe avere un problema di pass-frase, un problema modalità di cifratura a blocchi (EBC, CBC, ..) http://www.tools4noobs.com/online_tools/encrypt/help_modes.php o un problema di procedura.

Quindi quello che ho fatto in Perl per essere in grado di eguagliare la cifra-text da Java (che ha usato caratteri nulli padding):

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

Spero che questo aiuti

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top