Răsfoiți Sursa

SEC-366: Initial commit.

Ben Alex 19 ani în urmă
părinte
comite
49a2de8f0f

+ 123 - 0
core/src/main/java/org/acegisecurity/util/EncryptionUtils.java

@@ -0,0 +1,123 @@
+package org.acegisecurity.util;
+
+import java.io.UnsupportedEncodingException;
+import java.security.spec.KeySpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.DESedeKeySpec;
+
+import org.acegisecurity.AcegiSecurityException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.Validate;
+import org.springframework.util.Assert;
+
+/**
+ * A static utility class that can encrypt and decrypt text.
+ * 
+ * <p>This class is useful if you have simple needs and wish to use the DESede
+ * encryption cipher. More sophisticated requirements will need to use the
+ * Java crypto libraries directly.
+ * 
+ * @author Alan Stewart
+ * @author Ben Alex
+ */
+public class EncryptionUtils {
+
+	/**
+	 * This is a static class that should not be instantiated.
+	 */
+	private EncryptionUtils() {}
+
+	/**
+	 * Converts a String into a byte array using UTF-8, falling back to the
+	 * platform's default character set if UTF-8 fails.
+	 * 
+	 * @param input the input (required)
+	 * @return a byte array representation of the input string
+	 */
+	public static byte[] stringToByteArray(String input) {
+		Assert.hasLength(input, "Input required");
+		try {
+			return input.getBytes("UTF-8");
+		} catch (UnsupportedEncodingException fallbackToDefault) {
+			return input.getBytes();
+		}
+	}
+	
+	/**
+	 * Converts a byte array into a String using UTF-8, falling back to the
+	 * platform's default character set if UTF-8 fails.
+	 * 
+	 * @param byteArray the byte array to convert (required)
+	 * @return a string representation of the byte array
+	 */
+	public static String byteArrayToString(byte[] byteArray) {
+		Assert.notNull(byteArray, "ByteArray required");
+		Assert.isTrue(byteArray.length > 0, "ByteArray cannot be empty");
+		try {
+			return new String(byteArray, "UTF8");
+		} catch (final UnsupportedEncodingException e) {
+			return new String(byteArray);
+		}
+	}
+	
+	private static byte[] cipher(String key, byte[] passedBytes, int cipherMode) throws EncryptionException {
+		try {
+			final KeySpec keySpec = new DESedeKeySpec(stringToByteArray(key));
+			final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
+			final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
+			final SecretKey secretKey = keyFactory.generateSecret(keySpec);
+			cipher.init(cipherMode, secretKey);
+			return cipher.doFinal(passedBytes);
+		} catch (final Exception e) {
+			throw new EncryptionException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * Encrypts the inputString using the key.
+	 * 
+	 * @param key at least 24 character long key (required)
+	 * @param inputString the string to encrypt (required)
+	 * @return the encrypted version of the inputString
+	 * @throws EncryptionException in the event of an encryption failure
+	 */
+	public static String encrypt(String key, String inputString) throws EncryptionException {
+		isValidKey(key);
+		final byte[] cipherText = cipher(key, stringToByteArray(inputString), Cipher.ENCRYPT_MODE);
+		return byteArrayToString(Base64.encodeBase64(cipherText));
+	}
+
+	/**
+	 * Decrypts the inputString using the key.
+	 * 
+	 * @param key the key used to originally encrypt the string (required)
+	 * @param inputString the encrypted string (required)
+	 * @return the decrypted version of inputString
+	 * @throws EncryptionException in the event of an encryption failure
+	 */
+	public static String decrypt(String key, String inputString) throws EncryptionException {
+		Assert.hasText(key, "A key is required to attempt decryption");
+		final byte[] cipherText = cipher(key, Base64.decodeBase64(stringToByteArray(inputString)), Cipher.DECRYPT_MODE);
+		return byteArrayToString(cipherText);
+	}
+
+	private static void isValidKey(String key) {
+		Assert.hasText(key, "A key to perform the encryption is required");
+		Validate.isTrue(key.length() >= 24, "Key must be at least 24 characters long");
+	}
+
+	public static class EncryptionException extends AcegiSecurityException {
+		private static final long serialVersionUID = 1L;
+
+		public EncryptionException(String message, Throwable t) {
+			super(message, t);
+		}
+
+		public EncryptionException(String message) {
+			super(message);
+		}
+	}
+}

+ 93 - 0
core/src/test/java/org/acegisecurity/util/EncryptionUtilsTests.java

@@ -0,0 +1,93 @@
+package org.acegisecurity.util;
+
+import junit.framework.TestCase;
+
+import org.acegisecurity.util.EncryptionUtils.EncryptionException;
+
+/**
+ * JUnit tests for EncryptionUtils.
+ * 
+ * @author Alan Stewart
+ * @author Ben Alex
+ */
+public class EncryptionUtilsTests extends TestCase {
+	private final static String STRING_TO_ENCRYPT = "Alan K Stewart";
+	private final static String ENCRYPTION_KEY = "123456789012345678901234567890";
+
+	public void testEncryptsUsingDESEde() throws EncryptionException {
+		final String encryptedString = EncryptionUtils.encrypt(ENCRYPTION_KEY, STRING_TO_ENCRYPT);
+		assertEquals("3YIE8sIbaEoqGZZrHamFGQ==", encryptedString);
+	}
+
+	public void testEncryptionKeyCanContainLetters() throws EncryptionException {
+		final String encryptedString = EncryptionUtils.encrypt("ASDF asdf 1234 8983 jklasdf J2Jaf8", STRING_TO_ENCRYPT);
+		assertEquals("v4+DQoClx6qm5tJwBcRrkw==", encryptedString);
+	}
+
+	public void testDecryptsUsingDESEde() throws EncryptionException {
+		final String encryptedString = "3YIE8sIbaEoqGZZrHamFGQ==";
+		final String decryptedString = EncryptionUtils.decrypt(ENCRYPTION_KEY, encryptedString);
+		assertEquals(STRING_TO_ENCRYPT, decryptedString);
+	}
+
+	public void testCantEncryptWithNullEncryptionKey() throws EncryptionException {
+		try {
+			EncryptionUtils.encrypt(null, "");
+			fail("Should have thrown IAE");
+		} catch (final IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	public void testCantEncryptWithEmptyEncryptionKey() throws EncryptionException {
+		try {
+			EncryptionUtils.encrypt("", "");
+			fail("Should have thrown IAE");
+		} catch (final IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	public void testCantEncryptWithShortEncryptionKey() throws EncryptionException {
+		try {
+			EncryptionUtils.encrypt("01234567890123456789012", "");
+			fail("Should have thrown IAE");
+		} catch (final IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	public void testCantDecryptWithEmptyString() throws EncryptionException {
+		try {
+			EncryptionUtils.decrypt(ENCRYPTION_KEY, "");
+			fail("Should have thrown IAE");
+		} catch (final IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	public void testCantEncryptWithEmptyString() throws EncryptionException {
+		try {
+			EncryptionUtils.encrypt(ENCRYPTION_KEY, "");
+			fail("Should have thrown IAE");
+		} catch (final IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	public void testCantEncryptWithNullString() throws EncryptionException {
+		try {
+			EncryptionUtils.encrypt(ENCRYPTION_KEY, null);
+			fail("Should have thrown IAE");
+		} catch (final IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	public void testEncryptAndDecrypt() throws EncryptionException {
+		final String stringToEncrypt = "Alan Stewart";
+		final String encryptedString = EncryptionUtils.encrypt(ENCRYPTION_KEY, stringToEncrypt);
+		final String decryptedString = EncryptionUtils.decrypt(ENCRYPTION_KEY, encryptedString);
+		assertEquals(stringToEncrypt, decryptedString);
+	}
+}