Android uygulamasında kullanıcı ayarlarını depolamanın en uygun yolu nedir


308

Kullanıcı adını / parolayı kullanarak sunucuya bağlanan bir uygulama oluşturuyorum ve kullanıcı her uygulama başladığında parolayı girmek zorunda kalmamak için "Parolayı kaydet" seçeneğini etkinleştirmek istiyorum.

Paylaşılan Tercihler ile yapmaya çalışıyordum ama bunun en iyi çözüm olup olmadığından emin değilim.

Android uygulamasında kullanıcı değerlerinin / ayarlarının nasıl saklanacağı konusunda herhangi bir öneriyi takdir ediyorum.

Yanıtlar:


233

Genel olarak SharedPreferences tercihleri ​​saklamak için en iyi bahistir, bu nedenle genel olarak uygulama ve kullanıcı ayarlarını kaydetmek için bu yaklaşımı öneriyorum.

Burada tek endişe alan ne tasarruf olduğunu. Parolalar her zaman saklamak zor bir şeydir ve bunları açık metin olarak saklamaktan özellikle dikkatli olurum. Android mimarisi, uygulamanızın SharedPreferences'ın diğer uygulamaların değerlere erişmesini önlemek için korumalı olarak yerleştirileceği şekildedir, bu nedenle orada bir miktar güvenlik vardır, ancak bir telefona fiziksel erişim, değerlere erişime izin verebilir.

Mümkünse, sunucuyu erişim sağlamak için anlaşmalı bir simge kullanacak şekilde değiştirmeyi düşünürüm , OAuth gibi bir şey . Alternatif olarak, önemsiz olmasa da, bir tür şifreleme deposu oluşturmanız gerekebilir. En azından, şifreyi diske yazmadan önce şifrelediğinizden emin olun.


3
Ne demek istediğini sandboxed ile açıklayabilir misin?
Abhijit

14
korumalı alandaki bir program, işlemi ve bilgileri (bu paylaşılan tercihler gibi) uygulamaların geri kalanından gizli kalan herhangi bir uygulamadır. Bir pakette çalışan bir android uygulaması başka bir paketin içindeki hiçbir şeye doğrudan erişemez. Bu yüzden aynı paketteki (her zaman sizin olan) uygulamalar diğerlerinden gelen bilgilere erişebilir
Korcholis

@ Reto Meier benim bir belirteci kullandığım için halka açık web hizmetlerini korumak, paylaşılan tercihler üzerinde saklamak güvenli midir? i cihaz köklü olarak bulursa tüm paylaşılan tercihler verileri siler benim uygulamada bir bootup yayın alıcı var. Jetonumu korumak için bu yeterli mi?
pyus13

1
Başına android-developers.blogspot.com/2013/02/... , kullanıcı kimlik bilgileri MODE_PRIVATE bayrağı seti ile saklanmalıdır ve dahili olarak depolanır (saldırısına sonuçta açık yerel şifre her türlü depolama hakkında aynı uyarılar ile). Bununla MODE_PRIVATEbirlikte, yerel olarak depolanan verileri gizleme etkinliği açısından, dahili depolamada oluşturulan bir dosyayla aynı şeyi yapmaya eşdeğer SharedPreferences ile kullanmak mı?
qix

6
Paylaşılan tercihlerde bir şifre saklamayın. Kullanıcı telefonu kaybederse, şifreyi kaybeder. Okunacak. Bu şifreyi başka bir yerde kullansalar, kullandıkları her yer tehlikeye girer. Buna ek olarak, bu hesabı kalıcı olarak kaybettiniz çünkü şifre ile şifrenizi değiştirebilirler. Bunu yapmanın doğru yolu, parolayı bir kez sunucuya göndermek ve bir giriş belirteci geri almaktır. Bunu paylaşılan tercihte saklayın ve her istekle birlikte gönderin. Bu belirteç tehlikeye atılırsa, başka hiçbir şey kaybolmaz.
Gabe Sechan

211

Reto ve fiXedd ile hemfikirim. Objektif olarak konuşmak gerekirse, SharedPreferences'de şifreleri şifrelemek için önemli zaman ve çaba harcamak çok mantıklı değildir, çünkü tercihler dosyanıza erişimi olan herhangi bir saldırganın uygulamanızın ikili dosyasına ve dolayısıyla şifrelenmemiş anahtarların erişimine sahip olma olasılığı oldukça yüksektir. parola.

