##鎖屏密碼之九宮格密碼 你們都會知道咱們爲了確保手機的安全,常常會給手機上鎖,也就是常說的密碼。那麼你有想過它是怎麼加密又是怎麼樣存儲的嗎?想不想搞點惡做劇啥的(開玩笑的)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這個位置咯,若是你是懂逆向的小夥伴,如今就能夠測試下咯
最後,留給小夥伴一個小題目,你能分析出手勢密碼的算法過程嗎?