Question

Récemment, un problème a surgi au sujet de brancher un API avec un processeur de paiement qui demande une chaîne à chiffrer pour être utilisé comme un jeton, en utilisant la norme TripleDES. Nos applications fonctionnent en utilisant ColdFusion, qui a une étiquette Crypter - qui prend en charge TripleDES - mais le résultat que nous récupérons était pas ce que le processeur de paiement prévu.

Tout d'abord, voici le jeton résultant du processeur de paiement attendaient.

AYOF+kRtg239Mnyc8QIarw==

Et ci-dessous l'extrait de ColdFusion que nous utilisions, et la chaîne résultante.

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

Comme vous pouvez le voir, ce ne renvoyait pas la chaîne que nous espérions. A la recherche d'une solution, nous avons abandonné ColdFusion pour ce processus et tenté de reproduire le jeton en PHP.

Maintenant, je suis conscient du fait que plusieurs langues mettent en œuvre le chiffrement de différentes manières - par exemple dans le cryptage de gestion passé entre un back-end application C # et PHP, j'ai dû en jouer avec un rembourrage afin d'obtenir les deux à parler, mais mon expérience a été que PHP se comporte généralement en matière de normes de cryptage.

Quoi qu'il en soit, à la source de PHP, nous avons essayé, et la chaîne résultante.

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

Comme vous pouvez le voir clairement, nous avons une autre chaîne qui diffère à la fois la chaîne attendue par le processeur de paiement et celui produit par ColdFusion. techniques d'intégration Cue tête-contre-mur.

Après plusieurs va-et-vient des communications avec le processeur de paiement (lots et beaucoup de représentants disant «Nous ne pouvons pas aider les problèmes de codage, vous devez faire ce mal, lisez le manuel) nous avons finalement grimpé à quelqu'un avec plus d'un couple de cellules du cerveau se frotter ensemble, qui a pu pas en arrière et réellement regarder et diagnostiquer le problème.

Il est d'accord, nos tentatives des FC et PHP entraînaient pas dans la chaîne correcte. Après une recherche rapide, il a également convenu que ce n'était pas neccesarily notre source, mais plutôt comment les deux langues mis en œuvre leur vision de la norme TripleDES.

En entrant dans le bureau ce matin, nous avons été accueillis par un e-mail avec un extrait de code source, en Perl. C'est était le code qu'ils utilisent directement sur leur extrémité pour produire le jeton attendu.

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

Alors, il nous l'avons. Trois langues, trois implémentations de ce qu'ils citent dans la documentation comme TripleDES Encryption Standard, et trois chaînes résultantes totalement différentes.

Ma question est, de votre expérience de ces trois langues et leur mise en œuvre des TripleDES algorithme, vous avez été en mesure d'obtenir deux d'entre eux pour donner la même réponse, et si oui tweaks au code que vous avez eu à faire pour venir au résultat?

Je comprends cela est une question très élaboré, mais je voulais donner cadre clair et précis pour chaque étape de test que nous avons dû effectuer.

Je vais aussi effectuerons un travail plus d'enquête à ce sujet plus tard, et nous publierons les résultats que je viens avec cette question, afin que les autres peuvent éviter ce casse-tête.

Était-ce utile?

La solution

ne doit jamais être utilisé les TripleDES du Perl. Il fait tant de choses étranges et vous allez vous amuser.

Votre premier problème est que les clés en Perl sont hex et vous devez les convertir en binaire. Essayez ceci 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";

Le résultat est,

AYOF+kRtg239Mnyc8QIarw==

Ensuite, vous devez pad d'une façon bizarre. J'ai oublié les détails. Vous êtes chanceux avec cet exemple (il est 16 caractères).

Autres conseils

Le Coldfusion Réponse:

Le premier problème est que votre longueur de clé est incorrect pour Triple DES. ZZ Coder déduit à juste titre qu'il doit être rembourré à la bonne longueur des 0.

L'étape suivante est que la clé doit être converti en hexadécimal. Pour ce faire, dans les FC, nous avons:

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

La dernière étape est que le résultat ne soit pas rembourré non plus, donc nous devons préciser dans l'algorithme de chiffrement CF:

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