Bununla birlikte, parolalarını SharedPreferences'da açık metin olarak saklayan ve bu uygulamalara olumsuz ışık tutan mobil uygulamaları tanımlamaya devam eden bir tanıtım girişimi var gibi görünüyor. Bazı örnekler için bkz. Http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ ve http://viaforensics.com/appwatchdog .

Genel olarak güvenliğe daha fazla dikkat edilmemize rağmen, bu konudaki bu tür bir dikkatin aslında genel güvenliğimizi önemli ölçüde artırmayacağını iddia ediyorum. Ancak, algılar oldukları gibi, işte SharedPreferences'a yerleştirdiğiniz verileri şifrelemek için bir çözüm.

Kendi SharedPreferences nesnenizi bu nesneye sarın, okuduğunuz / yazdığınız tüm veriler otomatik olarak şifrelenecek ve şifresi çözülecektir. Örneğin.

final SharedPreferences prefs = new ObscuredSharedPreferences( 
    this, this.getSharedPreferences(MY_PREFS_FILE_NAME, Context.MODE_PRIVATE) );

// eg.    
prefs.edit().putString("foo","bar").commit();
prefs.getString("foo", null);

İşte sınıfın kodu:

/**
 * Warning, this gives a false sense of security.  If an attacker has enough access to
 * acquire your password store, then he almost certainly has enough access to acquire your
 * source binary and figure out your encryption key.  However, it will prevent casual
 * investigators from acquiring passwords, and thereby may prevent undesired negative
 * publicity.
 */
public class ObscuredSharedPreferences implements SharedPreferences {
    protected static final String UTF8 = "utf-8";
    private static final char[] SEKRIT = ... ; // INSERT A RANDOM PASSWORD HERE.
                                               // Don't use anything you wouldn't want to
                                               // get out there if someone decompiled
                                               // your app.


    protected SharedPreferences delegate;
    protected Context context;

    public ObscuredSharedPreferences(Context context, SharedPreferences delegate) {
        this.delegate = delegate;
        this.context = context;
    }

    public class Editor implements SharedPreferences.Editor {
        protected SharedPreferences.Editor delegate;

        public Editor() {
            this.delegate = ObscuredSharedPreferences.this.delegate.edit();                    
        }

        @Override
        public Editor putBoolean(String key, boolean value) {
            delegate.putString(key, encrypt(Boolean.toString(value)));
            return this;
        }

        @Override
        public Editor putFloat(String key, float value) {
            delegate.putString(key, encrypt(Float.toString(value)));
            return this;
        }

        @Override
        public Editor putInt(String key, int value) {
            delegate.putString(key, encrypt(Integer.toString(value)));
            return this;
        }

        @Override
        public Editor putLong(String key, long value) {
            delegate.putString(key, encrypt(Long.toString(value)));
            return this;
        }

        @Override
        public Editor putString(String key, String value) {
            delegate.putString(key, encrypt(value));
            return this;
        }

        @Override
        public void apply() {
            delegate.apply();
        }

        @Override
        public Editor clear() {
            delegate.clear();
            return this;
        }

        @Override
        public boolean commit() {
            return delegate.commit();
        }

        @Override
        public Editor remove(String s) {
            delegate.remove(s);
            return this;
        }
    }

    public Editor edit() {
        return new Editor();
    }


    @Override
    public Map<String, ?> getAll() {
        throw new UnsupportedOperationException(); // left as an exercise to the reader
    }

    @Override
    public boolean getBoolean(String key, boolean defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Boolean.parseBoolean(decrypt(v)) : defValue;
    }

    @Override
    public float getFloat(String key, float defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Float.parseFloat(decrypt(v)) : defValue;
    }

    @Override
    public int getInt(String key, int defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Integer.parseInt(decrypt(v)) : defValue;
    }

    @Override
    public long getLong(String key, long defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Long.parseLong(decrypt(v)) : defValue;
    }

    @Override
    public String getString(String key, String defValue) {
        final String v = delegate.getString(key, null);
        return v != null ? decrypt(v) : defValue;
    }

    @Override
    public boolean contains(String s) {
        return delegate.contains(s);
    }

