org.homeunix.thecave.moss.util.crypto
Class MossCryptoFactory

java.lang.Object
  extended by org.homeunix.thecave.moss.util.crypto.MossCryptoFactory
Direct Known Subclasses:
BuddiCryptoFactory

public abstract class MossCryptoFactory
extends java.lang.Object

A class which attempts to simplify the creation and reading of encrypted documents. To use this class, your document must consist of the following: X bytes of a Header. This is defined by you, and should be a unique way of identifying the document. This can be as long or as short as you wish; it is suggested that it be as long as needed to ensure uniqueness. Perhaps a byte array representation of your program name and version number would be apropriate. The header is not encrypted. Y bytes of salt. This is used in addition to the password to create a key for the encryption process. This is not encrypted either. Z bytes of a Canary. This is a known value to help determine if we have decrypted the file correctly. If this value turns up correctly, we know that the password used is correct. Like the header, it should be long enough to ensure that it does not match by fluke. Anything over 5 or 6 bytes should be sufficient. This value is encrypted, assuming we use a non-nul cipher. The rest of the file is now accessed. If we are using a non-null cipher, this will be encrypted. When we get the input stream from the getInputStream() method, the header has already been checked, the salt has been loaded, the cipher created, and (if the password is correct), the CipherInputStream is created and returned. At this point, the read pointer should be pointing to the start of your data; i.e., to the index X + Y + Z. This can allow you to read the file using whatever means you desire. If you specify true for isCompressData(), then we will also filter the plain text data through gzip before reading. This can result in much smaller data files, although it makes debugging harder.

Author:
wyatt

Constructor Summary
MossCryptoFactory()
          Creates a new CipherStreamFactory with the default parameters (AES encryption for the cipher, with compatible supporting algorithms).
MossCryptoFactory(java.lang.String cipherAlgorithm, java.lang.String keyAlgorithm, java.lang.String secureRandomAlgorithm, java.lang.String digestAlgorithm, int maxKeyLength, int saltLength)
          Creates a new cipher stream factory, using user-defined algorithms and key sizes.
 
Method Summary
protected  byte[] getCanary()
          The byte array to use as the canary.
 byte[] getDecryptedBytes(byte[] ciphertext, char[] password)
          Returns a byte array containing an decrypted copy of the given ciphertext byte array.
 java.io.InputStream getDecryptedStream(java.io.InputStream inputStream, char[] password)
          Returns an input stream pointing to the given file, after checking for a valid file header and password.
 java.lang.String getDecryptedString(java.lang.String ciphertext, char[] password)
          Returns a decrypted version of the given ciphertext string, using the given password.
 byte[] getEncryptedBytes(byte[] plaintext, char[] password)
          Returns a byte array containing an encrypted copy of the given plaintext byte array.
 java.io.OutputStream getEncryptedStream(java.io.OutputStream outputStream, char[] password)
          Returns an output stream writing to the given file.
 java.lang.String getEncryptedString(java.lang.String plaintext, char[] password)
          Returns an encrypted version of the given plaintext string, using the given password.
protected abstract  byte[] getHeader()
          The byte array to use as the header.
 java.util.Date getTimestamp(java.io.InputStream inputStream)
           
abstract  boolean isCompressData()
          Should we filter the data through gzip before encrypting it? For most situations, this should be true.
 boolean isHeaderCorrect(java.io.InputStream is)
          Checks if the header is correct.
abstract  boolean isSaveDate()
          Should we include the date timestamp at the beginning of the file? This value is unencrypted, and will appear right after the header.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

MossCryptoFactory

public MossCryptoFactory()
                  throws CipherException
Creates a new CipherStreamFactory with the default parameters (AES encryption for the cipher, with compatible supporting algorithms).

Throws:
CipherException - There was a problem creating the cipher. This is generally due to an invalid algorithm name for cipherAlgorithm. As this value is hardcoded in this constructor, you should never see this.

MossCryptoFactory

public MossCryptoFactory(java.lang.String cipherAlgorithm,
                         java.lang.String keyAlgorithm,
                         java.lang.String secureRandomAlgorithm,
                         java.lang.String digestAlgorithm,
                         int maxKeyLength,
                         int saltLength)
                  throws CipherException
Creates a new cipher stream factory, using user-defined algorithms and key sizes. For a list of valid names, please see http://java.sun.com/j2se/1.5.0/docs/guide/security/jce/JCERefGuide.html#AppA

Parameters:
cipherAlgorithm - The cipher algorithm
keyAlgorithm - The key algorithm
secureRandomAlgorithm - The secure random algorithm (for generating salt)
digestAlgorithm - The digest algorithm (for converting password to byte[] for key)
saltLength - Length of the salt byte array. This must be in a multiple of the block size.
Throws:
CipherException - There was a problem creating the cipher. This is probably due to an incorrect algorithm name for cipherAlgorithm.
Method Detail

getTimestamp

public java.util.Date getTimestamp(java.io.InputStream inputStream)
                            throws IncorrectDocumentFormatException
