/* * One Time Pad * Feel free to use this code in any way you like * */ package nz.co.peter.cryptography; import java.io.*; import java.security.SecureRandom; /* * One Time Pad * * Encrypted file format: first 8 bytes: long offset into keyfile (randomly generated on creation) * remaining bytes: xor-ed bytes of key and original * * @author Peter de Vocht */ public class OneTimePad { private static final long KeySizeGB = 1024L * 1024L * 1024L; /** * example: * * (generate a 1GB psuedo random keyfile with filename keyfile.bin) * java -jara OneTimePad.jar -g keyfile.bin 1 * * (encrypt a file testfile.txt and write the result to testfile.encrypted using keyfile.bin as a key) * java -jar OneTimePad.jar -e testfile.txt testfile.encrypted keyfile.bin * * (decrypt a file testfile.encrypted and write the result to testfile-decrypted.txt using keyfile.bin as a key) * java -jar OneTimePad.jar -d testfile.encrypted testfile-decrypted.txt keyfile.bin * */ public static void main(String[] args) throws FileNotFoundException, IOException { StringBuilder sb = new StringBuilder(); sb.append("One Time Pad encryption/decryption utility\n"); sb.append("options:\n"); sb.append(" -e fileToEncrypt encryptedFilename keyfile\n"); sb.append(" -d fileToDecrypt decryptedFilename keyfile\n"); sb.append(" -g keyfile sizeinGB\n"); // check the command if (args.length != 4 && args.length != 3) { System.out.println(sb.toString()); return; } if (args[0].equals("-e") && args.length != 4) { System.out.println("-e fileToEncrypt encryptedFilename keyfile"); return; } else if (args[0].equals("-d") && args.length != 4) { System.out.println(" -d fileToDecrypt decryptedFilename keyfile"); return; } else if (args[0].equals("-g") && args.length != 3) { System.out.println(" -g keyfile sizeinGB"); return; } else if (!args[0].equals("-e") && !args[0].equals("-d") && !args[0].equals("-g")) { System.out.println(sb.toString()); return; } // execute if (args[0].equals("-e")) { encryptFile(args[1], args[2], args[3]); } else if (args[0].equals("-d")) { decryptFile(args[1], args[2], args[3]); } else if (args[0].equals("-g")) { int size = Integer.parseInt(args[2]); generateKey(args[1], size); } } // encrypt a file given a keyfile public static void decryptFile(String fileToDecrypt, String decryptedOutputFilename, String keyfile) throws FileNotFoundException, IOException { // get file size File file = new File(fileToDecrypt); long fileSize = file.length(); File file2 = new File(keyfile); long keySize = file2.length(); if (fileSize > keySize) { throw new RuntimeException("file size exceeds key size"); } // key file FileInputStream keyFileReader = new FileInputStream(keyfile); BufferedInputStream keyfileStream = new BufferedInputStream(keyFileReader); // file to read FileInputStream fileReader = new FileInputStream(fileToDecrypt); BufferedInputStream infile = new BufferedInputStream(fileReader); // file to write FileOutputStream fileWriter = new FileOutputStream(decryptedOutputFilename); BufferedOutputStream outfile = new BufferedOutputStream(fileWriter); // get key offset byte[] longBuffer = new byte[8]; infile.read(longBuffer); long nextLong = toLong(longBuffer, 0); if (nextLong > keySize) { throw new RuntimeException("incorrect file type"); } keyfileStream.skip(nextLong); fileSize = fileSize - 8; // start encrypting while (fileSize > 0) { int data = infile.read(); int key = keyfileStream.read(); data = data ^ key; outfile.write(data); fileSize--; } outfile.close(); fileWriter.close(); infile.close(); fileReader.close(); keyfileStream.close(); keyFileReader.close(); } // encrypt a file given a keyfile public static void encryptFile(String fileToEncrypt, String encryptedOutputFilename, String keyfile) throws FileNotFoundException, IOException { SecureRandom random = new SecureRandom(); long nextLong = random.nextLong(); if (nextLong < 0) { nextLong = -nextLong; } // get file size File file = new File(fileToEncrypt); long fileSize = file.length(); File file2 = new File(keyfile); long keySize = file2.length(); if (fileSize > keySize) { throw new RuntimeException("file size exceeds key size"); } // key file FileInputStream keyFileReader = new FileInputStream(keyfile); BufferedInputStream keyfileStream = new BufferedInputStream(keyFileReader); // file to read FileInputStream fileReader = new FileInputStream(fileToEncrypt); BufferedInputStream infile = new BufferedInputStream(fileReader); // file to write FileOutputStream fileWriter = new FileOutputStream(encryptedOutputFilename); BufferedOutputStream outfile = new BufferedOutputStream(fileWriter); // get key offset nextLong = nextLong % (keySize - fileSize); keyfileStream.skip(nextLong); byte[] longBuffer = new byte[8]; writeLong(longBuffer, 0, nextLong); outfile.write(longBuffer); // start encrypting while (fileSize > 0) { int data = infile.read(); int key = keyfileStream.read(); data = data ^ key; outfile.write(data); fileSize--; } outfile.close(); fileWriter.close(); infile.close(); fileReader.close(); keyfileStream.close(); keyFileReader.close(); } // generate a key file public static void generateKey(String filename, int sizeinGB) throws FileNotFoundException, IOException { FileOutputStream fileWriter = new FileOutputStream(filename); BufferedOutputStream file = new BufferedOutputStream(fileWriter); SecureRandom random = new SecureRandom(); long cntr = 0; long size = KeySizeGB * (long)sizeinGB; while (cntr < size) { random.nextInt(); // skip int n1 = random.nextInt(); int n2 = random.nextInt() & 3; byte value = 0; switch (n2) { case 0: { value = (byte)(n1 & 0xff); break; } case 1: { value = (byte)((n1 >> 8) & 0xff); break; } case 2: { value = (byte)((n1 >> 16) & 0xff); break; } case 3: { value = (byte)((n1 >> 24) & 0xff); break; } default: throw new RuntimeException("failed"); } cntr++; // write byte to file file.write(value); } file.close(); fileWriter.close(); } // get a long from the buffer at index public static long toLong(byte[] buffer, int index) { return (((long)(buffer[index++] & 0xff) << 0) + ((long)(buffer[index++] & 0xff) << 8) + ((long)(buffer[index++] & 0xff) << 16) + ((long)(buffer[index++] & 0xff) << 24) + ((long)(buffer[index++] & 0xff) << 32) + ((long)(buffer[index++] & 0xff) << 40) + ((long)(buffer[index++] & 0xff) << 48) + ((long)(buffer[index++] & 0xff) << 56)); } // put a long into the buffer at offset public static void writeLong(byte[] buffer, int offset, long value) { buffer[offset++] = (byte)((value >> 0) & 0xff); buffer[offset++] = (byte)((value >> 8) & 0xff); buffer[offset++] = (byte)((value >> 16) & 0xff); buffer[offset++] = (byte)((value >> 24) & 0xff); buffer[offset++] = (byte)((value >> 32) & 0xff); buffer[offset++] = (byte)((value >> 40) & 0xff); buffer[offset++] = (byte)((value >> 48) & 0xff); buffer[offset++] = (byte)((value >> 56) & 0xff); } }