    @Override
    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }

    @Override
    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }




    protected String encrypt( String value ) {

        try {
            final byte[] bytes = value!=null ? value.getBytes(UTF8) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(Base64.encode(pbeCipher.doFinal(bytes), Base64.NO_WRAP),UTF8);

        } catch( Exception e ) {
            throw new RuntimeException(e);
        }

    }

    protected String decrypt(String value){
        try {
            final byte[] bytes = value!=null ? Base64.decode(value,Base64.DEFAULT) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(pbeCipher.doFinal(bytes),UTF8);

        } catch( Exception e) {
            throw new RuntimeException(e);
        }
    }

}

3
FYI Base64, API seviye 8 (2.2) ve sonrasında kullanılabilir. Önceki işletim sistemleri için iharder.sourceforge.net/current/java/base64 veya başka bir şey kullanabilirsiniz .
emmby

34
Evet, bunu yazdım.
Kullanmaktan

8
Size katılıyorum. Ancak şifre yalnızca sunucuda kullanılıyorsa neden Genel / özel anahtar şifrelemesini kullanmıyorsunuz? Parolayı kaydederken istemcide genel anahtar. Müşteri asla açık metin parolasını bir daha okumak zorunda kalmayacak, değil mi? Sunucu daha sonra özel anahtarla şifresini çözebilir. Birisi uygulama kaynak kodunuzdan geçse bile, sunucunuzu hackleyip özel anahtarı almaları dışında şifreyi alamazlar.
Patrick Boos

4
Bu koda birkaç özellik ekledim ve github.com/RightHandedMonkey/WorxForUs_Library/blob/master/src/… adresinden github'a yerleştirdim . Artık şifrelenmemiş tercihleri ​​şifrelenmiş olana geçirmeyi ele alıyor. Ayrıca çalışma zamanında anahtarı üretir, bu nedenle uygulamanın ayrıştırılması anahtarı serbest bırakmaz.
RightHandedMonkey

3
Geç ekleme, ancak @PatrickBoos'un yorumu harika bir fikir. Bununla ilgili bir sorun, şifreyi şifrelemiş olsanız bile, bu şifreyi çalan bir saldırganın sunucularınızda hala oturum açabilmesidir, çünkü sunucularınız şifre çözmeyi yapar. Bu yaklaşıma ek olarak, şifreyi bir zaman damgasıyla birlikte şifrelemek de mümkündür. Bu şekilde, örneğin, yalnızca yakın geçmişte kaydedilmiş şifrelere izin vermenize ("jetonunuza" bir bitiş tarihi eklemek gibi), hatta belirli kullanıcıların belirli bir tarihten bu yana zaman damgasına sahip olmasını zorunlu kılabilirsiniz ("iptal edelim" eski "simgeler").
adevine

29

Bir Android Etkinliğinde tek bir tercihi saklamanın en basit yolu hakkında böyle bir şey yapmaktır:

Editor e = this.getPreferences(Context.MODE_PRIVATE).edit();
e.putString("password", mPassword);
e.commit();

Bunların güvenliği konusunda endişeleriniz varsa, saklamadan önce şifreyi her zaman şifreleyebilirsiniz.


9
Bu basit yaklaşım hakkında sizinle daha fazla anlaşamadım; Ancak, sakladığınız şifrelerin güvenliği konusunda her zaman endişelenmelisiniz? Başvurunuza bağlı olarak, çalınan kişisel bilgiler için potansiyel yükümlülükleriniz vardır. Sadece banka hesapları veya eşit derecede önemli bir şeye gerçek şifreleri saklamaya çalışan herkes için bunu işaret edin. Yine de sana oy veriyorum.
While-E

1
Parolayı depolayan anahtarı nerede depolarsınız? Paylaşılan tercihlere diğer kullanıcılar tarafından erişilebiliyorsa, anahtar da öyle.
OrhanC1

@ OrhanC1 cevabı aldın mı?
eRaisedToX

10

Richard tarafından sağlanan snippet'i kullanarak şifreyi kaydetmeden önce şifreleyebilirsiniz. Ancak tercihler API'sı değeri yakalamak ve şifrelemek için kolay bir yol sağlamaz - bir OnPreferenceChange dinleyicisi aracılığıyla kaydedilmesini engelleyebilirsiniz ve teorik olarak bunu bir preferenceChangeListener aracılığıyla değiştirebilirsiniz, ancak bu sonsuz bir döngü ile sonuçlanır.

