web-dev-qa-db-fra.com

Comment sécuriser Android Préférences partagées?

L'emplacement commun où SharedPreferences sont stockés dans Android est:

/data/data/<package name>/shared_prefs/<filename.xml>

L'utilisateur disposant de privilèges root peut accéder à cet emplacement et peut modifier ses valeurs. La nécessité de le protéger est très importante.

De combien de façons nous pouvons crypter l'ensemble shared_pref's xml fichier?

Nous savons tous que nous pouvons crypter et enregistrer des données dans shared_pref's xml fichier, mais ce n'est pas seulement sûr à 100%, il faut donc crypter le fichier entier avec une clé. Besoin d'aide pour connaître les différentes méthodes de chiffrement du fichier xml entier. C'est une question générique, diverses méthodes de cryptage discutées comme réponses ici peuvent être utiles à tous les développeurs pour sécuriser les applications.

24
Harsh Dattani

Android a publié une bibliothèque de sécurité avec EncryptedSharedPreferences dans la bibliothèque crypto par Jetpack.

L'API minimale est de 23 (6.0+)

String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

   SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
       "secret_shared_prefs",
       masterKeyAlias,
       context,
       EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
       EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
   );

   // use the shared preferences and editor as you normally would
   SharedPreferences.Editor editor = sharedPreferences.edit();

https://developer.Android.com/reference/kotlin/androidx/security/crypto/EncryptedSharedPreferences.html

38
Bojan Kseneman

Google a publié EncryptedSharedPreferences dans le cadre de son androidx, je pense que cela devrait être le moyen préférable de crypter les préférences.

Voir https://developer.Android.com/reference/androidx/security/crypto/EncryptedSharedPreferences

11
Ch3D

Vous devez crypter vos données et écrire dans SharedPreferences. Lorsque vous souhaitez obtenir ces données, vous devez décrypter à partir de SharedPreferences. vous avez besoin de la classe d'assistance suivante pour cela

public class Encryption {
private final Builder mBuilder;

private Encryption(Builder builder) {
    mBuilder = builder;
}

public static Encryption getDefault(String key, String salt, byte[] iv) {
    try {
        return Builder.getDefaultBuilder(key, salt, iv).build();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    }
}

private String encrypt(String data) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException {
    if (data == null) return null;
    SecretKey secretKey = getSecretKey(hashTheKey(mBuilder.getKey()));
    byte[] dataBytes = data.getBytes(mBuilder.getCharsetName());
    Cipher cipher = Cipher.getInstance(mBuilder.getAlgorithm());
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, mBuilder.getIvParameterSpec(), mBuilder.getSecureRandom());
    return Base64.encodeToString(cipher.doFinal(dataBytes), mBuilder.getBase64Mode());
}

public String encryptOrNull(String data) {
    try {
        return encrypt(data);
    } catch (Exception e) {
        e.printStackTrace();
        return "";
    }
}

public void encryptAsync(final String data, final Callback callback) {
    if (callback == null) return;
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String encrypt = encrypt(data);
                if (encrypt == null) {
                    callback.onError(new Exception("Encrypt return null, it normally occurs when you send a null data"));
                }
                callback.onSuccess(encrypt);
            } catch (Exception e) {
                callback.onError(e);
            }
        }
    }).start();
}

private String decrypt(String data) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    if (data == null) return null;
    byte[] dataBytes = Base64.decode(data, mBuilder.getBase64Mode());
    SecretKey secretKey = getSecretKey(hashTheKey(mBuilder.getKey()));
    Cipher cipher = Cipher.getInstance(mBuilder.getAlgorithm());
    cipher.init(Cipher.DECRYPT_MODE, secretKey, mBuilder.getIvParameterSpec(), mBuilder.getSecureRandom());
    byte[] dataBytesDecrypted = (cipher.doFinal(dataBytes));
    return new String(dataBytesDecrypted);
}

public String decryptOrNull(String data) {
    try {
        return decrypt(data);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

public void decryptAsync(final String data, final Callback callback) {
    if (callback == null) return;
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String decrypt = decrypt(data);
                if (decrypt == null) {
                    callback.onError(new Exception("Decrypt return null, it normally occurs when you send a null data"));
                }
                callback.onSuccess(decrypt);
            } catch (Exception e) {
                callback.onError(e);
            }
        }
    }).start();
}

