2012-02-08 12 views
6

Oto moje wymagania:Dlaczego coś zaszyfrowane w PHP nie pasuje do tego samego ciągu zaszyfrowanego w Ruby?

Potrzebuję zaszyfrować ciąg znaków w PHP przy użyciu szyfrowania AES (w tym losowego iv), zakodować go w Base64, a następnie zakodować go w URL, aby mógł zostać przekazany jako parametr adresu URL.

Próbuję uzyskać taki sam wynik zarówno w PHP jak i Ruby, ale nie mogę sprawić, żeby działał.

Oto mój kod PHP:

function encryptData($data,$iv){ 
    $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); 
    $iv_size = mcrypt_enc_get_iv_size($cipher); 
    if (mcrypt_generic_init($cipher, 'g6zys8dlvvut6b1omxc5w15gnfad3jhb', $iv) != -1){ 
     $cipherText = mcrypt_generic($cipher,$data); 
     mcrypt_generic_deinit($cipher); 
     return $cipherText; 
    } 
    else { 
     return false; 
    } 
} 
$data = 'Mary had a little lamb'; 
$iv = '96b88a5f0b9efb43'; 
$crypted_base64 = base64_encode(encryptData($data, $iv)); 

Oto mój kod Ruby:

module AESCrypt 
    def AESCrypt.encrypt(data, key, iv) 
    aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    aes.encrypt 
    aes.key = key 
    aes.iv = iv 
    aes.update(data) + aes.final  
    end 
end 

plaintext = "Mary had a little lamb" 
iv = "96b88a5f0b9efb43" 
@crypted = AESCrypt::encrypt(plaintext, "g6zys8dlvvut6b1omxc5w15gnfad3jhb", iv) 
@crypted_base64 = Base64.encode64(@crypted) 
@crypted_base64_url = CGI.escape(@crypted_base64) 

irytujące jest to, że obie próbki kodu produkować podobny ale nie identyczne skrótów. Na przykład, powyższy kod generuje (base64 zakodowane, a nie URL zakodowane):

PHP: /aRCGgLBMOOAarjjtfTW2Qg2OtbPDLhx3KmgfgMzDJU=

Ruby /aRCGgLBMOOAarjjtfTW2XIZhZ9VjBx8PdozxSL8IE0=

może ktoś wyjaśnić, co robię źle tutaj? Ponadto łatwiej mi (ponieważ jestem facetem z Ruby, a nie PHP) naprawić kod Ruby zamiast kodu PHP. Jeśli więc chciałbyś zapewnić rozwiązanie w języku Ruby, które dobrze łączyłoby się z PHP, byłbym bardzo wdzięczny.

Aha, a także, w produkcji iv naprawdę będzie losowa, ale dla tego przykładu ustawiam ją tak, aby była na stałe taka sama, aby można było porównywać dane wyjściowe.

EDIT:

Dzięki za odpowiedź Eugen RIECK, przyjechałem w roztworze. Ruby blokuje bloki, ale PHP tego nie robi i musisz to zrobić ręcznie. Zmień kod PHP do następującego, a otrzymasz zaszyfrowane ciągi że powyższy kod Ruby można łatwo rozszyfrować:

$iv = '96b88a5f0b9efb43'; 
$data = 'Mary had a little lamb'; 

function encryptData($data,$iv){ 
    $key = 'g6zys8dlvvut6b1omxc5w15gnfad3jhb'; 
    $padded_data = pkcs5_pad($data); 
    $cryptogram = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $padded_data, MCRYPT_MODE_CBC, $iv); 
    return $cryptogram; 
} 

function pkcs5_pad ($text, $blocksize){ 
    $pad = $blocksize - (strlen($text) % $blocksize); 
    return $text . str_repeat(chr($pad), $pad); 
} 
+2

Nie mogę pomóc w rozwiązaniu, ale myślę, że może to mieć coś wspólnego z blokowaniem klocków (ponieważ ciągi wyjściowe są takie same dla pierwszych n znaków). Możesz spróbować dodać dopełnienie do zwykłego tekstu ręcznie w oparciu o rozmiar bloku. – Mikk

+2

Twój kod Ruby wywołuje AES-256. Twój kod PHP wywołuje AES-128. Jesteś pewien, że to prawda? Twoja IV jest wyraźnie dla 128 ... – Charles

+0

@Charles Nie rozumiem tego też. Ale to jedyny sposób, w jaki działa.Przełączenie PHP w celu wywołania AES-256 powoduje, że Ruby daje "zły odszyfrować" błąd z OpenSSL. (Zobacz wątek wymieniony w moim komentarzu do odpowiedzi poniżej, omawia to dalej). Również [this] (http://www.chilkatsoft.com/p/php_aes.asp) dostarcza pełnego wyjaśnienia dziwności szyfrowania PHP. Myślę, że to rozwiąże twoje pytanie. – John

Odpowiedz

8

okazuje, jest to dość proste: Wyściółka jest winowajcą.

AES jest szyfrem blokowym, więc działa na blokach o stałym rozmiarze. Oznacza to, że ostatni blok będzie zawsze wyściełany, i, wiesz, dobrą rzeczą w standardach jest to, że jest tak wiele do wyboru. PHP używa zerowej wyściółki, będziesz musiał zajrzeć do AESCrypt, aby dowiedzieć się, co używa Ruby.

Ten sam problem występuje w różnych bibliotekach JS AES i PHP. Skończyło się na tym, że robiliśmy własne wyściółki, bo to doprowadzało mnie do krwiożerczej furii kilka razy.

Oczywiście to wyjaśnia, dlaczego pierwsza część (niosąca informacje) jest identyczna, podczas gdy druga część (niosąca wypełnienie) jest inna.

+0

Czy masz na myśli to, że PHP używa zerowej obicia (to znaczy nie ma w ogóle wypełnienia) lub że używa zerowego wypełnienia (tj. Wypełnienia ostatniego bloku wartością 000000)? Dzisiaj wyśledziłem [to pytanie] (http://stackoverflow.com/questions/1864700/part-ii-how-to-make-ruby-aes256-cbc-and-php-mcrypt-rijndael-128 -play-well-toge), co wydaje się wskazywać, że Ruby podkłada bloki, ale mcrypt nie. Zmieniłem moje pytanie powyżej, aby dołączyć moją ostateczną odpowiedź. – John

+2

@John Nie można ** nie ** używać dopełnienia: Szyfr blokowy z definicji potrzebuje pełnego bloku danych do pracy, więc "brak dopełnienia w ogóle" nie jest opcją. PHP wypełnia niekompletny blok z 0x00, co jest jedną z, ale nie jedyną możliwością. IIRC OpenSSL używa RFC1423. Zobacz np. http://www.chilkatsoft.com/p/p_119.asp, aby uzyskać więcej informacji –

Powiązane problemy