Daha önce bunu başarmak için "gizli" bir tercih eklemeyi önermiştim. Kesinlikle en iyi yol değil. Daha uygulanabilir olduğunu düşündüğüm iki seçenek daha sunacağım.

Birincisi, en basit olanı bir tercihtirChangeListener, girilen değeri alabilir, şifreleyebilir ve daha sonra alternatif bir tercihler dosyasına kaydedebilirsiniz:

  public boolean onPreferenceChange(Preference preference, Object newValue) {
      // get our "secure" shared preferences file.
      SharedPreferences secure = context.getSharedPreferences(
         "SECURE",
         Context.MODE_PRIVATE
      );
      String encryptedText = null;
      // encrypt and set the preference.
      try {
         encryptedText = SimpleCrypto.encrypt(Preferences.SEED,(String)newValue);

         Editor editor = secure.getEditor();
         editor.putString("encryptedPassword",encryptedText);
         editor.commit();
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      // always return false.
      return false; 
   }

İkinci yol ve şimdi tercih ettiğim yöntem, kendi özel tercihinizi oluşturmak, EditTextPreference öğesini genişletmek, @ setText()ve getText()yöntemlerini geçersiz kılmak , böylece setText()şifreyi şifrelemek ve getText()null değerini döndürmektir.


Bu oldukça eski olduğunu biliyorum, ama EditTextPreference özel sürümü için kodunuzu gönderebilir misiniz, lütfen?
RenniePet

Boş ver, burada kullanılabilir bir örnek buldum groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M ve şimdi çalışıyorum. Bu yaklaşımı önerdiğiniz için teşekkürler.
RenniePet

6

Tamam; Cevap biraz karışık olduğu için bir süredir, ama burada birkaç yaygın cevap var. Bunu çılgın gibi araştırdım ve iyi bir cevap oluşturmak zordu

  1. Kullanıcının cihazı rootlamadığını varsayarsanız MODE_PRIVATE yöntemi genellikle güvenli kabul edilir. Verileriniz, dosya sisteminin yalnızca orijinal program tarafından erişilebilen bir bölümünde düz metin olarak saklanır. Bu, rootlu bir cihazdaki başka bir uygulama ile şifreyi kolayca yakalar. Sonra yine, köklü cihazları desteklemek istiyor musunuz?

  2. AES hala yapabileceğiniz en iyi şifrelemedir. Bunu yayınladığımdan bu yana bir süre sonra yeni bir uygulamaya başlıyorsanız bunu aramayı unutmayın. Bununla ilgili en büyük sorun "Şifreleme anahtarıyla ne yapmalı?"

Şimdi "Anahtarla ne yapmalı?" bölümü. Zor kısmı bu. Anahtarı almak o kadar da kötü değil. Bazı şifre almak ve onu oldukça güvenli bir anahtar yapmak için bir anahtar türetme işlevi kullanabilirsiniz. "PKFDF2 ile kaç geçiş yapıyorsunuz?" Gibi sorunlara giriyorsunuz, ama bu başka bir konu

  1. İdeal olarak, AES anahtarını cihaz dışında saklarsınız. Anahtarı sunucudan güvenli, güvenilir ve güvenli bir şekilde almanın iyi bir yolunu bulmalısınız.

  2. Bir tür giriş diziniz var (uzaktan erişim için yaptığınız orijinal giriş dizisi bile). Anahtar oluşturucunuzun iki çalışmasını aynı parolayla yapabilirsiniz. Bunun nasıl çalıştığı, anahtarı yeni bir tuz ve yeni bir güvenli başlatma vektörü ile iki kez türetmenizdir. Oluşturulan parolalardan birini aygıtta depolarsınız ve ikinci parolayı AES anahtarı olarak kullanırsınız.

Oturum açtığınızda, yerel oturum açmadaki anahtarı yeniden türetir ve depolanan anahtarla karşılaştırırsınız. Bu yapıldıktan sonra, AES için 2 numaralı derivasyon anahtarını kullanırsınız.

  1. "Genel olarak güvenli" yaklaşımını kullanarak, verileri AES kullanarak şifrelersiniz ve anahtarı MODE_PRIVATE içinde saklarsınız. Bu, yakın tarihli bir Android blog yayını tarafından önerilir. İnanılmaz derecede güvenli değil, ancak düz metin üzerinden bazı insanlar için çok daha iyi

Bunların birçok varyasyonunu yapabilirsiniz. Örneğin, tam oturum açma sırası yerine hızlı bir PIN (türetilmiş) yapabilirsiniz. Hızlı PIN, tam giriş sırası kadar güvenli olmayabilir, ancak düz metinden çok daha güvenli


5

Bunun biraz büyücü olduğunu biliyorum, ancak Android AccountManager'ı kullanmalısınız . Bu senaryo için özel olarak oluşturulmuştur. Biraz hantal ama yaptığı şeylerden biri, SIM kart değişirse yerel kimlik bilgilerini geçersiz kılmaktır, bu nedenle biri telefonunuzu kaydırır ve içine yeni bir SIM atarsa, kimlik bilgileriniz tehlikeye girmez.

Bu aynı zamanda kullanıcıya cihazda sahip oldukları herhangi bir hesap için depolanan kimlik bilgilerine tek bir yerden erişmenin (ve potansiyel olarak silinmesinin) hızlı ve kolay bir yolunu sunar.

SampleSyncAdapter , saklanan hesap kimlik bilgilerini kullanan bir örnektir.


1
AccountManager'ı kullanmanın yukarıda sağlanan diğer yöntemlerden daha güvenli olmadığını lütfen unutmayın! developer.android.com/training/id-auth/…
Sander Versluys

1
AccountManager için kullanım durumu, hesabın farklı uygulamalar ve farklı yazarlardan gelen uygulamalar arasında paylaşılması gerektiğidir. Şifreyi saklamak ve herhangi bir talep eden uygulamaya vermek uygun olmaz. Kullanıcı / parola kullanımı yalnızca tek bir uygulama içinse AccountManager'ı kullanmayın.
dolmen

1
@ dolmen, bu pek doğru değil. AccountManager, UID'si Authenticator'ınkiyle eşleşmeyen hiçbir uygulamaya hesap şifresini vermez. Adı, evet; auth jetonu, evet; şifre, hayır. Denerseniz, bir SecurityException oluşturur. Ve kullanım durumu bundan daha geniştir. developer.android.com/training/id-auth/identify.html
Jon O

5

Android'de genel olarak şifrelerin güvenliğini sağlamak için şapkamı ringe atacağım. Android'de, cihaz ikilisinin güvenliği ihlal edilmiş olarak düşünülmelidir - bu, doğrudan kullanıcı kontrolünde olan herhangi bir son uygulama için aynıdır. Kavramsal olarak, bir bilgisayar korsanı, şifreyi çözmek ve şifrelenmiş şifrelerinizi köklendirmek için ikili dosyaya gerekli erişimi kullanabilir.

Bu nedenle, güvenlik sizin için büyük bir endişe ise, oraya atmak istediğim iki öneri var:

1) Gerçek şifreyi saklamayın. Verilen bir erişim belirtecini saklayın ve oturum sunucusu tarafının kimliğini doğrulamak için erişim belirtecini ve telefonun imzasını kullanın. Bunun yararı, jetonun sınırlı bir süreye sahip olmasını, orijinal şifreden ödün vermemenizi ve daha sonra trafikle ilişkilendirmek için kullanabileceğiniz iyi bir imzaya sahip olmanızdır (örneğin saldırı girişimlerini kontrol etmek ve geçersiz kılmak için) belirteci yararsız hale getirir).

