15

ここでの 2 番目の応答で行われていることと同様に、SharedPreferences のカスタム サブクラスを使用して、保存した設定をアプリに暗号化しています: What is the most appropriate way to store user settings in Android application

保存しなければならない設定の数が増えています。以前は、カスタム ビューを使用してこれらの設定を更新していましたが、面倒になり、代わりに PreferenceActivity または PreferenceFragment を使用したいと考えています。問題は、これらのクラスのいずれかがサブクラスを使用してデータにアクセスする方法がないように見えることです。つまり、デフォルトの設定ファイルからプルされるデータは、復号化されていないため意味不明になります。

そこでデータを暗号化する Preference のカスタム実装を作成した人がいることがわかりましたが、SharedPreferences サブクラスでデータが既に暗号化/復号化されているため、それを維持したいと思います。仕方。私は PreferenceActivity と PreferenceManager のソース コードも調べてきましたが、これにアプローチする最善の方法がわかりません。

他の誰かがこのようなことを成し遂げて運が良かったので、どこから始めるべきかについて何か提案はありますか?

4

2 に答える 2

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>

編集

警告/逆コンパイル

これについてさらに考えてみると、注意点の 1 つは、APK を逆コンパイルするときにこの方法をバイパスするのは特に難しくないということであることに気付きました。これにより、レイアウト内のオーバーライドされたクラスの完全なクラス名が得られます (ただし、XML を使用しないことで回避できます)。

ただし、これがサブクラス化よりも大幅に安全性が低いとは思いませんSharedPreferences。それも、逆コンパイルの影響を受けやすいです。最終的に、より強力なセキュリティが必要な場合は、別の保存方法を検討する必要があります。おそらく、リンクされた投稿で提案されている OAuth または AccountManager です。

于 2012-09-20T20:04:19.473 に答える
1

これはどう:

  • byte[16] を .SO に格納します。.SO を使用しない場合は、その目的のためだけに作成してください。
  • そのバイト配列を使用して新しいバイト [16] を暗号化し、結果を Base64 エンコードします。クラスファイルにハードコードします。

キーのセットアップが完了したので、説明させてください。

はい、潜在的に.SOをのぞき見して、キーの代わりにバイト配列を見つけることができます。しかし、暗号化された key2 が base64 でエンコードされている場合、key2 バイトを抽出するには、それをデコードし、前述のキーで暗号化を逆にする必要があります。これまでのところ、これにはアプリの逆アセンブルのみが含まれます。

  • 暗号化されたデータを保存する場合は、最初に key1 で AES パスを実行し、次に Key2 と IV* で AES/CBC/Padding5 パスを実行します。
  • 新しいパスワードが保存されるたびに IV を変更したい場合は、IV を安全に Base64 エンコードして /data/data フォルダーに保存することができます。

この 2 つの手順により、アプリを逆アセンブルするだけでなく、暗号化されたデータにアクセスするためにランタイムを制御する必要が生じます。あなたが言わなければならないことは、保存されたパスワードにはかなり十分です.

次に、それを単純に 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 の使用について尋ねているため、トピックから外れている可能性があることは認識していますが、機密データの保存の問題に対する実用的な解決策を提供しています:)

于 2012-09-20T11:18:48.340 に答える