Le code complet résultant:

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

résultats dans:

AYOF+kRtg239Mnyc8QIarw==

Je vais inclure le code ci-dessous pour toute personne qui arrive à travailler sur la mise à niveau CCBill (qui sonne comme la société visée dans le message original). Les fonctions PHP ci-dessous correspondra à la sortie de 3DES / TripleDES de CCBill chiffrement interne tel que décrit dans la documentation ici: 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, c'est amusant!

> 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

Vous devriez parler à un expert Crypto, essayez peut-être les listes de diffusion OpenSSL-utilisateurs ou dev-tech-Crypto @ mozilla à moins que quelqu'un montre utile ici.

ZZ Coder y était presque. Il y a encore quelques mises en garde à la raison pour laquelle les codes Perl et PHP sont revenus différents cryptages.

Tout d'abord, chaque fois qu'il ya des lettres hexagonaux non valides (lettres après F), remplacez-les selon la règle suivante:

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

En utilisant cette méthode, la clé pour AZ98AZ98AZ98AZ98AZ98AZ98 est A398A398A398A398A398A398000000000000000000000000 (après remplissage par des zéros).

En second lieu, le texte doit être chiffré devrait être rembourré avec des espaces blancs pour que le nombre de caractères est divisible par 8. Dans cet exemple, le nom d'utilisateur = test123 est divisible par 8 donc il n'a pas besoin d'être rembourré. Mais, si elle était le nom d'utilisateur = test12, il a besoin d'un des espaces à la fin.

Le code PHP suivant retourne un cryptage qui correspond au cryptage 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 />";

Il m'a fallu plus d'une soirée, mais voici comment solution de @Eric Kigathi regards en rubis

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

Ne soyez prudent, cependant. Je ne suis pas tout à fait sûr de ce que le + et / convertir à la fin. Je devinais à 4 et 5, mais je ne peux pas vous dire si cela est vrai.

http: // opensourcetester. co.uk/2012/11/29/zeros-padding-3des-ruby-openssl/ le code de cryptage et des commentaires.

La réponse ColdFusion manque de modifier la clé de ccbill au travail (comme dans la réponse d'Eric) ... J'ai modifié la réponse au code Lucee d'Eric. Il ne devrait pas prendre beaucoup de travail pour le ramener à ACF code compatible (modification de la structure en ReplaceNoCase avec individuels).

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

Il y a deux problèmes (ou non) avec Crypt :: TripleDES:

  1. Le fait que les clés pour Crypt :: TripleDES sont HEX (expliqué plus tôt par ZZ Coder). Vous pouvez jeter un sort votre clé soit en utilisant Déballez ou en utilisant ord / sprintf ou un tas d'autres méthodes:

    • $ pass = Déballez ( "H *", "phrase secrète"); # Pack / Version Déballez

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

    Crypte :: TripleDES pads de la phrase de passe avec des espaces (ce qui était ok pour moi)

  2. Crypte :: TripleDES ne padding que les espaces du texte brut. Il existe de nombreuses méthodes de remplissage qui sont utilisés sur mcrypt_encrypt Java ou PHP:

    • (ie PKCS5, PKCS7, CMS.) - tampon avec des octets de la même valeur indiquant le nombre d'octets rembourré par exemple: "andrei" -> hex: 61 6E 64 72 65 69 -> rembourré: 61 6E 64 72 65 69 02 02
    • tampon avec des caractères nuls, par exemple: 64 72 61 6E 65 69 00 00
    • pad avec des espaces (Crypt :: TripleDES fait déjà)
    • tampon avec des zéros (les carac nulles) à l'exception du dernier octet qui sera le nombre d'octets rembourré par exemple: 64 72 61 6E 65 69 00 02
    • pad avec 0x80 suivi de caractères nuls, par exemple: 64 72 61 6E 65 69 80 00

Faites attention à votre chiffrement texte, si elle correspond jusqu'à un certain point, mais la fin est différente, alors vous avez un problème de remplissage texte brut. Sinon, vous pourriez avoir un problème phrase de passe, un problème de mode bloc de chiffrement (EBC, Radio-Canada, ..)

scroll top