2) 2 faktörlü kimlik doğrulama kullanın. Bu daha sinir bozucu ve müdahaleci olabilir, ancak bazı uyum durumları için kaçınılmazdır.



2

Bu, soru başlığına dayanarak buraya gelenler için ek bir cevaptır (yaptığım gibi) ve şifreleri kaydetmeyle ilgili güvenlik sorunlarıyla uğraşmak zorunda kalmazsınız.

Paylaşılan Tercihler nasıl kullanılır?

Kullanıcı ayarları genellikle SharedPreferencesbir anahtar / değer çifti kullanılarak Android'de yerel olarak kaydedilir . Sen kullanmak Stringkaydetmek veya ilişkili değeri aranacak anahtarı.

Paylaşılan Tercihlere Yaz

String key = "myInt";
int valueToSave = 10;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(key, valueToSave).commit();

Hemen yerine arka planda kaydetmek apply()yerine kullanın commit().

Paylaşılan Tercihlerden Okuyun

String key = "myInt";
int defaultValue = 0;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
int savedValue = sharedPref.getInt(key, defaultValue);

Anahtar bulunmazsa varsayılan değer kullanılır.

notlar

  • Yukarıda yaptığım gibi birden çok yerde yerel bir anahtar String kullanmak yerine, tek bir konumda sabit kullanmak daha iyi olur. Ayarlar etkinliğinizin en üstünde böyle bir şey kullanabilirsiniz:

    final static String PREF_MY_INT_KEY = "myInt";
  • Ben kullanılan intbenim örnekte, ama aynı zamanda da kullanabilirsiniz putString(), putBoolean(), getString(), getBoolean(), vb

  • Daha fazla ayrıntı için belgelere bakın.
  • SharedPreferences almanın birden çok yolu vardır. Neye dikkat etmeniz gerektiğini öğrenmek için bu cevaba bakınız .