private SecretKey getSecretKey(char[] key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
    SecretKeyFactory factory = SecretKeyFactory.getInstance(mBuilder.getSecretKeyType());
    KeySpec spec = new PBEKeySpec(key, mBuilder.getSalt().getBytes(mBuilder.getCharsetName()), mBuilder.getIterationCount(), mBuilder.getKeyLength());
    SecretKey tmp = factory.generateSecret(spec);
    return new SecretKeySpec(tmp.getEncoded(), mBuilder.getKeyAlgorithm());
}

private char[] hashTheKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
    MessageDigest messageDigest = MessageDigest.getInstance(mBuilder.getDigestAlgorithm());
    messageDigest.update(key.getBytes(mBuilder.getCharsetName()));
    return Base64.encodeToString(messageDigest.digest(), Base64.NO_PADDING).toCharArray();
}

public interface Callback {
    void onSuccess(String result);
    void onError(Exception exception);
}

private static class Builder {

    private byte[] mIv;
    private int mKeyLength;
    private int mBase64Mode;
    private int mIterationCount;
    private String mSalt;
    private String mKey;
    private String mAlgorithm;
    private String mKeyAlgorithm;
    private String mCharsetName;
    private String mSecretKeyType;
    private String mDigestAlgorithm;
    private String mSecureRandomAlgorithm;
    private SecureRandom mSecureRandom;
    private IvParameterSpec mIvParameterSpec;

    public static Builder getDefaultBuilder(String key, String salt, byte[] iv) {
        return new Builder()
                .setIv(iv)
                .setKey(key)
                .setSalt(salt)
                .setKeyLength(128)
                .setKeyAlgorithm("AES")
                .setCharsetName("UTF8")
                .setIterationCount(1)
                .setDigestAlgorithm("SHA1")
                .setBase64Mode(Base64.DEFAULT)
                .setAlgorithm("AES/CBC/PKCS5Padding")
                .setSecureRandomAlgorithm("SHA1PRNG")
                .setSecretKeyType("PBKDF2WithHmacSHA1");
    }

    private Encryption build() throws NoSuchAlgorithmException {
        setSecureRandom(SecureRandom.getInstance(getSecureRandomAlgorithm()));
        setIvParameterSpec(new IvParameterSpec(getIv()));
        return new Encryption(this);
    }

    private String getCharsetName() {
        return mCharsetName;
    }

    private Builder setCharsetName(String charsetName) {
        mCharsetName = charsetName;
        return this;
    }

    private String getAlgorithm() {
        return mAlgorithm;
    }

    private Builder setAlgorithm(String algorithm) {
        mAlgorithm = algorithm;
        return this;
    }

    private String getKeyAlgorithm() {
        return mKeyAlgorithm;
    }

    private Builder setKeyAlgorithm(String keyAlgorithm) {
        mKeyAlgorithm = keyAlgorithm;
        return this;
    }

    private int getBase64Mode() {
        return mBase64Mode;
    }

    private Builder setBase64Mode(int base64Mode) {
        mBase64Mode = base64Mode;
        return this;
    }

    private String getSecretKeyType() {
        return mSecretKeyType;
    }

    private Builder setSecretKeyType(String secretKeyType) {
        mSecretKeyType = secretKeyType;
        return this;
    }

    private String getSalt() {
        return mSalt;
    }

    private Builder setSalt(String salt) {
        mSalt = salt;
        return this;
    }

    private String getKey() {
        return mKey;
    }

    private Builder setKey(String key) {
        mKey = key;
        return this;
    }

    private int getKeyLength() {
        return mKeyLength;
    }

    public Builder setKeyLength(int keyLength) {
        mKeyLength = keyLength;
        return this;
    }

    private int getIterationCount() {
        return mIterationCount;
    }

    public Builder setIterationCount(int iterationCount) {
        mIterationCount = iterationCount;
        return this;
    }

    private String getSecureRandomAlgorithm() {
        return mSecureRandomAlgorithm;
    }

    public Builder setSecureRandomAlgorithm(String secureRandomAlgorithm) {
        mSecureRandomAlgorithm = secureRandomAlgorithm;
        return this;
    }

    private byte[] getIv() {
        return mIv;
    }

    public Builder setIv(byte[] iv) {
        mIv = iv;
        return this;
    }

    private SecureRandom getSecureRandom() {
        return mSecureRandom;
    }

    public Builder setSecureRandom(SecureRandom secureRandom) {
        mSecureRandom = secureRandom;
        return this;
    }

