package com.limegroup.gnutella.settings; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Properties; import com.limegroup.gnutella.ErrorService; /** * Class for a password setting. */ public final class PasswordSetting extends Setting { /* * Q: Why MD5 and not SHA1 as default? * A: MD5 is used in the Digest HTTP Authentication method * and we can recycle the password for it if necessary */ /** MD5 algorithm */ public static final String MD5 = "MD5"; /** Array of supported algorithms */ private static final String[] ALGORITHMS = { MD5 }; /** Separates the algorithm name and the encrypted password */ private static final String SEPERATOR = "/"; /** The hex numbers */ private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** The encryption algorithm */ private String algorithm; private String value; /** * Creates a new <tt>PasswordSetting</tt> instance with the specified key * and defualt value. * * @param key the constant key to use for the setting * @param defaultStr the default value to use for the setting */ PasswordSetting(Properties defaultProps, Properties props, String algorithm, String key, String defaultStr) { this(defaultProps, props, algorithm, key, defaultStr, null); } PasswordSetting(Properties defaultProps, Properties props, String algorithm, String key, String defaultStr, String simppKey) { super(defaultProps, props, key, (isEncrypted(defaultStr) ? defaultStr : encrypt(algorithm, defaultStr)), simppKey); postInitWithAlgorithm(algorithm); setPrivate(true); } /** * Returns the encryption algorithm */ public String getAlgorithm() { return algorithm; } /** * Returns true if the passed password equals the * current password */ public boolean equals(String password) { if (password == null) { return false; } if (!isEncrypted(password)) { password = encrypt(algorithm, password); } if (!password.startsWith(algorithm + SEPERATOR)) { throw new IllegalArgumentException("Algorithm mismatch"); } return value.equalsIgnoreCase(password); } /** * Accessor for the value of this setting. * * @return the value of this setting */ public String getValue() { return value; } /** * Mutator for this setting. * * @param str the <tt>String</tt> to store */ public void setValue(String str) { super.setValue(str); } /** * Load value from property string value * * @param sValue property string value */ protected void loadValue(String sValue) { if (algorithm != null && !isEncrypted(sValue)) { setValue(encrypt(algorithm, sValue)); return; } value = sValue; } /** * Sets the algorithm and encrypts the password if * necessary */ private void postInitWithAlgorithm(String algorithm) { this.algorithm = algorithm; if (algorithm != null && !isEncrypted(value)) { setValue(encrypt(algorithm, value)); } } /** * Returns true if password is encrypted (i.e. starts with * a known algorithm prefix) */ private static boolean isEncrypted(String password) { for(int i = 0; i < ALGORITHMS.length; i++) { if (password.startsWith(ALGORITHMS[i] + SEPERATOR)) { return true; } } return false; } /** * Encrypts the password and returns it as hex string */ private static String encrypt(String algorithm, String password) { return encrypt(algorithm, "UTF-8", password); } /** * Encrypts the password and returns it as hex string */ private static String encrypt(String algorithm, String encoding, String password) { if (password == null) return null; try { MessageDigest md = MessageDigest.getInstance(algorithm); byte[] digest = md.digest(password.getBytes(encoding)); return algorithm + SEPERATOR + toHexString(digest); } catch (UnsupportedEncodingException err) { ErrorService.error(err); return null; } catch (NoSuchAlgorithmException err) { ErrorService.error(err); return null; } } /** * Encodes and returns b as hex string */ private static String toHexString(byte[] b) { StringBuffer buffer = new StringBuffer(b.length * 2); for(int i = 0; i < b.length; i++) { buffer.append(HEX[((b[i] >> 4) & 0xF)]).append(HEX[b[i] & 0xF]); } return buffer.toString(); } }