package com.googlecode.totallylazy.security; import com.googlecode.totallylazy.functions.Function1; import com.googlecode.totallylazy.functions.Curried2; import com.googlecode.totallylazy.LazyException; import com.googlecode.totallylazy.Value; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; import static com.googlecode.totallylazy.Strings.bytes; import static com.googlecode.totallylazy.Strings.string; import static com.googlecode.totallylazy.security.GZip.gzip; import static com.googlecode.totallylazy.security.GZip.ungzip; import static javax.crypto.Cipher.DECRYPT_MODE; import static javax.crypto.Cipher.ENCRYPT_MODE; public class Key implements Value<String> { private final String secret; private Key(String secret) { this.secret = secret; } public static Key key() { return key(generate()); } public static Key key(String secret) { return new Key(secret); } public String encrypt(String value) { return Base64.encode(gzipAndEncrypt(value)); } public String encryptUrlSafe(String value) { return Base64.encodeUrlSafe(gzipAndEncrypt(value)); } private byte[] gzipAndEncrypt(String value) { try { return encrypt(gzip(bytes(value))); } catch (Exception e) { throw LazyException.lazyException(e); } } public String decrypt(String value) { return decryptAndUnzip(Base64.decode(value)); } public String decryptUrlSafe(String value) { return decryptAndUnzip(Base64.decodeUrlSafe(value)); } private String decryptAndUnzip(byte[] decode) { try { return string(ungzip(decrypt(decode))); } catch (Exception e) { throw LazyException.lazyException(e); } } @Override public String value() { return secret; } public static final String ALGORITHM = "AES"; private static String generate() { try { KeyGenerator generator = KeyGenerator.getInstance(ALGORITHM); generator.init(128); return Base64.encode(generator.generateKey().getEncoded()); } catch (NoSuchAlgorithmException e) { throw LazyException.lazyException(e); } } private byte[] encrypt(byte[] content) throws GeneralSecurityException { return cipher(ENCRYPT_MODE).doFinal(content); } private byte[] decrypt(byte[] content) throws GeneralSecurityException { return cipher(DECRYPT_MODE).doFinal(content); } private Cipher cipher(int mode) throws GeneralSecurityException { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(mode, new SecretKeySpec(Base64.decode(secret), ALGORITHM)); return cipher; } @Override public String toString() { return value(); } public Function1<? super String, String> encrypt() { return functions.encrypt().apply(this); } public Function1<? super String, String> decrypt() { return functions.decrypt().apply(this); } public static class functions { public static Curried2<Key, ? super String, String> encrypt() { return Key::encrypt; } public static Curried2<Key, ? super String, String> decrypt() { return Key::decrypt; } } }