【问题标题】:Using a custom subclass of SharedPreferences with PreferenceActivity or PreferenceFragment使用带有 PreferenceActivity 或 PreferenceFragment 的 SharedPreferences 的自定义子类
【发布时间】:2012-09-04 20:12:31
【问题描述】:

我正在使用 SharedPreferences 的自定义子类来加密我在应用中保存的设置,类似于此处第二个响应中所做的:What is the most appropriate way to store user settings in Android application

我必须保存的偏好数量正在增加。在我只是使用自定义视图来更新这些首选项之前,但这会变得很麻烦,我想改用 PreferenceActivity 或 PreferenceFragment。问题是,似乎没有办法让这些类中的任何一个使用我的子类访问我的数据,这意味着它从默认首选项文件中提取的数据将是乱码,因为它没有被解密。

我发现有些人创建了 Preference 的自定义实现来加密那里的数据,但我不希望这样做,因为数据已经在我的 SharedPreferences 子类中被加密/解密,我想保持这种方式。我也一直在查看 PreferenceActivity 和 PreferenceManager 的源代码,但我不确定解决这个问题的最佳方法。

有没有其他人幸运地完成了类似的事情并对我可以从哪里开始有任何建议?

【问题讨论】:

  • 我一直在考虑这样做以支持my own encrypted preferences project。我想出的唯一解决方案基本上需要分叉和克隆大部分偏好 UI 系统。至少,您必须分叉 PreferenceFragment(如果您支持 pre-HC 设备,则必须分叉 PreferenceActivity),以及它们直接引用的所有资源。这可能是需要的“全部”,但我不能排除需要分叉和克隆更多代码。
  • @JayLamont 抱歉打扰。请您提供加密数据的 Preference 自定义实现的链接。

标签: android sharedpreferences android-preferences


【解决方案1】:

这个怎么样:

  • 在 .SO 中存储一个字节 [16]。如果您不使用 .SO,请为此目的制作一个。
  • 使用该字节数组加密一个新字节[16],然后对结果进行 Base64 编码。在你的类文件中硬编码。

现在你已经设置了密钥,让我解释一下:

是的,可能有人可以窥视 .SO 并找到字节数组,从而成为您的密钥。但是由于加密的 key2 是 base64 编码的,他需要对其进行解码并使用所述密钥反转加密以提取 key2 字节。到目前为止,这只涉及拆解应用程序。

  • 当您要存储加密数据时,首先使用密钥 1 进行 AES 传递,然后使用密钥 2 和 IV* 进行 AES/CBC/Padding5 传递
  • 如果您想在每次存储新密码时更改 IV,您可以安全地对 IV 进行 Base64 编码并将其保存在 /data/data 文件夹中。

通过这两个步骤,不再需要拆卸应用程序,因为现在还需要控制运行时以获取加密数据。对于存储的密码,您不得不说这已经足够了。

然后您可以简单地将其存储到 SharedPreferences 中 :) 这样,如果您的 SharedPreferences 受到损害,数据仍然会被锁定。我不认为子类化它真的是正确的方法,但既然你已经写了你的类 - 哦,好吧。

这里有一些代码可以进一步说明我的意思

//use to encrypt key
public static byte[] encryptA(byte[] value) throws GeneralSecurityException, IOException
{
    SecretKeySpec sks = getSecretKeySpec(true);
    System.err.println("encrypt():\t" + sks.toString());
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, sks, cipher.getParameters());
    byte[] encrypted = cipher.doFinal(value);
    return encrypted;
}

//use to encrypt data
public static byte[] encrypt2(byte[] value) throws GeneralSecurityException, IOException
{
    SecretKeySpec key1 = getSecretKeySpec(true);
    System.err.println("encrypt():\t" + key1.toString());
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, key1, cipher.getParameters());
    byte[] encrypted = cipher.doFinal(value);

    SecretKeySpec key2 = getSecretKeySpec(false);
    System.err.println("encrypt():\t" + key2.toString());
    cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, key2, new IvParameterSpec(getIV()));
    byte[] encrypted2 = cipher.doFinal(encrypted);

    return encrypted2;//Base64Coder.encode(encrypted2);
}
//use to decrypt data
public static byte[] decrypt2(byte[] message, boolean A) throws GeneralSecurityException, IOException
{
    SecretKeySpec key1 = getSecretKeySpec(false);
    System.err.println("decrypt():\t" + key1.toString());
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, key1, new IvParameterSpec(getIV()));
    byte[] decrypted = cipher.doFinal(message);

    SecretKeySpec key2 = getSecretKeySpec(true);
    System.err.println("decrypt():\t" + key2.toString());
    cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, key2);
    byte[] decrypted2 = cipher.doFinal(decrypted);

    return decrypted2;
}

    //use to decrypt key
