001: package snow.fileencryptor;
002:
003: import javax.swing.*;
004: import javax.swing.filechooser.*;
005: import java.util.*;
006: import java.util.zip.*;
007: import java.security.*;
008: import java.security.spec.*;
009: import javax.crypto.spec.*;
010: import javax.crypto.*;
011: import snow.utils.gui.*;
012: import snow.utils.storage.*;
013: import snow.crypto.*;
014: import snow.Language.Language;
015: import java.io.*;
016:
017: /** Encrypt/decrypt a file and some utilities.
018: */
019: public final class SimpleFileEncryptor {
020: private SimpleFileEncryptor() {
021: }
022:
023: /** Overwrites the file content with a random buffer.
024: */
025: public static void wipeFile(File f, int bufferSize,
026: ProgressModalDialog progressDialog, int passes) {
027: long count = 0; // for the progress
028: RandomAccessFile raf = null;
029: for (int p = 0; p < passes; p++) {
030: try {
031: raf = new RandomAccessFile(f, "rw");
032:
033: raf.seek(0);
034: byte[] buffer = new byte[bufferSize];
035: int blocks = (int) (raf.length() / bufferSize) + 1;
036: for (int i = 0; i < blocks; i++) {
037: fillRandom(buffer);
038: raf.write(buffer);
039: if (count % passes == 1) {
040: progressDialog.incrementProgress(1);
041: }
042: count++;
043: }
044: } catch (Exception e) {
045: e.printStackTrace();
046: } finally {
047: if (raf != null) {
048: try {
049: raf.close();
050: } catch (Exception ee) {
051: ee.printStackTrace();
052: }
053: }
054: }
055: }
056:
057: f.delete();
058: if (f.exists()) {
059: System.out.println("Cannot delete the file " + f);
060: f.deleteOnExit();
061: }
062: }
063:
064: public static void fillRandom(byte[] buf) {
065: for (int i = 0; i < buf.length; i++) {
066: buf[i] = (byte) (Math.random() * 256);
067: }
068: }
069:
070: public static void encryptDirectory(File in, File out,
071: boolean delete, SecretKey key, int bufferSize,
072: ProgressModalDialog progressDialog) throws Exception {
073: // 1) Collect all directory files and zip them
074: //
075: List<File> allFilesToZip = new ArrayList<File>();
076: FileUtils.getAllFilesRecurse(in, allFilesToZip, false, false);
077:
078: ZipOutputStream zos = null;
079: File tempZipFile = new File(in.getParentFile(), "tempZip.zip");
080: tempZipFile.deleteOnExit();
081: try {
082: FileOutputStream fos = new FileOutputStream(tempZipFile);
083: zos = new ZipOutputStream(fos);
084: for (File f : allFilesToZip) {
085: FileUtils.addToZip(zos, f, f.getAbsolutePath());
086: }
087: } catch (Exception ze) {
088: throw ze;
089: } finally {
090: FileUtils.closeIgnoringExceptions(zos);
091: }
092:
093: // 2) encrypt the zip
094: //
095: try {
096: encryptFile(tempZipFile, out, key, bufferSize,
097: progressDialog);
098: } catch (Exception ex) {
099: throw ex;
100: }
101:
102: // 3) delete the zip file
103: //
104: wipeFile(tempZipFile, bufferSize, progressDialog, 2);
105:
106: if (delete) {
107: for (File f : allFilesToZip) {
108: wipeFile(f, bufferSize, progressDialog, 2);
109: }
110: }
111:
112: }
113:
114: /** The progress must be set so that it can be incremented by 1 on each buffersize read.
115: */
116: public static void encryptFile(File in, File out, SecretKey key,
117: int bufferSize, final ProgressModalDialog progressDialog)
118: throws Exception {
119: Cipher cipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
120: cipher.init(Cipher.ENCRYPT_MODE, key);
121:
122: FileInputStream fis = null;
123: FileOutputStream fos = null;
124: DataOutputStream dos;
125: GZIPOutputStream zos;
126: CipherOutputStream cos;
127: try {
128: fis = new FileInputStream(in);
129: fos = new FileOutputStream(out);
130: dos = new DataOutputStream(fos);
131: dos.writeInt(2);
132: dos.writeUTF("B");
133: dos.writeInt(key.getEncoded().length);
134: SecretKeyID ski = SecretKeyUtilities.computeSignature(key);
135: dos.write(ski.signature, 0, 4);
136:
137: cos = new CipherOutputStream(dos, cipher);
138: zos = new GZIPOutputStream(cos);
139:
140: byte[] buf = new byte[bufferSize];
141: int read;
142: while ((read = fis.read(buf)) != -1) {
143: zos.write(buf, 0, read);
144: if (progressDialog != null) {
145: progressDialog.incrementProgress(1);
146: }
147: }
148:
149: zos.close();
150: } catch (Exception e) {
151: throw e;
152: } finally {
153: FileUtils.closeIgnoringExceptions(fos);
154: FileUtils.closeIgnoringExceptions(fis);
155: out.setLastModified(in.lastModified());
156: }
157: }
158:
159: public static void decryptFile(File in, File out, SecretKey key,
160: int bufferSize, ProgressModalDialog progressDialog)
161: throws Exception {
162: FileOutputStream fos = null;
163: FileInputStream fis = null;
164: DataInputStream dis = null;
165: CipherInputStream cis = null;
166: GZIPInputStream zis = null;
167:
168: try {
169: fos = new FileOutputStream(out);
170:
171: fis = new FileInputStream(in);
172: dis = new DataInputStream(fis);
173:
174: // (clear) header read
175: //
176: int version = dis.readInt();
177: if (version == 2) {
178: // custom pass (or default, if not set...
179: String algo = dis.readUTF();
180: int keyLength = dis.readInt();
181: byte[] sign = new byte[4];
182: dis.readFully(sign, 0, 4);
183: SecretKeyID ski = new SecretKeyID(sign, keyLength);
184: if (!SecretKeyUtilities.computeSignature(key).equals(
185: ski)) {
186: throw new BadPasswordException(Language
187: .translate("Bad key"));
188: }
189: }
190:
191: else {
192: throw new Exception("Bad file version " + version);
193: }
194:
195: // read cipher vector part
196: //
197:
198: Cipher cipher = Cipher
199: .getInstance("Blowfish/ECB/PKCS5Padding");
200: cipher.init(Cipher.DECRYPT_MODE, key);
201:
202: cis = new CipherInputStream(dis, cipher);
203: zis = new GZIPInputStream(cis);
204:
205: byte[] buf = new byte[bufferSize];
206: int read = -1;
207: while ((read = zis.read(buf)) != -1) {
208: fos.write(buf, 0, read);
209: progressDialog.incrementProgress(1);
210: }
211:
212: zis.close();
213: } catch (Exception e) {
214: throw e;
215: } finally {
216: FileUtils.closeIgnoringExceptions(fis);
217: FileUtils.closeIgnoringExceptions(fos);
218:
219: out.setLastModified(in.lastModified());
220: }
221: }
222:
223: /** @return null if ok, an error message otherwise
224: */
225: @edu.umd.cs.findbugs.annotations.CheckForNull
226: public static String verifyEncryptedFile(File source,
227: File encrypted, SecretKey key, int bufferSize,
228: ProgressModalDialog progressDialog) throws Exception {
229:
230: FileInputStream fis = null;
231: DataInputStream dis = null;
232: CipherInputStream cis = null;
233: GZIPInputStream zis = null;
234:
235: try {
236: fis = new FileInputStream(encrypted);
237: dis = new DataInputStream(fis);
238:
239: // (clear) header read
240: //
241: int version = dis.readInt();
242: if (version == 2) {
243: // custom pass (or default, if not set...)
244: String algo = dis.readUTF();
245: int keyLength = dis.readInt();
246: byte[] sign = new byte[4];
247: dis.readFully(sign, 0, 4);
248: SecretKeyID ski = new SecretKeyID(sign, keyLength);
249: if (!SecretKeyUtilities.computeSignature(key).equals(
250: ski)) {
251: throw new BadPasswordException(Language
252: .translate("Bad key"));
253: }
254: }
255:
256: else {
257: throw new Exception("Bad file version " + version);
258: }
259:
260: // read cipher vector part
261: //
262:
263: Cipher cipher = Cipher
264: .getInstance("Blowfish/ECB/PKCS5Padding");
265: cipher.init(Cipher.DECRYPT_MODE, key);
266:
267: cis = new CipherInputStream(dis, cipher);
268: zis = new GZIPInputStream(cis);
269:
270: byte[] buf = new byte[bufferSize];
271: int read = -1;
272: long decrypetdLength = 0;
273: MessageDigest md = MessageDigest.getInstance("SHA1");
274: while ((read = zis.read(buf)) != -1) {
275: progressDialog.incrementProgress(1);
276: md.update(buf, 0, read);
277: decrypetdLength += read;
278: }
279: zis.close();
280:
281: // verif 1
282: if (decrypetdLength != source.length()) {
283: return "Decrpyted file has different length !";
284: }
285:
286: // verif2
287: byte[] md1 = md.digest();
288: byte[] md2 = calculateSHA1Hash(source);
289:
290: if (!Arrays.equals(md1, md2)) {
291: return "Decrypted file has different checksum !";
292: }
293: } catch (Exception e) {
294: throw e;
295: } finally {
296: FileUtils.closeIgnoringExceptions(fis);
297: }
298: return null;
299: }
300:
301: public static byte[] calculateSHA1Hash(File f) throws Exception {
302: FileInputStream fis = null;
303: MessageDigest md = MessageDigest.getInstance("SHA1");
304: try {
305: fis = new FileInputStream(f);
306: byte[] buf = new byte[256];
307: int read = 0;
308: while ((read = fis.read(buf)) != -1) {
309: md.update(buf, 0, read);
310: }
311: return md.digest();
312: } catch (Exception e) {
313: throw e;
314: } finally {
315: FileUtils.closeIgnoringExceptions(fis);
316: }
317: }
318:
319: /** @return the keyID of the enciphered file
320: */
321: public static SecretKeyID getKeyIDFromFile(File in)
322: throws Exception {
323: FileInputStream fis = null;
324: DataInputStream dis = null;
325: try {
326: fis = new FileInputStream(in);
327: dis = new DataInputStream(fis);
328: // header read
329: int version = dis.readInt();
330: if (version >= 2) {
331: String algo = dis.readUTF(); // actually ignored
332: int keyLength = dis.readInt();
333: byte[] sign = new byte[4];
334: dis.readFully(sign, 0, 4);
335: dis.close();
336:
337: SecretKeyID ski = new SecretKeyID(sign, keyLength);
338: return ski;
339: } else {
340: throw new Exception(Language.translate("Bad version %",
341: "" + version));
342: }
343: } catch (Exception e) {
344: throw e;
345: } finally {
346: FileUtils.closeIgnoringExceptions(fis);
347: }
348:
349: }
350:
351: }
|