    private IvParameterSpec getIvParameterSpec() {
        return mIvParameterSpec;
    }

    public Builder setIvParameterSpec(IvParameterSpec ivParameterSpec) {
        mIvParameterSpec = ivParameterSpec;
        return this;
    }

    private String getDigestAlgorithm() {
        return mDigestAlgorithm;
    }

    public Builder setDigestAlgorithm(String digestAlgorithm) {
        mDigestAlgorithm = digestAlgorithm;
        return this;
    }

}}

alors vous pouvez écrire dans SharedPreferences en chiffrant vos données comme suit

 Encryption encryption = Encryption.getDefault("Key", "Salt", new byte[16]);
 SharedPreferences preferences =    PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
 SharedPreferences.Editor editor = preferences.edit();
 editor.putString("token", encryption.encryptOrNull(userModel.getToken()));
 editor.apply()

vous pouvez enfin lire des données SharedPreferences de la manière suivante. De cette façon, les informations sensibles seront plus sûres tout en étant conservées au niveau matériel dans le téléphone

final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Encryption encryption = Encryption.getDefault("Key", "Salt", new byte[16]);
String token  = encryption.decryptOrNull(preferences.getString("token",""));
5
Bayar Şahintekin

Base64 n'est PAS un cryptage! Ne l'utilisez pas! Oui, les utilisateurs "root" peuvent accéder à ces données. Une chose que vous pouvez faire est d'utiliser AES pour crypter ces données ou utiliser un seul fichier de base de données NoSQL et crypter ce fichier. Lorsque l'application s'ouvre, vous déchiffrez la base de données et l'utilisez pour stocker des informations ou crypter tous les fichiers indépendamment.

Regardez ici: https://code.tutsplus.com/tutorials/storing-data-securely-on-Android--cms-30558

3
Digital Human
public class NodeCrypto {

        private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
        private IvParameterSpec ivspec;
        private SecretKeySpec keyspec;
        private Cipher cipher;

        private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)

        public void doKey(String key)
        {
                ivspec = new IvParameterSpec(iv.getBytes());

                key = padRight(key,16);

                Log.d("hi",key);

                keyspec = new SecretKeySpec(key.getBytes(), "AES");

                try {
                        cipher = Cipher.getInstance("AES/CBC/NoPadding");
                } catch (NoSuchAlgorithmException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (NoSuchPaddingException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
        }

        public byte[] encrypt(String text,String key) throws Exception
        {
                if(text == null || text.length() == 0)
                        throw new Exception("Empty string");

                doKey(key);

                byte[] encrypted = null;

                try {
                        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);

                        encrypted = cipher.doFinal(padString(text).getBytes());
                } catch (Exception e)
                {                       
                        throw new Exception("[encrypt] " + e.getMessage());
                }

                return encrypted;
        }

        public byte[] decrypt(String code,String key) throws Exception
        {
                if(code == null || code.length() == 0)
                        throw new Exception("Empty string");

                byte[] decrypted = null;

                doKey(key);

                try {
                        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

                        decrypted = cipher.doFinal(hexToBytes(code));
                } catch (Exception e)
                {
                        throw new Exception("[decrypt] " + e.getMessage());
                }
                return decrypted;
        }



        public static String bytesToHex(byte[] data)
        {
                if (data==null)
                {
                        return null;
                }

                int len = data.length;
                String str = "";
                for (int i=0; i<len; i++) {
                        if ((data[i]&0xFF)<16)
                                str = str + "0" + Java.lang.Integer.toHexString(data[i]&0xFF);
                        else
                                str = str + Java.lang.Integer.toHexString(data[i]&0xFF);
                }
                return str;
        }


        public static byte[] hexToBytes(String str) {
                if (str==null) {
                        return null;
                } else if (str.length() < 2) {
                        return null;
                } else {
                        int len = str.length() / 2;
                        byte[] buffer = new byte[len];
                        for (int i=0; i<len; i++) {
                                buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
                        }
                        return buffer;
                }
        }



        private static String padString(String source)
        {
          char paddingChar = ' ';
          int size = 16;
          int x = source.length() % size;
          int padLength = size - x;

          for (int i = 0; i < padLength; i++)
          {
                  source += paddingChar;
          }

          return source;
        }

        public static String padRight(String s, int n) {
            return String.format("%1$-" + n + "s", s);  
          }
}

-----------------------------------------------
from your activity or class call encrypt or decrypt method before saving or   retriving from SharedPreference
1
Mohammad nabil