Dateien verschlüsselt übers Netzwerk übertragen

Wahrscheinlich eine Riesen Sachen, aber ich hab bei meinen Ansatz zwei Probleme:

Dateiseite:

        while ((b = bis.read()) != -1) {
            out.write(byteToByte((byte) b));
        }
        out.flush();
        bis.close();```
```    private static byte byteToByte(byte b) throws GeneralSecurityException {
        Cipher ci = Cipher.getInstance("AES");
        ci.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(gehe, "AES"));
        return ci.doFinal(new byte[]{b})[0];
    }```

Das Verschicken funktioniert, das Zusammensetzen auf der anderen Seite aber nicht.

1) Nicht effizient,
2) AES BadPaddingException , da kein Vielfaches von 16.

Deswegen gibt es seit 1.4 den CipherOutputStream. Ich frage mich aber, wie es davor (low Level) gemacht wurde?

Grüße

*** Edit ***

Das Thema kann bitte wieder gelöscht werden, mit dem "richtigen" AES können auch einzelne Bytes übertragen werden.
Eine Frage/Situation/Sachverhalt hat sich bereits aufgeklärt.
Soryy für die "vorschnelle" Frage.

seufz nein wird es nicht. So schnell wird hier nix gelöscht, dass solltest du doch mittlerweile gelernt haben …

Interessantes Thema mit einer Fragestellung die so doch öfters auftaucht.

Letztendlich stellt sich die Frage, was out ist und woher dies kommt.

Wenn Dateien über das Netzwerk übertragen werden, dann hat man in der Regel einen ServerSocket der zu einem Socket führt, der wiederum zu einem OutputStream führt.

Alles unverschlüsselt.

Nimmt man an Stelle des ServerSocket einen SSLServerSocket, dann bekommt man die komplette Funktionalität von SSL und TLS, incl. Handshake, Session Keys und was man sonst noch so braucht für ordentliche Crypto schon mitgeliefert. Es müssen lediglich die entsprechenden Certificate erzeugt und eingebunden werden.

Output- und Input-Stream werden automatisch verschlüsselt ohne irgendwelche Verrenkungen, da dies schon beim erzeugen der jeweiligen Sockets geschieht.

SSLServerSocket (Java Platform SE 7 )
SSLSocket (Java Platform SE 7 )

out sei einfach eine OutputStream.

Dateiseite:

        ci.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(gehe, "AES"));
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fi));
        CipherInputStream cis = new CipherInputStream(bis, ci);
        while ((b = cis.read()) != -1) {
            out.write(b);
        }
        out.flush();
        cis.close();```

Clientseite:

```        Cipher ci = Cipher.getInstance("AES");
        ci.init(Cipher.DECRYPT_MODE, new SecretKeySpec(gehe, "AES"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(name));
        CipherOutputStream cos = new CipherOutputStream(bos, ci);
        while ((b = in.read()) != -1) {
            cos.write(b);
        }
        cos.flush();
        cos.close();```

Das funktioniert ganz gut, für kleinere Dateien. Für (extrem) große Dateien funktioniert das nicht mehr. Wieso?

SSL, TLS, Handshake, Sessionkeys, usw. brauche ich gar nicht. Ich hab mir ein eigenes Ablauf ausgedacht, der (erst mal) sicher sein sollte.

Klar, Beispiele hab ich dazu auch im I-nternet gefunden. Darf so etwas denn hier diskutiert werden?

Wenn einer weiß, woran was liegen tut, immer los damit. :D

Sicherheit funktioniert nicht einfach mit “denk ich mir mal so”, und selbst Experten scheitern manchmal an den verflixten Details.

Ja, aber wieso funktioniert es nicht mit beliebiger Dateigröße?

@ionutbaiu : Ja, es ist so, wie du es beschrieben hast, über ServerSocket und Socket wird Input- und OutputStream geöffnet.

Weil ich nicht genau weiß, wie viele Bytes optimalerweise über TCP verschickt werden sollten, schicke ich Byte für Byte. : S

