Ver-/Entschlüsselung mit Passwort

J

j-l-n

Guest
Folgendes Skript ver- und entschlüsselt Eingaben mit einem wählbaren Passwort. Außerdem wird bei einem falschen Passwort nicht einfach nur "Zeichensalat" ausgegeben, sondern es wird durch eine Endmarkierung überprüft, ob das Passwort richtig ist und bei Nicht-Übereinstimmung eine Fehlermeldung ausgegeben.

PHP:
<?php

/*
*
* © 2014 Julian
* http://forum.jswelt.de/members/julian.html
*
* PHP-Skript zur Ver- und Entschlüsselung von Texteingaben mit wählbarem Passwort
* inklusive Überprüfung auf richtiges Passwort durch Setzen einer Endmarkierung
*
*/


//Initialisierung und Vorbereitung der mcrypt-Funktionen
$ivsize = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivsize, MCRYPT_RAND);

$password = "Passwort" //Passwort zur Ver- & Entschlüsselung

//hängt definierten String an zu verschlüsselnden Text an, damit erkannt wird, dass an dieser Stelle zuende
$check_string = "END_OF_TEXT";
$check_length = strlen($prefix); //Länge des End-Markers

$text = "Test"; //zu verschlüsselnder Text
//Endmarker anhängen
$text = $text.$check_string;

//String encoden, um Probleme mit Umlauten o.ä. zu vermeiden
$text = base64_encode(htmlentities($text)); 

//Verschlüsselung mit Blowfish
$encrypt = mcrypt_encrypt(MCRYPT_BLOWFISH, $password, $text, MCRYPT_MODE_CBC, $iv); 

//Entschlüsselung:
$decrypt = mcrypt_decrypt(MCRYPT_BLOWFISH, $password, $encrypt, MCRYPT_MODE_CBC, $iv);

//entschlüsselten String wieder zurückdecoden
$decrypt = base64_decode($decrypt);
$length = strlen($decrypt); //Länge des gesamten Strings
$end = $length - $check_length; //Länge des Strings ohne End-Marker

//extrahiert End-Marker aus entschlüsselten String
$end_string = substr($output, -$check_length, $check_length);

//prüft, ob End-String derselbe wie oben definierter und damit Prüfung, ob Password richtig
if(end_string === $check_string){
$output = substr($decrypt, 0, $end); //Endmarker aus Text entfernen
}
else{
$output = "falsches Passwort!";
}

echo $output;
?>
 
Hierbei musst du dir aber nicht nur das Passwort, sondern auch den Initial Vektor merken... dieser wird aber im Skript immer zufällig erzeugt und nirgends gespeichert/ausgegeben.

Ich bin auch der Meinung, dass es gar nicht so schlecht ist, wenn man nicht erfährt, wenn man das falscher Passwort... dann hat einer, der da mit brute force dran geht es um einiges schwerer.
 
Hierbei musst du dir aber nicht nur das Passwort, sondern auch den Initial Vektor merken... dieser wird aber im Skript immer zufällig erzeugt und nirgends gespeichert/ausgegeben.

Zum Thema IV: an einigen Stellen habe ich gelesen, dass man den ruhig "öffentlich" mit übertragen kann, ohne dass Sicherheit verloren geht. An anderen Stellen wiederum wird dies bestritten und es für ein Sicherheitsrisiko gehalten, wenn man den IV "einfach so" mitüberträgt.
Wie sieht es denn nun in Echt damit aus?
 
Wenn du den IV veröffentlichst, ist deine Verschlüsselung einfach etwas weniger sicher. Kann aber durch das Passwort noch ausreichend sein. Kann man sich eventuell wie einen Salt vorstellen...

... aber wahrscheinlich (ich bin kein Kryptologe) hängt das davon ab, welche Block Cypher Methode du verwendest.
 
Vom Grundprinzip her könnte ich doch auch den bei der Verschlüsselung benutzten IV selbst verschlüsseln. Und dazu dann auch noch einen variablen Salt.
D.h., wenn ich beim Passwort einmal den festen Salt "Salz in der Suppe" plus den variablen Salt (z.B. per rand()) verwende, könnte ich diesen doch, gemeinsam mit dem bei der Verschlüsselung verwendeten IV, verschlüsseln (mit dem immer gleichen, festen IV) und an den eigentlichen verschlüsselten String anhängen...
Hoffentlich war das jetzt verständlich genug ;)
 
Äh.. nicht wirklich ;)

Schreib' das doch mal als Code.

OK, hab's befürchtet... ;)
Hier mal als Pseudo-Code:
PHP:
$ivsize = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivsize, MCRYPT_RAND);

$password = von User eingegebenes Passwort 

$salt = rand();
$password = hash("sha512", $password.$salt);

$zu_verschluesselnder_text = base64_encode(htmlentities(Eingabe des Users));  

$verschluesselter_text = mcrypt_encrypt(MCRYPT_BLOWFISH, $password, $zu_verschluesselnder_text, MCRYPT_MODE_CBC, $iv); 


//den bei der Verschlüsselung verwendeten IV und Salt mit einem festen Passwort verschlüsseln
$infos_zur_entschluesselung = "Initialisierungsvektor=".$iv."|verwendeter-Salt=".$salt;
$infos = mcrypt_encrypt(MCRYPT_BLOWFISH, " immer gleiches Passwort", $infos_zur_verschluesselung, MCRYPT_MODE_CBC, $iv); 


$output = $verschluesselter_text.$infos
 
OK.

Aber wofür soll das base64_encode und htmlentities an der Stelle gut sein? Wenn dann musst du die Ausgabe über Base 64 machen.