1

Bu cevap Mark tarafından önerilen bir yaklaşıma dayanmaktadır. EditTextPreference sınıfının, görünümde görülen düz metin ile tercihler deposunda saklanan parolanın şifrelenmiş bir sürümü arasında ileri geri dönüştürme yapan özel bir sürümü oluşturulur.

Bu konuya cevap veren çoğu kişi tarafından belirtildiği gibi, güvenlik derecesi kısmen kullanılan şifreleme / şifre çözme koduna bağlı olmasına rağmen, bu çok güvenli bir teknik değildir. Ancak oldukça basit ve kullanışlı ve en sıradan gözetlemeyi engelleyecek.

Özel EditTextPreference sınıfının kodu şöyledir:

package com.Merlinia.OutBack_Client;

import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
import android.util.Base64;

import com.Merlinia.MEncryption_Main.MEncryptionUserPassword;


/**
 * This class extends the EditTextPreference view, providing encryption and decryption services for
 * OutBack user passwords. The passwords in the preferences store are first encrypted using the
 * MEncryption classes and then converted to string using Base64 since the preferences store can not
 * store byte arrays.
 *
 * This is largely copied from this article, except for the encryption/decryption parts:
 * https://groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M
 */
public class EditPasswordPreference  extends EditTextPreference {

    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context) {
        super(context);
    }


    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }


    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context, AttributeSet attributeSet, int defaultStyle) {
        super(context, attributeSet, defaultStyle);
    }


    /**
     * Override the method that gets a preference from the preferences storage, for display by the
     * EditText view. This gets the base64 password, converts it to a byte array, and then decrypts
     * it so it can be displayed in plain text.
     * @return  OutBack user password in plain text
     */
    @Override
    public String getText() {
        String decryptedPassword;

        try {
            decryptedPassword = MEncryptionUserPassword.aesDecrypt(
                     Base64.decode(getSharedPreferences().getString(getKey(), ""), Base64.DEFAULT));
        } catch (Exception e) {
            e.printStackTrace();
            decryptedPassword = "";
        }

        return decryptedPassword;
    }


    /**
     * Override the method that gets a text string from the EditText view and stores the value in
     * the preferences storage. This encrypts the password into a byte array and then encodes that
     * in base64 format.
     * @param passwordText  OutBack user password in plain text
     */
    @Override
    public void setText(String passwordText) {
        byte[] encryptedPassword;

        try {
            encryptedPassword = MEncryptionUserPassword.aesEncrypt(passwordText);
        } catch (Exception e) {
            e.printStackTrace();
            encryptedPassword = new byte[0];
        }

        getSharedPreferences().edit().putString(getKey(),
                                          Base64.encodeToString(encryptedPassword, Base64.DEFAULT))
                .commit();
    }


    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        if (restoreValue)
            getEditText().setText(getText());
        else
            super.onSetInitialValue(restoreValue, defaultValue);
    }
}

