package org.bouncycastle.crypto.examples;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.engines.DESedeEngine;
import org.bouncycastle.crypto.generators.DESedeKeyGenerator;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.DESedeParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;
/**
* DESExample is a simple DES based encryptor/decryptor.
* <p>
* The program is command line driven, with the input
* and output files specified on the command line.
* <pre>
* java org.bouncycastle.crypto.examples.DESExample infile outfile [keyfile]
* </pre>
* A new key is generated for each encryption, if key is not specified,
* then the example will assume encryption is required, and as output
* create deskey.dat in the current directory. This key is a hex
* encoded byte-stream that is used for the decryption. The output
* file is Hex encoded, 60 characters wide text file.
* <p>
* When encrypting;
* <ul>
* <li>the infile is expected to be a byte stream (text or binary)
* <li>there is no keyfile specified on the input line
* </ul>
* <p>
* When decrypting;
* <li>the infile is expected to be the 60 character wide base64
* encoded file
* <li>the keyfile is expected to be a base64 encoded file
* <p>
* This example shows how to use the light-weight API, DES and
* the filesystem for message encryption and decryption.
*
*/
public class DESExample extends Object
{
// Encrypting or decrypting ?
private boolean encrypt = true;
// To hold the initialised DESede cipher
private PaddedBufferedBlockCipher cipher = null;
// The input stream of bytes to be processed for encryption
private BufferedInputStream in = null;
// The output stream of bytes to be procssed
private BufferedOutputStream out = null;
// The key
private byte[] key = null;
/*
* start the application
*/
public static void main(String[] args)
{
boolean encrypt = true;
String infile = null;
String outfile = null;
String keyfile = null;
if (args.length < 2)
{
DESExample de = new DESExample();
System.err.println("Usage: java "+de.getClass().getName()+
" infile outfile [keyfile]");
System.exit(1);
}
keyfile = "deskey.dat";
infile = args[0];
outfile = args[1];
if (args.length > 2)
{
encrypt = false;
keyfile = args[2];
}
DESExample de = new DESExample(infile, outfile, keyfile, encrypt);
de.process();
}
// Default constructor, used for the usage message
public DESExample() { }
/*
* Constructor, that takes the arguments appropriate for
* processing the command line directives.
*/
public DESExample(
String infile,
String outfile,
String keyfile,
boolean encrypt
)
{
/*
* First, determine that infile & keyfile exist as appropriate.
*
* This will also create the BufferedInputStream as required
* for reading the input file. All input files are treated
* as if they are binary, even if they contain text, it's the
* bytes that are encrypted.
*/
this.encrypt = encrypt;
try
{
in = new BufferedInputStream(new FileInputStream(infile));
}
catch (FileNotFoundException fnf)
{
System.err.println("Input file not found ["+infile+"]");
System.exit(1);
}
try
{
out = new BufferedOutputStream(new FileOutputStream(outfile));
}
catch (IOException fnf)
{
System.err.println("Output file not created ["+outfile+"]");
System.exit(1);
}
if (encrypt)
{
try
{
/*
* The process of creating a new key requires a
* number of steps.
*
* First, create the parameters for the key generator
* which are a secure random number generator, and
* the length of the key (in bits).
*/
SecureRandom sr = null;
try
{
sr = new SecureRandom();
/*
* This following call to setSeed() makes the
* initialisation of the SecureRandom object
* _very_ fast, but not secure AT ALL.
*
* Remove the line, recreate the class file and
* then run DESExample again to see the difference.
*
* The initialisation of a SecureRandom object
* can take 5 or more seconds depending on the
* CPU that the program is running on. That can
* be annoying during unit testing.
* -- jon
*/
sr.setSeed("www.bouncycastle.org".getBytes());
}
catch (Exception nsa)
{
System.err.println("Hmmm, no SHA1PRNG, you need the "+
"Sun implementation");
System.exit(1);
}
KeyGenerationParameters kgp = new KeyGenerationParameters(
sr,
DESedeParameters.DES_EDE_KEY_LENGTH*8);
/*
* Second, initialise the key generator with the parameters
*/
DESedeKeyGenerator kg = new DESedeKeyGenerator();
kg.init(kgp);
/*
* Third, and finally, generate the key
*/
key = kg.generateKey();
/*
* We can now output the key to the file, but first
* hex encode the key so that we can have a look
* at it with a text editor if we so desire
*/
BufferedOutputStream keystream =
new BufferedOutputStream(new FileOutputStream(keyfile));
byte[] keyhex = Hex.encode(key);
keystream.write(keyhex, 0, keyhex.length);
keystream.flush();
keystream.close();
}
catch (IOException createKey)
{
System.err.println("Could not decryption create key file "+
"["+keyfile+"]");
System.exit(1);
}
}
else
{
try
{
// read the key, and decode from hex encoding
BufferedInputStream keystream =
new BufferedInputStream(new FileInputStream(keyfile));
int len = keystream.available();
byte[] keyhex = new byte[len];
keystream.read(keyhex, 0, len);
key = Hex.decode(keyhex);
}
catch (IOException ioe)
{
System.err.println("Decryption key file not found, "+
"or not valid ["+keyfile+"]");
System.exit(1);
}
}
}
private final void process()
{
/*
* Setup the DESede cipher engine, create a PaddedBufferedBlockCipher
* in CBC mode.
*/
cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new DESedeEngine()));
/*
* The input and output streams are currently set up
* appropriately, and the key bytes are ready to be
* used.
*
*/
if (encrypt)
{
performEncrypt(key);
}
else
{
performDecrypt(key);
}
// after processing clean up the files
try
{
in.close();
out.flush();
out.close();
}
catch (IOException closing)
{
}
}
/*
* This method performs all the encryption and writes
* the cipher text to the buffered output stream created
* previously.
*/
private final void performEncrypt(byte[] key)
{
// initialise the cipher with the key bytes, for encryption
cipher.init(true, new KeyParameter(key));
/*
* Create some temporary byte arrays for use in
* encryption, make them a reasonable size so that
* we don't spend forever reading small chunks from
* a file.
*
* There is no particular reason for using getBlockSize()
* to determine the size of the input chunk. It just
* was a convenient number for the example.
*/
// int inBlockSize = cipher.getBlockSize() * 5;
int inBlockSize = 47;
int outBlockSize = cipher.getOutputSize(inBlockSize);
byte[] inblock = new byte[inBlockSize];
byte[] outblock = new byte[outBlockSize];
/*
* now, read the file, and output the chunks
*/
try
{
int inL;
int outL;
byte[] rv = null;
while ((inL=in.read(inblock, 0, inBlockSize)) > 0)
{
outL = cipher.processBytes(inblock, 0, inL, outblock, 0);
/*
* Before we write anything out, we need to make sure
* that we've got something to write out.
*/
if (outL > 0)
{
rv = Hex.encode(outblock, 0, outL);
out.write(rv, 0, rv.length);
out.write('\n');
}
}
try
{
/*
* Now, process the bytes that are still buffered
* within the cipher.
*/
outL = cipher.doFinal(outblock, 0);
if (outL > 0)
{
rv = Hex.encode(outblock, 0, outL);
out.write(rv, 0, rv.length);
out.write('\n');
}
}
catch (CryptoException ce)
{
}
}
catch (IOException ioeread)
{
ioeread.printStackTrace();
}
}
/*
* This method performs all the decryption and writes
* the plain text to the buffered output stream created
* previously.
*/
private final void performDecrypt(byte[] key)
{
// initialise the cipher for decryption
cipher.init(false, new KeyParameter(key));
/*
* As the decryption is from our preformatted file,
* and we know that it's a hex encoded format, then
* we wrap the InputStream with a BufferedReader
* so that we can read it easily.
*/
BufferedReader br = new BufferedReader(new InputStreamReader(in));
/*
* now, read the file, and output the chunks
*/
try
{
int outL;
byte[] inblock = null;
byte[] outblock = null;
String rv = null;
while ((rv = br.readLine()) != null)
{
inblock = Hex.decode(rv);
outblock = new byte[cipher.getOutputSize(inblock.length)];
outL = cipher.processBytes(inblock, 0, inblock.length,
outblock, 0);
/*
* Before we write anything out, we need to make sure
* that we've got something to write out.
*/
if (outL > 0)
{
out.write(outblock, 0, outL);
}
}
try
{
/*
* Now, process the bytes that are still buffered
* within the cipher.
*/
outL = cipher.doFinal(outblock, 0);
if (outL > 0)
{
out.write(outblock, 0, outL);
}
}
catch (CryptoException ce)
{
}
}
catch (IOException ioeread)
{
ioeread.printStackTrace();
}
}
}