/**
* Copyright (C) 2014 Iordan Iordanov
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
package com.iiordanov.bVNC;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Base64;
import android.util.Log;
public class PasswordManager {
private static String DELIM = "]";
String password = null;
public PasswordManager (String password) {
this.password = password;
}
private Cipher initialize (byte[] salt, byte[] iv, int cipherMode) throws UnsupportedEncodingException, NoSuchAlgorithmException,
InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException {
Cipher cipher = null;
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt,
Constants.numIterations, Constants.keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
if (cipherMode == Cipher.ENCRYPT_MODE) {
iv = randomBytes(cipher.getBlockSize());
}
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(cipherMode, key, ivParams);
return cipher;
}
public String encrypt (String plaintext) throws UnsupportedEncodingException, NoSuchAlgorithmException,
InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException {
Log.e("ENCRYPT", "ENCRYPT FUNCTION CALLED with password: " + plaintext);
byte[] ciphertext = null;
byte[] salt = randomBytes(Constants.saltLength);
Cipher cipher = initialize (salt, null, Cipher.ENCRYPT_MODE);
ciphertext = cipher.doFinal(plaintext.getBytes("UTF-8"));
String encrypted = String.format("%s%s%s%s%s", b64Encode(salt), DELIM, b64Encode(cipher.getIV()), DELIM, b64Encode(ciphertext));
Log.e("ENCRYPT-ENCRYPTED", encrypted);
return encrypted;
}
public String decrypt (String encrypted) throws UnsupportedEncodingException, NoSuchAlgorithmException,
InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException {
String[] fields = encrypted.split(DELIM);
byte[] salt = b64Decode(fields[0]);
byte[] iv = b64Decode(fields[1]);
byte[] ciphertext = b64Decode(fields[2]);
Cipher cipher = initialize (salt, iv, Cipher.DECRYPT_MODE);
byte[] plaintext = cipher.doFinal(ciphertext);
String decrypted = new String(plaintext , "UTF-8");
Log.e("DECRYPT-ENCRYPTED", encrypted);
Log.e("DECRYPT", "DECRYPT FUNCTION CALLED plaintext resulted in: " + decrypted);
return decrypted;
}
public static byte[] randomBytes (int length) {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[length];
random.nextBytes(bytes);
return bytes;
}
public static String randomString (int length) throws UnsupportedEncodingException {
return new String (PasswordManager.randomBytes(length), "UTF-8");
}
public static String randomBase64EncodedString (int length) throws UnsupportedEncodingException {
return b64Encode (PasswordManager.randomBytes(length));
}
public static String b64Encode (byte[] input) {
return Base64.encodeToString(input, Base64.NO_WRAP|Base64.NO_PADDING);
}
public static byte[] b64Decode (String input) {
return Base64.decode(input, Base64.NO_WRAP|Base64.NO_PADDING);
}
public static String computeHash (String password, byte[] saltBytes) throws NoSuchAlgorithmException,
InvalidKeySpecException {
char[] passwordChars = password.toCharArray();
PBEKeySpec spec = new PBEKeySpec(
passwordChars,
saltBytes,
Constants.numIterations,
Constants.keyLength
);
SecretKeyFactory key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hashedPassword = key.generateSecret(spec).getEncoded();
return String.format("%x", new BigInteger(hashedPassword));
}
public static String computeHash (String password, String salt) throws NoSuchAlgorithmException,
InvalidKeySpecException {
byte[] saltBytes = salt.getBytes();
return computeHash(password, saltBytes);
}
/**
* Example of how to set hash:
private void setMasterPasswordHash (String password) throws UnsupportedEncodingException,
NoSuchAlgorithmException, InvalidKeySpecException {
// Now compute and store the hash of the provided password and saved salt.
String salt = PasswordManager.randomBase64EncodedString(Constants.saltLength);
String hash = PasswordManager.computeHash(password, PasswordManager.b64Decode(salt));
SharedPreferences sp = getSharedPreferences("generalSettings", Context.MODE_PRIVATE);
Editor editor = sp.edit();
editor.putString("masterPasswordSalt", salt);
editor.putString("masterPasswordHash", hash);
editor.apply();
Log.i(TAG, "Setting master password hash.");
//Log.i(TAG, String.format("hash: %s, salt: %s", hash, new String(PasswordManager.b64Decode(salt))));
}
*/
/**
* Example of how to check hash:
SharedPreferences sp = getSharedPreferences("generalSettings", Context.MODE_PRIVATE);
String savedHash = sp.getString("masterPasswordHash", null);
byte[] savedSalt = PasswordManager.b64Decode(sp.getString("masterPasswordSalt", null));
//String savedSalt = sp.getString("masterPasswordSalt", null);
if (savedHash != null && savedSalt != null) {
String newHash = null;
try {
newHash = PasswordManager.computeHash(password, savedSalt);
//Log.i(TAG, String.format("savedHash: %s, savedSalt: %s, newHash: %s", savedHash, new String(savedSalt), newHash));
if (newHash.equals(savedHash)) {
result = true;
}
} catch (Exception e) { }
}
*/
}