Back to project page pswd.
The source code is released under:
MIT License
If you think the Android project pswd listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.waring.pswd; /* w w w .j av a 2 s .c om*/ import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.spongycastle.util.encoders.Hex; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.util.Base64; /** * This class holds the static functions related to PSWD */ public class PSWD { // A Base64 Encoded String for the AES Encryption Key (256 bits) private static final String ENCRYPT_KEY = "YOUR_PRIVATE_KEY"; /** * Check to see if the specified User Name has a cached User Token in SharedPreferences * @param context the Context of the Activity making the call * @param username the User Name of the User * @return true if the User has a cached User Token */ public static boolean hasUserToken(Context context, String username) { String token = getUserToken(context, username); if ( token.equals("") ) { return false; } else { return true; } } /** * Get the User Token of the specified User from SharedPreferences * @param context the Context of the Activity making the call * @param username the User Name of the User * @return a String of the plain-text User Token */ public static String getUserToken(Context context, String username) { // Get the User ID String userid = hash(username); // Get a saved token, if present // Return an empty String if no saved User Token is present SharedPreferences sharedPref = context.getSharedPreferences(MainActivity.PREFS_NAME, Context.MODE_PRIVATE); String token = sharedPref.getString(userid, ""); // Return the plain-text value of the User Token return PSWD.decrypt(token); } /** * Save the specified User Token for the given User * The tokens are stored in the SharedPreferences of the app (PSWD_PREFS.xml) * The key is the User ID (SHA256 hash of the User Name) * The value is the AES encrypted & base 64 encoded value of the User Token * @param context the Context of the Activity making the call * @param username the User Name of the User * @param token the User Token of the User */ public static void saveUserToken(Context context, String username, String token) { SharedPreferences sharedPref = context.getSharedPreferences(MainActivity.PREFS_NAME, Context.MODE_PRIVATE); Editor edit = sharedPref.edit(); // Key = User ID // Value = Encrypted User Token edit.putString(PSWD.getUserId(username), PSWD.encrypt(token)); edit.commit(); } /** * Remove the stored User Token from the SharedPreferences * @param context the Context of the Activity making the call * @param username the User Name of the User */ public static void eraseUserToken(Context context, String username) { SharedPreferences sharedPref = context.getSharedPreferences(MainActivity.PREFS_NAME, Context.MODE_PRIVATE); Editor edit = sharedPref.edit(); edit.remove(PSWD.getUserId(username)); edit.commit(); } /** * Generate the User Token for the specified User Name / Master Password pair * The User Token is a SHA256 hash of the User Name and Master Password that is looped 10^7 times * This is designed to take a significant amount of time. Progress of the hashing is monitored * using the HashListener interface (provides percent complete and estimated time remaining) * @param username the User Name of the User * @param password the Master Password of the User * @param listener the HashListener used to monitor progress * @return the generated User Token (plain-text String) */ public static String generateUserToken(String username, String password, HashListener listener) { String token = username + password; return hash(token, MainActivity.DEFAULT_K1, listener); } /** * Get the User ID for the specified User Name * This is a single SHA-256 hash of the User Name * @param username the User Name of the User * @return the User ID of the User */ public static String getUserId(String username) { return hash(username); } // GENERATE PASSWORD /** * Generate the site-specific password * @param username User Name * @param master_password Master Password * @param token User Token * @param domain site Domain * @param length length of final password * @param caps include uppercase letters * @param symbols include symbol characters * @param symchars the symbol characters to choose from * @param hashes the number of times the password is hashed * @return the final formatted site-specific password */ public static String generate(String username, String master_password, String token, String domain, int length, boolean caps, boolean symbols, String symchars, int hashes) { String key = domain + master_password + token; String password = key; // Hash the password 'hashes' number of times password = hash(password, hashes-1); // for the last hash: concatenate 2 hashes of password to lengthen the result password = hash(password + "1") + hash(password + "2"); // Decode the Hex String to a byte array int len = password.length(); byte[] hex = new byte[len / 2]; for (int i = 0; i < len; i += 2) { hex[i / 2] = (byte) ((Character.digit(password.charAt(i), 16) << 4) + Character.digit(password.charAt(i+1), 16)); } // Encode Password in Base64 password = new String(Base64.encode(hex, Base64.DEFAULT)); // Remove Base64 characters: + / = password = password.replace("+", ""); password = password.replace("/", ""); password = password.replace("=", ""); // Trim to final length password = password.substring(0, length); // generate nums string from key // these digits are used to determine the location of symbols and uppercase letters // hash the key (and a different salt) 4 times to ensure more than enough digits are generated String nums = hash(key + "numbers1") + hash(key + "numbers2") + hash(key + "numbers3") + hash(key + "numbers4"); nums = nums.replaceAll("[^\\d]", ""); // an index of the number being used from nums int num_index = 0; // ADD SYMBOLS, if requested if ( symbols ) { // create a list of characters to work with char[] s = password.toCharArray(); // get the number of symbols to add to the password // use the length of the password / the first digit int div = Integer.parseInt(Character.toString(nums.charAt(num_index))); num_index = num_index + 1; // increase num index if ( div <= 3 ) { // <= 3 creates too many symbols div = 4; // divide by at least 4 } int num_of_symbols = length / div; // loop to add each symbol for ( int i = 0; i < num_of_symbols; i++ ) { // get the location to add the symbols (two digits) int location = Integer.parseInt(Character.toString(nums.charAt(num_index))+Character.toString(nums.charAt(num_index+1))); location = location % length; num_index = num_index + 2; // get the symbol character location int symbol_location = Integer.parseInt(Character.toString(nums.charAt(num_index))+Character.toString(nums.charAt(num_index+1))); symbol_location = symbol_location % symchars.length(); num_index = num_index + 2; // get the symbol character Character symbol = symchars.charAt(symbol_location); // Add the symbol to the password s[location] = symbol; } password = new String(s); } // if caps are NOT requested, convert password to lowercase // if caps are requested, nothing needs to be done since Base64 includes a mix of case if ( !caps ) { password = password.toLowerCase(); } return password; } // SHA-256 HASH // /** * Hash the specified message using SHA-256 * @param msg the message to hash * @param passes the number of times the hash is repeated * @param listener the HashListener interface to monitor * the progress of long-running hashes * @return the hashed String */ private static String hash(String msg) { return hash(msg, 1, null); } private static String hash(String msg, int passes) { return hash(msg, passes, null); } private static String hash(String msg, int passes, HashListener listener) { try { try { MessageDigest sha = MessageDigest.getInstance("SHA-256"); byte[] bytes; // Monitor the progress of the loop int percent = 0; int prev_percent = 0; // Keep track of the amount of time each 1-percent interval // takes to complete - use the average to estimate the remaining time ArrayList<Long> deltas = new ArrayList<Long>(); long time = new Date().getTime(); // the initial time // Loop 'passes' number of times for ( int i = 0; i < passes; i++ ) { // the current progress percent = (int) Math.floor(((double) i / passes)*100); // Update the progress using the listener, if supplied // Only update the progress in 1-percent intervals if ( null != listener && percent > prev_percent ) { // ESTIMATE TIME REMAINING // Get amount of time for the previous percent chunk long currentTime = new Date().getTime(); long timeDelta = currentTime - time; time = currentTime; deltas.add(timeDelta); // Get the average time of the percent chunks long sum = 0; for (Long delta : deltas) { sum += delta; } long avgDelta = sum / deltas.size(); // Get the estimated remaining time int percentRemaining = 100 - percent; int timeRemaining = (int) avgDelta*percentRemaining; int sec = (timeRemaining/1000) % 60; int min = (timeRemaining/(1000*60)) % 60; // Format time remaining String format = ""; if (sec != 0) { format = sec + " seconds"; } if (min != 0) { format = min + " minutes " + format; } format = format + " remaining"; // Update the listener listener.updateProgress(percent, format); prev_percent = percent; } // Perform the Hash sha.update(msg.getBytes("iso-8859-1"), 0, msg.length()); bytes = sha.digest(); // Convert the digest to a Hex-encoded String msg = new String(Hex.encode(bytes)); } } catch (NoSuchAlgorithmException e) {} } catch (UnsupportedEncodingException e) {} return msg; } // AES ENCRYPTION / DECRYPTION // /** * Encrypt the plain-text message using AES * The output is Base64 encoded * @param plainMessage the message to encrypt * @return a Base64 encoded String of the encrypted message */ protected static String encrypt(final String plainMessage) { if ( plainMessage.equals("") ) { return plainMessage; } try { final byte[] symKeyData = Arrays.copyOfRange(Base64.decode(PSWD.ENCRYPT_KEY, Base64.DEFAULT), 0, 32); // Ensure 256 bits / 32 bytes final byte[] encodedMessage = plainMessage.getBytes(Charset.forName("UTF-8")); final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); final int blockSize = cipher.getBlockSize(); // create the key final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES"); // generate random IV using block size (possibly create a method for this) final byte[] ivData = new byte[blockSize]; final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG"); rnd.nextBytes(ivData); final IvParameterSpec iv = new IvParameterSpec(ivData); cipher.init(Cipher.ENCRYPT_MODE, symKey, iv); final byte[] encryptedMessage = cipher.doFinal(encodedMessage); // concatenate IV and encrypted message final byte[] ivAndEncryptedMessage = new byte[ivData.length + encryptedMessage.length]; System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize); System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage, blockSize, encryptedMessage.length); final String ivAndEncryptedMessageBase64 = Base64.encodeToString(ivAndEncryptedMessage, Base64.DEFAULT); return ivAndEncryptedMessageBase64; } catch (Exception e) { e.printStackTrace(); return plainMessage; } } /** * Decrypt the Base64 encoded String * @param ivAndEncryptedMessageBase64 the Base64 encoded encrypted String. * @return the plain-text message */ protected static String decrypt(final String ivAndEncryptedMessageBase64) { if ( ivAndEncryptedMessageBase64.equals("") ) { return ivAndEncryptedMessageBase64; } try { final byte[] symKeyData = Arrays.copyOfRange(Base64.decode(PSWD.ENCRYPT_KEY, Base64.DEFAULT), 0, 32); // Ensure 256 bits / 32 bytes final byte[] ivAndEncryptedMessage = Base64.decode(ivAndEncryptedMessageBase64, Base64.DEFAULT); final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); final int blockSize = cipher.getBlockSize(); // create the key final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES"); // retrieve random IV from start of the received message final byte[] ivData = new byte[blockSize]; System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize); final IvParameterSpec iv = new IvParameterSpec(ivData); // retrieve the encrypted message itself final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length - blockSize]; System.arraycopy(ivAndEncryptedMessage, blockSize, encryptedMessage, 0, encryptedMessage.length); cipher.init(Cipher.DECRYPT_MODE, symKey, iv); final byte[] encodedMessage = cipher.doFinal(encryptedMessage); final String message = new String(encodedMessage, Charset.forName("UTF-8")); return message; } catch (Exception e) { e.printStackTrace(); return ivAndEncryptedMessageBase64; } } // Hashing Progress Listener /** * Interface for monitoring long-running hashes * @param progress the percent complete * @param remaining a formatted String of the estimated time remaining */ public interface HashListener { public void updateProgress(int progress, String remaining); } }