/* * CryptoEngine.java * * * ==================================================== Professional Data Security (PDS) http://crypto.brettlee.com ==================================================== Copyright (c) 2009-2011, Brett Lee All rights reserved. Portions Copyright (C) 1995-2008, Sun Microsystems, Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ORGANIZATION nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ============================================================================= */ package com.brettlee.crypto; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StreamCorruptedException; import java.security.InvalidKeyException; import java.security.Key; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.ProgressMonitorInputStream; class CryptoEngine implements Runnable { CryptoFile cryptoFile; String newline = System.getProperty("line.separator"); String fileSeparator = java.io.File.separator; // For DES byte[] ivBytesDES = new byte[] { 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01 }; // For AES byte[] ivBytesAES = new byte[] { 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01 }; byte[] ivBytes; // Our constructors public CryptoEngine() { } public CryptoEngine( CryptoFile cryptoFilePassed ) { cryptoFile = cryptoFilePassed; } /////////////////////////////////////////////////////////////////////// /** * Threaded instances start() here * */ /////////////////////////////////////////////////////////////////////// public void run() { if ( cryptoFile.getAction() == 0 ) { // encrypt encrypt( cryptoFile ); } else if ( cryptoFile.getAction() == 1 ) { // decrypt decrypt( cryptoFile ); } else { System.out.println("Unknown encryption action in CryptoEngine.run()"); } } /////////////////////////////////////////////////////////////////////// /** * Encrypt * */ /////////////////////////////////////////////////////////////////////// boolean encrypt ( CryptoFile cryptoFile ) { CryptoFileExternal cryptoFileExternal; CipherOutputStream cos = null; ObjectOutputStream oos = null; // Begin // System.out.println(newline + "Beginning CryptoEngine.encrypt() ..."); // Print current state // // System.out.println("CryptoFile File : " + cryptoFile.getFullyPathedFileName() ); // System.out.println("CryptoFile KeyStore Name : " + cryptoFile.getKsName() ); // System.out.print("KeyStore Passphrase: "); // for ( int i=0; i < cryptoFile.getKsPass().length; i++ ) { // System.out.print( cryptoFile.getKsPass()[i] ); // } // System.out.println(); // System.out.println("CryptoFile Key Alias : " + cryptoFile.getKeyAlias() ); // System.out.print("Key Passphrase: "); // for ( int i=0; i < cryptoFile.getKeyPass().length; i++ ) { // System.out.print( cryptoFile.getKeyPass()[i] ); // } // System.out.println(); // Get the Key // // System.out.println("Executing CryptoKey().getKey() ..."); Key skey; try { skey = new CryptoKey().getKey ( cryptoFile ); } catch (Exception e) { System.out.println("Got an Exception getting the Key: " + e.getMessage()); if (cryptoFile instanceof CryptoFileExternal) { showError("encrypting"); } return false; } // System.out.println("Returned from CryptoKey().getKey() ..."); Cipher cipher; BufferedWriter bw = null; try { // Determine the cipher type String algorithm = skey.getAlgorithm(); // System.out.println("Algorithm: " + algorithm); // Create Algorithm specific KeySpec from the Secret Key byte[] rawBytes = skey.getEncoded(); SecretKeySpec skeySpec = new SecretKeySpec(rawBytes, algorithm ); // Create IV (spec) for the cipher chaining if (algorithm.equals("AES")) { ivBytes = ivBytesAES; } else { ivBytes = ivBytesDES; } IvParameterSpec ivSpec = new IvParameterSpec( ivBytes ); // Instantiate the cipher // System.out.println("Creating the cipher using algorithm: " + algorithm); cipher = Cipher.getInstance( algorithm + "/CBC/PKCS5Padding" ); // Initialize the cipher // This is where the 128-bit key size limit of standard Java is seen // - you can make a 192/256 bit key, but not a cipher to use it // System.out.println("Initializing the cipher..."); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec); } catch (InvalidKeyException e) { System.out.println("Unable to create the cipher."); System.out.println("Need Unlimited Strength Policy?"); JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Unable to create the cipher." + newline + newline + "Are you attempting to encrypt using an Unlimited Strength Key" + newline + "on a system that does not have the Unlimited Strength Policy?", "Unable to create the cipher", JOptionPane.WARNING_MESSAGE); return false; } catch (Exception e) { System.out.println("Error encrypting: " + e ); // e.printStackTrace(); showError("encrypting"); return false; } // ************************************************************* // We have the cipher, we have a key. We are a Crypto Engine. // // Begin Base64 block: both Native and, if explicitly requested, External files. // // Do we need to write Base64 ? // boolean writeBase64 = false; BufferedInputStream bis = null; ProgressMonitorInputStream pms = null; // nested try block, so we can "bw.close()" try { // Do we need to write Base64 ? // if (cryptoFile instanceof CryptoFileNative) { writeBase64 = true; bw = new BufferedWriter( new FileWriter( cryptoFile.getFullyPathedFileName() )); } else if ( cryptoFile instanceof CryptoFileExternal) { cryptoFileExternal = (CryptoFileExternal) cryptoFile; if ( cryptoFileExternal.getEncryptedOutputFormat() == CryptoFileExternal.kBASE64 ) { // System.out.println("External: Writing with Base64..."); writeBase64 = true; bw = new BufferedWriter( new FileWriter( cryptoFileExternal.getEncFilePath() )); } } else { // Should not get here System.out.println("Unknown type of CryptoFile..."); return false; } // Should the "newline" here be replaced with "/" for better Unix / Win interop? if ( writeBase64 ) { // Write the Base64 header // System.out.println("Writing metadata"); bw.write("//" + newline); bw.write("// Professional Data Security (PDS) ----------------------------------------" + newline); bw.write("// Copyright (C) 2009-2011, Brett Lee --------------------------------------" + newline); bw.write("// All rights reserved -----------------------------------------------------" + newline); bw.write("// http://crypto.brettlee.com ----------------------------------------------" + newline); bw.write("//" + newline); bw.write("//" + newline); bw.write("// BEGIN METADATA ----------------------------------------------------------" + newline); bw.write("KeyStore#" + StateObject.pathToState( cryptoFile.getKsName(), false ) + newline); bw.write("Alias#" + cryptoFile.getKeyAlias() + newline); if (cryptoFile instanceof CryptoFileNative) { bw.write("CryptoFile#NATIVE" + newline); bw.write("CryptoFileNativeFormat#DOCUMENT" + newline); } bw.write("// END METADATA ------------------------------------------------------------" + newline); bw.write("//" + newline); bw.write("//" + newline); bw.write("// ENCODED DATA FOLLOWS: ---------------------------------------------------" + newline); bw.write( newline ); bw.flush(); if (bw != null) { bw.close(); } // System.out.println("Done writing metadata"); if (cryptoFile instanceof CryptoFileNative) { // Be your true self, cryptoFile CryptoFileNative cryptoFileNative = (CryptoFileNative) cryptoFile; // Debug the bytes to encrypt // // System.out.println("Document size is: " + cryptoFile.getDocument().getLength()); // byte toEncrypt[] = // cryptoFileNative.getDocument().getText(0, // cryptoFile.getDocument().getLength()).getBytes(); // System.out.println("Text to encrypt:: "); // for (int i = 0; i < toEncrypt.length; i++) { // System.out.print( (char)toEncrypt[i] ); // } // System.out.println(); // // // // Scrub cleartext // for (int i = 0; i < toEncrypt.length; i++) { toEncrypt[i] = '0'; } // Write the bytes // // System.out.println("About to Encrypt Native..."); // Encrypt // ======================================================= // cos = new CipherOutputStream( // new BufferedOutputStream( // new Base64.OutputStream( // new BufferedOutputStream( // new FileOutputStream( // cryptoFileNative.getFullyPathedFileName(), true ) // ))), cipher); // cos.write( // cryptoFileNative.getActiveDocument().getText( // 0, cryptoFileNative.getActiveDocument().getLength() // ).getBytes() ); oos = new ObjectOutputStream ( new BufferedOutputStream( new CipherOutputStream( new BufferedOutputStream( new Base64.OutputStream( new BufferedOutputStream( new FileOutputStream( cryptoFileNative.getFullyPathedFileName(), true ) ) ) ), cipher ) ) ); oos.writeObject( cryptoFileNative.getActiveDocument() ); oos.flush(); oos.close(); // System.out.println("Done Encrypting Native..."); } else if ( cryptoFile instanceof CryptoFileExternal) { // CryptoFileExternal has requested Base64. // Be your true self, cryptoFileExternal cryptoFileExternal = (CryptoFileExternal) cryptoFile; // System.out.println("Instance is CryptoFileExternal"); // System.out.println("Input File: " + cryptoFileExternal.getFullyPathedFileName() ); // System.out.println("Output File: " + cryptoFileExternal.getEncFilePath() ); // Verify the file size upper limit // Construct a BufferedReader for the FileInputStream bis = new BufferedInputStream ( new FileInputStream ( cryptoFileExternal.getFullyPathedFileName() ) ); long length = bis.available(); // System.out.println("File length: " + length); // System.out.println("About to verify support of file size..."); if ( length > Integer.MAX_VALUE) { System.out.println("Error: File is to large for this system..."); showError("encrypting"); return false; } // Attach a Progress Monitor to the Input Stream pms = new ProgressMonitorInputStream(null, "Encrypting: " + newline + cryptoFileExternal.getFullyPathedFileName() + newline + "To: " + newline + cryptoFileExternal.getEncFilePath(), bis); // System.out.println("About to Encrypt External..."); // Encrypt // ======================================================= cos = new CipherOutputStream( new BufferedOutputStream( new Base64.OutputStream( new BufferedOutputStream( new FileOutputStream( cryptoFileExternal.getEncFilePath(), true )))), cipher); byte [] buf = new byte[1024]; int n; while ( ( n = pms.read( buf ) ) != -1 ) { // System.out.println("Read...."); cos.write( buf , 0 , n ); } bis.close(); pms.close(); cos.flush(); cos.close(); // System.out.println("Done Encrypting External..."); } } } catch (InterruptedIOException e) { // No error - Job was canceled on the Progress Monitor. try { bis.close(); pms.close(); cos.close(); } catch (Exception ex) { System.out.println("Error closing file handle."); } if ( cryptoFile instanceof CryptoFileExternal ) { cryptoFileExternal = (CryptoFileExternal) cryptoFile; File f = new File(cryptoFileExternal.getEncFilePath()); if ( ! f.delete() ) { System.out.println("Cancelled job - Unable to delete : " + cryptoFileExternal.getEncFilePath()); } } return true; } catch (Exception e) { System.out.println("Error encrypting: " + e.getMessage() ); e.printStackTrace(); // External reports errors here - Native does based on the boolean returned // if (cryptoFile instanceof CryptoFileExternal) { showError("encrypting"); } try { bis.close(); pms.close(); cos.close(); } catch (Exception ex) { System.out.println("Error closing file handle."); } if ( cryptoFile instanceof CryptoFileExternal ) { cryptoFileExternal = (CryptoFileExternal) cryptoFile; File f = new File(cryptoFileExternal.getEncFilePath()); if ( ! f.delete() ) { System.out.println("Unable to delete : " + cryptoFileExternal.getEncFilePath()); } } return false; } // And finally, SUCCESS. // End the Base64 encoded section and report Success if External file. // if ( cryptoFile instanceof CryptoFileNative) { if ( ! ((CryptoFileNative) cryptoFile).setSavedDocument() ) { System.out.println("Unable to create Document savedDocument"); } return true; } else { cryptoFileExternal = (CryptoFileExternal) cryptoFile; // only show success/exit if external file was Base64 format. if ( cryptoFileExternal.getEncryptedOutputFormat() == CryptoFileExternal.kBASE64 ) { JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Success encrypting:" + newline + cryptoFile.getFullyPathedFileName() + newline + "To: " + newline + cryptoFileExternal.getEncFilePath(), "Success", JOptionPane.INFORMATION_MESSAGE); return true; } } //////////////////////////////////////////////////////////////////////////// // All Base64 encoded encryption is done and the thread has returned. // Only Binary Files and Zipped Directories (both are External files) remain. // System.out.println("Using CryptoFileExternal."); cryptoFileExternal = (CryptoFileExternal) cryptoFile; FileHeader fileHeader = new FileHeader(); DataHeader dataHeader = new DataHeader(); // System.out.println("Input File: " + cryptoFileExternal.getFullyPathedFileName() ); // System.out.println("Output File: " + cryptoFileExternal.getEncFilePath() ); ZipOutputStream zout = null; BufferedOutputStream bos = null; try { if ( cryptoFileExternal.getDeviceType() == CryptoFileExternal.kDEVICE_RAW ) { bos = new BufferedOutputStream( new FileOutputStream( cryptoFileExternal.getEncFilePath(), false ), 2*1024*1024 ); } else { bos = new BufferedOutputStream( new FileOutputStream( cryptoFileExternal.getEncFilePath(), false )); } if ( cryptoFileExternal.getEncryptedOutputFormat() == CryptoFileExternal.kBINARY ) { // Encrypting a FILE - Binary mode // Construct a BufferedReader for the FileInputStream // bis = new BufferedInputStream ( new FileInputStream ( cryptoFileExternal.getFullyPathedFileName() ) ); // Verify the file size upper limit // long length = bis.available(); // System.out.println("File length: " + length); // System.out.println("About to verify support of file size..."); if ( length > Integer.MAX_VALUE) { System.out.println("Error: File is to large for this system..."); showError("encrypting"); return false; } // Attach a Progress Monitor to the Input Stream pms = new ProgressMonitorInputStream(null, "Encrypting: " + newline + cryptoFileExternal.getFullyPathedFileName() + newline + "To: " + newline + cryptoFileExternal.getEncFilePath(), bis); // Write Header - Unencrypted // ======================================================= oos = new ObjectOutputStream( bos ); fileHeader.setVersion( 1 ); dataHeader.setVersion( 1 ); dataHeader.setDataFormat( 0 ); dataHeader.setKsPathAndName( cryptoFile.getKsName() ); // System.out.println("Data Header KeyStore: " + dataHeader.getKsName()); dataHeader.setKeyAlias( cryptoFile.getKeyAlias() ); // System.out.println("Data Header Key Alias: " + dataHeader.getKeyAlias()); oos.writeObject(new String("PDS")); oos.writeObject(fileHeader); oos.writeObject(dataHeader); oos.flush(); // Write Data - Encrypted // ======================================================= cos = new CipherOutputStream( bos, cipher ); byte [] buf = new byte[1024]; int n; while ( ( n = pms.read( buf ) ) != -1 ) { // System.out.println("Read...."); cos.write( buf , 0 , n ); } bis.close(); pms.close(); cos.flush(); cos.close(); oos.close(); bos.close(); } else if ( cryptoFileExternal.getEncryptedOutputFormat() == CryptoFileExternal.kZIP ) { // Encrypting into a ZIP file // System.out.println("Encrypting a directory in ZIP format."); // System.out.println("Encrypting: " + cryptoFileExternal.getFullyPathedFileName()); // System.out.println("Encrypting to: " + cryptoFileExternal.getEncFilePath()); // Write Headers // ======================================================= oos = new ObjectOutputStream( bos ); fileHeader.setVersion( 1 ); dataHeader.setVersion( 1 ); dataHeader.setDataFormat( 1 ); dataHeader.setKsPathAndName( cryptoFile.getKsName() ); // System.out.println("Data Header KeyStore: " + dataHeader.getKsName()); dataHeader.setKeyAlias( cryptoFile.getKeyAlias() ); // System.out.println("Data Header Key Alias: " + dataHeader.getKeyAlias()); oos.writeObject(new String("PDS")); oos.writeObject(fileHeader); oos.writeObject(dataHeader); oos.flush(); // Write Directory - Encrypted // ======================================================= if ( cryptoFileExternal.getDeviceType() == CryptoFileExternal.kDEVICE_RAW ) { zout = new ZipOutputStream( new BufferedOutputStream( new CipherOutputStream( bos, cipher ), 2*1024*1024 )); } else { zout = new ZipOutputStream( new BufferedOutputStream( new CipherOutputStream( bos, cipher ) )); } // System.out.println("Compression Level: " + cryptoFileExternal.getCompressionLevel() ); zout.setLevel( cryptoFileExternal.getCompressionLevel() ); zout.setMethod( ZipOutputStream.DEFLATED ); // Create File object from source directory File sourceDir = new File( cryptoFileExternal.getFullyPathedFileName() ); // Always append a slash (no Windows File.separator) // System.out.println("Adding the parent directory " + sourceDir.getName()); zout.putNextEntry(new ZipEntry(sourceDir.getName() + "/" )); // Zip the Directory if (! addDirectory(zout, sourceDir, sourceDir.getName() + "/", cryptoFileExternal) ) { oos.close(); zout.close(); File f = new File(cryptoFileExternal.getEncFilePath()); f.delete(); if ( StateObject.getFileChooserCancel() ) { // System.out.println("Canceled."); StateObject.setFileChooserCancel(false); return true; } return false; } zout.flush(); zout.close(); oos.close(); bos.close(); } else { // Should not get here System.out.println("Unknown output file format: " + cryptoFileExternal.getEncryptedOutputFormat()); return false; } } catch (InterruptedIOException e) { // No error - Directory Zip was canceled on the Progress Monitor. // try { bis.close(); pms.close(); oos.flush(); cos.close(); zout.close(); } catch (Exception ex) { System.out.println("Error closing file handle."); ex.printStackTrace(); } if ( ! (cryptoFileExternal.getDeviceType() == CryptoFileExternal.kDEVICE_RAW) ) { File f = new File(cryptoFileExternal.getEncFilePath()); if ( ! f.delete() ) { System.out.println("Cancelled job - Unable to delete : " + cryptoFileExternal.getEncFilePath()); } } return true; } catch (Exception e) { // Exceptions - either Binary file or Zip directory // System.out.println("Error encrypting: " + e.getMessage() ); e.printStackTrace(); showError("encrypting"); try { bis.close(); pms.close(); oos.flush(); cos.close(); zout.close(); } catch (Exception ex) { System.out.println("Error closing file handle."); } if ( ! (cryptoFileExternal.getDeviceType() == CryptoFileExternal.kDEVICE_RAW) ) { File f = new File(cryptoFileExternal.getEncFilePath()); if ( ! f.delete() ) { System.out.println("Unable to delete : " + cryptoFileExternal.getEncFilePath()); } } return false; } // And finally, SUCCESS encrypting (either Binary or Zip). JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Success encrypting:" + newline + cryptoFile.getFullyPathedFileName() + newline + "To: " + newline + cryptoFileExternal.getEncFilePath() , "Success", JOptionPane.INFORMATION_MESSAGE, Gui.createImageIcon("images/PDS_Logo-32.png")); return true; } // Modified from: http://www.java-examples.com/ // /create-zip-file-directory-recursively-using-zipoutputstream-example // private static boolean addDirectory(ZipOutputStream zout, File fileSource, String path, CryptoFileExternal cryptoFileExternal) { // Get sub-folder/files list File[] files = fileSource.listFiles(); // System.out.println("Adding another directory " + fileSource.getName()); // System.out.println("defined path: " + path); for(int i=0; i < files.length; i++) { // If the file is directory, add directory entry and call this function recursively if( files[i].isDirectory() ) { try { // Create zip entry for this directory // System.out.println("Adding new directory: " + path + files[i].getName() + "/" ); zout.putNextEntry(new ZipEntry(path + files[i].getName() + "/" )); } catch(IOException ioe) { System.out.println("IOException :" + ioe); } if (! addDirectory( zout, files[i], path + (files[i]).getName() + "/", cryptoFileExternal ) ) { return false; } continue; } else { BufferedInputStream bis = null; ProgressMonitorInputStream pms = null; try { byte[] buf = new byte[1024]; bis = new BufferedInputStream ( new FileInputStream(files[i]) ); pms = new ProgressMonitorInputStream(null, "Encrypting: " + System.getProperty("line.separator") + path + files[i].getName() , bis); // Create zip entry // System.out.println("Path for new file: " + path); // System.out.println("Adding new file: " + path + files[i].getName()); zout.putNextEntry(new ZipEntry(path + files[i].getName())); // and write the file... int length; while((length = pms.read(buf)) > 0) { zout.write(buf, 0, length); } // Close the current entry and position the stream to // write the next entry. zout.closeEntry(); bis.close(); pms.close(); } catch (InterruptedIOException e) { // No error - Job was canceled on the Progress Monitor. // try { bis.close(); pms.close(); zout.close(); } catch (Exception ex) { System.out.println("Exception :" + ex); e.printStackTrace(); } StateObject.setFileChooserCancel(true); return false; } catch(Exception e) { System.out.println("Exception :" + e); e.printStackTrace(); return false; } } } // System.out.println("Looks good."); return true; } /////////////////////////////////////////////////////////////////////// /** * Decrypt * */ /////////////////////////////////////////////////////////////////////// boolean decrypt ( CryptoFile cryptoFile ) { // Begin // System.out.println(newline + "Beginning CryptoEngine().decrypt() ..."); // Print current state // // System.out.println("CryptoFile File : " + cryptoFile.getFullyPathedFileName() ); // System.out.println("CryptoFile KeyStore Name : " + cryptoFile.getKsName() ); // System.out.print("KeyStore Passphrase: "); // for ( int i=0; i < cryptoFile.getKsPass().length; i++ ) { // System.out.print( cryptoFile.getKsPass()[i] ); // } // System.out.println(); // System.out.println("CryptoFile Key Alias : " + cryptoFile.getKeyAlias() ); // System.out.print("Key Passphrase: "); // for ( int i=0; i < cryptoFile.getKeyPass().length; i++ ) { // System.out.print( cryptoFile.getKeyPass()[i] ); // } // System.out.println(); // Get the Key // // System.out.println("Executing CryptoKey().getKey() ..."); Key skey; try { skey = new CryptoKey().getKey ( cryptoFile ); } catch (Exception e) { System.out.println("Got an Exception getting the Key: " + e.getMessage()); if (cryptoFile instanceof CryptoFileExternal) { showError("decrypting"); } return false; } // System.out.println("Returned from CryptoKey().getKey() ..."); Cipher cipher = null; try { // Determine the cipher type String algorithm = skey.getAlgorithm(); // System.out.println("Algorithm: " + algorithm); // Create Algorithm specific KeySpec from the Secret Key // System.out.println("Creating the cipher using algorithm: " + algorithm); byte[] rawBytes = skey.getEncoded(); SecretKeySpec skeySpec = new SecretKeySpec(rawBytes, algorithm ); // Create IV (spec) for the cipher chaining if (algorithm.equals("AES")) { ivBytes = ivBytesAES; } else { ivBytes = ivBytesDES; } IvParameterSpec ivSpec = new IvParameterSpec( ivBytes ); // Instantiate the cipher // System.out.println("Creating the cipher using algorithm: " + algorithm); cipher = Cipher.getInstance( algorithm + "/CBC/PKCS5Padding" ); // Initialize the cipher // This is where the 128-bit key size limit of standard Java is seen // In short, you can make a 192/256 bit key, but not a cipher to use it // System.out.println("Initializing the cipher..."); cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec); } catch (InvalidKeyException e) { System.out.println("Unable to create the cipher."); System.out.println("Need Unlimited Strength Policy?"); JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Unable to create the cipher." + newline + newline + "Are you attempting to decrypt using an Unlimited Strength Key" + newline + "on a system that does not have the Unlimited Strength Policy?", "Unable to create the cipher", JOptionPane.WARNING_MESSAGE); return false; } catch (Exception e) { System.out.println("Error encrypting: " + e ); // e.printStackTrace(); showError("decrypting"); return false; } // ************************************************************* // We have the key, we have the cipher. We are a (de)Crypto Engine. // // Begin decrypt block (Native then External files) // BufferedInputStream bis = null; ObjectInputStream ois = null; ProgressMonitorInputStream pms = null; CipherInputStream cis = null; BufferedOutputStream bos = null; boolean pdsFormat = false; try { if (cryptoFile instanceof CryptoFileNative) { // Be your true self, cryptoFile CryptoFileNative cryptoFileNative = (CryptoFileNative) cryptoFile; // Decrypt // // bytes[] > base64.decode -> decrypt > Document // // Construct a BufferedReader for the FileInputStream bis = new BufferedInputStream ( new FileInputStream ( cryptoFileNative.getFullyPathedFileName() ) ); // Read from the BufferedInputStream until past the header int last = 0; int i; while ( ! ( ( ( i = bis.read() ) == 10 ) && ( last == 10 ) ) ) { // System.out.print( "i=" + i + ":" + (char) i + "; last=" + last + ":" + (char) last ); if ( i != 13 ) { last = i; } if ( i == -1 ) { break; } } // System.out.println("Empty Line. Getting data..."); // Attach a Progress Monitor to the Input Stream pms = new ProgressMonitorInputStream(null, "Decrypting: " + newline + cryptoFileNative.getFullyPathedFileName(), bis); // Construct the secret decoder ring cis = new CipherInputStream( new Base64.InputStream ( (InputStream) pms , Base64.DECODE ) , cipher ); if ( cryptoFileNative.getIsStyledDocument() ) { // In this format, a StyledDocument has been serialized/encrypted // System.out.println("Is a StyledDocument"); ois = new ObjectInputStream ( cis ); Object oisObject = ois.readObject(); ois.close(); if ( oisObject instanceof MyDefaultStyledDocument ) { cryptoFileNative.setActiveDocument ( (MyDefaultStyledDocument) oisObject ); } } else { // In this older format, the plain text from a Document was encrypted. // Eventually, this should go away, as all documents are written the other way. // System.out.println("Is not a StyledDocument"); boolean displayThisErrorOnlyOnce = true; byte [] buf = new byte[1024]; // Read from the secret decoder ring. // Covert them to a string, and append it to the document. while (( i = cis.read( buf )) != -1 ) { // System.out.println("Buf size: " + buf.length); // System.out.println("Bytes read: " + i); try { byte [] buf2string = new byte[i]; System.arraycopy(buf, 0, buf2string, 0, i); cryptoFileNative.document.insertString( cryptoFileNative.document.getLength() , new String( buf2string ) , null); } catch (java.lang.OutOfMemoryError ome) { cis.close(); pms.close(); bis.close(); if ( displayThisErrorOnlyOnce ) { displayThisErrorOnlyOnce = false; // notify on failure JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Sorry. The amount of memory that the JVM could allocate " + newline + "was not enough to perform this task. This application " + newline + "provides a simple editor, but isn't really set up for large " + newline + "files; for that you should use another editor. If you really " + newline + "want to edit large files with this application, you can increase " + newline + "the amount of memory that this JVM can allocate. Please see " + newline + "the release notes for details.", "Out of Memory.", JOptionPane.ERROR_MESSAGE); return false; } } } } cis.close(); pms.close(); bis.close(); return true; } else if (cryptoFile instanceof CryptoFileExternal) { // System.out.println("Decrypting External ..."); // Be your true self, cryptoFile CryptoFileExternal cryptoFileExternal = (CryptoFileExternal) cryptoFile; // Decrypt // // base64 bytes[] > base64.decode -> decrypt > fileoutputstream // or binary bytes[] > decrypt > fileoutputstream // // Gui / VerifyKeyStore has already verified that the // CryptoFileExternal is populated with a Path to a KeyStore. // What we don't know is the format that the data is written in. // First, check if the file to be decrypted is a PDS binary // // System.out.println("Checking for PDS file format"); bis = new BufferedInputStream ( new FileInputStream ( cryptoFileExternal.getFullyPathedFileName() ), 2*1024*1024 ); try { // System.out.println("About to create input stream"); ois = new ObjectInputStream( bis ); // System.out.println("About to read object"); Object oisObject = ois.readObject(); // System.out.println("About to test object"); if (oisObject instanceof String) { // System.out.println("Object is a string - testing for PDS"); if (((String) oisObject).equals("PDS")) { // System.out.println("Found PDS Binary format"); pdsFormat = true; oisObject = ois.readObject(); // Read the FILE header if (oisObject instanceof FileHeader ) { oisObject = ois.readObject(); // Read the DATA header if (oisObject instanceof DataHeader ) { DataHeader dataHeader = (DataHeader) oisObject; if ( dataHeader.getDataFormat() == DataHeader.kBINARY ) { // System.out.println("Found a valid PDS file."); // See if this was what was requested if ( cryptoFileExternal.getDecryptionTypeRequested() == CryptoFileExternal.kREQUESTED_DIR ) { // Requested a dir but selected a file JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Incorrect type of PDS file selected." + newline + newline + "You selected the option to decrypt a encrypted directory," + newline + "but you did not select an encrypted directory.", "Incorrect type of PDS file selected", JOptionPane.WARNING_MESSAGE); bis.close(); ois.close(); return false; } // Attach a Progress Monitor to the Input Stream pms = new ProgressMonitorInputStream(null, "Decrypting: " + newline + cryptoFileExternal.getFullyPathedFileName(), bis); // Will read from CIS the rest of the way cis = new CipherInputStream( (InputStream) pms , cipher ); // Use this to write the decrypted file bos = new BufferedOutputStream( new FileOutputStream( cryptoFileExternal.getDecFilePath() ) ); byte [] buf = new byte[1024]; int i; while (( i = cis.read( buf )) != -1 ) { // System.out.println("Buf size: " + buf.length); // System.out.println("Buf contents: " + buf.toString()); bos.write( buf , 0 , i ); } bis.close(); ois.close(); pms.close(); cis.close(); bos.flush(); bos.close(); } else if ( dataHeader.getDataFormat() == DataHeader.kDIRECTORY ) { // We have an encrypted directory // // System.out.println("Found a PDS directory."); // See if this was what was requested if ( cryptoFileExternal.getDecryptionTypeRequested() == CryptoFileExternal.kREQUESTED_FILE ) { // Requested a dir but selected a file JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Incorrect type of PDS file selected." + newline + newline + "You selected the option to decrypt an encrypted file," + newline + "but you selected an encrypted directory.", "Incorrect type of PDS file selected", JOptionPane.WARNING_MESSAGE); bis.close(); ois.close(); return false; } // Output can be 0) extracted to a directory, or 1) create a ZIP file // // For extract to dir, strip off file name from current output destination // if ( cryptoFileExternal.getDecryptedOutputFormat() == CryptoFileExternal.kDECRYPT_EXTRACT ) { // System.out.println("In Path: " + cryptoFileExternal.getFullyPathedFileName() ); // System.out.println("Out Path: " + cryptoFileExternal.getDecFilePath() ); // System.out.println("File Name: " + cryptoFileExternal.getFileName() ); Pattern pattern = Pattern.compile("(.*)" + cryptoFileExternal.getFileName(), Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(cryptoFileExternal.getDecFilePath() + ".PDS"); if( matcher.matches() ) { // System.out.println("Contains extension: " + cryptoFileExternal.getDecFilePath()); cryptoFileExternal.setDecFilePath( matcher.group(1) ); // System.out.println("Truncated name : " + cryptoFileExternal.getDecFilePath()); } // System.out.println("Extracting to: " + cryptoFileExternal.getDecFilePath()); } // Attach a Progress Monitor to the Input Stream pms = new ProgressMonitorInputStream(null, "Decrypting: " + newline + cryptoFileExternal.getFullyPathedFileName() + newline + "To: " + newline + cryptoFileExternal.getDecFilePath(), bis); // Need to decrypt cis = new CipherInputStream( (InputStream) pms , cipher ); // Buffer to write files - Zipped File or Extracted Dir byte [] buf = new byte[1024]; // Extract the directory // if ( cryptoFileExternal.getDecryptedOutputFormat() == CryptoFileExternal.kDECRYPT_EXTRACT ) { // Extract the encrypted/zipped directory to a directory // ZipEntry entry; // Will read from ZIS the rest of the way ZipInputStream zis = new ZipInputStream( cis ); String path = cryptoFileExternal.getDecFilePath(); Pattern pattern = Pattern.compile("(.*)[/\\\\]"); Matcher matcher = null; // System.out.println("Decrypting to: " + cryptoFileExternal.getDecFilePath()); while((entry = zis.getNextEntry()) != null) { // System.out.println("Entry: " + entry.getName()); matcher = pattern.matcher( entry.getName() ); if ( matcher.matches() ) { // System.out.println("Directory: " + entry.getName()); // Does it currently exist if ( new File( path + matcher.group(1)).exists() ) { if ( new File( path + matcher.group(1)).isDirectory() ) { JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Unable to continue." + newline + newline + "Attempting to extract \"" + matcher.group(1) + "\""+ newline + "To: \"" + path + "\"" + newline + newline + "A directory named \"" + matcher.group(1) + "\"" + " already exists at:" + newline + path, "Unable to continue", JOptionPane.WARNING_MESSAGE); return false; } else { JFrame frame = new JFrame(); int n = JOptionPane.showOptionDialog( frame, "The file \"" + matcher.group(1) + "\" exists in:" + newline + cryptoFileExternal.getDecFilePath() + newline + "OK to remove the file and extract the" + newline + "\"" + entry.getName() + "\" directory?"+ newline, "Overwrite existing file?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[] {"Yes", "No"}, "No"); if ( n != 0 ) { // System.out.println("No..."); return false; } else { // System.out.println("Removing the unwanted file..."); File f = new File(path + matcher.group(1)); if ( ! f.delete() ) { // System.out.println("Unable to delete : " // + cryptoFileExternal.getDecFilePath()); JOptionPane.showMessageDialog( frame, "Unable to remove the file:" + newline + path + matcher.group(1) + newline + newline + "Please verify that you have permission to overwrite " + newline + "this file and try again.", "Unable to Extract the Directory", JOptionPane.ERROR_MESSAGE); return false; } } } } // System.out.println("Create the new directory..."); if ( ! (new File( path + matcher.group(1))).mkdirs() ) { System.out.println("Unable to create \"" + path + matcher.group(1) + "\""); JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Unable to extract the directory:" + newline + matcher.group(1) + newline + "To:" + newline + path + newline + "Please verify that you have permission create" + newline + "this directory and try again.", "Unable to Extract the Directory", JOptionPane.ERROR_MESSAGE); return false; } } else { // System.out.println("File: " + entry.getName()); FileOutputStream fos = null; try { fos = new FileOutputStream(path + entry.getName()); int len = 0; while ((len = zis.read(buf)) > 0) { fos.write(buf, 0, len); } } finally { if( fos != null ) { fos.flush(); fos.close(); } } } } bis.close(); ois.close(); pms.close(); cis.close(); zis.close(); } else if ( cryptoFileExternal.getDecryptedOutputFormat() == CryptoFileExternal.kDECRYPT_TOZIP ) { // Write the encrypted/zipped directory as a unencrypted zip file // Write the decrypted Zip file using "bos" bos = new BufferedOutputStream( new FileOutputStream( cryptoFileExternal.getDecFilePath() ) ); // Read each file to buffer from "cis" and send to "bos" int i; while (( i = cis.read( buf )) != -1 ) { // System.out.println("Buf size: " + buf.length); // System.out.println("Buf contents: " + buf.toString()); bos.write( buf , 0 , i ); } // Close 'em down bis.close(); ois.close(); pms.close(); cis.close(); bos.flush(); bos.close(); } else { System.out.println("Unknown output format - Extract or Create Zip File"); return false; } } else { System.out.println("Unknown Data Header format"); return false; } } else { System.out.println("Unknown File Header format"); return false; } } else { System.out.println("Unknown Magic in file."); return false; } } } } catch ( StreamCorruptedException e ) { // No error, just not in PDS binary format // Continue below... // System.out.println("Not in PDS Binary format."); } if (! pdsFormat ) { // Ok, it wasn't a PDS binary - how about Base64 ? // // System.out.println("Attempting PDS Base64 format"); // Construct a BufferedReader for the FileInputStream // - this will *only* be read from to read past the header bis = new BufferedInputStream ( new FileInputStream ( cryptoFileExternal.getFullyPathedFileName() ) ); // Read from the BufferedInputStream until past the header int last = 0; int i; while ( ! ( ( ( i = bis.read() ) == 10 ) && ( last == 10 ) ) ) { // System.out.print( "i=" + i + ":" + (char) i + "; last=" + last + ":" + (char) last ); if ( i != 13 ) { last = i; } if ( i == -1 ) { break; } } // System.out.println("Empty Line. Getting data..."); // Attach a Progress Monitor to the Input Stream pms = new ProgressMonitorInputStream(null, "Decrypting: " + newline + cryptoFileExternal.getFullyPathedFileName() + newline + "To: " + newline + cryptoFileExternal.getDecFilePath(), bis); // Use this to decode, decrypt and recreate the original file bos = new BufferedOutputStream( new Base64.OutputStream( new BufferedOutputStream( new CipherOutputStream( new BufferedOutputStream( new FileOutputStream( cryptoFileExternal.getDecFilePath() ) ) , cipher) ) , Base64.DECODE) ); // And here we go... byte [] buf = new byte[1024]; while (( i = pms.read( buf )) != -1 ) { // System.out.println("Buf size: " + buf.length); // System.out.println("Buf contents: " + buf.toString()); bos.write( buf , 0 , i ); } bis.close(); pms.close(); bos.flush(); bos.close(); } } else { System.out.println("Unknown file type in decrypt..."); return false; } } catch (InterruptedIOException e) { // No error - Job was canceled on the Progress Monitor. try { bis.close(); if ( pdsFormat ) { ois.close(); } pms.close(); cis.close(); bos.flush(); bos.close(); } catch (Exception ex) { System.out.println("Error closing file handle."); ex.printStackTrace(); } if (cryptoFile instanceof CryptoFileExternal) { CryptoFileExternal cryptoFileExternal = (CryptoFileExternal) cryptoFile; File f = new File(cryptoFileExternal.getDecFilePath()); if ( ! f.delete() ) { System.out.println("Unable to delete : " + cryptoFileExternal.getDecFilePath()); } } return true; } catch (Exception e) { System.out.println("Error decrypting: " + e ); e.printStackTrace(); try { bis.close(); if ( pdsFormat ) { ois.close(); } pms.close(); cis.close(); bos.flush(); bos.close(); } catch (Exception ex) { System.out.println("Error closing file handle."); ex.printStackTrace(); } if (cryptoFile instanceof CryptoFileExternal) { showError("decrypting"); CryptoFileExternal cryptoFileExternal = (CryptoFileExternal) cryptoFile; File f = new File(cryptoFileExternal.getDecFilePath()); if ( ! f.delete() ) { System.out.println("Unable to delete : " + cryptoFileExternal.getDecFilePath()); } } return false; } if (cryptoFile instanceof CryptoFileExternal) { JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Success decrypting:" + newline + cryptoFile.getFullyPathedFileName() + newline + "To: " + newline + ((CryptoFileExternal)cryptoFile).getDecFilePath() , "Success", JOptionPane.INFORMATION_MESSAGE, Gui.createImageIcon("images/PDS_Logo-32.png")); } // And finally... return true; } void showError(String s) { JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Received an error while " + s + "." + newline + "Please retry using different credentials or " + newline + "check the console for additional details.", "Encryption / Decryption Error.", JOptionPane.ERROR_MESSAGE); } }