· 5 years ago · Jan 24, 2020, 06:02 AM
1package com.bytepace.dimusco.utils
2
3import android.graphics.Bitmap
4import android.security.keystore.KeyGenParameterSpec
5import android.security.keystore.KeyProperties
6import com.bytepace.dimusco.DimuscoApp
7import java.io.*
8import java.security.KeyStore
9import javax.crypto.Cipher
10import javax.crypto.CipherInputStream
11import javax.crypto.KeyGenerator
12import javax.crypto.SecretKey
13import javax.crypto.spec.IvParameterSpec
14
15private const val KEY_ALIAS = "DimuscoKey"
16private const val KEY_STORE = "AndroidKeyStore"
17private const val CIPHER_MODE = "AES/CBC/PKCS7Padding"
18private const val FILE_ACCESS_MODE = "rw"
19private const val CBC_IV_SIZE = 16
20
21fun saveImage(filename: String, bitmap: Bitmap): Throwable? {
22 val bytes = ByteArrayOutputStream().use {
23 bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
24 return@use it.toByteArray()
25 }
26 return writeFile(filename, bytes)
27}
28
29fun writeFile(filename: String, bytes: ByteArray): Throwable? {
30 var throwable: Throwable? = null
31
32 try {
33 val file = File(DimuscoApp.getContext().filesDir, filename)
34 if (!file.exists()) file.parentFile.mkdirs()
35 RandomAccessFile(file, FILE_ACCESS_MODE).use { rFile ->
36 deleteFile(file)
37 val cipher = Cipher
38 .getInstance(CIPHER_MODE)
39 .apply {
40 init(Cipher.ENCRYPT_MODE, getSecretKey())
41 }
42
43 val lock = rFile.channel.lock()
44
45 ByteArrayInputStream(bytes).use { bais ->
46 CipherInputStream(bais, cipher).use { cis ->
47 FileOutputStream(file).use { out ->
48 out.write(cipher.iv)
49 out.write(cis.readBytes())
50 }
51 }
52 }
53 lock.release()
54 }
55 } catch (e: java.lang.Exception) {
56 throwable = e
57 } finally {
58 return throwable
59 }
60}
61
62fun readFile(filename: String): ByteArray? {
63 val file = getFile(filename)
64 if (!file.exists()) return null
65
66 val bytes = FileInputStream(file).use { it.readBytes() }
67
68 val cipher = Cipher
69 .getInstance(CIPHER_MODE)
70 .apply {
71 init(
72 Cipher.DECRYPT_MODE,
73 getSecretKey(),
74 IvParameterSpec(bytes, 0, CBC_IV_SIZE)
75 )
76 }
77
78 return try {
79 ByteArrayInputStream(bytes).use { bais ->
80 CipherInputStream(bais, cipher).use { cis ->
81 cis.readBytes().drop(CBC_IV_SIZE).toByteArray()
82 }
83 }
84 } catch (e: java.lang.Exception) {
85 e.printStackTrace()
86 null
87 }
88}
89
90fun deleteFile(filename: String): Boolean {
91 return deleteFile(getFile(filename))
92}
93
94private fun deleteFile(file: File): Boolean {
95 return try {
96 when (file.isDirectory) {
97 true -> file.deleteRecursively()
98 else -> file.delete()
99 }
100 } catch (e: Exception) {
101 e.printStackTrace()
102 false
103 }
104}
105
106private fun getFile(filename: String): File {
107 return File("${DimuscoApp.getContext().filesDir}/$filename")
108}
109
110private fun getSecretKey(): SecretKey? {
111 val keyStore = KeyStore
112 .getInstance(KEY_STORE)
113 .apply { load(null) }
114
115 val keyEntry = keyStore.getEntry(KEY_ALIAS, null) as? KeyStore.SecretKeyEntry
116 return keyEntry?.secretKey ?: generateSecretKey()
117}
118
119private fun generateSecretKey(): SecretKey {
120 val spec = KeyGenParameterSpec
121 .Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
122 .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
123 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
124 .build()
125
126 KeyGenerator
127 .getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE)
128 .apply {
129 init(spec)
130 return generateKey()
131 }
132}