static byte[] messageDigest( InputStream is, String algo ) throws NoSuchAlgorithmException, IOException { MessageDigest messageDigest = MessageDigest.getInstance( algo ); // algo z.B.: "MD5" oder "SHA-1" byte[] ba = new byte[8192]; for( int n = 0; (n = is.read( ba )) > -1; ) { messageDigest.update( ba, 0, n ); } return messageDigest.digest(); // 16 Bytes fuer MD5, 20 Bytes fuer SHA-1 }
static byte[] signature( InputStream is, PrivateKey privateKey ) throws GeneralSecurityException, IOException { Signature signature = Signature.getInstance( "SHA/DSA" ); signature.initSign( privateKey ); byte[] ba = new byte[8192]; for( int n = 0; (n = is.read( ba )) > -1; ) { signature.update( ba, 0, n ); } return signature.sign(); }
import javax.crypto.Cipher; import java.security.*; public class MaximaleSchluessellaengen { public static void main( String[] args ) throws GeneralSecurityException { System.out.println( "\nSecurity-Provider:" ); for( Provider prov : Security.getProviders() ) { System.out.println( " " + prov + ": " + prov.getInfo() ); } System.out.println( "\nMaxAllowedKeyLength (fuer '" + Cipher.getInstance( "AES" ).getProvider() + "' mit aktuellen 'JCE Policy Files'):\n" + " DES = " + Cipher.getMaxAllowedKeyLength( "DES" ) + "\n" + " Triple DES = " + Cipher.getMaxAllowedKeyLength( "Triple DES" ) + "\n" + " AES = " + Cipher.getMaxAllowedKeyLength( "AES" ) + "\n" + " Blowfish = " + Cipher.getMaxAllowedKeyLength( "Blowfish" ) + "\n" + " RSA = " + Cipher.getMaxAllowedKeyLength( "RSA" ) + "\n" ); } }
.asc | OpenPGP-Zertifikat, OpenPGP-Signatur oder per OpenPGP verschlüsselte Datei im Textformat (ASCII, "armor") |
.bpg | Per Bouncy Castle Crypto API verschlüsselte Datei im Binärformat |
.crt | X.509-Zertifikat (application/pkix-cert) |
.der | X.509-Zertifikat (application/x-x509-ca-cert) |
.gpg | OpenPGP-Zertifikat oder per OpenPGP verschlüsselte Datei im Binärformat (application/pgp-encrypted) |
.p7m | Per S/MIME verschlüsselte Datei im Binärformat (PKI PKCS#7-Format) |
.p7s | S/MIME-Signatur im Binärformat (PKI PKCS#7-Format) |
.p12 | X.509-Zertifikat (PKI PKCS#12-Format) |
.pem | X.509-Zertifikat, S/MIME-Signatur oder per S/MIME verschlüsselte Datei oder E-Mail im Base64-Textformat (Privacy Enhanced Mail) |
.sig | OpenPGP-Signatur im Binärformat |
Option | Vorteile | Nachteile |
---|---|---|
Symmetrisch AES, proprietär programmiert |
+ keine Lib-Abhängigkeit | - Übertragung des geheimen Passworts - proprietär, genaue Absprachen notwendig - Kompatibilität Java / C#/.NET schwierig - Sicherheit schwieriger zu beurteilen |
Asymmetrisch RSA + AES, proprietär programmiert |
+ öffentliche Schlüssel + keine Lib-Abhängigkeit |
- proprietär, genaue Absprachen notwendig - Kompatibilität Java / C#/.NET schwierig - Sicherheit schwieriger zu beurteilen |
Symmetrisch AES, mit OpenPGP-Lib, z.B. Bouncy Castle Crypto API |
+ kompatibel zu OpenPGP + kompatibel zu GnuPG + Lib für Java und C#/.NET |
- Übertragung des geheimen Passworts - Abhängig von Fremd-Lib |
Asymmetrisch RSA + AES, mit OpenPGP-Lib, z.B. Bouncy Castle Crypto API |
+ öffentliche Schlüssel + kompatibel zu OpenPGP + kompatibel zu GnuPG + Lib für Java und C#/.NET |
- Abhängig von Fremd-Lib |
Asymmetrisch RSA + AES, plus digitale Signatur, mit OpenPGP-Lib, z.B. Bouncy Castle Crypto API |
+ öffentliche Schlüssel + etwas erweiterte Sicherheit + kompatibel zu OpenPGP + kompatibel zu GnuPG + Lib für Java und C#/.NET |
- umständlicher, weil mehrere Schlüssel - Absprachen erforderlich - event. wesentlich schlechtere Performance - Abhängig von Fremd-Lib |
Asymmetrisch RSA + AES, GnuPG per Batch-Skript |
+ öffentliche Schlüssel + kompatibel zu OpenPGP + kompatibel zu GnuPG |
- nur Kommandozeilentool für Skripte - keine Java-Lib - benötigt Extra-Prozess (z.B. cron-Job) |
Das folgende rudimentäre Programmierbeispiel zeigt den Einsatz der Cipher-Klasse zur Ver- und Entschlüsselung nach dem symmetrischen AES-, Blowfish- und DES-Algorithmus.
Um das Beispiel einfach zu halten, wird vom Passwort lediglich ein MD5-Hash erzeugt, aber auf "Salt", "Initialization Vector (IV)", "Iterationen" etc. verzichtet (siehe auch PBE (Password-based Encryption)).
Erzeugen Sie die Klasse: CryptoSimple.java
import java.io.*; import java.security.*; import java.util.Arrays; import javax.crypto.*; import javax.crypto.spec.SecretKeySpec; /** Rudimentaeres Cipher-Beispiel, nicht fuer reale Verwendung geeignet */ public class CryptoSimple { public static void main( String[] args ) throws GeneralSecurityException, IOException { if( args.length == 5 ) { if( args[0].toLowerCase().startsWith( "-e" ) ) { // encrypt encrypt( args[1], args[2], args[3], args[4] ); return; } else if( args[0].toLowerCase().startsWith( "-d" ) ) { // decrypt decrypt( args[1], args[2], args[3], args[4] ); return; } } System.out.println( "Fehler: Es werden vier Parameter benoetigt:\n" + "Zum Verschluesseln:\n" + " -e Quelldatei EncryptedZieldatei CipherAlgorithmus Passwort\n" + "Zum Entschluesseln:\n" + " -d EncryptedQuelldatei DecryptedZieldatei CipherAlgorithmus Passwort\n" ); } /** Verschluesseln */ public static void encrypt( String srcFile, String encryptedDstFile, String algorithm, String password ) throws GeneralSecurityException, IOException { encrypt( new FileInputStream( srcFile ), new FileOutputStream( encryptedDstFile ), algorithm, password ); } /** Verschluesseln (Streams werden mit close() geschlossen) */ public static void encrypt( InputStream inpStream, OutputStream encryptedOutStream, String algorithm, String password ) throws GeneralSecurityException, IOException { SecretKey secKey = new SecretKeySpec( hashPwd( password, algorithm ), algorithm ); Cipher cipher = Cipher.getInstance( algorithm ); byte[] byteBuffer = new byte[64 * 1024]; int n; cipher.init( Cipher.ENCRYPT_MODE, secKey ); CipherOutputStream cos = new CipherOutputStream( encryptedOutStream, cipher ); try { while( (n = inpStream.read( byteBuffer )) > 0 ) { cos.write( byteBuffer, 0, n ); } } finally { cos.close(); encryptedOutStream.close(); inpStream.close(); } } /** Entschluesseln */ public static void decrypt( String encryptedSrcFile, String decryptedDstFile, String algorithm, String password ) throws GeneralSecurityException, IOException { decrypt( new FileInputStream( encryptedSrcFile ), new FileOutputStream( decryptedDstFile ), algorithm, password ); } /** Entschluesseln (Streams werden mit close() geschlossen) */ public static void decrypt( InputStream encryptedInpStream, OutputStream decryptedOutStream, String algorithm, String password ) throws GeneralSecurityException, IOException { SecretKey secKey = new SecretKeySpec( hashPwd( password, algorithm ), algorithm ); Cipher cipher = Cipher.getInstance( algorithm ); byte[] byteBuffer = new byte[64 * 1024]; int n; cipher.init( Cipher.DECRYPT_MODE, secKey ); CipherInputStream cis = new CipherInputStream( encryptedInpStream, cipher ); try { while( (n = cis.read( byteBuffer )) > 0 ) { decryptedOutStream.write( byteBuffer, 0, n ); } } finally { cis.close(); encryptedInpStream.close(); decryptedOutStream.close(); } } /** MD5-Hash */ private static byte[] hashPwd( String password, String algorithm ) throws NoSuchAlgorithmException, UnsupportedEncodingException { MessageDigest md = MessageDigest.getInstance( "MD5" ); md.update( password.getBytes( "ISO-8859-1" ) ); return ( "DES".equals( algorithm ) ) ? Arrays.copyOf( md.digest(), 8 ) : md.digest(); } }
Zur Vorbereitung:
Kompilieren und Testdatei erstellen (es kann auch eine beliebige andere Datei verwendet werden, auch binäre Dateien):
javac CryptoSimple.java
echo Mein Text Abc Xyz > MeineDatei.txt
AES-Ver- und -Entschlüsselung:
java CryptoSimple -e MeineDatei.txt MeineDatei.txt.aes AES "geheim"
java CryptoSimple -d MeineDatei.txt.aes MeineDatei.txt.aes.txt AES "geheim"
Blowfish-Ver- und -Entschlüsselung:
java CryptoSimple -e MeineDatei.txt MeineDatei.txt.blowfish Blowfish "geheim"
java CryptoSimple -d MeineDatei.txt.blowfish MeineDatei.txt.blowfish.txt Blowfish "geheim"
DES-Ver- und -Entschlüsselung:
java CryptoSimple -e MeineDatei.txt MeineDatei.txt.des DES "geheim"
java CryptoSimple -d MeineDatei.txt.des MeineDatei.txt.des.txt DES "geheim"
In allen drei Fällen stimmt die Ergebnisdatei MeineDatei.txt...txt mit der ursprünglichen Datei MeineDatei.txt überein. Die drei nicht auf .txt endenden Dateien enthalten jeweils den verschlüsselten Inhalt.
Das folgende Verschlüsselungsprogrammierbeispiel hat die Eigenschaften:
Führen Sie folgende Schritte durch:
Erstellen Sie ein Projektverzeichnis:
cd \MeinWorkspace
md CryptoRsaAes
cd CryptoRsaAes
md src\main\java\de\meinefirma\meinprojekt\crypto
md src\test\java\de\meinefirma\meinprojekt\crypto
tree /F
Erzeugen Sie im CryptoRsaAes-Projektverzeichnis die Maven-Projektkonfigurationsdatei: pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>de.meinefirma.meinprojekt</groupId> <artifactId>CryptoRsaAes</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>CryptoRsaAes</name> <properties> <project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.4</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies> </project>
Erzeugen Sie im Verzeichnis src\main\java\de\meinefirma\meinprojekt\crypto die Klasse: CryptoRsaAes.java
package de.meinefirma.meinprojekt.crypto; import java.io.*; import java.security.*; import javax.crypto.*; public class CryptoRsaAes { static final String ASYMMETRIC_ALGO = "RSA"; static final String SYMMETRIC_ALGO = "AES"; // oder z.B. "Blowfish" public static void main( String[] args ) throws GeneralSecurityException, ClassNotFoundException, IOException { if( args.length == 4 ) { if( args[0].toLowerCase().startsWith( "-g" ) ) { // generateKeyPair generateKeyPair( args[1], args[2], Integer.parseInt( args[3] ) ); return; } else if( args[0].toLowerCase().startsWith( "-e" ) ) { // encrypt encrypt( args[1], args[2], args[3] ); return; } else if( args[0].toLowerCase().startsWith( "-d" ) ) { // decrypt decrypt( args[1], args[2], args[3] ); return; } } System.out.println( "\nFehler: Es werden vier Parameter benoetigt:\n" + "Zum Generieren des privaten und oeffentlichen RSA-Schluessels (RsaKeySize z.B. 2048):\n" + " -g PrivateKeyFile PublicKeyFile RsaKeySize\n" + "Zum Verschluesseln:\n" + " -e PublicKeyFile InputFile EncryptedFile\n" + "Zum Entschluesseln:\n" + " -d PrivateKeyFile EncryptedFile OutputFile\n" ); } /** Generiere privaten und oeffentlichen RSA-Schluessel */ public static void generateKeyPair( String privateKeyFile, String publicKeyFile, int rsaKeySize ) throws NoSuchAlgorithmException, IOException { generateKeyPair( new FileOutputStream( privateKeyFile ), new FileOutputStream( publicKeyFile ), rsaKeySize ); } /** Generiere privaten und oeffentlichen RSA-Schluessel (Streams werden mit close() geschlossen) */ public static void generateKeyPair( OutputStream privateKeyFile, OutputStream publicKeyFile, int rsaKeySize ) throws NoSuchAlgorithmException, IOException { try { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance( ASYMMETRIC_ALGO ); keyPairGen.initialize( rsaKeySize ); KeyPair keyPair = keyPairGen.generateKeyPair(); ObjectOutputStream out = new ObjectOutputStream( publicKeyFile ); try { out.writeObject( keyPair.getPublic() ); } finally { out.close(); } out = new ObjectOutputStream( privateKeyFile ); try { out.writeObject( keyPair.getPrivate() ); } finally { out.close(); } } finally { privateKeyFile.close(); publicKeyFile.close(); } } /** Verschluesseln */ public static void encrypt( String publicKeyFile, String inputFile, String encryptedFile ) throws GeneralSecurityException, ClassNotFoundException, IOException { encrypt( new FileInputStream( publicKeyFile ), new FileInputStream( inputFile ), new FileOutputStream( encryptedFile ) ); } /** Verschluesseln (Streams werden mit close() geschlossen) */ public static void encrypt( InputStream publicKeyFile, InputStream inputFile, OutputStream encryptedFile ) throws GeneralSecurityException, ClassNotFoundException, IOException { try { KeyGenerator keyGen = KeyGenerator.getInstance( SYMMETRIC_ALGO ); keyGen.init( Math.min( 256, Cipher.getMaxAllowedKeyLength( SYMMETRIC_ALGO ) ) ); SecretKey symKey = keyGen.generateKey(); Key publicKey; ObjectInputStream keyIn = new ObjectInputStream( publicKeyFile ); try { publicKey = (Key) keyIn.readObject(); } finally { keyIn.close(); } Cipher cipher = Cipher.getInstance( ASYMMETRIC_ALGO ); cipher.init( Cipher.WRAP_MODE, publicKey ); byte[] wrappedKey = cipher.wrap( symKey ); DataOutputStream out = new DataOutputStream( encryptedFile ); try { out.writeInt( wrappedKey.length ); out.write( wrappedKey ); cipher = Cipher.getInstance( SYMMETRIC_ALGO ); cipher.init( Cipher.ENCRYPT_MODE, symKey ); transform( inputFile, out, cipher ); } finally { out.close(); } } finally { publicKeyFile.close(); inputFile.close(); encryptedFile.close(); } } /** Entschluesseln */ public static void decrypt( String privateKeyFile, String encryptedFile, String outputFile ) throws GeneralSecurityException, ClassNotFoundException, IOException { decrypt( new FileInputStream( privateKeyFile ), new FileInputStream( encryptedFile ), new FileOutputStream( outputFile ) ); } /** Entschluesseln (Streams werden mit close() geschlossen) */ public static void decrypt( InputStream privateKeyFile, InputStream encryptedFile, OutputStream outputFile ) throws GeneralSecurityException, ClassNotFoundException, IOException { try { DataInputStream in = new DataInputStream( encryptedFile ); try { int length = in.readInt(); byte[] wrappedKey = new byte[length]; in.read( wrappedKey, 0, length ); Key privateKey; ObjectInputStream keyIn = new ObjectInputStream( privateKeyFile ); try { privateKey = (Key) keyIn.readObject(); } finally { keyIn.close(); } Cipher cipher = Cipher.getInstance( ASYMMETRIC_ALGO ); cipher.init( Cipher.UNWRAP_MODE, privateKey ); Key symKey = cipher.unwrap( wrappedKey, SYMMETRIC_ALGO, Cipher.SECRET_KEY ); cipher = Cipher.getInstance( SYMMETRIC_ALGO ); cipher.init( Cipher.DECRYPT_MODE, symKey ); transform( in, outputFile, cipher ); } finally { in.close(); } } finally { privateKeyFile.close(); encryptedFile.close(); outputFile.close(); } } private static void transform( InputStream in, OutputStream out, Cipher cipher ) throws IOException, GeneralSecurityException { int blockSize = cipher.getBlockSize(); byte[] input = new byte[blockSize]; byte[] output = new byte[cipher.getOutputSize( blockSize )]; int len; while( (len = in.read( input )) == blockSize ) { int outLength = cipher.update( input, 0, blockSize, output ); out.write( output, 0, outLength ); } out.write( ( len > 0 ) ? cipher.doFinal( input, 0, len ) : cipher.doFinal() ); } }
Erzeugen Sie im Verzeichnis src\test\java\de\meinefirma\meinprojekt\crypto den JUnit-Test: CryptoRsaAesTest.java
package de.meinefirma.meinprojekt.crypto; import java.io.*; import java.security.GeneralSecurityException; import java.util.*; import org.junit.*; public class CryptoRsaAesTest { @Test public void testCryptoRsaAes() throws GeneralSecurityException, ClassNotFoundException, IOException { String privateKeyFile = "target/PrivateKey.rsa"; String publicKeyFile = "target/PublicKey.rsa"; String srcFile = "target/MeineSrcDatei.txt"; String encFile = "target/MeineVerschluesselteDatei.aes.rsa"; String dstFile = "target/MeineDstDatei.txt"; String text = "Blablupp \u00E4\u00F6\u00FC\u00DF\u20AC"; // Umlaute und Eurozeichen String charEnc = "UTF-8"; BufferedWriter out = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( srcFile ), charEnc ) ); try { out.write( text ); } finally { out.close(); } CryptoRsaAes.generateKeyPair( privateKeyFile, publicKeyFile, 2048 ); Assert.assertTrue( new File( privateKeyFile ).exists() ); Assert.assertTrue( new File( publicKeyFile ).exists() ); CryptoRsaAes.encrypt( publicKeyFile, srcFile, encFile ); Assert.assertTrue( new File( encFile ).exists() ); CryptoRsaAes.decrypt( privateKeyFile, encFile, dstFile ); Assert.assertTrue( new File( dstFile ).exists() ); List<String> ss = new ArrayList<String>(); BufferedReader in = new BufferedReader( new InputStreamReader( new FileInputStream( dstFile ), charEnc ) ); try { String line; while( (line = in.readLine()) != null ) { ss.add( line ); } } finally { in.close(); } Assert.assertEquals( 1, ss.size() ); Assert.assertEquals( text, ss.get( 0 ) ); } }
Die Projektstruktur sieht jetzt so aus:
cd \MeinWorkspace\CryptoRsaAes
tree /F
[\MeinWorkspace\CryptoRsaAes] |- [src] | |- [main] | | '- [java] | | '- [de] | | '- [meinefirma] | | '- [meinprojekt] | | '- [crypto] | | '- CryptoRsaAes.java | '- [test] | '- [java] | '- [de] | '- [meinefirma] | '- [meinprojekt] | '- [crypto] | '- CryptoRsaAesTest.java '- pom.xml
Führen Sie den JUnit-Test aus:
mvn clean test
Der Test läuft fehlerfrei durch und im target-Verzeichnis entstehen folgende Dateien:
[\MeinWorkspace\CryptoRsaAes] |- [...] | '- ... '- [target] |- [...] | '- ... |- MeineDstDatei.txt . . . . . . . . . . . [wiederhergestellte entschlüsselte Datei] |- MeineSrcDatei.txt . . . . . . . . . . . [ursprüngliche Originaldatei] |- MeineVerschluesselteDatei.aes.rsa . . . [verschlüsselte Datei] |- PrivateKey.rsa . . . . . . . . . . . . [Privater Schlüssel] '- PublicKey.rsa . . . . . . . . . . . . . [Öffentlicher Schlüssel]
Die ursprüngliche Originaldatei und das Ergebnis der Entschlüsselung stimmen überein:
notepad target\MeineSrcDatei.txt
notepad target\MeineDstDatei.txt
Folgendermaßen können Sie Public/Private-Key-Paare erzeugen und beliebige Input-Dateien verschlüsseln und wieder entschlüsseln:
java -cp target\classes de.meinefirma.meinprojekt.crypto.CryptoRsaAes -g PrivateKeyFile.rsa PublicKeyFile.rsa 2048
java -cp target\classes de.meinefirma.meinprojekt.crypto.CryptoRsaAes -e PublicKeyFile.rsa MeineInputDatei MeineVerschluesselteDatei.aes.rsa
java -cp target\classes de.meinefirma.meinprojekt.crypto.CryptoRsaAes -d PrivateKeyFile.rsa MeineVerschluesselteDatei.aes.rsa MeineEntschluesselteDatei
GnuPG (= GPG, GNU Privacy Guard) ist das bekannteste Open-Source-Verschlüsselungstool. Es implementiert OpenPGP und S/MIME und bietet beispielsweise: RSA, DSA, Elgamal, AES-256, AES-128, CAST5, Triple-DES, Blowfish, SHA-1, SHA-2, RIPEMD-160, ZIP. Hinweise zu präferierten Algorithmen finden Sie hier.
GnuPG gibt es sowohl für Windows als auch für Linux und kann wahlweise als mächtiges Kommandozeilentool oder bequem über grafische Aufsätze (z.B. Kleopatra) oder eingebettet im E-Mail-Programm verwendet werden.
Doku zu GnuPG finden Sie beispielsweise unter: GnuPG-Handbücher, Gpg4win-Kompendium und Deutsche GnuPG-Anleitung von Kai Billen.
Im Folgenden werden nur einige wenige der vielen Möglichkeiten von GnuPG als Kommandozeilentool demonstriert, um dann weiter unten die kompatible Zusammenarbeit mit dem Bouncy Castle Crypto API zeigen zu können.
Der einfachste Weg, um GnuPG unter Windows zu installieren, führt über Gpg4win (für die hier gezeigten Beispiele genügt eine Minimalinstallation). Downloaden Sie Gpg4win (z.B. gpg4win-2.1.0.exe) von http://www.gpg4win.de/download-de.html.
Starten Sie das Gpg4win-Installationsprogramm (z.B. gpg4win-2.1.0.exe).
Beim Dialog "Komponenten auswählen" können Sie alles außer GnuPG deaktivieren
(es sei denn, Sie wollen auch andere Komponenten nutzen).
Bestätigen Sie in allen anderen Dialogen die Voreinstellungen mit "Weiter".
Alternativ zur Dialog-gesteuerten Installation gibt es auch die Möglichkeit der
Silent Installation.
Unter Windows erweitert die Installation den Suchpfad um das GnuPG-Verzeichnis %ProgramFiles(x86)%\GNU\GnuPG\pub (z.B. C:\Program Files (x86)\GNU\GnuPG\pub), siehe je nach Windows-Version:
dir "%ProgramFiles(x86)%\GNU\GnuPG\pub"
dir "%ProgramFiles%\GNU\GnuPG\pub"
Unter Windows werden persönliche GnuPG-Daten gespeichert in %APPDATA%\gnupg (z.B. C:\Users\<Benutzername>\AppData\Roaming\gnupg), siehe:
dir "%APPDATA%\gnupg"
Von diesem Verzeichnis sollten Sie regelmäßig ein Backup sichern.
Öffnen Sie ein Kommandozeilenfenster und testen Sie folgende Kommandos:
gpg2.exe --version
gpg2.exe -h
Generieren Sie für Tests mit asymmetrischer (hybrider) Verschlüsselung ein Test-Public/Private-Schlüsselpaar (wenn Sie als Kommentartext das vorgeschlagene "GPG-Test" eintragen, können Sie die weiter unten folgenden Beispiele leichter nachvollziehen):
gpg2 --gen-key
1 (für "RSA und RSA")
2048 (Schlüssellänge)
0 (verfällt nie)
j (richtig)
Mein Name
MeinName@MeinServer.de
GPG-Test
f
Meine geheime GPG-Passphrase 42
Sie erhalten:
trust-db erzeugt
uid: Mein Name (GPG-Test) <MeinName@MeinServer.de>
Lassen Sie sich die gespeicherten Schlüssel anzeigen (mit verschiedenen Aufrufvarianten):
gpg2 --list-keys
gpg2 --list-keys "Mein Name"
gpg2 --list-keys "MeinName@MeinServer.de"
gpg2 --list-keys "GPG-Test"
gpg2 --list-keys --fingerprint
gpg2 --list-sigs --fingerprint
Die erste Zahl (z.B. 1024, 2048) bedeutet die asymmetrische Schlüssellänge, dahinter der einzelne Buchstabe steht für den asymmetrischen Schlüssel (R = RSA, D = DSA, g = Elgamal) und nach dem Schrägstrich folgt die 8-stellige Schlüssel-ID (= letzte 8 Stellen des 40-stelligen Fingerprint).
Weitere Erläuterungen finden Sie unter Schlüsseltypen, Algorithmen und Abkürzungen bei Schlüsseln.
Exportieren Sie die erstellten Public und Private Keys (der Ausdruck "GPG-Test" identifiziert als Teilstring des Schlüsselnamens den Schlüssel):
gpg2 -ao GPG-Test-pubkey.asc --export "GPG-Test"
gpg2 -o GPG-Test-seckey.gpg --export-secret-keys "GPG-Test"
Fügen Sie andere übergebene Schlüssel Ihrem Schlüsselring hinzu:
gpg2 --import <Schlüsseldatei>
gpg2 --fetch-keys <URL>
Löschen Sie Schlüssel aus Ihrem Schlüsselring:
gpg2 --batch --yes --delete-secret-and-public-key <Key-ID>
Lassen Sie sich Eigenschaften des Schlüssels anzeigen, zum Beispiel die präferierten Algorithmen:
gpg2 --edit-key "GPG-Test"
pref (Kurzversion)
showpref (ausführlichere Version)
quit
Weitere Infos zum Schlüssel:
gpg2 --list-packets GPG-Test-pubkey.asc
Erläuterungen finden Sie unter Schlüsseltypen, Algorithmen und Abkürzungen bei Schlüsseln.
Erstellen Sie eine beliebige Testdatei (wahlweise Text oder binär), zum Beispiel so:
echo Mein Text Abc Xyz > MeineDatei.txt
Testen Sie symmetrische Ver- und Entschlüsselung ("-c" bedeutet symmetrisch, "-d" decrypt, "s2k" String-to-Key):
gpg2 --cipher-algo AES --s2k-digest-algo SHA1 -c MeineDatei.txt
gpg2 -o MeineDatei-AES-decrypted.txt -d MeineDatei.txt.gpg
type MeineDatei-AES-decrypted.txt
Testen Sie asymmetrische (hybride) Ver- und Entschlüsselung ("-e" steht für encrypt, "-d" für decrypt, "-r" für recipient):
if exist MeineDatei.txt.gpg del MeineDatei.txt.gpg
gpg2 -er "GPG-Test" MeineDatei.txt
gpg2 -o MeineDatei-RSA-decrypted.txt -d MeineDatei.txt.gpg
type MeineDatei-RSA-decrypted.txt
(Weiter unten finden Sie eine realistischere Kommandofolge, falls Versender und Empfänger verschiedene Personen sind.)
Wenn Sie nicht nur verschlüsseln, sondern zusätzlich vorher signieren wollen ("-s" für signieren):
gpg2 -ser "GPG-Test" MeineDatei.txt
(Auch hierzu finden Sie weiter unten eine realistischere Kommandofolge.)
Wenn Sie beim Entschlüsseln die geheime Passphrase nicht per Dialog, sondern per Kommandozeile übergeben wollen (was Sie normalerweise vermeiden sollten, weil es ein Sicherheitsrisiko darstellt):
gpg2 --batch --passphrase "Meine geheime GPG-Passphrase 42" -o MeineDatei-RSA-decrypted.txt -d MeineDatei.txt.gpg
Oder alternativ per Passphrasen-Datei (achten Sie darauf, dass in der Passphrasen-Datei nach der Passphrase kein Zeilenendezeichen folgen darf):
gpg2 --batch --passphrase-file MeinePassphrasenDatei -o MeineDatei-RSA-decrypted.txt -d MeineDatei.txt.gpg
Anzeige des Message-Digest-Hash einer Datei (z.B. mit MD5, SHA1, RIPEMD160):
gpg2 --print-md SHA1 MeineDatei.txt
Wie bereits weiter oben erwähnt, bietet das Bouncy Castle Crypto API:
Im Folgenden wird die Verwendung einiger in den Bouncy-Castle-examples vorbereiteten Beispielen zu OpenPGP gezeigt.
Downloaden Sie von
http://www.bouncycastle.org/latest_releases.html:
bcprov-jdk15on-147.jar,
bcpg-jdk15on-147.jar und
bcpg-jdk15on-147.zip.
Entzippen Sie bcpg-jdk15on-147.zip und die darin enthaltene src.zip.
Erstellen Sie ein neues Verzeichnis (z.B. \MeinWorkspace\CryptoOpenPgpBouncyCastle) und kopieren Sie dort hinein die beiden .jar-Libs bcprov-jdk15on-147.jar und bcpg-jdk15on-147.jar sowie aus bcpg-jdk15on-147\src.zip\org\bouncycastle\openpgp\examples die sechs .java-Dateien: KeyBasedFileProcessor.java, KeyBasedLargeFileProcessor.java, PBEFileProcessor.java, PGPExampleUtil.java, RSAKeyPairGenerator.java und SignedFileProcessor.java.
Um kurze einfache Kommandozeilen zu ermöglichen, entfernen Sie aus allen sechs kopierten .java-Dateien jeweils die erste Zeile (package org.bouncycastle.openpgp.examples;).
Ihr Verzeichnis sieht jetzt so aus:
[\MeinWorkspace\CryptoOpenPgpBouncyCastle] |- bcpg-jdk15on-147.jar |- bcprov-jdk15on-147.jar |- KeyBasedFileProcessor.java |- KeyBasedLargeFileProcessor.java |- PBEFileProcessor.java |- PGPExampleUtil.java |- RSAKeyPairGenerator.java '- SignedFileProcessor.java
Kompilieren Sie und erstellen Sie eine beliebige Testdatei (wahlweise Text oder binär), zum Beispiel so:
javac -cp .;* *.java
echo Mein Text Abc Xyz > _MeineDatei.txt
Testen Sie die symmetrische Passwortverschlüsselung von Dateien:
java -cp .;* PBEFileProcessor -e -i _MeineDatei.txt "geheim"
del _MeineDatei.txt
java -cp .;* PBEFileProcessor -d _MeineDatei.txt.bpg "geheim"
type _MeineDatei.txt
Sehen Sie sich den Inhalt der verschlüsselten Datei _MeineDatei.txt.bpg an.
Generieren Sie ein RSA-Public-/Private-Key-Paar:
java -cp .;* RSAKeyPairGenerator -a "MeinName (BC-Test) <ich@myserver.de>" "Meine geheime BC-Passphrase 42"
dir *.asc
Durch den -a-Schalter werden nicht binäre, sondern stattdessen ASCII-Schlüsseldateien erstellt (pub.asc und secret.asc), so dass der öffentliche Schlüssel bequem per E-Mail versendet werden kann.
Testen Sie die asymmetrische Public-Key-Based-Verschlüsselung von Dateien:
java -cp .;* KeyBasedFileProcessor -e -i _MeineDatei.txt pub.asc
del _MeineDatei.txt
java -cp .;* KeyBasedFileProcessor -d _MeineDatei.txt.bpg secret.asc "Meine geheime BC-Passphrase 42"
type _MeineDatei.txt
Wenn Sie besonders große Dateien haben, sollten Sie den KeyBasedLargeFileProcessor bevorzugen:
java -cp .;* KeyBasedLargeFileProcessor -e -i _MeineDatei.txt pub.asc
del _MeineDatei.txt
java -cp .;* KeyBasedLargeFileProcessor -d _MeineDatei.txt.bpg secret.asc "Meine geheime BC-Passphrase 42"
type _MeineDatei.txt
Falls Sie nicht nur verschlüsseln, sondern auch signieren wollen:
Signieren ("-s") und verschlüsseln ("-e"):
java -cp .;* SignedFileProcessor -s _MeineDatei.txt secret.asc "Meine geheime BC-Passphrase 42"
java -cp .;* KeyBasedFileProcessor -e -i _MeineDatei.txt.bpg pub.asc
dir _MeineDatei.*
Entschlüsseln ("-d") und verifizieren ("-v"):
del _MeineDatei.txt
del _MeineDatei.txt.bpg
java -cp .;* KeyBasedFileProcessor -d _MeineDatei.txt.bpg.bpg secret.asc "Meine geheime BC-Passphrase 42"
java -cp .;* SignedFileProcessor -v _MeineDatei.txt.bpg pub.asc
type _MeineDatei.txt
(Weiter unten finden Sie eine realistischere Kommandofolge, falls Versender und Empfänger verschiedene Personen sind.)
Beim Verschlüsseln sehr großer Dateien müssen Sie beim Aufruf von PBEFileProcessor, KeyBasedFileProcessor etc. mit dem "-Xmx"-Kommandozeilenparameter für ausreichend Heapspeicher sorgen (z.B.: java -Xmx1300M ...).
Beim Signieren sehr großer Dateien mit SignedFileProcessor sollten Sie unbedingt die Hinweise unter Bemerkungen zur Performance und Verarbeitung großer Dateien beachten.
Für den einfacheren Gebrauch können Sie die .jar-Libs und die .class-Dateien in eine einzige ausführbare .jar-Datei packen. Diese kann zum Beispiel mit dem Maven Assembly Plugin erzeugt werden.
Im Folgenden wird angenommen, dass Nachrichten zwischen zwei Teilnehmern ausgetauscht werden sollen, wobei der eine Teilnehmer GnuPG und der andere BC (Bouncy Castle Crypto API) verwendet. Dies ist möglich, weil sowohl GnuPG als auch BC kompatibel zum OpenPGP-Standard sind.
Dieses Kapitel setzt die beiden vorangegangenen Kapitel OpenPGP-Verschlüsselung mit GnuPG (GNU Privacy Guard) und OpenPGP-Verschlüsselung mit dem Bouncy Castle Crypto API fort.
Erstellen Sie eine beliebige Testdatei (wahlweise Text oder binär), zum Beispiel so:
echo Mein Text Abc Xyz > _MeineDatei.txt
Mit BC symmetrisch verschlüsseln ("-e") zu .bpg:
java -cp .;* PBEFileProcessor -e -i _MeineDatei.txt "geheim"
.bpg mit GnuPG entschlüsseln ("-d"):
del _MeineDatei.txt
gpg2 --batch --passphrase "geheim" -o _MeineDatei-mit-GnuPG-sym-decrypted.txt -d _MeineDatei.txt.bpg
type _MeineDatei-mit-GnuPG-sym-decrypted.txt
Mit GnuPG symmetrisch verschlüsseln ("-c") zu .gpg:
gpg2 --batch --passphrase "geheim" --cipher-algo AES --s2k-digest-algo SHA1 -c _MeineDatei.txt
.gpg mit BC entschlüsseln ("-d"):
del _MeineDatei.txt
java -cp .;* PBEFileProcessor -d _MeineDatei.txt.gpg "geheim"
type _MeineDatei.txt
Zur Vorbereitung müssen beide Teilnehmer jeweils in ihren Systemen Public/Private-Key-Paare generieren und anschließend jeweils den eigenen öffentlichen Public-Key veröffentlichen bzw. dem anderen Teilnehmer zur Verfügung stellen. Die geheimen Private-Keys dürfen niemals jemand anderem mitgeteilt werden.
BC-Teilnehmer:
Die Schlüsselgenerierung (mit "RSAKeyPairGenerator") ist weiter oben beschrieben. Sie haben als Ergebnis das Public/Private-Key-Paar pub.asc und secret.asc erhalten. pub.asc veröffentlichen Sie (oder übertragen Sie zu Ihrem Partner).
GPG-Teilnehmer:
Die Schlüsselgenerierung (mit "gpg2 --gen-key") ist weiter oben beschrieben. Überzeugen Sie sich, dass der Schlüssel mit dem Kommentar "GPG-Test" noch gespeichert ist:
gpg2 --list-keys
Importieren Sie den fremden Public-Key des anderen Teilnehmers:
gpg2 --import pub.asc
gpg2 --list-keys
Exportieren Sie den eigenen Public-Key in die Datei GPG-Test-pubkey.asc:
gpg2 -ao GPG-Test-pubkey.asc --export "GPG-Test"
dir *.asc
GPG-Test-pubkey.asc veröffentlichen Sie (oder übertragen Sie zu Ihrem Partner).
Erstellen Sie eine beliebige Testdatei (wahlweise Text oder binär), zum Beispiel so:
echo Mein Text Abc Xyz > _MeineDatei.txt
Mit BC verschlüsseln ("-e") (mit dem fremden Public Key des Empfängers):
java -cp .;* KeyBasedFileProcessor -e -i _MeineDatei.txt GPG-Test-pubkey.asc
Mit GnuPG entschlüsseln ("-d") (mit dem eigenen geheimen Private Key):
gpg2 --batch --passphrase "Meine geheime GPG-Passphrase 42" -o _MeineDatei-mit-GnuPG-asym-decrypted.txt -d _MeineDatei.txt.bpg
type _MeineDatei-mit-GnuPG-asym-decrypted.txt
Mit GnuPG verschlüsseln ("-e" = encrypt, "-r" = recipient) (mit dem fremden Public Key des Empfängers):
if exist _MeineDatei.txt.gpg del _MeineDatei.txt.gpg
gpg2 --trust-model always -er "BC-Test" _MeineDatei.txt
Mit BC entschlüsseln ("-d") (mit dem eigenen geheimen Private Key):
del _MeineDatei.txt
java -cp .;* KeyBasedFileProcessor -d _MeineDatei.txt.gpg secret.asc "Meine geheime BC-Passphrase 42"
type _MeineDatei.txt
Mit BC signieren ("-s") und verschlüsseln ("-e"):
del *.bpg
java -cp .;* SignedFileProcessor -s _MeineDatei.txt secret.asc "Meine geheime BC-Passphrase 42"
java -cp .;* KeyBasedFileProcessor -e -i _MeineDatei.txt.bpg GPG-Test-pubkey.asc
dir _MeineDatei.*
Mit GnuPG entschlüsseln und verifizieren (beides mit "-d"):
del _MeineDatei.txt
del _MeineDatei.txt.bpg
gpg2 --batch --passphrase "Meine geheime GPG-Passphrase 42" -o _MeineDatei-mit-GPG-asym-decr-verif.bpg -d _MeineDatei.txt.bpg.bpg
gpg2 -o _MeineDatei-mit-GPG-asym-decr-verif.txt -d _MeineDatei-mit-GPG-asym-decr-verif.bpg
type _MeineDatei-mit-GPG-asym-decr-verif.txt
Mit GnuPG signieren ("-s") und verschlüsseln ("-e") ("-r" = recipient):
gpg2 --batch --default-key "GPG-Test" --passphrase "Meine geheime GPG-Passphrase 42" -s _MeineDatei.txt
gpg2 --trust-model always -er "BC-Test" _MeineDatei.txt.gpg
dir _MeineDatei.*
Mit BC entschlüsseln ("-d") und verifizieren ("-v"):
del _MeineDatei.txt
del _MeineDatei.txt.gpg
java -cp .;* KeyBasedFileProcessor -d _MeineDatei.txt.gpg.gpg secret.asc "Meine geheime BC-Passphrase 42"
java -cp .;* SignedFileProcessor -v _MeineDatei.txt.gpg GPG-Test-pubkey.asc
type _MeineDatei.txt
Mit dem E-Mail-Roboter "Adele (adele@gnupp.de)" können Sie erste Verschlüsselungsversuche durchführen.
Erzeugen Sie ein Schlüsselpaar, wie oben für GnuPG und BC beschrieben ist. Im Falle von GnuPG exportieren Sie den öffentlichen Schlüssel:
gpg2 -ao GPG-Test-pubkey.asc --export "GPG-Test"
Klicken Sie mit der rechten Maustaste auf die Datei des öffentlichen Schlüssels (z.B. GPG-Test-pubkey.asc) und wählen Sie "Senden an | E-Mail-Empfänger", um das Zertifikat als E-Mail-Anhang zu versenden.
Tragen Sie im E-Mail-Programm als Empfänger adele@gnupp.de ein und versenden Sie die E-Mail. Sie erhalten nach kurzer Zeit eine mit Ihrem Public Key verschlüsselte Antwort-E-Mail.
Speichern Sie den Antworttext von "-----BEGIN PGP MESSAGE-----" bis inklusive "-----END PGP MESSAGE-----" in die Textdatei AdelesEmail.asc. Entschlüsseln Sie im Falle von GnuPG mit:
gpg2 --batch --passphrase "Meine geheime GPG-Passphrase 42" -o AdelesEmail.asc.txt -d AdelesEmail.asc
Die entschlüsselte Antwort in AdelesEmail.asc.txt enthält einen kurzen Text und den Public Key von Adele.
Extrahieren Sie den Public Key von "-----BEGIN PGP PUBLIC KEY BLOCK-----" bis inklusive "-----END PGP PUBLIC KEY BLOCK-----" und speichern ihn in die Textdatei AdelesPublicKey.asc. Importieren Sie Adeles Public Key:
gpg2 --import AdelesPublicKey.asc
gpg2 --list-keys
Speichern Sie eine beliebige Nachricht in einer Textdatei, zum Beispiel so:
echo Hallo Adele, hier ist meine verschluesselte Nachricht > MeineNachrichtAnAdele.txt
Verschlüsseln Sie diese mit dem Public Key von Adele:
gpg2 --trust-model always -a -er "Adele" MeineNachrichtAnAdele.txt
Es entsteht die verschlüsselte Datei MeineNachrichtAnAdele.txt.asc. Versenden Sie diese Datei an adele@gnupp.de.
Nach kurzer Zeit antwortet Adele. Extrahieren Sie aus dem Antworttext den Teil von "-----BEGIN PGP MESSAGE-----" bis inklusive "-----END PGP MESSAGE-----" in die Datei AdelesAntwort.asc und entschlüsseln Sie diese Datei:
gpg2 --batch --passphrase "Meine geheime GPG-Passphrase 42" -o AdelesAntwort.asc.txt -d AdelesAntwort.asc
type AdelesAntwort.asc.txt
Adele hat die verschlüsselte Nachricht entschlüsselt und zurückgesendet. Das bedeutet: Adele kann Nachrichten entschlüsseln, die Sie mit Adeles Public Key verschlüsselt haben, und Sie können Nachrichten entschlüsseln, die Adele mit Ihrem Public Key verschlüsselt hat.
Das folgende Verschlüsselungsprogrammierbeispiel hat die Eigenschaften:
Führen Sie folgende Schritte durch:
Erstellen Sie ein Projektverzeichnis:
cd \MeinWorkspace
md CryptoWinZipAES
cd CryptoWinZipAES
Downloaden Sie winzipaes (z.B. winzipaes_src_20110911.zip) in das neue Projektverzeichnis und führen Sie folgende Kommandos aus:
jar xf winzipaes_src_20110911.zip
md src\main\java
move src\de src\main\java
md src\main\java\de\meinefirma\meinprojekt\crypto
md src\test\java\de\meinefirma\meinprojekt\crypto
tree /F
Erzeugen Sie im CryptoWinZipAES-Projektverzeichnis die Maven-Projektkonfigurationsdatei: pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>de.meinefirma.meinprojekt</groupId> <artifactId>CryptoWinZipAES</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>CryptoWinZipAES</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.4</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.12</version> <configuration> <argLine>-Xmx1300M</argLine> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.47</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies> </project>
Erzeugen Sie im Verzeichnis src\main\java\de\meinefirma\meinprojekt\crypto die Klasse: CryptoWinZipAES.java
package de.meinefirma.meinprojekt.crypto; import java.io.*; import java.util.List; import java.util.zip.DataFormatException; import de.idyl.winzipaes.*; import de.idyl.winzipaes.impl.*; /** * @see http://code.google.com/p/winzipaes * @see http://www.bouncycastle.org * @see http://www.winzip.com/aes_info.htm */ public class CryptoWinZipAES { public static void main( String[] args ) throws IOException, DataFormatException { if( args.length == 4 ) { if( args[0].toLowerCase().startsWith( "e" ) || args[0].toLowerCase().startsWith( "z" ) ) { zipAndEncrypt( args[1], args[2], args[3] ); return; } else if( args[0].toLowerCase().startsWith( "d" ) || args[0].toLowerCase().startsWith( "u" ) ) { unzipAndDecrypt( args[1], args[2], args[3] ); return; } } System.out.println( "Fehler: Es werden vier Parameter benoetigt:\n" + "Zum Zippen und Verschluesseln:\n" + " zip SrcFile DstZipFile Passwort\n" + "Zum Unzippen und Entschluesseln:\n" + " unzip SrcZipFile DstDir Passwort\n" ); } public static void zipAndEncrypt( String srcFile, String dstZipFile, String password ) throws IOException { AesZipFileEncrypter.zipAndEncrypt( new File( srcFile ), new File( dstZipFile ), password, new AESEncrypterBC() ); } public static void zipAndEncrypt( List<String> srcFiles, String dstZipFile, String password ) throws IOException { AesZipFileEncrypter aesZip = new AesZipFileEncrypter( dstZipFile, new AESEncrypterBC() ); for( String srcFile : srcFiles ) { aesZip.add( new File( srcFile ), password ); } aesZip.close(); } public static void unzipAndDecrypt( String srcZipFile, String dstDir, String password ) throws IOException, DataFormatException { String dir = ( dstDir == null ) ? "" : ( dstDir.endsWith( "/" ) || dstDir.endsWith( "\\" ) ) ? dstDir : (dstDir + "/"); AesZipFileDecrypter decrypter = new AesZipFileDecrypter( new File( srcZipFile ), new AESDecrypterBC() ); List<ExtZipEntry> zipEntries = decrypter.getEntryList(); for( ExtZipEntry zipEntry : zipEntries ) { File outFile = new File( dir + zipEntry.getName() ); String parentPath = outFile.getParent(); if( parentPath != null && parentPath.trim().length() > 0 ) { File f = new File( parentPath ); if( !f.exists() ) { f.mkdirs(); } } decrypter.extractEntry( zipEntry, outFile, password ); } } }
Erzeugen Sie im Verzeichnis src\test\java\de\meinefirma\meinprojekt\crypto den JUnit-Test: CryptoWinZipAESTest.java
package de.meinefirma.meinprojekt.crypto; import java.io.*; import java.util.*; import java.util.zip.DataFormatException; import org.junit.*; public class CryptoWinZipAESTest { @Test public void testWinZipAES() throws IOException, DataFormatException { String srcFile = "target/MeineDatei.txt"; String zipFile = "target/MeineVerschluesselteZipDatei.zip"; String dstDir = "target/testergebnis"; String dstFile = dstDir + File.separator + srcFile; String password = "geheim"; String text = "Blablupp \u00E4\u00F6\u00FC\u00DF\u20AC"; // Umlaute und Eurozeichen String charEnc = "UTF-8"; BufferedWriter out = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( srcFile ), charEnc ) ); try { out.write( text ); } finally { out.close(); } CryptoWinZipAES.zipAndEncrypt( srcFile, zipFile, password ); Assert.assertTrue( new File( zipFile ).exists() ); CryptoWinZipAES.unzipAndDecrypt( zipFile, dstDir, password ); Assert.assertTrue( new File( dstFile ).exists() ); List<String> ss = new ArrayList<String>(); BufferedReader in = new BufferedReader( new InputStreamReader( new FileInputStream( dstFile ), charEnc ) ); try { String line; while( (line = in.readLine()) != null ) { ss.add( line ); } } finally { in.close(); } Assert.assertEquals( 1, ss.size() ); Assert.assertEquals( text, ss.get( 0 ) ); } }
Die Projektstruktur sieht jetzt so aus:
cd \MeinWorkspace\CryptoWinZipAES
tree /F
[\MeinWorkspace\CryptoWinZipAES] |- [src] | |- [main] | | '- [java] | | '- [de] | | |- [idyl] | | | '- [winzipaes] | | | |- [impl] | | | | '- ... | | | |- AesZipFileDecrypter.java | | | '- AesZipFileEncrypter.java | | '- [meinefirma] | | '- [meinprojekt] | | '- [crypto] | | '- CryptoWinZipAES.java | '- [test] | '- [java] | '- [de] | '- [meinefirma] | '- [meinprojekt] | '- [crypto] | '- CryptoWinZipAESTest.java |- pom.xml '- winzipaes_src_20110911.zip
Führen Sie den JUnit-Test aus:
mvn clean test
Der Test läuft fehlerfrei durch und im target-Verzeichnis entstehen folgende Dateien:
[\MeinWorkspace\CryptoWinZipAES] |- [...] | '- ... '- [target] |- [...] | '- ... |- [testergebnis] | '- [target] | '- MeineDatei.txt . . . . . . . . [Ergebnis der Entschlüsselung] |- MeineDatei.txt . . . . . . . . . . . . [ursprüngliche Originaldatei] '- MeineVerschluesselteZipDatei.zip . . . [verschlüsselte Zip-Datei]
Die ursprüngliche Originaldatei und das Ergebnis der Entschlüsselung stimmen überein:
notepad target\MeineDatei.txt
notepad target\testergebnis\target\MeineDatei.txt
Sie können die verschlüsselte Datei auch mit WinZip entpacken. Klicken Sie entweder mit der rechten Maustaste auf MeineVerschluesselteZipDatei.zip oder führen Sie aus:
C:\Programme\WinZip\WinZip32.exe target\MeineVerschluesselteZipDatei.zip
WinZip fragt dann nach dem Passwort.
Auch diverse andere Programme unterstützen diese Art der Verschlüsselung, zum Beispiel 7-Zip. Wenn Sie das Passwort kennen, erfolgt eine Entschlüsselung entweder per rechter Maustaste oder mit dem Kommando:
C:\Programme\7-Zip\7z.exe x -oZielverzeichnis -pgeheim target\MeineVerschluesselteZipDatei.zip
SignedFileProcessor.java aus bcpg-jdk15on-147.zip arbeitet bei großen Dateien extrem langsam. Signieren und Verifizieren können in der Summe je nach PC bei einer 1-GByte-XML-Datei über 1 Stunde dauern.
Durch folgende einfache Maßnahme erreichen Sie eine deutliche Verbesserung: Kopieren Sie SignedFileProcessor.java nach SignedFileProcessorBuffered.java. Ersetzen Sie in SignedFileProcessorBuffered.java überall:
FileInputStream ... = new FileInputStream( ... )
durch
InputStream ... = new BufferedInputStream( new FileInputStream( ... ) )
und
FileOutputStream ... = new FileOutputStream( ... )
durch
OutputStream ... = new BufferedOutputStream( new FileOutputStream( ... ) )
Ergänzen Sie entsprechende "import java.io.Buffered..."-Anweisungen und ersetzen Sie "class SignedFileProcessor" durch "class SignedFileProcessorBuffered".
jar cvfM MeineGrosseDatei.zip MeineGrosseDatei
... signieren/verschlüsseln mit MeineGrosseDatei.zip statt MeineGrosseDatei ...
... beim Empfänger nach dem Entschlüsseln zuletzt:
jar xf MeineGrosseDatei.zip
Entschlüsseln mit "-d" verbraucht wenig Heap-Speicher. Aber Verschlüsseln mit "-e" benötigt bei großen Dateien viel Heap-Speicher, dessen Limit Sie mit "java -Xmx..." erweitern müssen. Dabei gibt es eine Besonderheit: Nicht nur bei einem zu niedrigen, sondern auch bei einem zu hohen Heap-Speicher-Limit kann KeyBasedFileProcessor eine OutOfMemoryError-Exception werfen. Letzteres passiert reproduzierbar bei einer unkomprimierten 1-GByte-XML-Datei mit folgender Kommandozeile (BC 1.46, JDK 1.6.0_26, Windows XP 32 Bit):
java -Xmx1400M -cp .;* KeyBasedFileProcessor -e Meine-1GByte-Datei.xml pub.asc
Mit "java -Xmx1300M ..." funktioniert alles fehlerfrei.
Der folgende Performancevergleich soll nur als erster Anhaltspunkt dienen.
Die Messwerte variieren stark, abhängig von PC-Performance, Dateiinhalt, Algorithmus, Schlüssellänge etc.
Bitte beachten Sie, dass der Vergleich insbesondere deshalb unfair ist, weil meistens die Defaulteinstellungen übernommen wurden,
die bei unterschiedlichen Tools zu unterschiedlichen Algorithmen und Schlüssellängen führen.
Auffällig ist die sehr schlechte Performance der Signierung beim Bouncy Castle Crypto API.
Programm, Algorithmus, Kommandozeile | i7-920, Windows XP, 32 Bit 1 GByte XML Verschlüsseln Entschlüsseln |
i7-950, Windows 7, 64 Bit 1 GByte XML Verschlüsseln Entschlüsseln | ||
---|---|---|---|---|
Keine Verschlüsselung, nur Zip-Komprimierung mit jar.exe: jar cvfM x.zip x jar xf x.zip |
34 s | 12 s | 26 s | 6 s |
GnuPG, symmetrisch: gpg2 --cipher-algo AES --s2k-digest-algo SHA1 -c ... |
43 s | 71 s | 37 s | 63 s |
GnuPG, asymmetrisch/hybrid: gpg2 -e ... |
42 s | 73 s | 36 s | 65 s |
GnuPG, asymmetrisch/hybrid, mit signieren, 1 Kommando: gpg2 -se ... |
99 s | 72 s | 86 s | 63 s |
GnuPG, asymmetrisch/hybrid, mit signieren, 2 Kommandos: gpg2 -s ... gpg2 -e ... |
71 s | 40 s | 61 s | 35 s |
Bouncy Castle, symmetrisch, ohne Integrity Check: java -Xmx1300M PBEFileProcessor -e ... |
38 s | 27 s | 29 s | 8 s |
Bouncy Castle, symmetrisch, mit Integrity Check: java -Xmx1300M PBEFileProcessor -e -i ... |
40 s | 29 s | 30 s | 10 s |
Bouncy Castle, asymmetr./hybrid, ohne Integrity Check: java -Xmx1300M KeyBasedFileProcessor -e ... |
35 s | 27 s | 29 s | 8 s |
Bouncy Castle, asymmetr./hybrid, mit Integrity Check: java -Xmx1300M KeyBasedFileProcessor -e -i ... |
40 s | 30 s | 30 s | 10 s |
Bouncy Castle, asymmetr./hybrid, mit Integr. Check, LargeFileProc.: java -Xmx1300M KeyBasedLargeFileProcessor -e -i ... |
39 s | 21 s | 28 s | 10 s |
Bouncy Castle, asymmetr./hybrid, signieren, vorher zippen, Buffered: jar cvfM ... java SignedFileProcessorBuffered -s ... java -Xmx1300M KeyBasedFileProcessor -e ... jar xf ... |
154 s | 103 s | 84 s | 40 s |
Bouncy Castle, asymmetr./hybrid, signieren, ohne Zip, Buffered: java SignedFileProcessorBuffered -s ... java -Xmx1300M KeyBasedFileProcessor -e ... |
840 s | 700 s | 377 s | 252 s |
Bouncy Castle, asymmetr./hybrid, signieren, ohne Zip, ohne Buff.: java SignedFileProcessor -s ... java -Xmx1300M KeyBasedFileProcessor -e ... |
1.800 s | 2.600 s | 1.700 s | 1.970 s |
Stream-Util für Bouncy-Castle, asymmetr./hybrid, mit Integr. Check: java -Xmx1300M OpenPgpBouncyCastleStreamUtilMain -e ... |
40 s | 30 s | 30 s | 10 s |
CryptoRsaAes, vorher zippen: jar cvfM ... java CryptoRsaAes -e ... jar xf ... |
63 s | 42 s | 54 s | 34 s |
CryptoSimple, vorher zippen: jar cvfM ... java CryptoSimple -e ... AES ... jar xf ... |
37 s | 18 s | 28 s | 9 s |
CryptoWinZipAES: java -Xmx3500M CryptoWinZipAES ... |
OutOfMemoryError: Java heap space | 83 s | 31 s |
Erläuterungen:
Erläuterungen zu den verwendeten Programmen und Kommandozeilen finden Sie unter GnuPG, Bouncy Castle, Kooperation GnuPG und BC, CryptoRsaAes, CryptoSimple und CryptoWinZipAES.
"Buffered" bedeutet: Statt der Original-SignedFileProcessor.java-Klasse aus bcpg-jdk15on-147.zip wird die oben beschriebene SignedFileProcessorBuffered.java verwendet.
"vorher zippen" bedeutet: Der Versender zip-komprimiert vor der Signierung/Verschlüsselung und der Empfänger entkomprimiert nach dem Entschlüsseln/Verifizieren, so wie oben beschrieben. Die angegebenen Zeitmesswerte sind inklusive der Zip- und Unzip-Zeiten.
"Stream-Util für Bouncy-Castle": Das CryptoOpenPgpBouncyCastleStream-Projekt mit der OpenPgpBouncyCastleStreamUtil-Klasse finden Sie in Crypto.zip. Darin finden Sie auch ein Beispiel, wie Streams vom FTP-Server entschlüsselt und Streams zum FTP-Server verschlüsselt werden können (in OpenPgpBouncyCastleStreamUtilFtpTest.java).