PHP | openssl_decrypt() gibt FALSE zurück, warum?
lima-city → Forum → Programmiersprachen → PHP, MySQL & .htaccess
anzahl
bit
code
dank
ende
fehler
frage
funktion
hilfe
http
input
null
problem
re
string
teil
test
text
url
zeile
-
Ich habe etwas Text mit der OpenSSL Library in C++ verschlüsselt.
Nun möchte ich diesen Text mit der OpenSSL Library in PHP wieder entschlüsseln, allerdings liefert mir die ref="/tag/funktion">Funktion (openssl_decrypt()) ständig false zurück.
<?php ini_set('error_reporting', E_ALL); ini_set('display_errors', 1); $ctext = "CiUqILbZo+WJBr19IiovRVc1dqGvrastwo0k67TTrs51HB8AbJe8S4uxvB2D7Dkr"; $key = "aQkrZD/zwMFU0VAqjYSWsrkfJfS28pQJXym20UEYNnE="; if($test = openssl_decrypt($ctext, 'aes-256-cbc', $key)) { echo $test; } else { echo "fail"; } ?>
Sowohl der encryptete Text (cText) als auch der Key (key) sind Base64 encoded.
Ich erhalte keine Errormeldungen, OpenSSL ist installiert und funktioniert einwandfrei (getestet).
Laut Funktionsbeschreibung wird false zurückgegeben, wenn die Decryption fehlschlägt....aber was kann die Decryption fehlschlagen lassen?
Im schlechtesten Fall sollte er mir doch zumindest etwas Kauderwelsch liefern, wenn am Key oder Text was falsch wäre?!?
Kann sich jemand einen Reim darauf machen und mir kurz weiter helfen?
EDIT:
Anscheinend akzeptiert die Funktion keine Base64-encodeten Keys....habe also nun den Key mittels "base64_decode(key)" übergeben und er liefert mir nun ein Ergebnis.
Das Problem, was ich jetzt habe....der Text ist nur zur Hälfte entschlüsselt? o.O
Herauskommen sollte: "12345678901234567890123456789012abc"
Bekommen tu ich: "7890123456789012abc"
Die Decryption scheint also zu funktionieren, aber wieso liefert der mir nur den hinteren Abschnitt?
Key und Text sind genau die selben, die ich auch in der OpenSSL-Shell verwendet hab....da macht er's richtig.
Aktueller Code:
<?php ini_set('error_reporting', E_ALL); ini_set('display_errors', 1); $ctext = "CiUqILbZo+WJBr19IiovRVc1dqGvrastwo0k67TTrs51HB8AbJe8S4uxvB2D7Dkr"; $key = base64_decode("aQkrZD/zwMFU0VAqjYSWsrkfJfS28pQJXym20UEYNnE="); if($test = openssl_decrypt($ctext, 'aes-256-cbc', $key)) { echo $test; } else { echo "fail"; } ?>
Beitrag zuletzt geändert: 20.8.2013 15:11:48 von adelwoehrer -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
ich würde mal unter cpp so encrypten (parameter):
date = '12345678901234567890123456789012abc' method = 'aes-256-cbc' password = $key options = null iv = "123456789012345\0" (oder was auch immer, die länge ist wichtig und dass es 0-terminiert ist)
dann ist in php das decrypten so:openssl_decrypt($ctext, 'aes-256-cbc', $key, null, "123456789012345\0")
fürt das zu nichts, bin ich ratlos :( -
Hallo czibere,
danke für Deine Antwort.
Ich benutze zur Erstellung des Keys und des IVs eine OpenSSL Funktion, sollte das also nicht selbst machen müssen, außer ich verstehe die Logik dahinter nicht, was gut sein kann.
Ganz konkret nutze ich die EVP_BytesToKey() Funktion, die mir anhand des Passworts und der mitgegebenen Daten einen Key und einen IV generieren sollte.
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), NULL, key_data, key_data_len, nrounds, key, iv);
key_data ist hier mein Passwort (1hA!dh==sJAh48S8Ak!?skiitFi120xX)
Daraus, und aus nrounds (28) sollte er mir in die Variablen key und iv den Output geben.
Die Variablen key und iv sind Anfangs unsigned char* Datentypen, ich muss beide also erst in const char* umwandeln, um sie in ein ByteArray speichern und an PHP übergeben zu können.
Das mache ich mit: QByteArray bkey = reinterpret_cast<const char*>(key) und QByteArray bvi = reinterpret_cast<const char*>(iv).
Das Problem hier ist....bkey enthält dann ordnungsgemäß den richtigen Key zum entschlüsseln, den ich auch verwendet habe. (siehe erster Post)
Aber biv ist null....ich ging also davon aus, dass ich keinen iv brauche.
Vielleicht kann ich ihn aber nur nicht mit reinterpret_cast umwandeln?!?
Für den gesamten Code bzw. etwas Codehighlighting hätte ich die Frage auch auf stackoverflow gestellt: http://stackoverflow.com/questions/18312657/aes-encrypt-with-openssl-decrypt-with-mcrypt
Dort hab ich aber mit mcrypt decrypten versucht, allerdings das gleiche Problem mit dem abgeschnittenen Ergebnis....sollte also der selbe Fehler sein.
Danke mal für Deine Hilfe.
Beitrag zuletzt geändert: 21.8.2013 14:56:34 von adelwoehrer -
ich lese dort ganz unten gerade:
... Just create a zero'ed out IV instead and use all of the data in mdecrypt_generic ...
-
Ja, das hab ich gemacht.
Jetzt zeigt er mir die vorderen Zeichen zwar an, allerdings als zufällige Sonderzeichen, nicht decrypted.
-
ok. wie sieht jetzt dein php aus? (beim stackoverflow fehlt daraus noch immer 'iv'.)
-
Mein PHP sieht jetzt mal so aus:
<?php function decrypt_data($data, $iv, $key) { $cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); // initialize encryption handle if (mcrypt_generic_init($cypher, $key, $iv) != -1) { // decrypt $decrypted = mdecrypt_generic($cypher, $data); // clean up mcrypt_generic_deinit($cypher); mcrypt_module_close($cypher); return $decrypted; } return false; } $ctext = "CiUqILbZo+WJBr19IiovRVc1dqGvrastwo0k67TTrs51HB8AbJe8S4uxvB2D7Dkr"; $key = "aQkrZD/zwMFU0VAqjYSWsrkfJfS28pQJXym20UEYNnE="; $res = decrypt_data(base64_decode($ctext), "\0\0\0\0\0....usw.", base64_decode($key)); echo $res; ?>
Das mit den "\0" ist doch, was mit null'ed out gemeint war, oder lieg ich da falsch?
Zumindest hab ich das in google so gefunden.
Die Anzahl der Nullen ändert da leider gar nichts.
Ob ich jetzt einmal oder 32 mal "\0" in den iv schreib kommt aufs Gleiche raus.
Wenn ich es einfach auf "null" lasse, hab ich halt wieder andere Steuerzeichen davor.
Der hintere Teil bleibt wie gehabt, aber die vordere Hälfte wird mir nun halt als zufällige Steuerzeichen angezeigt, anstatt gar nicht.
Also anstatt 7890123456789012abc jetzt eben 1sC@]’ö¿Š4Kt¯æ$‰7890123456789012abc.
Beitrag zuletzt geändert: 22.8.2013 13:20:12 von adelwoehrer -
adelwoehrer schrieb:
Mein PHP sieht jetzt mal so aus:
<?php function decrypt_data($data, $iv, $key) { $cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); // initialize encryption handle if (mcrypt_generic_init($cypher, $key, $iv) != -1) { // decrypt $decrypted = mdecrypt_generic($cypher, $data); // clean up mcrypt_generic_deinit($cypher); mcrypt_module_close($cypher); return $decrypted; } return false; } $ctext = "CiUqILbZo+WJBr19IiovRVc1dqGvrastwo0k67TTrs51HB8AbJe8S4uxvB2D7Dkr"; $key = "aQkrZD/zwMFU0VAqjYSWsrkfJfS28pQJXym20UEYNnE="; $res = decrypt_data(base64_decode($ctext), "\0\0\0\0\0....usw.", base64_decode($key)); echo $res; ?>
Das mit den "\0" ist doch, was mit null'ed out gemeint war, oder lieg ich da falsch?
Zumindest hab ich das in google so gefunden.
Die Anzahl der Nullen ändert da leider gar nichts.
Ob ich jetzt einmal oder 32 mal "\0" in den iv schreib kommt aufs Gleiche raus.
Wenn ich es einfach auf "null" lasse, hab ich halt wieder andere Steuerzeichen davor.
Der hintere Teil bleibt wie gehabt, aber die vordere Hälfte wird mir nun halt als zufällige Steuerzeichen angezeigt, anstatt gar nicht.
Also anstatt 7890123456789012abc jetzt eben 1sC@]’ö¿Š4Kt¯æ$‰7890123456789012abc.
OH MANN!
du hast dein ganzes encrypt/decrypt paradigma auf den kopf gestellt. etliche teile deines programmes dürften geändert worden sein, zu dem, was du vorher hier, aber auch im anderen forum dargestellt hast. das hilft nicht sehr beim hilfeversuch. ich frage mich nur, warum glaubst, dass von openssl auf mcrypt umzusteigen dein problem lösen wird?
adelwoehrer
ja.leider. es heißt, dass das ein zeichekette ist, das mit '0' abgeschlossen ist. aber nicht dass eine zeihenkette aus n binären '0'-en besteht!
Das mit den "\0" ist doch, was mit null'ed out gemeint war, oder lieg ich da falsch?
Zumindest hab ich das in google so gefunden.
ähmm ... wo?
Die Anzahl der Nullen ändert da leider gar nichts. Ob ich jetzt einmal oder 32 mal "\0" in den iv schreib kommt aufs Gleiche raus. Wenn ich es einfach auf "null" lasse, hab ich halt wieder andere Steuerzeichen davor.
das ist allerding - trauriger weise - gold richtig.
also dann ein weiterer versuch:
du hast ja in dem encrypt-teil (c/cpp) von dir ein 'iv'. genau dieses 'iv' ist ein null-terminierter string!
jetzt kommts ;) genau dieses null-terminierte 'iv' sollst du statt
in zeile 23 einsetzen und nicht IRGENDEINEN string mit '0' am ende oder gar laute binäre nullen in einer wurst."\0\0\0\0\0....usw."
demnach - probire mal mit zeile 23 (ich habe es mir erlaubt dein php-code zu nummerieren):
(wie du 'iv' auch herbekommen magst aus deinem c/cpp-teil! UND - natürlich!? ob das jetzt von dor MIT "\0" rüberkommt oder nicht!? wenn nicht dann schreibst du statt $iv einfach "$iv\0").$res = decrypt_data(base64_decode($ctext), $iv, base64_decode($key));
gilt nur für die 'version', die in deiner vorigen frage vorkommt. solltest du zu oft neue versionen einführen, müssen wir sie irgendwie administrieren!
Beitrag zuletzt geändert: 23.8.2013 2:20:17 von czibere -
ich frage mich nur, warum glaubst, dass von openssl auf mcrypt umzusteigen dein problem lösen wird?
Habe gehört, dass das Padding eventuell unterschiedlich gehandhabt wird....keine Ahnung ehrlich....trial and error halt, wollts nur versuchen.
Es kommt bei beiden Methoden derzeit das Gleiche raus, openssl und mcrypt....ich hab den Code für openssl ja noch, können damit auch weitermachen.
Ich schein nur nicht zu verstehen, was ich mit dem IV anfangen soll.
du hast ja in dem encrypt-teil (c/cpp) von dir ein 'iv'. genau dieses 'iv' ist ein null-terminierter string!
jetzt kommts ;) genau dieses null-terminierte 'iv' sollst du statt
"\0\0\0\0\0....usw."
in zeile 23 einsetzen und nicht IRGENDEINEN string mit '0' am ende oder gar laute binäre nullen in einer wurst.
Das Problem liegt hier darin, dass ich diesen Null-Terminierten string nicht in ein sinnvolles Format bekomm, um ihn in PHP verwenden zu können.
In meinem C++ Code hab ich die Funktion:
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), NULL, key_data, key_data_len, nrounds, key, iv);
Wenn ich das ausführe, SOLLTE die Variable "key" jetzt eigentlich den binären Key enthalten, die Variable "iv" den IV.
Damit ich das sinnvoll an PHP übergeben kann (via GET) , caste ich das von nem unsigned char* zu nem const char*, schreib das in ein ByteArray und encode es mit Base64.
QByteArray bkey = reinterpret_cast<const char*>(key) QByteArray biv = reinterpret_cast<const char*>(iv) QByteArray bkey2 = bkey.toBase64(). QByteArray biv2 = biv.toBase64().
In bkey2 steht dann der Key, den ich in PHP verwendet habe.
In biv2 steht NICHTS drinnen....der reinterpret_cast gibt schon nichts zurück. (Soll das so sein?)
Das ist leer....
Ich versteh nicht?
Was vom iv soll ich da übergeben?
Es ist nichts da, was ich übergeben kann?
Schau Dir bitte mal den Debug an: http://img689.imageshack.us/img689/1475/43bw.png -
nach unseren etlichen pn's und wegen einen überaschenden grund für eine doch frappante lösung, solltest du - glaube ich - hier für alle das ergebnis mal schildern, trotz dass sich hier nicht viele um die sicherheit kümmern (wie schade).
-
Um das zu erklären, poste ich erstmal den ursprünglichen (falschen) Code, den ich in C++ zur Encryption verwendet hatte.
QByteArray Crypto::Encrypt(QByteArray source, QString password) { EVP_CIPHER_CTX en; unsigned char *key_data; int key_data_len; QByteArray ba = password.toLatin1(); key_data = (unsigned char*)ba.data(); key_data_len = strlen((char*)key_data); int nrounds = 28; unsigned char key[32], iv[32]; EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), NULL, key_data, key_data_len, nrounds, key, iv); EVP_CIPHER_CTX_init(&en); EVP_EncryptInit_ex(&en, EVP_aes_256_cbc(), NULL, key, iv); char *input = source.data(); char *out; int len = source.size(); int c_len = len + 16, f_len = 0; unsigned char *ciphertext = (unsigned char *)malloc(c_len); EVP_EncryptInit_ex(&en, NULL, NULL, NULL, NULL); EVP_EncryptUpdate(&en, ciphertext, &c_len, (unsigned char *)input, len); EVP_EncryptFinal_ex(&en, ciphertext+c_len, &f_len); len = c_len + f_len; out = (char*)ciphertext; EVP_CIPHER_CTX_cleanup(&en); return QByteArray(out, len); }
Nach etlichen Versuchen und rumprobieren, haben wir rausgefunden, dass die Funktion EVP_BytesToKey() einen IV liefert, wenn ich int nrounds = 28; auf eine beliebige andere Zahl ändere.
Erst gab es also die Vermutung, dass die 28 Runden mit dem Key kombiniert einfach aus Zufall genau einen Null-IV berechnen.
Es schaffte dann zwar Abhilfe, die Runden auf 5 zu ändern, im Nachhinein lieferte PHP aber zusammen mit dem richtigen Klartext die entscheidende Warnmeldung.
Warning: openssl_decrypt(): IV passed is 17 bytes long which is longer than the 16 expected by selected cipher, truncating in /var/www/aestest.php on line 9
12345678901234567890123456789012abc
Das eigentliche Problem lag daran, dass das IV-Array mit 256 Bits definiert war, aber nur 128 Bits haben durfte.
Der Grund für den Fehler lag also auch nicht an den Runden, sondern hier: unsigned char key[32], iv[32];
Richtig lauten würde es nämlich so: unsigned char key[32], iv[16];
Was jetzt auch logisch erscheint, da die Länge unten mit 16 addiert wird.
Es war also prinzipiell ein Fehler im Tutorial.
Nachdem das also geklärt war, hab ich trotzdem von der Funktion EVP_BytesToKey() Abstand genommen, da ich einfach nicht weiß, was der da im Hintergrund ausrechnet.
Verblieben bin ich also mit folgendem Code.
QList<QByteArray> Crypto::Encrypt(QByteArray source, QByteArray password) { QList<QByteArray> result; Keys keys; QByteArray randIV = keys.generateRandom(16, NULL); unsigned char* key = (unsigned char*) password.constData(); unsigned char* iv = (unsigned char*) randIV.constData(); result.append(fromBase64ToUrl(QByteArray((const char*) iv).toBase64())); EVP_CIPHER_CTX en; EVP_CIPHER_CTX_init(&en); EVP_EncryptInit_ex(&en, EVP_aes_256_cbc(), NULL, key, iv); char *input = source.data(); int len = source.size(); int c_len = len + 16, f_len = 0; unsigned char *ciphertext = (unsigned char *)malloc(c_len); EVP_EncryptUpdate(&en, ciphertext, &c_len, (unsigned char *)input, len); EVP_EncryptFinal_ex(&en, ciphertext+c_len, &f_len); len = c_len + f_len; result.append(fromBase64ToUrl(QByteArray((char*)ciphertext, len).toBase64())); EVP_CIPHER_CTX_cleanup(&en); return result; }
Der einzige Unterschied ist hier, dass ich den IV explizit mit einer Random-Funktion und 128 Bits erstelle und dann eine Liste mit Ciphertext und IV zurückgebe.
Ist so sauberer und der Code ist kürzer.
Danke auch an czibere für die Hilfe.
Problem sollte damit gelöst sein.
Beitrag zuletzt geändert: 30.8.2013 7:59:26 von adelwoehrer -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage