Added encryption for the case, that no database on the server exists and decryption.
This commit is contained in:
@ -9,6 +9,9 @@ import okhttp3.OkHttpClient
|
|||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.spec.IvParameterSpec
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
object DatabaseDownloader {
|
object DatabaseDownloader {
|
||||||
|
|
||||||
@ -16,6 +19,9 @@ object DatabaseDownloader {
|
|||||||
private const val API_TOKEN = "MEIN_SUPER_GEHEIMES_TOKEN_12345"
|
private const val API_TOKEN = "MEIN_SUPER_GEHEIMES_TOKEN_12345"
|
||||||
private const val SERVER_DOWNLOAD_URL = "http://49.13.157.44/downloadFull.php?token=$API_TOKEN"
|
private const val SERVER_DOWNLOAD_URL = "http://49.13.157.44/downloadFull.php?token=$API_TOKEN"
|
||||||
|
|
||||||
|
// AES-256 Key (muss exakt 32 Bytes lang sein)
|
||||||
|
private const val AES_KEY = "12345678901234567890123456789012"
|
||||||
|
|
||||||
private val client = OkHttpClient()
|
private val client = OkHttpClient()
|
||||||
|
|
||||||
fun downloadAndReplaceDatabase(context: Context) {
|
fun downloadAndReplaceDatabase(context: Context) {
|
||||||
@ -33,7 +39,8 @@ object DatabaseDownloader {
|
|||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
val downloadedFile = File(context.cacheDir, "downloaded_database")
|
// Zwischenspeichern der verschlüsselten Datei
|
||||||
|
val downloadedFile = File(context.cacheDir, "downloaded_database.enc")
|
||||||
response.body?.byteStream()?.use { input ->
|
response.body?.byteStream()?.use { input ->
|
||||||
FileOutputStream(downloadedFile).use { output ->
|
FileOutputStream(downloadedFile).use { output ->
|
||||||
input.copyTo(output)
|
input.copyTo(output)
|
||||||
@ -41,15 +48,34 @@ object DatabaseDownloader {
|
|||||||
}
|
}
|
||||||
Log.d("DOWNLOAD", "Datei gespeichert: ${downloadedFile.absolutePath}")
|
Log.d("DOWNLOAD", "Datei gespeichert: ${downloadedFile.absolutePath}")
|
||||||
|
|
||||||
|
// Entschlüsselung
|
||||||
|
val decryptedBytes = decryptFile(downloadedFile)
|
||||||
val dbFile = context.getDatabasePath(DB_NAME)
|
val dbFile = context.getDatabasePath(DB_NAME)
|
||||||
if (dbFile.exists()) dbFile.delete()
|
if (dbFile.exists()) dbFile.delete()
|
||||||
downloadedFile.copyTo(dbFile, overwrite = true)
|
FileOutputStream(dbFile).use { fos ->
|
||||||
|
fos.write(decryptedBytes)
|
||||||
|
}
|
||||||
|
|
||||||
Log.d("DOWNLOAD", "Neue DB erfolgreich eingesetzt")
|
Log.d("DOWNLOAD", "Neue DB erfolgreich entschlüsselt und eingesetzt")
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("DOWNLOAD", "Fehler beim Download oder Ersetzen der DB", e)
|
Log.e("DOWNLOAD", "Fehler beim Download oder Ersetzen der DB", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun decryptFile(file: File): ByteArray {
|
||||||
|
val fileBytes = file.readBytes()
|
||||||
|
if (fileBytes.size < 16) throw IllegalArgumentException("Datei zu kurz, kein IV vorhanden")
|
||||||
|
|
||||||
|
val iv = fileBytes.copyOfRange(0, 16)
|
||||||
|
val cipherBytes = fileBytes.copyOfRange(16, fileBytes.size)
|
||||||
|
|
||||||
|
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
|
val keySpec = SecretKeySpec(AES_KEY.toByteArray(Charsets.UTF_8), "AES")
|
||||||
|
val ivSpec = IvParameterSpec(iv)
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec)
|
||||||
|
|
||||||
|
return cipher.doFinal(cipherBytes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,17 +9,20 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
object DatabaseUploader {
|
object DatabaseUploader {
|
||||||
|
|
||||||
private const val DB_NAME = "questionnaire_database"
|
private const val DB_NAME = "questionnaire_database"
|
||||||
// TODO entferne uploadDeltaTest.php
|
// TODO entferne uploadDeltaTest2.php
|
||||||
private const val SERVER_DELTA_URL = "http://49.13.157.44/uploadDeltaTest.php"
|
private const val SERVER_DELTA_URL = "http://49.13.157.44/uploadDeltaTest2.php"
|
||||||
private const val SERVER_CHECK_URL = "http://49.13.157.44/checkDatabaseExists.php"
|
private const val SERVER_CHECK_URL = "http://49.13.157.44/checkDatabaseExists.php"
|
||||||
private const val API_TOKEN = "MEIN_SUPER_GEHEIMES_TOKEN_12345"
|
private const val API_TOKEN = "MEIN_SUPER_GEHEIMES_TOKEN_12345"
|
||||||
|
|
||||||
@ -57,10 +60,10 @@ object DatabaseUploader {
|
|||||||
val exists = checkDatabaseExists()
|
val exists = checkDatabaseExists()
|
||||||
if (exists) {
|
if (exists) {
|
||||||
Log.d("UPLOAD", "Server-Datenbank vorhanden → Delta-Upload")
|
Log.d("UPLOAD", "Server-Datenbank vorhanden → Delta-Upload")
|
||||||
uploadPseudoDelta(dbFile)
|
uploadPseudoDelta(context, dbFile)
|
||||||
} else {
|
} else {
|
||||||
Log.d("UPLOAD", "Keine Server-Datenbank → Delta-Upload")
|
Log.d("UPLOAD", "Keine Server-Datenbank → Delta-Upload")
|
||||||
uploadPseudoDelta(dbFile)
|
uploadPseudoDelta(context, dbFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -95,7 +98,15 @@ object DatabaseUploader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun uploadPseudoDelta(file: File) {
|
/**
|
||||||
|
* Wichtig: Diese Funktion wurde erweitert, sodass:
|
||||||
|
* - die DB als JSON in eine temporäre Datei geschrieben wird,
|
||||||
|
* - diese JSON-Datei AES-verschlüsselt wird (mit AES256Helper.encryptFile),
|
||||||
|
* - die verschlüsselte Datei als Multipart 'file' an den Server gesendet wird.
|
||||||
|
*
|
||||||
|
* (Funktionalität: gleiche Signatur wie vorher behalten)
|
||||||
|
*/
|
||||||
|
private fun uploadPseudoDelta(context: Context, file: File) {
|
||||||
try {
|
try {
|
||||||
val db = SQLiteDatabase.openDatabase(file.absolutePath, null, SQLiteDatabase.OPEN_READONLY)
|
val db = SQLiteDatabase.openDatabase(file.absolutePath, null, SQLiteDatabase.OPEN_READONLY)
|
||||||
|
|
||||||
@ -115,10 +126,30 @@ object DatabaseUploader {
|
|||||||
|
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
// Schreibe JSON in temporäre Datei
|
||||||
|
val tmpJson = File(context.cacheDir, "payload.json")
|
||||||
|
tmpJson.writeText(data.toString())
|
||||||
|
|
||||||
|
// Verschlüssele JSON -> tmpEnc
|
||||||
|
val tmpEnc = File(context.cacheDir, "payload.enc")
|
||||||
|
try {
|
||||||
|
AES256Helper.encryptFile(tmpJson, tmpEnc)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("UPLOAD", "Fehler bei der Verschlüsselung der JSON-Datei", e)
|
||||||
|
// cleanup
|
||||||
|
tmpJson.delete()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val requestBody = MultipartBody.Builder()
|
val requestBody = MultipartBody.Builder()
|
||||||
.setType(MultipartBody.FORM)
|
.setType(MultipartBody.FORM)
|
||||||
.addFormDataPart("token", API_TOKEN)
|
.addFormDataPart("token", API_TOKEN)
|
||||||
.addFormDataPart("data", data.toString())
|
// Datei-Feld "file" mit verschlüsselter Payload
|
||||||
|
.addFormDataPart(
|
||||||
|
"file",
|
||||||
|
"payload.enc",
|
||||||
|
tmpEnc.asRequestBody("application/octet-stream".toMediaType())
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
@ -129,6 +160,9 @@ object DatabaseUploader {
|
|||||||
client.newCall(request).enqueue(object : Callback {
|
client.newCall(request).enqueue(object : Callback {
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
Log.e("UPLOAD", "Delta-Upload fehlgeschlagen: ${e.message}")
|
Log.e("UPLOAD", "Delta-Upload fehlgeschlagen: ${e.message}")
|
||||||
|
// cleanup
|
||||||
|
tmpJson.delete()
|
||||||
|
tmpEnc.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
override fun onResponse(call: Call, response: Response) {
|
||||||
@ -150,9 +184,14 @@ object DatabaseUploader {
|
|||||||
if (journalFile.exists() && journalFile.delete()) {
|
if (journalFile.exists() && journalFile.delete()) {
|
||||||
Log.d("UPLOAD", "Journal-Datei gelöscht.")
|
Log.d("UPLOAD", "Journal-Datei gelöscht.")
|
||||||
}
|
}
|
||||||
|
// cleanup temp files
|
||||||
|
tmpJson.delete()
|
||||||
|
tmpEnc.delete()
|
||||||
exitProcess(0)
|
exitProcess(0)
|
||||||
} else {
|
} else {
|
||||||
Log.e("UPLOAD", "Delta-Upload fehlgeschlagen: ${response.code} $body")
|
Log.e("UPLOAD", "Delta-Upload fehlgeschlagen: ${response.code} $body")
|
||||||
|
tmpJson.delete()
|
||||||
|
tmpEnc.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user