public static byte[] decryptKey(String message, byte[] key) throws GeneralSecurityException
{
    SecretKeySpec sks = new SecretKeySpec(key, ALGORITHM);
    System.err.println("decryptKey()");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, sks);
    byte[] decrypted = cipher.doFinal(Base64Coder.decode(message));
    return decrypted;
}

//method for fetching keys
private static SecretKeySpec getSecretKeySpec(boolean fromSO) throws NoSuchAlgorithmException, IOException, GeneralSecurityException
{
    return new SecretKeySpec(fromSO ? getKeyBytesFromSO() : getKeyBytesFromAssets(), "AES");
}

你怎么看?

我意识到这可能是题外话,因为您询问是否使用自己的 SharedPreferences,但我正在为您提供存储敏感数据问题的有效解决方案 :)

【讨论】:

  • “你怎么看?” -- 我们已经知道如何以加密的SharedPreferences 存储数据。我们不知道如何将其与标准 PreferenceScreen UI 联系起来。我看不到您的解决方案如何解决这个问题。
  • ...如果没用我想我会删除它。
  • 不是没用,但也没有回答问题。
  • 就我个人而言,我会将加密数据存储在常规 SharedPreferences 中,但如果您想反过来 - 这是您可以使用的后备计划 :) 我对它的发展方式很感兴趣也是。
【解决方案2】:

我认为通过将加密保留在已有的 SharedPrefs 子类中,可以限制模块化和关注点分离。

所以我建议重新考虑对偏好类本身进行子分类(例如 CheckBoxPreference)并在那里执行您的计算。

理想情况下,您还可以使用某种类型的组合或静态实用程序,这样虽然您可能必须对您使用的每种偏好类型进行子类化,但您可以使用单个位置来执行加密/解密计算。如果您需要加密或解密某些其他数据或 API 发生变化,这也将为您提供更大的灵活性。

对于子分类也许你可以这样做:

例如:

class ListPreferenceCrypt extends ListPreference
{
    ListPreferenceCrypt (Context context, AttributeSet attrs)   {
        super ( context, attrs );
    }
    ListPreferenceCrypt (Context context)   {
        super ( context );
    }

    @Override
    public void setValue( String value )
    {
        //encrypt value
        String encryptedVal = MyCryptUtil.encrypt(value);
        super.setValue ( encryptedVal );
    }

    @Override
    public String getValue( String key )
    {
        //decrypt value
        String decryptedValue = MyCryptUtil.decrypt(super.getValue ( key ));
        return decryptedValue;
    }

}

注意以上是伪代码,会有不同的方法覆盖


您的 XML 可能如下所示:

<PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android">

    <PreferenceCategory
            android:title="@string/inline_preferences">

        <com.example.myprefs.ListPreferenceCrypt
                android:key="listcrypt_preference"
                android:title="@string/title_listcrypt_preference"
                android:summary="@string/summary_listcrypt_preference" />

    </PreferenceCategory>

</PreferenceScreen>

编辑

注意事项/反编译

当我更多地考虑这一点时,我意识到其中一个警告是,在反编译 APK 时,这种方法并不是特别难以绕过。这确实给出了布局中覆盖类的完整类名(尽管可以通过不使用 XML 来避免)

但是,我认为这并不比子类化SharedPreferences 安全得多。这也容易被反编译。最终,如果您想要更强的安全性,您应该考虑替代存储方法。可能是您链接帖子中建议的 OAuth 或 AccountManager。

【讨论】:

  • OP 写道:“我发现有些人已经创建了 Preference 的自定义实现来加密那里的数据,但我不想这样做,因为数据已经被加密/解密在我的 SharedPreferences 子类中,我想保持这种状态。”
  • 对,但我的建议(正如我在帖子中所说)是重新考虑这种立场:)
  • 看起来越来越像这将是处理它的最佳方式,而我的 SharedPreferences 实现不应该是访问数据的万能网关。谢谢你的建议!!
猜你喜欢
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 2019-01-12
  • 1970-01-01
  • 2012-12-07
  • 1970-01-01
  • 2014-02-13
相关资源
最近更新 更多