AES verschlüsselten String (PHP-seitig) via HTTP Request an iPhone App senden und entschlüsseln

  • OsnaTiger schrieb:

    Es wird kein festes Passwort geben.

    Das Tutorial werde ich verfassen sobald ich etwas Zeit habe.
    Dann können wir gerne noch einmal über die Lösung diskutieren.

    Schönen Gruss

    Dann mach es noch schön und "härte" das benutze Passwort. Da gibt es auch Beispielcode im Netz genau dafür. Hier der Grund:
    Nimmst Du Stringpasswörter wie "00000000000000000000000000000000", scheint das zwar wie ein 32*8-Bit Key. Die 256 Bit sind aber nicht unabhängig und zufällig, da Du nur darstellbare Zeichen verwendest. Ganz dramatisch könnte man sagen, dass etwa jedes achte bit immer 0 ist (weil Du nur Zeichen kleiner 0x7F hast), und das gefährdet natürlich die Sicherheit.
    Auch wenn das jetzt nach einer eher theoretischen Bedrohung klingt, tut es ja nicht weh, den Key vorher zu "härten". Ganz trivial ist da der Ansatz, den Key einfach mit einem (festen) Salt mehrere hundertmal hintereinander zu hashen (SHA256 etwa), damit jedes Bit im Key am ende mehr oder minder gleichwahrscheinlich gesetzt ist. Das heisst also, Du verwendest nicht den Key selbst für das ver- und entschlüsseln, sondern sowas wie SHA(SHA(SHA(....SHA(key+salt)+salt)+salt)...)
    Das bringt auch gleich noch den Vorteil mit dass der Nutzer einen kürzeren String wählen kann, ohne die Sicherheit zu gefährden, und ein Angreifer, der Brute-Force alle Keys probieren will, deutlich länger braucht, weil er jedes mal noch 100mal die Hash-funktion ausführen muss. Und kosten tut Dich das ganze so gut wie nichts ;)
    C++
  • zerm schrieb:

    OsnaTiger schrieb:

    Es wird kein festes Passwort geben.

    Das Tutorial werde ich verfassen sobald ich etwas Zeit habe.
    Dann können wir gerne noch einmal über die Lösung diskutieren.

    Schönen Gruss

    Dann mach es noch schön und "härte" das benutze Passwort. Da gibt es auch Beispielcode im Netz genau dafür. Hier der Grund:
    Nimmst Du Stringpasswörter wie "00000000000000000000000000000000", scheint das zwar wie ein 32*8-Bit Key. Die 256 Bit sind aber nicht unabhängig und zufällig, da Du nur darstellbare Zeichen verwendest. Ganz dramatisch könnte man sagen, dass etwa jedes achte bit immer 0 ist (weil Du nur Zeichen kleiner 0x7F hast), und das gefährdet natürlich die Sicherheit.
    Auch wenn das jetzt nach einer eher theoretischen Bedrohung klingt, tut es ja nicht weh, den Key vorher zu "härten". Ganz trivial ist da der Ansatz, den Key einfach mit einem (festen) Salt mehrere hundertmal hintereinander zu hashen (SHA256 etwa), damit jedes Bit im Key am ende mehr oder minder gleichwahrscheinlich gesetzt ist. Das heisst also, Du verwendest nicht den Key selbst für das ver- und entschlüsseln, sondern sowas wie SHA(SHA(SHA(....SHA(key+salt)+salt)+salt)...)
    Das bringt auch gleich noch den Vorteil mit dass der Nutzer einen kürzeren String wählen kann, ohne die Sicherheit zu gefährden, und ein Angreifer, der Brute-Force alle Keys probieren will, deutlich länger braucht, weil er jedes mal noch 100mal die Hash-funktion ausführen muss. Und kosten tut Dich das ganze so gut wie nichts ;)

    Mensch, das "harte" Passwort gefällt mir!
    Das werde ich auf jeden Fall noch mit einbauen.


    Ich bin gerade dabei den Code zu optimieren und dabei viel mir auf, dass der String nicht AES 256 bit verschlüsselt werden kann, wenn in ihm Sonderzeichen, wie: '@, $, ü, ö, ä, ...', enthalten sind.

    Mein Teststring würde so aussehen:

    Quellcode

    1. NSString *str = @"$TEST@f34481ec3cc627bacd5dc3fb08f273e6"


    Das '$' und '@' kann der Algo nicht verarbeiten:

    Quellcode

    1. #import <CommonCrypto/CommonCryptor.h>
    2. - (NSData *)AES256EncryptWithKey:(NSString *)key {
    3. // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    4. char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    5. bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
    6. // fetch key data
    7. [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    8. NSUInteger dataLength = [self length];
    9. //See the doc: For block ciphers, the output size will always be less than or
    10. //equal to the input size plus the size of one block.
    11. //That's why we need to add the size of one block here
    12. size_t bufferSize = dataLength + kCCBlockSizeAES128;
    13. void *buffer = malloc(bufferSize);
    14. size_t numBytesEncrypted = 0;
    15. CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode,
    16. keyPtr, kCCKeySizeAES256,
    17. NULL, // initialization vector (optional)
    18. [self bytes], dataLength, // input
    19. buffer, bufferSize, // output
    20. &numBytesEncrypted);
    21. if (cryptStatus == kCCSuccess) {
    22. //the returned NSData takes ownership of the buffer and will free it on deallocation
    23. return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    24. }
    25. free(buffer); //free the buffer;
    26. return nil;
    27. }
    Alles anzeigen


    Als Resultat liefert mir der Verschlüsselungsalgo 'null'.

    Das liegt definitiv nur an den Sonderzeichen.
    Was wäre denn dann wenn ich Schriftzeichen aus anderen Sprachen - aus einem Textfeld - verschlüsseln möchte.

    Dann würde die ganze Verschlüsselung so nicht mehr funktionieren.


    Nun muss das ganze Paket noch schön verschnürt werden.

    Ich werde mich nun erstmal um das "harte" Passwort kümmern.

    Quellcode

    1. md5(md5(md5(md5(md5(...(0000000000000))))))



    Vielen lieben Dank für eure Ratschläge und Tips ;)
  • zerm schrieb:

    Noch was: In deinem PHP Code nimmst Du ECB. Das ist eigentlich nicht so klug. Und wenn Deine Obj-C Implementation CBC nimmt, passt es auch nicht zusammen. Hab grad noch nicht herausgefunden, was der default bei CommonCrypto ist, aber ich würde sogar fast auf CBC tippen.
    Tausch also am besten mal das unsägliche ECB in Deinem PHP Code gegen CBC

    EDIT
    Noch ein paar Punkte:
    - Für CBC muss beiden Seiten der gleich IV bekannt sein
    - Im PHP machst Du base64 encoding/decoding, in Objective-C fehlt das einfach!
    - Schau mal hier: php.net/manual/de/function.mcrypt-encrypt.php#69580
    - Ein bisschen in Kryptographie einlesen wäre nicht schlecht ;)

    Guten Abend!


    - Für CBC muss beiden Seiten der gleich IV bekannt sein
    - Im PHP machst Du base64 encoding/decoding, in Objective-C fehlt das einfach!

    Die beiden oberen Punkte habe ich abgearbeitet.
    Bei der Lösung von heut morgen habe ich auf CBC verzichtet.
    Allerdings ist der ECB Mode nun eben gar nicht sonderlich sicher wie ich auch in der Lektüre lesen konnte.


    Nun habe ich ein PHP Skript mit dem ich einen StringValue mit Sonderzeichen kodieren kann.
    Der IV ist dann auch auf beiden Seiten bekannt via base64 encoding/decoding.

    In ObjC nutze ich den gleichen IV auch. Also, ich nutzte momentan nur einen bekannten IV für beide Seiten und Plattformen.
    In ObjC funktioniert auch das base64 encoding/decoding. Das musste ich selbst implementieren

    Wenn ich nun allerdings den IV in ObjC zum initialisieren des AES CBC nutze, dann erhalte ich einen anderen verschlüsselten String als ich in PHP erhalte.

    PHP-Skript:

    $sValue: f34481ec3cc627bacd5dc3fb08f273e6
    $sSecretKey: 00000000000000000000000000000000

    PHP-Quellcode

    1. // ZAqZUWUYxGpRiXYeGZ4xUpQJZc+pDehgONDsdiG8QZU=
    2. $iv = base64_decode("hjbE84MfAzSJMsMUl62DchRWaUJpiegEPAB+u4t34Zo=");
    3. $crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sValue, MCRYPT_MODE_CBC, $iv);
    4. return trim(base64_encode($crypttext)); //encode for cookie


    Bekannt ist demnach mein IV: hjbE84MfAzSJMsMUl62DchRWaUJpiegEPAB+u4t34Zo=
    Mein verschlüsselter String lautet: ZAqZUWUYxGpRiXYeGZ4xUpQJZc+pDehgONDsdiG8QZU=

    Würde ich nun php-seitig wieder entschlüsseln, so würde ich wieder auf meinen StringValue kommen. Das funktioniert auch.


    Nun mache ich das auch in ObjC:

    $sValue: f34481ec3cc627bacd5dc3fb08f273e6
    $sSecretKey: 00000000000000000000000000000000
    IV: hjbE84MfAzSJMsMUl62DchRWaUJpiegEPAB+u4t34Zo=

    Quellcode

    1. - (NSData *)AES256EncryptWithKey:(NSString *)key {
    2. NSData *iv = [self generateIV];
    3. // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    4. char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    5. bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
    6. // fetch key data
    7. [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    8. NSUInteger dataLength = [self length];
    9. //See the doc: For block ciphers, the output size will always be less than or
    10. //equal to the input size plus the size of one block.
    11. //That's why we need to add the size of one block here
    12. size_t bufferSize = dataLength + kCCBlockSizeAES128;
    13. void *buffer = malloc(bufferSize);
    14. size_t numBytesEncrypted = 0;
    15. CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
    16. kCCAlgorithmAES128,
    17. kCCOptionPKCS7Padding,
    18. keyPtr,
    19. kCCKeySizeAES256,
    20. [iv bytes], // initialization vector (optional)
    21. [self bytes], dataLength, // input
    22. buffer, bufferSize, // output
    23. &numBytesEncrypted);
    24. if (cryptStatus == kCCSuccess) {
    25. //the returned NSData takes ownership of the buffer and will free it on deallocation
    26. return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    27. }
    28. free(buffer); //free the buffer;
    29. return nil;
    30. }
    31. - (NSData*)generateIV
    32. {
    33. NSString *iv = @"hjbE84MfAzSJMsMUl62DchRWaUJpiegEPAB+u4t34Zo=";
    34. return [NSData_AES_BASE64 base64DataFromString:iv];
    35. }
    Alles anzeigen


    Mein verschlüsselter String lautet: 16TTzlvpXug6mppNzVAtacKe90gblQY8hjAuz9KddaCd9V97nLBpRmkkkOF2eg+/

    (PHP verschlüsselt) ZAqZUWUYxGpRiXYeGZ4xUpQJZc+pDehgONDsdiG8QZU= != (ObjC verschlüsselt) 16TTzlvpXug6mppNzVAtacKe90gblQY8hjAuz9KddaCd9V97nLBpRmkkkOF2eg+/


    Beide verschlüsselte String stimmen bei mir nicht überein.


    Ich weiß einfach nicht was ich hier falsch mache.
    Eigentlich müsste das doch zumindest der richtige Weg sein.

    Selbst wenn das funktionieren würde.
    Ist es gut nur einen IV nu nutzen in PHP und ObjC?
    Wenn sich dieser immer ändern sollte, wie kann ich den neuen IV dann sicher an ObjC übermitteln?


    Hey Jungs ... für einen kleinen Denkanstoss wäre ich euch sehr dankbar!
    Dieses kleine Projekt steckt doch wirklich voller überraschungen :P


    Schönen Gruss ;)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von OsnaTiger ()

  • Der Weg der Kodierung verläuft bspw. so: Nachrichtentext -> Gemeinsame Zeichenkodierung -> Nachrichtenkodierung -> Transportkodierung -> Nachrichtenkodierung -> Nachrichtentext.

    Was bringen zwei (oder mehr) Initialisierungsvektoren? Was macht der denn? -> Lies was dazu.

    Die Übertragung eines Geheimnisses über einen unsicheren Kanal ist ein Standard-Problem. -> Lies was dazu.
    Verwende dafür ein Schlüsselaustauschprotokoll.

    Bei der Verwendung freiwählbarer Kennworte/Schlüssel ist Salt unbedingt anzuraten. Warum? -> Lies was dazu.

    Das EBC nicht zu verwenden ist, riet Dir bereits zerm. Da Du es dennoch verwenden wolltest... -> Lies was dazu.

    Lies mal insbesondere mal hier den allerletzten Satz: de.wikipedia.org/wiki/Electronic_Code_Book_Mode

    OsnaTiger schrieb:

    Dieses kleine Projekt steckt doch wirklich voller Überraschungen.

    Mit Verlaub. Mal "eben ein bisschen" Verschlüsselung machen wollen ist schon blauäugig. Wenn die auch nur ein bisschen halten soll, muss man sich da richtig einarbeiten und der Code muss richtig geprüft und ggf. gehärtet werden. Auch schon der zerm riet Dir Dich mal wenigstens einzulesen. Aber dieser Code hat gar keinen Stresstest und auch sonst keine Prüfung gesehen, daher würde ich den höchstens zur Übertragung von High-Scores verwenden.

    Das ist ein echtes und hartes Expertenthema. Dessen muss man sich bewusst sein.


    P.S.: Du wendest auf den Initialisierungsvektor eine Base64-Kodierung an? Macht das kryptographisch Sinn?
    * Kann Spuren von Erdnüssen enthalten.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von NSObject ()

  • Ich hab noch einmal nachgeschaut, hier steht schön beschrieben, wie das mit Wiederverwertung des IVs ist:
    security.stackexchange.com/que…nto-using-a-static-iv-aes

    Über einen offenen Kanal würde ich den IV aber auch nicht übertragen, da Du da wieder auf die gleichen Probleme stoßen könntest.

    Warum es nicht funktioniert, ist jetzt nicht ganz leicht zu sagen. Eventuell hat es auch was mit dem Padding zu tun. AES-256 müsste auf 256bit (=32Byte) Blöcken arbeiten. Wenn also Deine Nachricht (etwa wegen Konvertierung in UTF-8) länger als 32 Bytes (und kürzer als 64 Bytes) ist, brauchts Padding, und da gibt es auch wieder verschiedene Ansätze. Musst Du auch schauen, dass diese gleich sind.

    NSObject schrieb:

    P.S.: Du wendest auf den Initialisierungsvektor eine Base64-Kodierung an? Macht das kryptographisch Sinn?

    Das ist doch völlig in Ordnung für PHP, damit das in ein String passt. Im IV sollten ja alle bits möglichst auch gleichwahrscheintlich sein. Alternative wäre sonst "\xAA\x99..." wobei wieder das Problem mit der \x00 mittem im String ist, und dass man das vielleicht auch mal ausgeben will etc.
    C++
  • zerm schrieb:

    NSObject schrieb:

    P.S.: Du wendet auf den Initialisierungsvektor eine Base64-Kodierung an? Macht das kryptographisch Sinn?

    Das ist doch völlig in Ordnung für PHP, damit das in ein String passt. Im IV sollten ja alle bits möglichst auch gleichwahrscheintlich sein. Alternative wäre sonst "\xAA\x99..." wobei wieder das Problem mit der \x00 mittem im String ist, und dass man das vielleicht auch mal ausgeben will etc.

    Ich bezog mit auf den ObjC-Code. Dort hat er einen fixen IV bereits in Form eines Strings. Daher kann ich darin keinen Sinn oder Nutzen erkennen.

    P.S. Ich könnte verstehen, wenn er Ihn unmittelbar vorher mit etwas Salt würzt, aber das...

    Noch ein P.S.:

    zerm schrieb:

    Warum es nicht funktioniert, ist jetzt nicht ganz leicht zu sagen. Eventuell hat es auch was mit dem Padding zu tun.

    Die Vermutung hatte ich auch. Es könnte vielleicht auch mit dieser o.g. Base64 Kodierung des IV zu tun haben. Die hat er ja selbst implementiert.
    * Kann Spuren von Erdnüssen enthalten.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von NSObject ()