Adrian
12 years ago
6 changed files with 336 additions and 4 deletions
@ -0,0 +1,115 @@ |
|||||||
|
package com.englishtown.stash.hook; |
||||||
|
|
||||||
|
import com.atlassian.sal.api.pluginsettings.PluginSettings; |
||||||
|
import org.apache.commons.codec.binary.Base64; |
||||||
|
|
||||||
|
import javax.crypto.*; |
||||||
|
import javax.crypto.spec.SecretKeySpec; |
||||||
|
import java.io.UnsupportedEncodingException; |
||||||
|
import java.security.InvalidKeyException; |
||||||
|
import java.security.NoSuchAlgorithmException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Service to encrypt/decrypt git user passwords |
||||||
|
*/ |
||||||
|
public class DefaultPasswordEncryptor implements PasswordEncryptor { |
||||||
|
|
||||||
|
private SecretKey secretKey; |
||||||
|
|
||||||
|
public static final String ENCRYPTED_PREFIX = "encrypted:"; |
||||||
|
public static final String SETTINGS_CRYPTO_KEY = "crypto.key"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void init(PluginSettings pluginSettings) { |
||||||
|
|
||||||
|
try { |
||||||
|
String keyBase64; |
||||||
|
Object value = pluginSettings.get(SETTINGS_CRYPTO_KEY); |
||||||
|
|
||||||
|
if (value == null || value.toString().isEmpty()) { |
||||||
|
KeyGenerator gen = KeyGenerator.getInstance("AES"); |
||||||
|
secretKey = gen.generateKey(); |
||||||
|
keyBase64 = Base64.encodeBase64String(secretKey.getEncoded()); |
||||||
|
pluginSettings.put(SETTINGS_CRYPTO_KEY, keyBase64); |
||||||
|
} else { |
||||||
|
keyBase64 = value.toString(); |
||||||
|
byte[] data = Base64.decodeBase64(keyBase64); |
||||||
|
secretKey = new SecretKeySpec(data, 0, data.length, "AES"); |
||||||
|
} |
||||||
|
|
||||||
|
} catch (NoSuchAlgorithmException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
protected byte[] runCipher(byte[] data, boolean encrypt) { |
||||||
|
|
||||||
|
try { |
||||||
|
int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; |
||||||
|
Cipher cipher = getCipher(mode); |
||||||
|
return cipher.doFinal(data); |
||||||
|
|
||||||
|
} catch (IllegalBlockSizeException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} catch (BadPaddingException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Cipher getCipher(int mode) { |
||||||
|
|
||||||
|
try { |
||||||
|
// Create Cipher
|
||||||
|
Cipher cipher = Cipher.getInstance("AES"); |
||||||
|
|
||||||
|
// Initialize Cipher with key
|
||||||
|
cipher.init(mode, secretKey); |
||||||
|
return cipher; |
||||||
|
|
||||||
|
} catch (NoSuchAlgorithmException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} catch (NoSuchPaddingException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} catch (InvalidKeyException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEncrypted(String password) { |
||||||
|
if (password == null || password.isEmpty()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return password.startsWith(ENCRYPTED_PREFIX); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String encrypt(String password) { |
||||||
|
if (isEncrypted(password)) { |
||||||
|
return password; |
||||||
|
} |
||||||
|
try { |
||||||
|
byte[] encryptedData = runCipher(password.getBytes("UTF-8"), true); |
||||||
|
return ENCRYPTED_PREFIX + Base64.encodeBase64String(encryptedData); |
||||||
|
} catch (UnsupportedEncodingException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String decrypt(String password) { |
||||||
|
if (!isEncrypted(password)) { |
||||||
|
return password; |
||||||
|
} |
||||||
|
try { |
||||||
|
byte[] encryptedData = Base64.decodeBase64(password.substring(ENCRYPTED_PREFIX.length())); |
||||||
|
byte[] clearData = runCipher(encryptedData, false); |
||||||
|
return new String(clearData, "UTF-8"); |
||||||
|
} catch (UnsupportedEncodingException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package com.englishtown.stash.hook; |
||||||
|
|
||||||
|
import com.atlassian.sal.api.pluginsettings.PluginSettings; |
||||||
|
|
||||||
|
/** |
||||||
|
* Service to encrypt/decrypt git user passwords |
||||||
|
*/ |
||||||
|
public interface PasswordEncryptor { |
||||||
|
|
||||||
|
void init(PluginSettings pluginSettings); |
||||||
|
|
||||||
|
boolean isEncrypted(String password); |
||||||
|
|
||||||
|
String encrypt(String password); |
||||||
|
|
||||||
|
String decrypt(String password); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,129 @@ |
|||||||
|
package com.englishtown.stash.hook; |
||||||
|
|
||||||
|
import com.atlassian.sal.api.pluginsettings.PluginSettings; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.runner.RunWith; |
||||||
|
import org.mockito.Mock; |
||||||
|
import org.mockito.runners.MockitoJUnitRunner; |
||||||
|
|
||||||
|
import static org.junit.Assert.*; |
||||||
|
import static org.mockito.Mockito.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* DefaultPasswordEncryptor unit tests |
||||||
|
*/ |
||||||
|
@RunWith(MockitoJUnitRunner.class) |
||||||
|
public class DefaultPasswordEncryptorTest { |
||||||
|
|
||||||
|
private final static String CRYPTO_KEY = "m3ys5YexQc7irRlmJeCwAw=="; |
||||||
|
|
||||||
|
@Mock |
||||||
|
private PluginSettings pluginSettings; |
||||||
|
|
||||||
|
private DefaultPasswordEncryptor encryptor; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() throws Exception { |
||||||
|
|
||||||
|
when(pluginSettings.get(DefaultPasswordEncryptor.SETTINGS_CRYPTO_KEY)).thenReturn(CRYPTO_KEY); |
||||||
|
|
||||||
|
encryptor = new DefaultPasswordEncryptor(); |
||||||
|
encryptor.init(pluginSettings); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testInit() throws Exception { |
||||||
|
|
||||||
|
DefaultPasswordEncryptor encryptor = new DefaultPasswordEncryptor(); |
||||||
|
PluginSettings pluginSettings = mock(PluginSettings.class); |
||||||
|
encryptor.init(pluginSettings); |
||||||
|
|
||||||
|
verify(pluginSettings).put(eq(DefaultPasswordEncryptor.SETTINGS_CRYPTO_KEY), anyString()); |
||||||
|
|
||||||
|
when(pluginSettings.get(DefaultPasswordEncryptor.SETTINGS_CRYPTO_KEY)).thenReturn(CRYPTO_KEY); |
||||||
|
|
||||||
|
encryptor.init(pluginSettings); |
||||||
|
|
||||||
|
// Verify put hasn't been called again
|
||||||
|
verify(pluginSettings).put(eq(DefaultPasswordEncryptor.SETTINGS_CRYPTO_KEY), anyString()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testRunCipher() throws Exception { |
||||||
|
|
||||||
|
DefaultPasswordEncryptor encryptor = new DefaultPasswordEncryptor(); |
||||||
|
encryptor.init(pluginSettings); |
||||||
|
|
||||||
|
String clearText = "clear text"; |
||||||
|
byte[] clearData = clearText.getBytes(); |
||||||
|
byte[] encryptedData; |
||||||
|
byte[] resultData; |
||||||
|
String resultText; |
||||||
|
|
||||||
|
encryptedData = encryptor.runCipher(clearData, true); |
||||||
|
|
||||||
|
resultData = encryptor.runCipher(encryptedData, false); |
||||||
|
resultText = new String(resultData); |
||||||
|
|
||||||
|
assertArrayEquals(clearData, resultData); |
||||||
|
assertEquals(clearText, resultText); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testIsEncrypted() throws Exception { |
||||||
|
|
||||||
|
DefaultPasswordEncryptor encryptor = new DefaultPasswordEncryptor(); |
||||||
|
String password = "clear-text-key"; |
||||||
|
boolean result; |
||||||
|
|
||||||
|
result = encryptor.isEncrypted(password); |
||||||
|
assertFalse(result); |
||||||
|
|
||||||
|
password = null; |
||||||
|
|
||||||
|
result = encryptor.isEncrypted(password); |
||||||
|
assertFalse(result); |
||||||
|
|
||||||
|
password = ""; |
||||||
|
|
||||||
|
result = encryptor.isEncrypted(password); |
||||||
|
assertFalse(result); |
||||||
|
|
||||||
|
password = DefaultPasswordEncryptor.ENCRYPTED_PREFIX + "encrypted-key"; |
||||||
|
|
||||||
|
result = encryptor.isEncrypted(password); |
||||||
|
assertTrue(result); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testEncrypt() throws Exception { |
||||||
|
|
||||||
|
String password = "test"; |
||||||
|
String encrypted; |
||||||
|
String clear; |
||||||
|
String result; |
||||||
|
|
||||||
|
assertFalse(encryptor.isEncrypted(password)); |
||||||
|
|
||||||
|
encrypted = encryptor.encrypt(password); |
||||||
|
assertTrue(encryptor.isEncrypted(encrypted)); |
||||||
|
|
||||||
|
result = encryptor.encrypt(encrypted); |
||||||
|
assertEquals(encrypted, result); |
||||||
|
|
||||||
|
clear = encryptor.decrypt(encrypted); |
||||||
|
assertEquals(password, clear); |
||||||
|
|
||||||
|
assertFalse(encryptor.isEncrypted(clear)); |
||||||
|
|
||||||
|
result = encryptor.decrypt(clear); |
||||||
|
assertEquals(clear, result); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue