Afrikaans (Afrikaans) translation by Meyria (you can also view the original English article)
Die geloofwaardigheid van 'n program hang tans baie van hoe die gebruiker se persoonlike data bestuur word. Die Android Stack het baie kragtige API's rondom geloofsbriewe en sleutel berging, met spesiale eienskappe wat slegs in sekere weergawes beskikbaar is. Hierdie kort reeks sal begin met 'n eenvoudige benadering om op te staan deur te kyk na die bergingstelsel en hoe om sensitiewe data te enkripteer en op te slaan via 'n paswoord wat deur gebruikers verskaf word. In die tweede handleiding gaan ons kyk na meer komplekse maniere om sleutels en geloofsbriewe te beskerm.
Die Basiese
Die eerste vraag om na te dink, is hoeveel data jou werklik nodig het. 'N Goeie benadering is om persoonlike data te voorkom as jou dit nie regtig moet doen nie.
Vir data wat jou moet stoor, is die Android-argitektuur gereed om te help. Sedert 6.0 Marshmellow, is die volle skyf enkripsie standaard aangeskakel vir toestelle met die vermoë. Lêers en SharedPreferences
wat deur die program gestoor word, word outomaties ingestel met die MODE_PRIVATE
konstante. Dit beteken dat die data slegs deur jou eie program verkry kan word.
Dit is 'n goeie idee om by hierdie standaard te hou. Jou kan dit eksplisiet stel wanneer jou 'n gedeelde voorkeur stoor.
SharedPreferences.Editor editor = getSharedPreferences("preferenceName", MODE_PRIVATE).edit(); editor.putString("key", "value"); editor.commit();
Of Wanneer die Lêer Gestoor Word.
FileOutputStream fos = openFileOutput(filenameString, Context.MODE_PRIVATE); fos.write(data); fos.close();
Vermy die stoor van data in eksterne berging, aangesien data dan deur ander programme en gebruikers besigtig kan word. Om te verhoed dat dit moeiliker maak vir mense om jou binêre en programdata te kopieer, kan jou gebruikers verbied om programme op eksterne berging te installeer. Adding android: installLocation
met waarde internalOnly
na die manifeste lêer sal dit doen.
Jou kan ook voorkom dat programme en data gerugsteun word. Dit voorkom ook dat inhoud afgelaai word van die persoonlike data gids wat die program gebruik adb backup
. Om dit te doen, stel die android: allowBackup
attribuut vals
in die manifestlêer. Standaard word hierdie kenmerk as waar
gestel.
Dit is 'n beste praktyk, maar werk nie vir gekompromitteerde of gewortelde toestelle nie, en skyf enkripsie is net nuttig wanneer die toestel met 'n sluitskerm beveilig word. Dit is waar jou 'n wagwoord vir die toepassing het wat sy data beskerm met nuttige enkripsie.
Sekuriteit van Gebruikersdata Met Wagwoord
Conceal is 'n goeie keuse vir 'n enkripsie-biblioteek omdat dit jou vinnig opduik sonder om bekommerd te wees oor die onderliggende besonderhede. Egter, 'n uitbuiting wat gerig is op 'n gewilde raamwerk, sal gelyktydig al die programme wat daarop staatmaak, beïnvloed.
Dit is ook belangrik om kennis te hê van hoe die enkripsiestelsel werk, sodat jy kan sien of jou 'n spesifieke raamwerk veilig gebruik. Dus, vir hierdie pos sal ons ons hande vuil kry deur direk na kriptografiese verskaffers te kyk.
AES en Wagwoord Gebaseerde Sleutel Afleiding
Ons sal die aanbevole AES standaard gebruik, wat data versamel wat 'n sleutel verskaf. Dieselfde sleutel wat gebruik word om die data te enkripteer, word gebruik om die data te dekripteer, wat simmetriese enkripsie genoem word. Daar is verskillende sleutel groottes, en AES256 (256 bisse) is die voorkeurlengte vir gebruik met sensitiewe data.
Selfs as jou program se gebruikerservaring gebruikers moet dwing om 'n sterk wagwoord te gebruik, is dit moontlik dat dieselfde kode ook deur ander gebruikers gekies word. Die beveiliging van ons geïnkripteer data in die hande van die gebruiker is nie veilig nie. Ons data moet eerder beveilig word met 'n sleutel wat willekeurig en groot genoeg is (dit wil sê genoeg entropie) om sterk beskou te word. Daarom word dit nooit aanbeveel om 'n wagwoord direk te gebruik om data te enkripteer nie. Dit is waar 'n funksie genoem Wagwoordgebaseerde Sleutel Afleidingfunksie (PBKDF2) in die spel kom.
PDKDF2 neem die sleutel van die wagwoord deur hashing baie keer met sout. Dit word 'n sleutelstrek genoem. Die sout is net 'n ewekansige reeks data en maak die afgeleide sleutel uniek, selfs al is dieselfde wagwoord deur iemand anders gebruik. Kom ons begin deur daardie sout te genereer.
SecureRandom random = new SecureRandom(); byte salt[] = new byte[256]; random.nextBytes(salt);
Die SecureRandom
klas waarborg dat die gegenereerde uitset moeilik sal wees om te voorspel. Dit is 'n "kriptografies sterk willekeurige getalgenerator". Ons kan nou die sout en wagwoord in 'n wagwoordgebaseerde enkripsie-voorwerp plaas: PBEKeySpec
. Die voorwerp se konstruktor neem ook 'n iterasie telvorm wat die sleutel sterker maak. Dit is omdat die verhoging van die aantal iterasies die tyd wat dit sal neem om op 'n stel sleutels te werk tydens 'n brute kragaanval, vermeerder. Die PBEKeySpec word dan geslaag in die SecretKeyFactory, wat uiteindelik die sleutel genereer as 'n byte [] skikking. Ons sal daardie rou byte [] skikking in 'n geheimeKeySpec-voorwerp wikkel.
char[] passwordChar = passwordString.toCharArray(); //Turn password into char[] array PBEKeySpec pbKeySpec = new PBEKeySpec(passwordChar, salt, 1324, 256); //1324 iterations SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] keyBytes = secretKeyFactory.generateSecret(pbKeySpec).getEncoded(); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
Let daarop dat die wagwoord geslaag is as 'n char[]
skikking en die PBEKeySpec
klas stoor dit ook as 'n char[]
skikking. char[]
skikkings word gewoonlik gebruik vir enkripsiefunksies, want terwyl die String
klas onveranderlik is, kan 'n char[]
skikking wat sensitiewe inligting bevat, oorskry word, waardeur die sensitiewe data heeltemal verwyder word uit die phyc RAM van die toestel.
Vector Initialisering
Ons is nou gereed om die data te enkripteer, maar ons het nog een ding om te doen. Daar is verskillende modusse van enkripsie met AES, maar ons sal die aanbevole een gebruik: cipher block chaining (CBC). Dit werk een blok op 'n slag op ons data. Die groot ding oor hierdie modus is dat elke volgende ongecodeerde databasis XOR'd is met die vorige geïnkripteer blok om die enkripsie sterker te maak. Dit beteken egter, dat die eerste blok nooit so uniek is soos al die ander nie!
As 'n boodskap wat geïnkripteer moet word, sou dieselfde wees as 'n ander boodskap wat geïnkripteer sou wees, sou die begin-geïnkripteerde uitset dieselfde wees, en dit sou 'n aanvaller 'n idee gee om uit te vind wat die boodskap mag wees. Die oplossing is om 'n aanvangsvektor (IV) te gebruik.
'N IV is slegs 'n blok willekeurige grepe wat XOR'd sal wees met die eerste blok gebruikersdata. Aangesien elke blok afhang van alle blokke wat verwerk word tot op daardie punt, sal die hele boodskap versleutelde unieke identiese boodskappe geïnkripteer word met dieselfde sleutel, nie dieselfde resultate lewer nie. Kom ons skep nou 'n IV.
SecureRandom ivRandom = new SecureRandom(); //not caching previous seeded instance of SecureRandom byte[] iv = new byte[16]; ivRandom.nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
'N Nota oor SecureRandom
. Op weergawes 4.3 en onder het die Java Cryptography Architecture 'n kwesbaarheid weens onbehoorlike initialisering van die onderliggende pseudorandom-nommergenerator (PRNG). As jou gerig is op weergawes 4.3 en onder, a fix is available.
Enkripteer Data
Gewapen met 'n IvParameterSpec
, kan ons nou die werklike enkripsie doen.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); byte[] encrypted = cipher.doFinal(plainTextBytes);
Hier slaag ons in 'n tou "AES/CBC/PKCS7Padding"
. Dit spesifiseer AES enkripsie met leë blokmonsters. Die laaste gedeelte van hierdie string verwys na PKCS7, wat 'n gevestigde standaard is vir die opvulling van data wat nie perfek in die blokgrootte pas nie. (Blokke is 128 bisse, en die vulling is voor kodering gedoen.)
Om ons voorbeeld te voltooi, sal ons hierdie kode in 'n enkripsie-metode plaas wat die resultaat sal verpak in 'n HashMap
wat die versleutelde data bevat, tesame met die sout- en inisiatiewe-vektor wat nodig is vir dekripsie.
private HashMap<String, byte[]> encryptBytes(byte[] plainTextBytes, String passwordString) { HashMap<String, byte[]> map = new HashMap<String, byte[]>(); try { //Random salt for next step SecureRandom random = new SecureRandom(); byte salt[] = new byte[256]; random.nextBytes(salt); //PBKDF2 - derive the key from the password, don't use passwords directly char[] passwordChar = passwordString.toCharArray(); //Turn password into char[] array PBEKeySpec pbKeySpec = new PBEKeySpec(passwordChar, salt, 1324, 256); //1324 iterations SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] keyBytes = secretKeyFactory.generateSecret(pbKeySpec).getEncoded(); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); //Create initialization vector for AES SecureRandom ivRandom = new SecureRandom(); //not caching previous seeded instance of SecureRandom byte[] iv = new byte[16]; ivRandom.nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv); //Encrypt Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); byte[] encrypted = cipher.doFinal(plainTextBytes); map.put("salt", salt); map.put("iv", iv); map.put("encrypted", encrypted); } catch(Exception e) { Log.e("MYAPP", "encryption exception", e); } return map; }
Die Dekripsie Metode
Jou hoef net die IV en sout met jou data te stoor. Terwyl soute en IV's as publiek beskou word, maak seker dat hulle nie opeenvolgend verhoog of hergebruik word nie. Om die data te dekripteer, is alles wat ons moet doen, die modus in die Cipher
konstruktor van ENCRYPT_MODE
na DECRYPT_MODE
verander. Die dekripsie metode sal 'n HashMap
neem wat dieselfde vereiste inligting bevat (geënkripteerde data, sout en IV) en gee 'n gekodeerde byte[]
skikking, met die korrekte wagwoord. Die dekripsie metode sal die enkripsiesleutel van die wagwoord herstel. Die sleutel moet nooit gestoor word nie!
private byte[] decryptData(HashMap<String, byte[]> map, String passwordString) { byte[] decrypted = null; try { byte salt[] = map.get("salt"); byte iv[] = map.get("iv"); byte encrypted[] = map.get("encrypted"); //regenerate key from password char[] passwordChar = passwordString.toCharArray(); PBEKeySpec pbKeySpec = new PBEKeySpec(passwordChar, salt, 1324, 256); SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] keyBytes = secretKeyFactory.generateSecret(pbKeySpec).getEncoded(); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); //Decrypt Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); decrypted = cipher.doFinal(encrypted); } catch(Exception e) { Log.e("MYAPP", "decryption exception", e); } return decrypted; }
Toets Enkripsie en Dekripsie
Om die voorbeeld eenvoudig te hou, laat ons foutkontrole weg wat seker maak dat die HashMap
die vereiste sleutel, waardepare bevat. Ons kan nou ons metodes toets om te verseker dat die data korrek gedekripteer word na enkripsie.
//Encryption test String string = "My sensitive string that I want to encrypt"; byte[] bytes = string.getBytes(); HashMap<String, byte[]> map = encryptBytes(bytes, "UserSuppliedPassword"); //Decryption test byte[] decrypted = decryptData(map, "UserSuppliedPassword"); if (decrypted != null) { String decryptedString = new String(decrypted); Log.e("MYAPP", "Decrypted String is : " + decryptedString); }
Die metodes gebruik 'n byte[]
skikking, sodat jou arbitrêre data kan enkripteer in plaas van slegs String
voorwerpe.
Stoor Geïnkripteer Data
Noudat ons 'n versleutelde byte[]
skikking het, kan ons dit stoor.
FileOutputStream fos = openFileOutput("test.dat", Context.MODE_PRIVATE); fos.write(encrypted); fos.close();
As jou nie die IV en sout apart wil stoor nie, is HashMap
serialiseerbaar met die ObjectInputStream
en ObjectOutputStream
klasse.
FileOutputStream fos = openFileOutput("map.dat", Context.MODE_PRIVATE); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(map); oos.close();
Stoor veilige data op SharedPreferences
Jou kan ook veilige data stoor in jou program se SharedPreferences
.
SharedPreferences.Editor editor = getSharedPreferences("prefs", Context.MODE_PRIVATE).edit(); String keyBase64String = Base64.encodeToString(encryptedKey, Base64.NO_WRAP); String valueBase64String = Base64.encodeToString(encryptedValue, Base64.NO_WRAP); editor.putString(keyBase64String, valueBase64String); editor.commit();
Aangesien die SharedPreferences
'n XML stelsel is wat slegs spesifieke primitiewe en voorwerpe as waardes aanvaar, moet ons ons data omskep in 'n verenigbare formaat soos 'n String
voorwerp. Base64 stel ons in staat om die rou data te omskep in 'n String
voorstelling wat slegs die karakters wat deur die XML formaat toegelaat word, bevat. Enkripteer beide die sleutel en die waarde, sodat 'n aanvaller nie kan uitvind waaroor 'n waarde mag wees nie. In die voorbeeld hierbo is encryptedKey
en encryptedValue
beide geïnkripteer byte[]
raamwerke wat van ons encryptBytes()
metode teruggekeer word. Die IV en sout kan gered word in die voorkeure lêer of as 'n aparte lêer. Om die geïnkripteer grepe uit die SharedPreferences
terug te kry, kan ons 'n Base64 dekodeer op die gestoor String
toepas.
SharedPreferences preferences = getSharedPreferences("prefs", Context.MODE_PRIVATE); String base64EncryptedString = preferences.getString(keyBase64String, "default"); byte[] encryptedBytes = Base64.decode(base64EncryptedString, Base64.NO_WRAP);
Nie Veilige Data vVrwyder Uit ou Weergawe
Nou is die gestoor data veilig, jou kan dalk 'n vorige weergawe van die program hê wat die data onveilig stoor. By opgradering kan data uitgevee en herkodeer word. Die volgende kode skrap lêers deur gebruik te maak van ewekansige data.
In teorie kan jou gedeelde voorkeure uitvee deur te skrap /data/data/com.your.package.name/shared_prefs/your_prefs_name.xmland your_prefs_name.bak lêer, en your_prefs_name.bak lêer, en skoongemaakte voorkeur in geheue met die volgende kode:
getSharedPreferences("prefs", Context.MODE_PRIVATE).edit().clear().commit();
In plaas daarvan om te probeer om ou data uit te vee en hoop dat dit werk, word dit in die eerste plek beter geïnkripteer! Dit geld veral vir vaste hardeskywe wat dikwels data-skryf na verskillende streke versprei om slytasie te voorkom. Dit beteken dat selfs indien jou lêers in die lêerstelsel oorskryf, fisiese vaste-staat geheue jou data op die oorspronklike plek op die skyf kan stoor.
public static void secureWipeFile(File file) throws IOException { if (file != null && file.exists()) { final long length = file.length(); final SecureRandom random = new SecureRandom(); final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rws"); randomAccessFile.seek(0); randomAccessFile.getFilePointer(); byte[] data = new byte[64]; int position = 0; while (position < length) { random.nextBytes(data); randomAccessFile.write(data); position += data.length; } randomAccessFile.close(); file.delete(); } }
Gevolgtrekking
Dit verswak ons tutoriaal oor die stoor van geënkripteerde data. In hierdie pos het jou geleer hoe om sensitiewe data veilig te enkripteer en te dekodeer met 'n wagwoord wat deur gebruikers verskaf word. Dit is maklik om te doen wanneer jou weet hoe, maar dit is belangrik om al die beste praktyke te volg om te verseker dat jou gebruikers se data werklik veilig is.
In die volgende pos sal ons kyk hoe om die KeyStore
en ander geloofsgebaseerde API's te gebruik om items veilig te stoor. In die tussentyd, gaan na 'n paar van ons ander wonderlike artikels oor Android appontwikkeling.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post