@CB
Wenn du dir mal die Mühe gemacht und im richtigen Sub-Forum gesucht hättest wärst du auf einen Thread von mir gestoßen in dem ich einen eigenen CipherStream basteln wollte weil ich ähnliche Probleme hatte. Da kam dann die sehr einfache Antwort Cipher mal auf den richtigen Modus einzustellen und dann den vorhandenen CipherStream zu nutzen: AES/GCM/NoPadding
Wie man da eine funktionierende Instanz baut fragst du bitte Google.
Aber ich muss dem bereits gesagten zustimmen: nutz die Factories um dir einen SSLServerSocket zu baurn und überlass den Rest Java. Ist sauberer, schneller und einfacher. Und wie man ein beliebiges root-ca-cert zur Runtime läd um damit eine Verbindung zu einer entsprechenden Gegenstellen aufzubauen nimmst du bitte aus meinem Thread “Java und StartSSL”.
Dazu der Hint: Wenn du dir mit openssl für deine Verbindung ein Zertifikat bauen willst erstell vorher eine CA und ein zusätzliches SSL-cert. So kannst du beliebige Certs erstellen - musst aber nur einmal das root-ca installieren.

Nagut, kann das Thema nicht doch gelöscht werden? Ich weiß nicht, ob nach den ganzen “Skandalen” über so etwas diskutiert werden darf. Über Atomphysik darf auch nicht gesprochen od. geschrieben werden, wenn ich mich erinnere. Schutz d. Allgemeinheit oder so.

Oder bin ich jetzt bei allen unten durch?

What? Natürlich darf man über Sicherheitstechnikdiskutieren, wer sollte dir das verbieten? Stackexchange hat dafür eine Unterseite: Information Security Stack Exchange

Und wenn ich über Atomphysik diskutieren will, tue ich das auch. Wusstest du, dass der Blitz bei den größten Atombomben (nämlich russischen) bis zu 45s andauern kann?

Nur Problm.
Bei "AES/GCM/NoPadding", "SunJCE" fliegt mir alles um die Ohren,
da das nicht kompatibel mit Java 1.6 ist (ja, ich bin ein bisschen rückständig/fortschrittsfeindlich :wink: ).
Eigentlich müsste es mit obigen Code doch auch mit Padding funktionieren - auch wenn die Datei größer ist, s. d. nicht alles vor einem flush() geschrieben wird?
Ich schicke jetzt noch mal byteweise und sammle auf Clientseite alles in einem byte[] ein - das ist nur für den Speicher unsinnig.

Landei, das war doch nur ein Beispiel. Ich glaub, das hab ich (mal) im Fernsehen gesehen. Jemand wollte ein Kinderbuch über Experimente schreiben, wobei der Verlag aber gesagt hatte, das und das wäre zu gefährlich, rauslassen.

1.6 sollte man schon mal gar nicht mehr nutzen - mindestens 1.7 sollte schon sein.
Ansonsten direkte Antwort auf direkte Frage: Wenn du etwas mit Padding verarbeitest - aber dann nur byte-für-byte überträgst muss es zwangsläufig zu einer BadPaddingException kommen - da der Output mit Padding bei einem Input von genau einem Byte nun mal mindestens Blocklänge ist - und die muss beim Entschlüsseln auch wieder als solcher kompletter Block als Input kommen - sonst gehts nicht. Alternativ: ohne Padding.
btw: Warum GCM? Ich hab keinen blassen Schimmer - kann dir aber nach einigen Tests sagen dass es der einzige Modus ist der sauber bei CipherStream funktioniert - alle anderen BlockModes wie CBC und andere gleich ob mit oder ohne Padding (wobei NoPadding immer explizit angegeben werden muss) packen es irgendwie nicht.
Aber um mich und andere zu wiederholen: um dir den ganzen Krams zu sparen solltest du lieber wirklich SSLSocket nutzen - da sollte auch bei 1.6 noch was dabei sein um das halbwegs einfach umzusetzen (oder halt mal auf 1.7/1.8 updaten - ist ja nicht so als ob es so schwer wäre).

[OT]man sollte lieber keine Frage stellen, wenn man die Antwort nicht hören will[/OT]

Gut - ich muss mal unseren bunten Kanarienvogel hier doch “etwas” zur Seite ziehen - und muss gestehen dass auch ich dachte es wäre einfacher (nach dem ich mich nun etwas länger schon mit dem Thema befasse).

