##锁屏密码之九宫格密码 你们都会知道咱们为了确保手机的安全,常常会给手机上锁,也就是常说的密码。那么你有想过它是怎么加密又是怎么样存储的吗?想不想搞点恶做剧啥的(开玩笑的)java
首先说明一点,要想找到这个神器的东西,就必须进入到frameworks层中,这篇文章也算是我开始踏入frameworks的第一步吧,首先经过工具查找发现入口在LockPatternView.java,下面咱们就从这入手android
源码版本(Android sdk:5.1): 根目录:算法
frameworks/base/core/java/com/ android/internal/widget/ LockPatternView.java数据库
/**
* Displays and detects the user's unlock attempt, which is a drag of a finger across 9 regions of the screen. * Is also capable of displaying a static pattern in "in progress", "wrong" or * "correct" states. */ public class LockPatternView extends View { 复制代码
从源码中的注释中可知,这个View就是用于绘制九宫格和手势密码的地方,具体内容就不过多讲解了,重点仍是在密码这一块安全
在LockPatternView.java中的onSaveInstanceState
和onRestoreInstanceState
方法中提到了一个Util:bash
LockPatternUtils.patternToString(mPattern)
LockPatternUtils.stringToPattern(ss.getSerializedPattern()));
复制代码
最后咱们跟踪到LockPatternUtils这个类发现这是一个锁屏密码工具类,既然是密码工具类那么确定就会有保存密码的方法和检测密码的方法 今天咱们主要是想知道它是如何实现加密的,因此咱们只须要关心一下检测密码方法就行了dom
经过查找发现了一个checkPasswordHistory的方法,这个方法就是来对比密码是否一致的,可否成功解锁手机的工具
public boolean checkPasswordHistory(String password) {
String passwordHashString = new String(
passwordToHash(password, getCurrentOrCallingUserId()));
String passwordHistory = getString(PASSWORD_HISTORY_KEY);
if (passwordHistory == null) {
return false;
}
// Password History may be too long...
int passwordHashLength = passwordHashString.length();
int passwordHistoryLength = getRequestedPasswordHistoryLength();
if(passwordHistoryLength == 0) {
return false;
}
int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength
+ passwordHistoryLength - 1;
if (passwordHistory.length() > neededPasswordHistoryLength) {
passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength);
}
return passwordHistory.contains(passwordHashString);
}
复制代码
String passwordHashString = new String(passwordToHash(password, getCurrentOrCallingUserId()));
复制代码
这个代码中的passwordToHash方法就是咱们苦苦寻找的输入密码的算法测试
public byte[] passwordToHash(String password, int userId) {
if (password == null) {
return null;
}
String algo = null;
byte[] hashed = null;
try {
byte[] saltedPassword = (password + getSalt(userId)).getBytes();
byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
hashed = (toHex(sha1) + toHex(md5)).getBytes();
} catch (NoSuchAlgorithmException e) {
Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo);
}
return hashed;
}
复制代码
这个方法中传递的参数是输入的密码和当前用户的ID,通常来讲一个设备不会有多个用户,因此此处的userId默认为0 敲黑板了啊,重点来了,此处是最最最核心的加密算法了,公式 (saltedPassword+sha1),而后分别进行SHA-1和MD5加密,再将sha1和md5的值分别转换成Hex值进行拼接,最终获得究极密码。 此处注意几个核心点:ui
①getSalt(重点)如何获取到设备对应的salt值:
private String getSalt(int userId) {
long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
if (salt == 0) {
try {
salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
setLong(LOCK_PASSWORD_SALT_KEY, salt, userId);
Log.v(TAG, "Initialized lock password salt for user: " + userId);
} catch (NoSuchAlgorithmException e) {
// Throw an exception rather than storing a password we'll never be able to recover throw new IllegalStateException("Couldn't get SecureRandom number", e); } } return Long.toHexString(salt); } 复制代码
查看getSalt方法分析 long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
首先根据字段key为LOCK_PASSWORD_SALT_KEY从某个位置获取salt值,而后经过if判断,若是salt的值为0的话就随机生成一个,保存到key为LOCK_PASSWORD_SALT_KEY的那个方法,最后将salt值转换为Hex值便可。 查看getLong方法
try {
return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle);
} catch (RemoteException re) {
return defaultValue;
}
}
复制代码
猜测应该是保存到了某个数据库了,继续跟踪getLockSettings代码
private ILockSettings getLockSettings() {
if (mLockSettingsService == null) {
mLockSettingsService = LockPatternUtilsCache.getInstance(
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings")));
}
return mLockSettingsService;
}
复制代码
看到这里咱们发现这个方法竟然是AIDL的类型了,那么咱们就要它相应的Service类了 该Service路径为:
frameworks/base/services/core/java/com /android/server /LockSettingsService.java
而后咱们在这个类中寻找getLong方法
checkReadPermission(key, userId);
String value = mStorage.readKeyValue(key, null, userId);
return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
}
复制代码
到这里咱们能够很是的肯定是保存在数据库中的了,并且在LockSettingsService构造方法中咱们也看到了熟悉的SQLiteDatabase的字眼
private final LockSettingsStorage mStorage;
继续查看LockSettingsStorage.java类,在这个类有一个DatabaseHelper工具类,继续跟踪这个工具类
class DatabaseHelper extends SQLiteOpenHelper { private static final String TAG = "LockSettingsDB"; private static final String DATABASE_NAME = "locksettings.db"; `
咱们如今找到了这个存储的数据库文件**locksettings.db
**,那么它保存在哪里呢?莫急,请继续往下看,回到LockSettingsStorage这个类,咱们看到有以下几个属性
private static final String SYSTEM_DIRECTORY = "/system/";
private static final String LOCK_PATTERN_FILE = "gesture.key";
private static final String LOCK_PASSWORD_FILE = "password.key";
复制代码
看到这里,相信聪明的小伙伴都已经知道具体位置了吧。 当当当当,没错,路径就颇有多是在/data/system/password.key这个位置咯,若是你是懂逆向的小伙伴,如今就能够测试下咯
最后,留给小伙伴一个小题目,你能分析出手势密码的算法过程吗?