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 java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
object DatabaseDownloader {
|
||||
|
||||
@ -16,6 +19,9 @@ object DatabaseDownloader {
|
||||
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"
|
||||
|
||||
// AES-256 Key (muss exakt 32 Bytes lang sein)
|
||||
private const val AES_KEY = "12345678901234567890123456789012"
|
||||
|
||||
private val client = OkHttpClient()
|
||||
|
||||
fun downloadAndReplaceDatabase(context: Context) {
|
||||
@ -33,7 +39,8 @@ object DatabaseDownloader {
|
||||
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 ->
|
||||
FileOutputStream(downloadedFile).use { output ->
|
||||
input.copyTo(output)
|
||||
@ -41,15 +48,34 @@ object DatabaseDownloader {
|
||||
}
|
||||
Log.d("DOWNLOAD", "Datei gespeichert: ${downloadedFile.absolutePath}")
|
||||
|
||||
// Entschlüsselung
|
||||
val decryptedBytes = decryptFile(downloadedFile)
|
||||
val dbFile = context.getDatabasePath(DB_NAME)
|
||||
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) {
|
||||
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 android.database.Cursor
|
||||
import okhttp3.*
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
object DatabaseUploader {
|
||||
|
||||
private const val DB_NAME = "questionnaire_database"
|
||||
// TODO entferne uploadDeltaTest.php
|
||||
private const val SERVER_DELTA_URL = "http://49.13.157.44/uploadDeltaTest.php"
|
||||
// TODO entferne uploadDeltaTest2.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 API_TOKEN = "MEIN_SUPER_GEHEIMES_TOKEN_12345"
|
||||
|
||||
@ -57,10 +60,10 @@ object DatabaseUploader {
|
||||
val exists = checkDatabaseExists()
|
||||
if (exists) {
|
||||
Log.d("UPLOAD", "Server-Datenbank vorhanden → Delta-Upload")
|
||||
uploadPseudoDelta(dbFile)
|
||||
uploadPseudoDelta(context, dbFile)
|
||||
} else {
|
||||
Log.d("UPLOAD", "Keine Server-Datenbank → Delta-Upload")
|
||||
uploadPseudoDelta(dbFile)
|
||||
uploadPseudoDelta(context, dbFile)
|
||||
}
|
||||
|
||||
} 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 {
|
||||
val db = SQLiteDatabase.openDatabase(file.absolutePath, null, SQLiteDatabase.OPEN_READONLY)
|
||||
|
||||
@ -115,10 +126,30 @@ object DatabaseUploader {
|
||||
|
||||
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()
|
||||
.setType(MultipartBody.FORM)
|
||||
.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()
|
||||
|
||||
val request = Request.Builder()
|
||||
@ -129,6 +160,9 @@ object DatabaseUploader {
|
||||
client.newCall(request).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
Log.e("UPLOAD", "Delta-Upload fehlgeschlagen: ${e.message}")
|
||||
// cleanup
|
||||
tmpJson.delete()
|
||||
tmpEnc.delete()
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
@ -150,9 +184,14 @@ object DatabaseUploader {
|
||||
if (journalFile.exists() && journalFile.delete()) {
|
||||
Log.d("UPLOAD", "Journal-Datei gelöscht.")
|
||||
}
|
||||
// cleanup temp files
|
||||
tmpJson.delete()
|
||||
tmpEnc.delete()
|
||||
exitProcess(0)
|
||||
} else {
|
||||
Log.e("UPLOAD", "Delta-Upload fehlgeschlagen: ${response.code} $body")
|
||||
tmpJson.delete()
|
||||
tmpEnc.delete()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user