Fakt: Es ist ein ziemlicher Krampf überhaupt erstmal einen Key zu laden wenn dieser aus einem private-key-File und einem dazu gehörigem public-certificate-File besteht (wie es ja auch sein soll). Gut, es kommt etwas drauf an in welchem Format das key-File vorliegt ob es man es einfach bequem laden kann oder sich was um die Ecke bauen muss - aber es sollte auf das Ergebnis keinen Einfluss haben.
Weiter: Selbst wenn man alle nötige Daten zusammen hat ist es doch sehr aufwändig und umständlich alles richtig “aufzusetzen” um am Ende dann einen sauberen SSLServerSocket über die Factory zu erstellen.
Und Ergebnis: Nach dem ich meinen ganzen Nachmittag des Herrentages dran verschwendet habe bin ich lediglich soweit dass ich entweder den Fehler bekomme die Signatur sei ungültig oder mein (übrigens über Apache und meinen Mail-Server sauber laufendes) Zertifikat sei nicht dazu berechtigt eine Signatur (was auch immer die Fehlermeldung damit meint) zu erzeugen.

Auch habe ich das Verhalten beobachtet dass lokal alles sehr schnell geht - so bald ich die Verbindung remote testen will mit meinem Root bei dem Apache und Mail-Server sehr schnell antworten dauert es gefühlte Ewigkeiten bis das Timeout kommt und der Verbindungsversuch letztlich abgebrochen wird.

Da Google zwar einiges liefert - jedoch nicht so ausführlich wie ich es letztlich zusammengeschustert habe (bzw. musste auf Grund des private-key) - und ich aktuell auch keine lauffähige Anleitung für einen SSLServerSocket liefern kann müsste ich erstmal an die “einfacherer” Variante mit RSA und AES über Cipher verweisen (zumindest das hab ich schon Jahren zum laufen bekommen).

Auch ist es halt sehr umständlich da die SSL-Verbindung gültige Zertifikate verlangt - was wegfällt wenn man schnell einen RSA-Key-exchange mit AES-Session-Key bastelt (DHE müsste auch einfach umsetzbar sein - dürfte aber, wenn bei jeder Verbindung RSA-Key zufällig erzeugt, keinen Sicherheitsgewinn bringen).

// EDIT

Nachtrag: Nach dem ich nun die Reihenfolge der Cert-Chain korrigiert hab (dachte das wäre egal - aber man muss drauf achten in der Chain erst das eigene Cert und dann den Weg aufwärts bis zum CA-Root-Cert) kommt zumindest auf localhost mit entsprechender Warnung sofort eine Verbindung zu stande. Remote mit meinem Root zwar auch mal irgendwann - aber es dauert ewig. Der Server-Test von SSLLabs.com verweigert schon mal jeden Mitarbeit da es ihm nach Timeout zu lange dauert.
Auf Verdacht dass meinem Root die Entropie ausgegangen sein könnte erstmal reboot und andere SSL-Dienste wie SSH oder meinen Mail-Server getestet - die weisen das Problem nicht auf - also schein genug Entropie da zu sein um die Services zu bedienen.
Da mir leider nicht klar werden will was nun diese extreme Verzögerung auf meinem Linux-Root verursacht und warum es local auf der win7-Kiste geht - kein Plan.

SSL war ja nicht meine Frage, dennoch Danke, das du so viel Zeit dafür aufwendest. Ich werd das jetzt komplett anders machen, aber immer-noch mit AES. Ich schreibe mit einem Cypher Output Steam, lese und schreibe alles in eine Datei und werde dann entschlüsseln - etwas umständlich.

SSL bei Stack over Flow vorbeischauen. Das Thema ist aber gelöst, imho.

Naja, SSL ist halt als transparente Zwischenschicht gedacht die die Cryptodetails wegkapseln soll. Und da ich den Weg über RSA/AES über die Cipher-Klassen kenne und daher auch schon oft in das Problem gerannt bin dass ein CipherOutputStream einfach nichts senden will weil sich der interne Block-Buffer auch mit flush() nicht dazu bewegen lässt den nächsten Block zu padden oder dass das was beim CipherInputStream dass was ankommt nicht wieder entschlüsselt werden kann dachte ich halt dass die SSL-Klassen dies verbequemern würden - hab es aber vorher noch nie selbst ausprobiert.
Da ich nun aber weis wie aufwändig es ist (zumindest wenn man keine zusätzlichen Libs nutzen will) und aktuell trotzdem irgendwas nicht funktioniert - und Google mir dabei erstmal auch nicht weiter hilft (hab noch nicht all zu viel nach dem Problem gesucht) kann ich dir sagen: Für deine Zwecke dürfte RSA-KeyExchange mit AES-SessionKey und ohne HMAC ausreichen. Und du sparst dir wie gesagt das ganze Drumherum mit den Zertifikaten.

