There is no 100% way to stop people reading files, but you can make it a lot more difficult. What you are need to do is encrypt the file to prevent casual reading, or to sign the file so that you can detect authenticity and tampering.
Here is an overview of how you might encrypt a file with public / private key encryption. The tools you will need are:
- GPG. You will be creating keys with this
- BouncyCastle PGP. This is an encryption API for Java that implements the OpenPGP specification.
You will be encrypting files with the command line with GPG and reading them from Java with BouncyCastle (BC).
The steps involved in doing this in a secure way are.
Create a unique public / private key pair for signing / encryption with GPG. e.g. gpg --gen-key
and follow the onscreen instructions. You can usually pick the default settings, and call your key something like "[email protected]"
Encrypt the file your Java will process. e.g. gpg -e myfiles.zip
to encrypt. You could do this from a script fairly easily if its data that changes a lot. Encryption works by encrypting the file with the public key. Someone who wishes to decrypt the file needs the corresponding private key.
Export the private key from the keypair. e.g. file gpg --export-secret-key -a > decryption.key
.
Create a new keyring just containing the key you exported. e.g.
mkdir tmpkeys
gpg -homedir=tmpkeys --import decryption.key
cp tmpkeys/secring.gpg keyring
In your Java program, embed the keyring as a resource or by base64 encoding it and injecting it into the code as a string. Keep your public key and ensure you do not inadvertently ship it with your app.
Use BouncyCastle PGP to open the keyring.
This is pseudo code, so read the BC APIs for details.
// During initialisation
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// During decryption
InputStream is = openKeyring(); // Wherever your keyring is
InputStream isData = openDataFile();
try {
PGPSecretKeyRing kr = new PGPSecretKeyRing();
PGPSecretKey sk = kr.getSecretKey();
PGPPrivateKey pl = sk.extractPrivateKey("mypassphrase", securityProvider);
PGPObjectFactory of = new PGPObjectFactory(isData);
Object o;
while ((o = of.nextObject) != null) {
if (o instanceof PGPCompressedData) {
readAndDoWhateverINeedtoDo((PGPCompressedData) o).getDataStream());
}
}
}
catch (Exception e) {
rejectFile(e);
}
So basically the Java app gets an encrypted file, gives it to BC along with the private key and gets back an InputStream that it can read the plaintext from. If it has a problem it will throw an exception that you can treat as a fatal error.
Note a few things:
- You will need to embed a passphrase somewhere in the Java app, but it doesn't have to be the same one that protects your public key. Remember we exported the private key into a temporary keyring so you could change the passphrase there.
- A determined attacked could still hack your Java file to extract the private key & password, or print out the plaintext or remove this test altogether. I suggest if this is a worry you should be obfuscating your code and putting surreptitious checks to ensure validation is not bypassed in some way. Nothing will prevent this 100% though.
- Just signing a file is similar to encryption except you give out the public key and keep the private key secret. In this case the Java app can detect file tampering, but the payload is plaintext.
- BouncyCastle is straightforward when you know it, but the documentation is terrible. You should download the source code since there are some test samples which will put you on the right course.