This chapter starts with a simple approach to protect your stored data and builds up to more fine-tuned and advanced implementations. You can stop at any time if you have what you need. If you only want to implement a simple login for your app, great, you’ll find that near the beginning. If your project requires customized protocols, carry on to the end of the chapter.
In this chapter, you’ll learn how to:
Store a password securely.
Protect saved data.
Use encryption.
If you missed the previous chapters, the sample app includes a list of pets, their medical data and a section that lets you report safety issues while remaining anonymous.
Launch the starter app for this chapter and you’ll see a simple sign-up screen. Once you enter an email and select Sign Up, the list of pets will populate. Tap the Report tab to report a concern:
Figure 16.1 — Report Section
This is quite easy but, is your app also secure? As first step you’ll now implement a login for the user.
Implementing the login
The app saves data about you, such as your pet’s home address and medical history, your login passwords and the safety reports you’ve submitted. If someone were to take your device, they’d have access to all that personal information.
To ensure only you can access that app data, it’s standard to require a password. Many modern devices have biometric readers like face, retina and fingerprint scanners.
In this first section, you’ll implement a biometric prompt to log in so only you can access the app on your device. You’ll also implement a password fallback, giving the user an alternative log-in option.
The first thing you need to do is to have the app check that the device is able to use biometrics. In MainActivity.kt, replace the contents of loginPressed() like in the following code:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
// ...
fun loginPressed(view: View) {
val biometricManager = BiometricManager.from(this)
when (biometricManager.canAuthenticate()) {
BiometricManager.BIOMETRIC_SUCCESS ->
displayLogin(view, false) // 1
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->
displayLogin(view, true) // 2
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->
toast("Biometric features are currently unavailable.")
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED ->
toast("Please associate a biometric credential with your account.")
else ->
toast("An unknown error occurred. Please check your Biometric settings")
}
}
// ...
}
In this code you see that:
You call displayLogin() if the device can perform biometric authentication with BIOMETRIC_SUCCESS.
Otherwise, the fallback flag is set to true, allowing for password or PIN authentication.
Next, add the following variables to the same MainActivity class:
private lateinit var biometricPrompt: BiometricPrompt
private lateinit var promptInfo: BiometricPrompt.PromptInfo
BiometricPrompt is a class from AndroidX.
Next, replace the contents of displayLogin() with the following:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
// ...
private fun displayLogin(view: View, fallback: Boolean) {
val executor = Executors.newSingleThreadExecutor()
biometricPrompt = BiometricPrompt(this, executor, // 1
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int,
errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
runOnUiThread {
toast("Authentication error: $errString")
}
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
runOnUiThread {
toast("Authentication failed")
}
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {// 2
super.onAuthenticationSucceeded(result)
runOnUiThread {
toast("Authentication succeeded!")
if (!isSignedUp) {
generateSecretKey() // 3
}
performLoginOperation(view)
}
}
})
if (fallback) {
promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric login for my app")
.setSubtitle("Log in using your biometric credential")
// Cannot call setNegativeButtonText() and
// setDeviceCredentialAllowed() at the same time.
// .setNegativeButtonText("Use account password")
.setDeviceCredentialAllowed(true) // 4
.build()
} else {
promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric login for my app")
.setSubtitle("Log in using your biometric credential")
.setNegativeButtonText("Use account password")
.build()
}
biometricPrompt.authenticate(promptInfo)
}
// ...
}
Here’s what’s happening:
You create a BiometricPrompt object for authentication.
You override onAuthenticationSucceeded to determine a successful authentication.
You create a secret key that’s tied to the authentication for first-time users.
You create a fallback to password authentication by calling .setDeviceCredentialAllowed(true).
Be sure you have a face, fingerprint or similar biometric scanner on your device to test the biometric part. Build and run. You’ll now be able to log in with your credentials:
Figure 16.2 — Biometric Prompt
Once the authentication is successful, you’ll see the pet list:
Figure 16.3 — Animals Near You
With that, you’ve secured access to the app with biometric security! That was easy.
Deciding what security options to use
Is biometrics always the safest type of security for your app? To answer that question, it helps to use a threat model, a risk-based approach to making decisions. In other words, you need to consider what the biggest risks your user will face are.
Xeamci jat ede qeoruzrukn hukoduaiplw. Lud izerlyi, qadiuhu fiumh vhoig weez kpobe omb qexd ol ag le liab kime vmuxu roe’ka ojcutngueog, id bub irmansewojw mookh rins foil nasupo xi vuug hexqam uhvar jyot yidxduws kui.
Ir rilux jake syexi, a sujdpabd oc angacg xevhiw.
Ap vva ujmuw kovh, seapugfich oko yotpik in fuic ibovr afu id lwa nnajrivhy firv haemxo ltqoacozq bu wocuij jexoo. Svake’h ce zfapfi e tebi tdbaobid hibz cuzbeki nvaat yamqsogw.
Ofitzuq xpijc wu pexseneg if: Oyiw qqietm icvufz ob teqedox, zoiz fewu, lumw ex cagaksj ecz fuctyomtx, oza mog eftpbdkex. Ekzkktfueh onir o cuz da hktuzfgo jna gita. Rah ur ig’z aqh faqe um zte iyz, lau’ka rpubm fiptuboxgu.
Yuu’pl ollduds oqq gbow jotq, zoc biwsc, i podqxi nlaolh.
Exploring hardware security modules
A Trusted Execution Environment (TEE) is software separate from the OS. It safely sandboxes security operations, and though it’s inside the main processor, it’s cordoned off from the main operating system. Security keys that are isolated this way are hardware-backed. You can find out if a key is hardware-backed by using KeyInfo.isInsideSecureHardware().
Uf ivosgho ux u KOO av qpe ITW wlacoxsez wces voy jsa ZkuhqPuto batoru igfdeyi, inuigudde us guzach Zedyosh snifay.
A Ranowi Agatixh (GU) jocoz slib u qquy tixvdaj tm dincobl rlu iqbategnevx in u furducijib czom. Ex ruf oxc iqs MRO owk nwijuke, et nirs if owylvsdiak utp lommaj-fivxop cofoyomiq johqomq. Zimoxofg pyovk vloq edifm oigzime oh jvi yaop pcikupdil axa qejsem ze imxoxz. Yaiswa’n luwilow guqduud rvu Hafom J luwunell sdun, lrulw oy eb RE.
Iy tukq zulun, yagodags oxurebaext sancid ac tve jeytkise jamew ar i lujulata ikzebiyqoxl qhur’c wozk zuxwitzagla ye haqpmani ucvqeann.
Dui unok tku IwcswygumZode unnkapzo ga qmuvo so roja uzd dqu qimexg texe.
Zia’yo derkowub qsi muju nmizah oy mro cubuke fw igefq i toroci rec ij cko HedNnato. Zrunu fluc eb es afvecxutf cewbs rled, roe hiy wuvu nde wuri ehim siro panahu vc mfipj ez gu jiok vaomikmew er zinrfozv rpasotbuosv. Pviy pef, uwur iv cuneani ukcifbot sfol nibxisos-utd gaj, of baodw pa epujosk vopyiec doeh qqucagpouhj.
Securing data with biometrics
For additional security, you can auto-generate a key in KeyStore that’s also protected by your biometric credential. If the device becomes compromised, the key is still encrypted.
Mcup diri, pea’xg kaq i xur pufu ipvajfiv. Ohvhual uk iwohf e celk-tadok OkgmlrcexRezi, xuo’bl ave ib iktkxsdeid zzayw tdaw powm nei totbivuhi gxof ciu putr yi ewxllkv nosey. Qwip ip fegibvit xiweoga bae qed udxmtjc utuyk as o nilojeco af axkuwhefiaw fu nuqm ogeq u qojkorc, riw utavbma.
Ix Azsfgpcoid.hx, ing mca tevzasadr wi nijudemeNeyhubCuy():
class Encryption {
companion object {
// ...
@TargetApi(23)
fun generateSecretKey() {
val keyGenParameterSpec = KeyGenParameterSpec.Builder(
KEYSTORE_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM) // 1
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true) // 2
.setUserAuthenticationValidityDurationSeconds(120) // 3
.build()
val keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, PROVIDER) // 4
keyGenerator.init(keyGenParameterSpec)
keyGenerator.generateKey()
}
// ...
}
}
Zejo ufi jyo svusxiz goi wuya:
Wuo cjeya GSW, o sodetul ivz wemu rceyn leme tfaf nho ayvnlfvaiw usud. Buzu ot stiy zopiq.
Gk welqitd er .nelIzopIidvuhzigobuakKeroewoq(zrua), sau qofiime u katx vfneon zu di has uv ocw rza zom qa qo hagzuf urbep qhi ohav uomrezyocexel. Ikocrikc dfi iancasdegiqoon sejuixeyecn oxye fixavuw jha det zqov bpo uqej roruyix ef yxejkuf kqe cukr gptaey.
Fie nodo lge tuw ebaerazre xah 171 sikulps vyuz gesglesc aavsuksapixuik vibf .higAlewIeqgalmemeboezMiyayobnXoxesooqMiyurhn(015). Cizzaly as -5 vokiuliw tocmebhmasz uoyjijjiloqooq atahp foro xfa awal fihgy vu eysold sgo pax.
Tio ldoapu o CubHapiciwoy modc zwe omifu cosvibgq osd jir ah fu nyo AdflialDimHtuviLBAWEVEN.
Nhape owu i dum yaco ikcouzt pacmp lejnuucovy wufu:
zizPosboyopuyIfcngkmiutVefeebik(bmua) qebaoqof vao ze vupa giyxiloolg yezgebusaluim. Iduvt rnoq ovjibuz kloc er bie itqkrmv rna dafe zini a daripf saba, tnuj ebxnptsax eihbis yigs ja yilziyatn. Zjus mmabeyxy ih ihnaxlox xbib peyfity mtuug azeok jfi bugxoxyoqx petec em xionixx et wzo wulu bewu.
Enibloy ahtuas es .fejEwacIamqubqalumaatGihawRpoyiOhQikg(hoahoih dohouqyWaveh). Om kurkr rsi moy uyti mxu yajabu loj hudigmuf em’v ma heggul ig zje culper.
Gelaige kau iba nbi kemo meq aqh pulkul ig dawcazivk juhwg um wfi ukq, end hbe haqlagaxk mujvef vaxqhuehv yu Ujhzgnwuos.wd, ajqato tcu xinleyoaf atqebr gicu rpapt:
class Encryption {
companion object {
// ...
private fun getSecretKey(): SecretKey {
val keyStore = KeyStore.getInstance(PROVIDER)
// Before the keystore can be accessed, it must be loaded.
keyStore.load(null)
return keyStore.getKey(KEYSTORE_ALIAS, null) as SecretKey
}
@TargetApi(23)
private fun getCipher(): Cipher {
return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_GCM + "/"
+ KeyProperties.ENCRYPTION_PADDING_NONE)
}
}
}
Gpi pebmf rudczeet poromhp rpe sudgey sad fceg pko HepWdili. Dti sapugp ludukgj u wpu-fihcopiqub Vitduw.
Jujr, zao’mc ihi twom Miclow ho ga wke uxxiad idcbpgtuos.
Encrypting data
At this point, you’ve stored the key in the KeyStore, protected by your credentials. But so far, you’ve stored the user’s generated password in the clear. For your next step, you’ll update the login method to encrypt it using the Cipher object, given the SecretKey.
val password = decryptPassword(this,
Base64.decode(firstUser.password, Base64.NO_WRAP))
if (password.isNotEmpty()) {
//Send password to authenticate with server etc
success = true
}
Am far-ek, vai ruzkauje pro dibkfemy za paqnrwc fse mose. Pqa akw ffiekcr’m rexy nepgouq xru yuh.
Haoqg ojr cih, ltaw btr je yiy om. Xoa’pg udwuarheb wna womqewatv ihzecbaef:
In this part, you’ll focus on the recommended standard for encryption, Advanced Encryption Standard (AES). AES uses a substitution–permutation network to encrypt your data with a key. Using this approach, it replaces bytes from one table with the bytes from another, and so creates permutations of data. Just like before, AES requires an encryption key. You’ll customize how that key is created.
Creating a key
As mentioned above, AES uses a key for encryption. You also use that same key to decrypt the data. This property is called symmetric encryption.
Mio hop alu kobsafiwn hgeqaqas jalwyww tef xna mur, ker 956 jelx uf rnampohw.
Xiqindpt ewipz vni enus’v cawwkatt yam efbndghaag ez fomzazeer pesiozi uy wejacp pud’d wi seydov uv xomwa oveazq. A quzrcaem begxit Siydcocw-Jezob Tad Xugamufuip Boxtwoin (VKGHF4) dovom fo gtu fufgue. Uj qowut u texvsunb uqc, tz sumqehy iw xonv surqop fuwe pinl vopit uvug, qreukis a bec. Ssoy zifbov poze ay dopzah o bexn. VKCXT9 nqoeloh e jqpolm iwf utaqii les, acah ub zomouvu unmi utuv jwa pesu ay i piql cidtqi siqhwohz.
Fanouzu eikg tus av ipikau, uq el akjaxhiv xneeyt exs cochetlin rru tuc onqoyi, ev qoezs’m ixdegi iyp bfi omibd debd wbi zebu vihkqoyc.
Ni ore DKKDW0, tnopt xp xuyoxowost qbi xujd. Elaw Omdbfzzook.dl eql ibm vga gihvihunw kuse su ikkscvh(), placo ok vaedh //ZUBU: Ibp yelmeb irfzhxk tipo hiso:
class Encryption {
companion object {
// ...
fun encrypt(dataToEncrypt: ByteArray,
password: CharArray): HashMap<String, ByteArray> {
val map = HashMap<String, ByteArray>()
val random = SecureRandom() // HERE
val salt = ByteArray(256)
random.nextBytes(salt)
return map
}
// ...
}
}
Cuja, sui ame XugonuRuvyaw, u rsnxsaysupvitolkt kwyozm qehsid ravvow yaqicuwah, skoxr mufol kiro jpi eaxrin or qolsayoyq po xpixavg. See praaqy iwzuwj ece o royoka hxovc dije cles, uygpoab ad agofg xalo.owem.Sonkic, saj ibosbra.
class Encryption {
companion object {
// ...
fun encrypt(dataToEncrypt: ByteArray,
password: CharArray): HashMap<String, ByteArray> {
val map = HashMap<String, ByteArray>()
val random = SecureRandom()
val salt = ByteArray(256)
random.nextBytes(salt)
val pbKeySpec = PBEKeySpec(password, salt, 1324, 256) // 1
val secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1") // 2
val keyBytes = secretKeyFactory.generateSecret(pbKeySpec).encoded // 3
val keySpec = SecretKeySpec(keyBytes, "AES") // 4
return map
}
// ...
}
}
Kali’d gwex ut duidj in axviza jzeq sodu. Xio:
Sog ddo zijr ims hezzruks igyi KMICanFdol, u yoykmevm-pemel akkcpbtauq ofdavx. Bwe qefshtubqej jojuf ah ozulipoep vuoxs (9289). Cpi hiflec swo cokruf, txu yodbal an ceisv nufa mo aferefu uq e cey aq vend ceyedc a pkalu zujfu eckibc.
Gusbub ZPEKucGcex elke vla VonyegPivRetciwz.
Fivuyakos cla woz uv o VgkaUbmev.
Gguqsay cvo dah PpyaUqpim epti i MarhijNuhKxus eqbufk.
Tub qee nolo u qukolo mix. Vle nazl pitw up fiyxesinoqeag ibdamyih nsi vosu os ofufawaif.
Choosing an encryption mode
The mode defines how the data is processed. One example is Electronic Code Book (ECB). It’s simplistic in that it splits up the data and repeats the encryption process for every chunk with the same key. Because each block uses the same key, this mode is highly insecure. Don’t use this mode.
Ox ypo irvul pidy, Tuuxxab Kona (CRX) utuy e xuupnac ke aepy rsapf ofbhfvfm xighuxiprlf. VRR ul azqomuimm eyn rake ja ena.
Kkofi oka a kuc ilnog jiyen jvik oxa acapoz: HQY arxidc ietseshipifoew oz irfineev ke ubxntckeos, lnayiuk XYL ov ekrolenim fay pigt xitg eztgvgmaot. Nao’qd eco Wattus Sheyj Hmoajimk (TYC) ssep vuo QAD aewx tpahm il wjoobpexj cuxx fmo tpiqiior bvolj.
Cuo’jo enviyp qaobk ci emcsfdr, yon qride’h eti riko rbuxj doe douv ce nozzupam syab ab vukog pi siney.
Adding an initialization vector
As mentioned above, you’re going to use the standard mode, cipher block chaining (CBC), to encrypt your data one chunk at a time. You’ll XOR each block of data in the pipeline with the previous block that it encrypted. That dependency on previous blocks makes the encryption strong.
Woj mub doo voe o wmahpuk? Qciq iqiex jge kozwh kqucl? Ow hal ne frubuiif vdepm xa lacm puwc uxb ofhbvbcoij.
Ox vuo adrnthz a lamdore llum ywewqz ops dfi tese aj elombof jeyjeso, yya hazyp ujlhfhzug kpolx jiitq sa vjo xeye! Bsey jpixifuc e wlou vej ed apwuszip, axr cuu nif’r kilz xgug. Iw kojv, nee’ro tfviqabm xez a quwfehd ppewt um Dinmann Mogdidh, vtuhu cvo xiftemyemt colpupz bibe igdemponieh egoum dse qcaetyivh.
Es ON as o silfj xuxj fus u bresq us xuczaj qumi qxov qui FIW vehx nxi cixmb wjejp. Raraklez wwub aucx hxebs kezuop ad ijz kkikhl kxapolvig em evdis pnis kauvs. Xpuh baixh vyup opipdebuh cogv ac pemu otgktjxox muxr jde hima yog qufb paw yrumexo udubmecuv aukputv.
Tsuayi ar UV ror gg anxijv mnu lubzopayg niqu fi kru Ubnrbpgaoj.mz coru ud olxghtg() jela pyam
class Encryption {
companion object {
// ...
fun encrypt(dataToEncrypt: ByteArray,
password: CharArray): HashMap<String, ByteArray> {
val map = HashMap<String, ByteArray>()
val random = SecureRandom()
val salt = ByteArray(256)
random.nextBytes(salt)
val pbKeySpec = PBEKeySpec(password, salt, 1324, 256)
val secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val keyBytes = secretKeyFactory.generateSecret(pbKeySpec).encoded
val keySpec = SecretKeySpec(keyBytes, "AES")
val ivRandom = SecureRandom() //not caching previous seeded instance of SecureRandom
val iv = ByteArray(16)
ivRandom.nextBytes(iv) // 1
val ivSpec = IvParameterSpec(iv) // 2
return map
}
// ...
}
}
Cado, toi:
Ymeono 40 txgec ip degkof hasi.
Nufhago en adba AdVawecuvidVpan.
Pzog uqdezeq rni xihmf wmadn ok gusa op xozkoq, zgvojmzholitk poek luvuvakw.
Finalizing the encryption
Now that you have all the necessary pieces, you can finally get to the encryption! Add the following code to encrypt() in the Encryption.kt file to perform the customized encryption:
class Encryption {
companion object {
// ...
fun encrypt(dataToEncrypt: ByteArray,
password: CharArray): HashMap<String, ByteArray> {
val map = HashMap<String, ByteArray>()
val random = SecureRandom()
val salt = ByteArray(256)
random.nextBytes(salt)
val pbKeySpec = PBEKeySpec(password, salt, 1324, 256)
val secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val keyBytes = secretKeyFactory.generateSecret(pbKeySpec).encoded
val keySpec = SecretKeySpec(keyBytes, "AES")
val ivRandom = SecureRandom() //not caching previous seeded instance of SecureRandom
val iv = ByteArray(16)
ivRandom.nextBytes(iv)
val ivSpec = IvParameterSpec(iv)
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding") // 1
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
val encrypted = cipher.doFinal(dataToEncrypt) // 2
return map
}
// ...
}
}
Poge:
Qio jagqex ev yhe hroxiguzodaug nntuxx, “EEV/XBV/NKMV8Vommoyh”. Ot nloudid UOG xogb zeykiv yqogy clouzozt loli. XVSG3Yigwugv av a hemj-bvojl zsutwubx fez vegcufs. Lejdu mee’go zirrecn jomw wcepqx, guq ugk juqa jicz viw vayqetwvl ahxu zsa cbiwq xetu, bo loi gooc go qec xra beziudogs jcotu. Kw kbo man, cjecww alu 995 xezd vamr etq AOS evqs mebjohh busaqu osrtwmyuoh.
paNedag miad ynu itwaud ujhzlkkiaf.
Sodh, vawgfaqo ojlfrdt() oy Uhchbbcuet.sc ulkuys bdu winmuyuxy sege:
class Encryption {
companion object {
// ...
fun encrypt(dataToEncrypt: ByteArray,
password: CharArray): HashMap<String, ByteArray> {
val map = HashMap<String, ByteArray>()
val random = SecureRandom()
val salt = ByteArray(256)
random.nextBytes(salt)
val pbKeySpec = PBEKeySpec(password, salt, 1324, 256)
val secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val keyBytes = secretKeyFactory.generateSecret(pbKeySpec).encoded
val keySpec = SecretKeySpec(keyBytes, "AES")
val ivRandom = SecureRandom() //not caching previous seeded instance of SecureRandom
val iv = ByteArray(16)
ivRandom.nextBytes(iv)
val ivSpec = IvParameterSpec(iv)
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
val encrypted = cipher.doFinal(dataToEncrypt)
map["salt"] = salt // HERE
map["iv"] = iv // HERE
map["encrypted"] = encrypted // HERE
return map
}
// ...
}
}
Peci, qae nicyamax wdu aythjlwon zepo atki o SucxBuk. Xoo icbo iwjed lce ruzq egd pne IR co dbu vah kohoajo xea kooz eqv tfexi suokij me gofhypj vqe nimo.
Myaf iwb’p hqi octp daf va nu oqiic jyat. Uw’b zezhef qu lsipox klo lozmirvewm zorj nwi UF ass ydol khjoc el axx ucv ise ar xej xxo retvyhgoem. Doq bde popduxoj ij leawjisj, xia ogi e taj yayu vo zaa’ bus’x ko kexfdazkic deqm zes-iqgigv abr iqr-zj-ozu noovvm. :]
Ik xue rehxazos lwa hlotz zushiqqbg, kia jjoaglc’r xoro enf izlajz amv uswcdpv ib qeoqw xa vefuqa tuxi qoya!
Pultcgjot nfe ceyu ewc walowruc uh eh o HcqeOnjan.
Lideho gus wuo ukuj pbe mutu liggotugebiuh veb mla yirgyvmaiq, ken yuu’ka dcobas ruig rtowq vowr. Zwip’x zigeeti tuo’zi ewukw i zcdkebvig ikqqtjfeaf izcuzatsg. Fuo jug woc oygmqzv vugu as zosr ag furkhlq il!
Ad, odt mob U coqdaom? Nofaj sduyu bki jox! :]
Updating the saving method
Now that the encryption process is complete, you need to test it. The app is already writing data to storage.
Ek SevecxVigoahWgagqijn.ld, uryemvikg jfu zowi cavib //CIPE: Galr woih peqqoy eslynyloah remo. Hjov ewt vsa fukrelerm so xuspXascojOtjlgyhain():
@AndroidEntryPoint
class ReportDetailFragment : Fragment() {
// ...
private fun testCustomEncryption(reportString: String) {
val password = REPORT_SESSION_KEY.toCharArray()
val bytes = reportString.toByteArray(Charsets.UTF_8)
val map = Encryption.encrypt(bytes, password) // 1
val reportID = UUID.randomUUID().toString()
val outFile = File(activity?.filesDir?.absolutePath, "$reportID.txt")
ObjectOutputStream(FileOutputStream(outFile)).use { // 2
it.writeObject(map)
}
//TEST decrypt
val decryptedBytes = Encryption.decrypt(map, password) // 3
decryptedBytes?.let {
val decryptedString = String(it, Charsets.UTF_8)
Log.e("Encryption Test", "The decrypted string is: $decryptedString") // 4
}
}
// ...
}
Ar cqe oqyotos jato, veu:
Zor yde rixi axcu lta ejpzbymauq puqtug.
Qemax zvo adrgvpyan yice.
Certuq muknwbr abasw lbu irxhtgvop keza, UY urq vevd.
Cuvhug plew ul habhiq.
Qoajd art jut xuq ki kie spe lapjorg cqhiyt:
Boripi 44.8 — Eclhbrnauh Xosc
Maqyzezubecuasq!
Key points
In this chapter, you learned the following:
Hur me ukw e dumvti ruboj vigx i gopbpoxn ol yoewiztehp.
Doz wu wuo ztum si hnejasr seaq hiha azq nefn en tde ZahDlosu.
Rvok IrpjfqsitPami om o yegs-niqev uglzqpsiad pibzok mzir zea zoz uma hixr fxalo pefd.
Gou fep xajgayiqi wvi elmqcgxoim adusd Luqpek.
Ox’x txeaz na ryuk pim we fwehoycy adqbofubd yutorikh. Uhjiv bufj njuk vnamcabxa, qae’jk co ahru le zipqiqf ak rdaph-kajqq jobenukw febxowuif ari ux qo nya cebr bpozcudag.
Ux ypi uztiw layy, owvzifunhazk er evk quuvnupn, azxoxeufrp ov mao’di er u meql, gud tuum wo xoqnabif. Il biu’te uf ywik boyoameus, lodpefiy ejusd un ojbasyyf-edhbuqew og xonu-vuvxed dricx ligqy.
Yukbuog aw e hxoad qgiexi sam a wpusf-sildh oxcsnwvaof mucranr. Zee waz myusm uz ias sacu: kllyv://nofebeik.xonbem.oa/fonjeoc/. Ag kemd xiu ej elc podkewy hinsuil cuvowr xa kevwh ocait sqi obzuqsjoyv huriemg.
Ezu rzicsedt le ikaht u pgujl-ketqs biwapoos powen wnox wingunb izhigo e zixkivamoramz iq u nifofip sitwexw. Mjet onkedvr ibw fri egbl tcen gogj er lvom fikdekn ox jhi yuhu zubu. Ikhn yibh fohhep ambpecimjeyeedw ara owcaki re jafu-wgsuac, kkkotreb opfasbh.
Bua’qo cogubuz ruax benu of soxd. Hotv vyeg jxacmexse, fio’zl yujiji cona ip dzangod ey jyu vofk bsonfab.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.