Bu nasıl kullanılabileceğini gösterir - bu, tercihler ekranını yönlendiren "öğeler" dosyasıdır. Üç sıradan EditTextPreference görünümü ve özel EditPasswordPreference görünümlerinden birini içerdiğini unutmayın.

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

    <EditTextPreference
        android:key="@string/useraccountname_key"
        android:title="@string/useraccountname_title"
        android:summary="@string/useraccountname_summary"
        android:defaultValue="@string/useraccountname_default"
        />

    <com.Merlinia.OutBack_Client.EditPasswordPreference
        android:key="@string/useraccountpassword_key"
        android:title="@string/useraccountpassword_title"
        android:summary="@string/useraccountpassword_summary"
        android:defaultValue="@string/useraccountpassword_default"
        />

    <EditTextPreference
        android:key="@string/outbackserverip_key"
        android:title="@string/outbackserverip_title"
        android:summary="@string/outbackserverip_summary"
        android:defaultValue="@string/outbackserverip_default"
        />

    <EditTextPreference
        android:key="@string/outbackserverport_key"
        android:title="@string/outbackserverport_title"
        android:summary="@string/outbackserverport_summary"
        android:defaultValue="@string/outbackserverport_default"
        />

</PreferenceScreen>

Gerçek şifreleme / şifre çözme gelince, bu okuyucu için bir egzersiz olarak bırakılır. Şu anda bu makaleye dayalı bazı kod kullanıyorum http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/ , farklı değerlere rağmen ve başlatma vektörü için.


1

Her şeyden önce, Kullanıcı verilerinin telefonda depolanmaması gerektiğini düşünüyorum ve verileri telefonda bir yerde saklamak gerekiyorsa, uygulamaların özel verilerinde şifrelenmelidir. Kullanıcı kimlik bilgilerinin güvenliği uygulamanın önceliği olmalıdır.

Hassas veriler güvenli bir şekilde saklanmalı veya hiç saklanmamalıdır. Kayıp bir cihaz veya kötü amaçlı yazılım bulaşması durumunda, güvenli olmayan şekilde saklanan veriler tehlikeye girebilir.


1

ECB modunda RSA kullanarak şifreyi şifrelemek ve daha sonra SharedPreferences'de kaydetmek için Android KeyStore'u kullanıyorum.

Şifreyi geri istediğimde şifrelenmiş olanı SharedPreferences'dan okudum ve KeyStore'u kullanarak şifresini çözdüm.

Bu yöntemle, özel olanın Android tarafından güvenli bir şekilde saklandığı ve yönetildiği bir genel / özel Anahtar çifti oluşturursunuz.

İşte bunun nasıl yapılacağı ile ilgili bir bağlantı: Android KeyStore Eğitimi


-2

paylaşılan tercihler uygulama verilerimizi depolamanın en kolay yoludur. ancak herkesin uygulama yöneticisi aracılığıyla paylaşılan tercihlerimizi temizleyebilmesi mümkündür. bu yüzden uygulamamız için tamamen güvenli olduğunu düşünmüyorum.


-3

şifreleri saklamak için sqlite, güvenlik apit kullanmanız gerekir. İşte şifreleri saklayan en iyi örnek, - passwordsafe. kaynak ve açıklama için bağlantı - http://code.google.com/p/android-passwordsafe/


3
OP'nin bir kullanıcı adı ve şifre çifti saklaması gerekir. Bu tek kullanım için tüm bir veritabanı tablosu oluşturmayı düşünmek saçma olurdu
HXCaine

@HXCaine saygıyla katılmıyorum - bir kullanıcı / şifreler sqlite tablosunun en az 1 diğer kullanımını görebilirsiniz. RİSKİ (sqlite kullanarak) KABUL ETİYORSANIZ, basit uygulama giriş kimlik doğrulamasının yanı sıra, birden fazla ftp şifresini (uygulamanız bazen ftp - mine do kullanıyorsa) saklamak için tabloyu kullanabilirsiniz. artı, bu manipülasyon için bir sqlite adaptör sınıfı oluşturmak ortak plaka basittir.
tony gil

İki yaşındaki bir yorumun güzel dirilişi! Adil olmak gerekirse, yorumum cevaptan bir yıl sonra oldu :) Bir avuç FTP şifresiyle bile, ek yük, SQLite tablosu ile hem alan hem de kodlama açısından SharedPreferences'dan çok daha büyük. Kesinlikle bu gerekli olamaz
HXCaine
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.