· 6 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}