Throws:
IncorrectDocumentFormatException

getDecryptedStream

public java.io.InputStream getDecryptedStream(java.io.InputStream inputStream,
                                              char[] password)
                                       throws CipherException,
                                              IncorrectPasswordException,
                                              IncorrectDocumentFormatException,
                                              java.io.IOException
Returns an input stream pointing to the given file, after checking for a valid file header and password.

Parameters:
file - The file to load from
password - The password to use for decryption. Pass in null to use a null cipher (no encryption).
Returns:
Throws:
CipherException - There was a problem relating to the cipher. Generally this is due to incorrect algorithm names.
IncorrectPasswordException - The cipher was set up properly, but the given password was incorrect. You should probably handle this to re-prompt the user for a new password.
IncorrectDocumentFormatException - The document is not the correct type. This means that the plain text document header did not match the one supplied for this file type.
java.io.IOException - There was a problem accessing the given file.

getEncryptedStream

public java.io.OutputStream getEncryptedStream(java.io.OutputStream outputStream,
                                               char[] password)
                                        throws CipherException,
                                               java.io.IOException
Returns an output stream writing to the given file. Writes the header and a random salt in plaintext, and then encrypts the stream. The stream is passed back, and everything else written to it is encrypted.

Parameters:
file - The file to write to
password - The password to use for encryption. It is recommended that you verify this before passing it to this method. Pass in null to use a null cipher (no encryption).
Returns:
Throws:
CipherException - There was a problem relating to the cipher. Generally this is due to incorrect algorithm names.
java.io.IOException - There was a problem accessing the given file.

getEncryptedBytes

public byte[] getEncryptedBytes(byte[] plaintext,
                                char[] password)
                         throws CipherException
Returns a byte array containing an encrypted copy of the given plaintext byte array.

Parameters:
plaintext -
password -
Returns:
Throws:
CipherException

getDecryptedBytes

public byte[] getDecryptedBytes(byte[] ciphertext,
                                char[] password)
                         throws CipherException,
                                IncorrectPasswordException,
                                IncorrectDocumentFormatException
Returns a byte array containing an decrypted copy of the given ciphertext byte array.

Parameters:
ciphertext -
password -
Returns:
Throws:
CipherException
IncorrectPasswordException
IncorrectDocumentFormatException

getEncryptedString

public java.lang.String getEncryptedString(java.lang.String plaintext,
                                           char[] password)
                                    throws CipherException
Returns an encrypted version of the given plaintext string, using the given password.

Parameters:
plaintext -
password -
Returns:
Throws:
CipherException

getDecryptedString

public java.lang.String getDecryptedString(java.lang.String ciphertext,
                                           char[] password)
                                    throws CipherException,
                                           IncorrectPasswordException,
                                           IncorrectDocumentFormatException
Returns a decrypted version of the given ciphertext string, using the given password.

Parameters:
ciphertext -
password -
Returns:
Throws:
CipherException
IncorrectPasswordException
IncorrectDocumentFormatException

isHeaderCorrect

public boolean isHeaderCorrect(java.io.InputStream is)
                        throws java.io.IOException
Checks if the header is correct. Just checks each byte from the input stream with the known good value, supplied by the implementing class' getHeader() method.

Parameters:
is -
Returns:
Throws:
java.io.IOException

getHeader

protected abstract byte[] getHeader()
The byte array to use as the header. This should be a constant and distinct byte array for each file type. It is stored in plain text at the beginning of the file, and is used by the getCipher*Stream() methods to verify that the file is of the correct type. It can also be used by the OS to associate files with programs, if the windowing system supports signature based file association. You should not change this between versions of the data file, or else you will not be able to load old files in the new program version.

Returns:
An array of bytes representing the document's header (or file signature).

getCanary

protected byte[] getCanary()
The byte array to use as the canary. This is a short (about 8 - 16 byte) string which is placed at the beginning of the file, right after the salt. The canary is encrypted, and is checked by the getCipher*Stream() methods to ensure that the file is correctly decoded (i.e., that the password is correct). By default, this is set to the byte equivalent of '01234456789abcdef'. This method is provided for you to override this if you wish, but there is probably no reason to do so. You should definitely not change this between versions of the data file, or else you will not be able to load old files in the new program version.

Returns:
An array of bytes representing the document's canary value

isSaveDate

public abstract boolean isSaveDate()
Should we include the date timestamp at the beginning of the file? This value is unencrypted, and will appear right after the header. You cannot change this value for different versions of the program, or the data files will be invalid.

Returns:
true if we should save the date, false otherwise.

isCompressData

public abstract boolean isCompressData()
Should we filter the data through gzip before encrypting it? For most situations, this should be true. If you need to use this for debugging purposes, or if the data is already very compressed, or if for speed reasons, it is better to not compress the data, you can return false here. When reading the files, we first check if it is compressed; if so, we read it in, but if not we assume the data is not compressed, and try it again. Because of this, you can mix and match compression in files, with no ill effects.

Returns: