Android从0开始,加密算法分析

1,217 阅读4分钟

Android从0开始,加密算法分析

##锁屏密码之九宫格密码 大家都会知道我们为了确保手机的安全,经常会给手机上锁,也就是常说的密码。那么你有想过它是怎么加密又是怎么样存储的吗?想不想搞点恶作剧啥的(开玩笑的)

首先说明一点,要想找到这个神器的东西,就必须进入到frameworks层中,这篇文章也算是我开始踏入frameworks的第一步吧,首先通过工具查找发现入口在LockPatternView.java,下面我们就从这入手

源码版本(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中的onSaveInstanceStateonRestoreInstanceState方法中提到了一个Util:

LockPatternUtils.patternToString(mPattern)
LockPatternUtils.stringToPattern(ss.getSerializedPattern()));

最后我们跟踪到LockPatternUtils这个类发现这是一个锁屏密码工具类,既然是密码工具类那么肯定就会有保存密码的方法和检测密码的方法 今天我们主要是想知道它是如何实现加密的,所以我们只需要关心一下检测密码方法就好了

通过查找发现了一个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值进行拼接,最终得到究极密码。 此处注意几个核心点:

①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这个位置咯,如果你是懂逆向的小伙伴,现在就可以测试下咯

最后,留给小伙伴一个小题目,你能分析出手势密码的算法过程吗?