Ich werd es mal testen, wenn wieder etwas Zeit ist. Danke Dir erst mal!

[*] An der Stelle ist es in Java eben sehr kompliziert.

Ok, ich gebs auf. Nach dem ich nun zwei Tage damit verschwendet habe zu versuchen die SSL-Klassen ans laufen zu bekommen - was letztlich zwar funktioniert hat, aber aus irgendeine Grund ziemliche Performance-Probleme hat - und auch “mal eben” versucht habe AES/GCM/NoPadding ans laufen zu bekommen - was auch scheitert - muss ich gestehen: Ich würde für sowas einen meiner ur-alt Codes aus 2011 nutzen der letztlich mit AES/CBC/PKCS5Padding und einem “selbst-gebasteltem” Cipher.doFinal() arbeitet. Sicherheitsproblem an dem Code: Wenn nach Cipher.doFinal() ein Cipher.update() kommt wird automatisch Cipher.reset() gecallt was dazu führt dass der init-IV wieder nach vorne kommt - was dann die Möglichkeit für replay-Attacken gibt. Ich habs jetzt nicht ausprobiert - und will es auch aktuell nicht mehr - aber ich glaube dass wenn man nach einem Cipher.doFinal() Cipher.getIV() callt dass dies lediglich den init-IV liefert und nicht den aktuellen IV nach doFinal().

Es wird sicherlich irgendwie gehen die CipherIn/OutputStream-Klassen “transparent” drunter zu schieben - aber da ich mich seit mehr als 6 Jahren damit befasse und aktuell nicht in der Lage bin die von mir genannten Hinweise selbst umzusetzen (ja ja - große Klappe und - shame on me - und nu weiter im Text) muss ich auf eine unsichere Variante verweisen.

Grundsätzliches Problem an allem was nicht mit Zertifikaten arbeitet: Jeder kann den Code de-compilen und sich unbemerkt als MITM dazwischenschalten da auf Client-Seite der vom Server kommende RSA-Schlüssel gegen keine Referenz geprüft werden kann.

Ich werd vielleicht die Woche über noch mal was testen - aber so lange ich nicht weis warum die SSL-Variante zwar lokal aber nicht remote funktionert (also diesen Performance-Bug nicht gefixt bekomme) hake auch ich das Thema erstmal ab.

Mir würde, wenn du Linux nutzt, noch die Möglichkeit mit SSH-FS einfallen - so kannst du über eine SSH-Verbindung einen remote-Path in den lokalen Baum mounten (klappt ganz gut) - dann muss man sich nicht selbst damit befassen. Obs unter Windows mit Cygwin möglich ist wage ich stark zu bezweifeln da schon dass Aufsetzen eines SSHD eine verdammt komplizierte Kiste ist.

Ich denke da wir aktuell alle Möglichkeiten der ursprünglichen Frage durchgegangen sind kann man das Thema ruhig erstmal auf solved setzen.

// EDIT

Nachtrag1: Das Performance-Problem scheint wohl generell auf meinem Linux-Root zu bestehen. Denn dort hat auch der lokale Test, der unter Win7 ohne Probleme lief, den gleichen Fehler. Muss ich mal auf ner VM testen.
Nachtrag2: Was mir gerade im debug auffällt: Da auf meinem Linux-Root das OpenJDK läuft wird dort als Cipher-Suite ECDHE-RSA-AES256-SHA384 genutzt - ich rall es nicht mehr. Ist OpenJDK nicht auch irgendwie von Oracle? Warum unterliegt das Oracle-JDK diesen kernbehinderten Export-Beschränkungen dass nur AES128 möglich ist?
Nachtrag3: Ok, in einer VM funzt es. Muss also irgendwas mit der Hardware meines Root-Servers sein - so ein crap.

Hmm, ich muss doch noch mal pushen - immerhin weis ich jetzt wo das Problem bei meinem Server liegt: Da ich, wie in nem anderen Thread gepostet, auf Grund eines Problems die Kiste neu aufsetzen musste hab ich danach erstmal komplett vergessen die DNS-config wieder richtig hinzubiegen. Macht zwar aktuell immer noch macken und zickt i-wie noch rum - aber dass bekomm ich auch wiedr in Griff. Dürfte aber die Erklärung für das Problem sein. Mehr dazu beim großen G: “java ssl reverse dns lookup”