Auch wird die Entschlüsselung nicht funktionieren, da du, um den IV zu bekommen, den IV schon brauchst. Wenn dann musst du beim zweiten mcrypt_encrypt auch einen festen IV verwenden.

Aber lies dir doch einfach mal das dazu durch: http://en.wikipedia.org/wiki/Key_derivation_function
 
Aber wofür soll das base64_encode und htmlentities an der Stelle gut sein?
Hatte davor immer Probleme mit Sonderzeichen...

Auch wird die Entschlüsselung nicht funktionieren, da du, um den IV zu bekommen, den IV schon brauchst. Wenn dann musst du beim zweiten mcrypt_encrypt auch einen festen IV verwenden.
Ja, stimmt; Denkfehler.

Soweit ich das jetzt verstanden habe, wäre das Hashen des Passworts so eine KDF.(?)
 
Also prinzipell musst du vor dem Verschlüsseln gar nichts encoden/maskieren. Vor der Ausgabe im HTML musst du natürlich was tun.

Wenn du das Verschlüsselte ausgeben willst, kannst du sowieso nicht erwarten, dass irgendwelche sinnvollen Zeichencodierungen dabei rauskommen. Also ist da base64 (alleine) ein gutes Ausgabeformat. htmlentities ist dagegen recht sinnfrei.

Bei Entschlüsselten musst du natürlich entscheiden, was du da genau entschlüsselt hast und wie du das Darstellst. Bei Text ist htmlentites sinnvoll, wenn du das Originalencoding kennst. Bei einem Bild wäre z.B. eine Data-URL sinnvoll. Bei beliebigen Daten kann base64 auch wieder sinnvoll sein.
 
Wer noch Interesse hat, das Ganze mal online auszuprobieren: Textnachrichten online verschlüsseln
Habe dort übrigens auch die Idee von vorher umgesetzt, nämlich den bei der Verschlüsselung verwendeten IV und Salt wiederum selbst verschlüsselt zu speichern und an den ausgegebenen String anzuhängen.

Gerne Rückmeldung und Feedback zur Seite an sich!
 
Zeig' doch auch noch den endgültigen Code, da das hier ja ein Tutorial ist.

PS: dein Signaturbild funktioniert schon wieder nicht...
 
PHP:
/* 
* 
* © 2014 Julian http://forum.jswelt.de/members/julian.html 
* 
* PHP-Skript zur Ver- und Entschlüsselung von Texteingaben mit wählbarem Passwort
* inklusive Überprüfung auf richtiges Passwort durch Setzen einer Endmarkierung
* 
* DIESER VERMERK DARF BEI VERWENDUNG (VON TEILEN) DIESES SKRIPTS NICHT ENTFERNT WERDEN!!!
*/ 

$end_marker = "END_OF_TEXT";
$seperator = "+info+"; //Trennung zwischen verschlüsseltem Code und verschlüsselten Infos über IV etc.

$iv_for_information_encryption = ""; //festen IV dafür wählen
$password_for_information_encryption = ""; //festes Passwort dafür wählen

//Start- und Endmarkierungen für verschlüsselte Infos
$iv_marker = "<initialization-vector>";
$salt_marker = "<salt>";


//Initialisierung und Vorbereitung der mcrypt-Funktionen 
$ivsize = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivsize, MCRYPT_RAND);

$password = "Passwort" //Passwort zur Ver- & Entschlüsselung

$salt = rand();
$key_derivation = hash("sha256", $password.$salt, true);
    
$string = $input.$end_marker;
$string = base64_encode($string);

$encrypted = mcrypt_encrypt(MCRYPT_BLOWFISH, $key_derivation, $string, MCRYPT_MODE_CBC, $iv);
$encoded_encrypted = base64_encode($encrypted);

//bei Verschlüsselung verwendeten IV und Salt als Informations-String verschlüsseln 
$used_iv = base64_encode($iv);
$infos = $salt_marker.$salt.$salt_marker.$iv_marker.$used_iv.$iv_marker.$end_marker;

$encrypted_information = mcrypt_encrypt(MCRYPT_BLOWFISH, $password_for_information_encryption, $infos, MCRYPT_MODE_CBC, $iv_for_information_encryption);
$encoded_information = base64_encode($encrypted_information);

$output = $encoded_encrypted.$seperator.$encoded_information;


//Infoteil aus verschlüsseltem String abtrennen
$split = explode($seperator, $output);
$input = $split[0];
$info = $split[1];

$decoded_info = base64_decode($info);
$decrypted_info = mcrypt_decrypt(MCRYPT_BLOWFISH, $password_for_information_encryption, $decoded_info, MCRYPT_MODE_CBC, $iv_for_information_encryption);

//verwendeten Salt auslesen
$split_salt = explode($salt_marker, $decrypted_info);
$salt = $split_salt[1];

//verwendeten IV auslesen
$split_iv = explode($iv_marker, $decrypted_info);
$iv = $split_iv[1];
$iv = base64_decode($iv);

//Entschlüsselung vorbereiten
$decoded_input = base64_decode($input);
$key_derivation = hash("sha256", $password.$salt, true);

$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key_derivation, $decoded_input, MCRYPT_MODE_CBC, $iv);
$decoded_output = base64_decode($decrypted);

//Korrektheit des Endmarkers prüfen
$end_marker_length = strlen($end_marker);
$check_string = substr($decoded_output, -$end_marker_length, $end_marker_length);

if($check_string === $end_marker){
    $split_from_end_marker = explode($end_marker, $decoded_output);
    $output = $split_from_end_marker[0];
}
else{
    $output = "falsches Passwort";
}




PS: dein Signaturbild funktioniert schon wieder nicht...
Komisch, bei mir wird es ganz normal korrekt angezeigt...
 
Zurück
Oben