· 5 years ago · Dec 18, 2019, 10:44 PM
1package com.example.fingerprint
2
3import android.content.Context
4import android.content.Intent
5import androidx.appcompat.app.AppCompatActivity
6import android.os.Bundle
7import android.security.keystore.KeyGenParameterSpec
8import android.security.keystore.KeyProperties
9import android.util.Base64
10import android.widget.Button
11import android.widget.EditText
12import android.widget.Toast
13import java.security.KeyStore
14import java.security.SecureRandom
15import javax.crypto.Cipher
16import javax.crypto.KeyGenerator
17import javax.crypto.SecretKey
18import javax.crypto.SecretKeyFactory
19import javax.crypto.spec.GCMParameterSpec
20import javax.crypto.spec.IvParameterSpec
21import javax.crypto.spec.PBEKeySpec
22import javax.crypto.spec.SecretKeySpec
23
24class Note : AppCompatActivity() {
25    private val charPool : List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
26    private val secretKeyAlias = "note_key"
27    private val sharedPrefFile = "com.example.note.prefsFinger"
28
29    override fun onCreate(savedInstanceState: Bundle?) {
30        super.onCreate(savedInstanceState)
31        setContentView(R.layout.activity_note)
32        var noteText = findViewById<EditText>(R.id.noteText)
33
34        val createNoteButton = findViewById<Button>(R.id.saveNoteButton)
35
36        //val key = generateSecretKey()
37        if (readDataKeyStore() != "") {
38            noteText.setText(readDataKeyStore())
39        }
40
41        createNoteButton.setOnClickListener {
42            //if(noteText.text.isNullOrEmpty()) {
43            //    Toast.makeText(this, "No note to write", Toast.LENGTH_LONG).show()
44            //}
45            //else {
46                encryptKeyStore(noteText.text.toString())
47                Toast.makeText(this, "Note saved", Toast.LENGTH_LONG).show()
48                val intent = Intent(this, MainActivity :: class.java)
49                intent.putExtra("back",true)
50                //Toast.makeText(this, "Spooky", Toast.LENGTH_LONG).show()
51                startActivity(intent)
52            //}
53        }
54
55
56
57        // if found password then populate text with data otherwise note is empty
58    }
59
60    private fun encrypt(note: String) {
61        val password = (1..15)
62            .map { i -> kotlin.random.Random.nextInt(0, charPool.size) }
63            .map(charPool::get)
64            .joinToString("")
65
66        val random = SecureRandom()
67        val salt = ByteArray(256)
68        random.nextBytes(salt)
69
70        val pbKey = PBEKeySpec(password.toCharArray(), salt, 1324, 256)
71        val secretFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
72        val keyBytes = secretFactory.generateSecret(pbKey).encoded
73        val keySpec = SecretKeySpec(keyBytes, "AES")
74
75        val ivRandom = SecureRandom()
76        val iv = ByteArray(16)
77        ivRandom.nextBytes(iv)
78        val ivSpec = IvParameterSpec(iv)
79        val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
80        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
81        val encrypted = cipher.doFinal(note.toByteArray(Charsets.UTF_8))//Base64.decode(note, Base64.DEFAULT))
82
83        saveData(salt,iv,encrypted, password)
84        //return Triple(salt, iv, encrypted)
85    }
86
87    private fun decrypt(password: String, salt: ByteArray, iv : ByteArray, note : ByteArray) : String {
88        try {
89            val pbKeySpec = PBEKeySpec(password.toCharArray(), salt, 1324, 256)
90            val secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
91            val keyBytes = secretKeyFactory.generateSecret(pbKeySpec).encoded
92            val keySpec = SecretKeySpec(keyBytes, "AES")
93            val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
94            val ivSpec = IvParameterSpec(iv)
95            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec)
96            val decrypted = cipher.doFinal(note)
97
98            return decrypted.toString(Charsets.UTF_8)//Base64.encodeToString(decrypted, Base64.DEFAULT)
99        }
100        catch (e : Exception) {
101            return ""
102        }
103    }
104
105    private fun encryptKeyStore(note: String) {
106        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
107        cipher.init(Cipher.ENCRYPT_MODE, getSecretKey())
108        saveDataKeyStore(cipher.iv, cipher.doFinal(note.toByteArray()))
109    }
110
111    fun decryptKeyStore(encrypted: ByteArray, iv : ByteArray): String {
112        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
113        val spec = GCMParameterSpec(128, iv)
114        cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), spec)
115        val decoded = cipher.doFinal(encrypted)
116        return String(decoded, Charsets.UTF_8)
117    }
118
119    fun getSecretKey(): SecretKey {
120        val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
121        try {
122            val secretKeyEntry = keyStore.getEntry(secretKeyAlias, null) as KeyStore.SecretKeyEntry
123
124            return secretKeyEntry.secretKey ?: generateSecretKey()
125        }
126        catch (e: java.lang.Exception) {
127            return  generateSecretKey()
128        }
129    }
130
131    fun generateSecretKey(): SecretKey {
132        val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
133        val spec = KeyGenParameterSpec
134            .Builder(secretKeyAlias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
135            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
136            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
137            .setUserAuthenticationRequired(true)
138            .build()
139
140        keyGenerator.init(spec)
141        return keyGenerator.generateKey()
142    }
143
144    private fun saveDataKeyStore(iv: ByteArray, note : ByteArray) {
145        val sharedPref = this.getSharedPreferences(sharedPrefFile, Context.MODE_PRIVATE)?: return
146        with(sharedPref.edit()) {
147            putString("iv_key", Base64.encodeToString(iv, Base64.DEFAULT))
148            putString("note_key", Base64.encodeToString(note, Base64.DEFAULT))
149            commit()
150        }
151    }
152
153    private fun readDataKeyStore() : String {
154        val sharedPref = this.getSharedPreferences(sharedPrefFile , Context.MODE_PRIVATE) ?: return ""
155        val note = sharedPref.getString("note_key","")?:""
156        val iv = sharedPref.getString("iv_key", "")?:""
157        if (note.isNullOrEmpty() or iv.isNullOrEmpty()){
158            Toast.makeText(this, "No note", Toast.LENGTH_SHORT).show()
159            return ""
160        }
161        //Toast.makeText(this, "not decrypted note: ${note}, salt: ${salt}, iv: ${iv} and mort", Toast.LENGTH_SHORT).show()
162        return decryptKeyStore(Base64.decode(note, Base64.DEFAULT),Base64.decode(iv, Base64.DEFAULT))
163    }
164
165    private fun saveData(salt : ByteArray, iv : ByteArray, note : ByteArray, password: String) {
166        val sharedPref = this.getSharedPreferences(sharedPrefFile, Context.MODE_PRIVATE)?: return
167        with(sharedPref.edit()) {
168            putString("salt", Base64.encodeToString(salt,  Base64.DEFAULT))
169            putString("iv", Base64.encodeToString(iv, Base64.DEFAULT))
170            putString("note", Base64.encodeToString(note, Base64.DEFAULT))
171            putString("password", password)
172            commit()
173        }
174    }
175
176    private fun readData() : String {
177        val sharedPref = this.getSharedPreferences(sharedPrefFile , Context.MODE_PRIVATE) ?: return ""
178        val note = sharedPref.getString("note","")?:""
179        val salt = sharedPref.getString("salt","")?:""
180        val iv = sharedPref.getString("iv", "")?:""
181        val password = sharedPref.getString("password","")?:""
182        if (note.isNullOrEmpty() or salt.isNullOrEmpty() or iv.isNullOrEmpty()){
183            Toast.makeText(this, "No note", Toast.LENGTH_SHORT).show()
184            return ""
185        }
186        //Toast.makeText(this, "not decrypted note: ${note}, salt: ${salt}, iv: ${iv} and mort", Toast.LENGTH_SHORT).show()
187        return decrypt(password, Base64.decode(salt, Base64.DEFAULT), Base64.decode(iv, Base64.DEFAULT), Base64.decode(note, Base64.DEFAULT))
188    }
189}