package ilarkesto.base; import java.io.ByteArrayOutputStream; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; public class PBECrypt { public static void main(String[] args) { String s = "hello world"; String encrypted = encrypt(s, "geheim"); System.out.flush(); System.out.println(encrypted); System.out.flush(); System.out.println(decrypt(encrypted, "geheim")); System.out.println(decrypt(encrypted, "falsch")); } /* * The "iteration count" for the key generation algorithm. Basically, this means that the processing that * is done to generate the key happens 1000 times. You won't even notice the difference while encrypting * or decrypting text, but an attacker will notice a *big* difference when brute forcing keys! */ static final int ITERATION_COUNT = 1000; /* Length of the salt (see below for details on what the salt is) */ static final int SALT_LENGTH = 8; /* Which encryption algorithm we're using. */ static final String ALGORITHM = "PBEWithMD5AndDES"; /* * The name of a provider class to add to the system before running, if using a provider that's not * permanently installed. */ static final String EXTRA_PROVIDER = null; public static String encrypt(String s, String password) { try { return new String(encrypt(s.getBytes(), password.toCharArray())); } catch (Exception ex) { throw new RuntimeException(ex); } } public static byte[] encrypt(byte[] input, char[] password) throws Exception { /* * Get ourselves a random number generator, needed in a number of places for encrypting. */ SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); /* * A "salt" is considered an essential part of password-based encryption. The salt is selected at * random for each encryption. It is not considered "sensitive", so it is tacked onto the generated * ciphertext without any special processing. It doesn't matter if an attacker actually gets the salt. * The salt is used as part of the key, with the very useful result that if you encrypt the same * plaintext with the same password twice, you get *different* ciphertexts. There are lots of pages on * the 'net with information about salts and password-based encryption, so read them if you want more * details. Suffice to say salt=good, no salt=bad. */ byte[] salt = new byte[SALT_LENGTH]; sr.nextBytes(salt); /* * We've now got enough information to build the actual key. We do this by encapsulating the variables * in a PBEKeySpec and using a SecretKeyFactory to transform the spec into a key. */ PBEKeySpec keyspec = new PBEKeySpec(password, salt, ITERATION_COUNT); SecretKeyFactory skf = SecretKeyFactory.getInstance(ALGORITHM); SecretKey key = skf.generateSecret(keyspec); /* * We'll use a ByteArrayOutputStream to conveniently gather up data as it's encrypted. */ ByteArrayOutputStream baos = new ByteArrayOutputStream(); /* * We've to a key, but to actually encrypt something, we need a "cipher". The cipher is created, then * initialized with the key, salt, and iteration count. We use a PBEParameterSpec to hold the salt and * iteration count needed by the Cipher object. */ PBEParameterSpec paramspec = new PBEParameterSpec(salt, ITERATION_COUNT); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key, paramspec, sr); /* * First, in our output, we need to save the salt in plain unencrypted form. */ baos.write(salt); /* * Next, encrypt our plaintext using the Cipher object, and write it into our output buffer. */ baos.write(cipher.doFinal(input)); /* * We're done. For security reasons, we probably want the PBEKeySpec object to clear its internal copy * of the password, so it can't be stolen later. */ keyspec.clearPassword(); return baos.toByteArray(); } public static final String decrypt(String s, String password) { try { return new String(decrypt(s.getBytes(), password.toCharArray())); } catch (Exception ex) { throw new RuntimeException(ex); } } public static byte[] decrypt(final byte[] input, final char[] password) throws Exception { /* * The first SALT_LENGTH bytes of the input ciphertext are actually the salt, not the ciphertext. */ byte[] salt = new byte[SALT_LENGTH]; System.arraycopy(input, 0, salt, 0, SALT_LENGTH); /* * We can now create a key from our salt (extracted just above), password, and iteration count. Same * procedure to create the key as in encrypt(). */ PBEKeySpec keyspec = new PBEKeySpec(password, salt, ITERATION_COUNT); SecretKeyFactory skf = SecretKeyFactory.getInstance(ALGORITHM); SecretKey key = skf.generateSecret(keyspec); /* * Once again, create a PBEParameterSpec object and a Cipher object. */ PBEParameterSpec paramspec = new PBEParameterSpec(salt, ITERATION_COUNT); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key, paramspec); /* * Decrypt the data. The parameters we pass into doFinal() instruct it to skip the first SALT_LENGTH * bytes of input (which are actually the salt), and then to encrypt the next (length - SALT_LENGTH) * bytes, which are the real ciphertext. */ byte[] output = cipher.doFinal(input, SALT_LENGTH, input.length - SALT_LENGTH); /* Clear the password and return the generated plaintext. */ keyspec.clearPassword(); return output; } }