From 15e4a004a87fe61eb4bce6bd7dc3aefe99402352 Mon Sep 17 00:00:00 2001 From: Juan Miguel Boyero Corral Date: Sat, 1 Dec 2012 22:07:45 +0100 Subject: [PATCH] Added encryption to notifications --- SWADroid/src/es/ugr/swad/swadroid/Global.java | 2 +- .../src/es/ugr/swad/swadroid/Preferences.java | 1 + .../src/es/ugr/swad/swadroid/SWADMain.java | 3 + .../swad/swadroid/model/DataBaseHelper.java | 90 ++++++++----- .../src/es/ugr/swad/swadroid/model/Test.java | 10 +- .../notifications/NotificationItem.java | 4 +- .../modules/notifications/Notifications.java | 5 +- .../NotificationsCursorAdapter.java | 38 +++--- .../NotificationsSyncAdapterService.java | 2 +- .../es/ugr/swad/swadroid/utils/Crypto.java | 121 ++++++++++++++++++ 10 files changed, 215 insertions(+), 61 deletions(-) create mode 100644 SWADroid/src/es/ugr/swad/swadroid/utils/Crypto.java diff --git a/SWADroid/src/es/ugr/swad/swadroid/Global.java b/SWADroid/src/es/ugr/swad/swadroid/Global.java index 38334644..162f36cc 100644 --- a/SWADroid/src/es/ugr/swad/swadroid/Global.java +++ b/SWADroid/src/es/ugr/swad/swadroid/Global.java @@ -36,7 +36,7 @@ public class Global { /** * SWAD application key */ - private static final String AppKey = ""; //DELETE BEFORE COMMIT!!! + private static final String AppKey = ""; //DELETE THE KEY BEFORE COMMIT!!! /** * Server URL */ diff --git a/SWADroid/src/es/ugr/swad/swadroid/Preferences.java b/SWADroid/src/es/ugr/swad/swadroid/Preferences.java index 8478512a..9e449abc 100644 --- a/SWADroid/src/es/ugr/swad/swadroid/Preferences.java +++ b/SWADroid/src/es/ugr/swad/swadroid/Preferences.java @@ -36,6 +36,7 @@ import android.preference.PreferenceActivity; import android.preference.PreferenceManager; import es.ugr.swad.swadroid.modules.Courses; import es.ugr.swad.swadroid.modules.notifications.Notifications; +import es.ugr.swad.swadroid.utils.Base64; /** * Preferences window of application. diff --git a/SWADroid/src/es/ugr/swad/swadroid/SWADMain.java b/SWADroid/src/es/ugr/swad/swadroid/SWADMain.java index d7d2ee58..ee00e26d 100644 --- a/SWADroid/src/es/ugr/swad/swadroid/SWADMain.java +++ b/SWADroid/src/es/ugr/swad/swadroid/SWADMain.java @@ -43,6 +43,8 @@ import android.widget.SimpleCursorAdapter; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; +import es.ugr.swad.swadroid.gui.ImageExpandableListAdapter; +import es.ugr.swad.swadroid.gui.MenuExpandableListActivity; import es.ugr.swad.swadroid.model.Course; import es.ugr.swad.swadroid.model.DataBaseHelper; import es.ugr.swad.swadroid.model.Model; @@ -316,6 +318,7 @@ public class SWADMain extends MenuExpandableListActivity { } else if(lastVersion < currentVersion) { //showUpgradeDialog(); dbHelper.upgradeDB(this); + dbHelper.encryptNotifications(); //prefs.upgradeCredentials(); //Configure automatic synchronization diff --git a/SWADroid/src/es/ugr/swad/swadroid/model/DataBaseHelper.java b/SWADroid/src/es/ugr/swad/swadroid/model/DataBaseHelper.java index 4b6627d5..8d8f90ed 100644 --- a/SWADroid/src/es/ugr/swad/swadroid/model/DataBaseHelper.java +++ b/SWADroid/src/es/ugr/swad/swadroid/model/DataBaseHelper.java @@ -44,6 +44,8 @@ import com.android.dataframework.Entity; import es.ugr.swad.swadroid.Global; //import es.ugr.swad.swadroid.Preferences; +import es.ugr.swad.swadroid.Preferences; +import es.ugr.swad.swadroid.utils.Crypto; /** * @author Juan Miguel Boyero Corral @@ -62,19 +64,19 @@ public class DataBaseHelper { /** * Application preferences */ - //private Preferences prefs = new Preferences(); + private Preferences prefs = new Preferences(); /** * Database name */ - //private String DBName = "swadroid_db"; + //private String DBName = "swadroid_db_crypt"; /** * Database passphrase */ - //private String DBKey; + private String DBKey; /** * Database passphrase length */ - //private int DB_KEY_LENGTH = 128; + private int DB_KEY_LENGTH = 128; /** * Constructor @@ -82,24 +84,13 @@ public class DataBaseHelper { */ public DataBaseHelper(Context ctx) { mCtx = ctx; - //prefs.getPreferences(mCtx); - //DBKey = prefs.getDBKey(); + prefs.getPreferences(mCtx); + DBKey = prefs.getDBKey(); db = DataFramework.getInstance(); //Initialize SQLCipher libraries //SQLiteDatabase.loadLibs(mCtx); - //If the passphrase is empty, generate a random passphrase and recreate database - /*if(DBKey.equals("")) { - DBKey = Global.randomString(DB_KEY_LENGTH); - prefs.setDBKey(DBKey); - mCtx.deleteDatabase(DBName); - SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(mCtx.getDatabasePath("swadroid_db_crypt"), DBKey, null); - database.close(); - } - - Log.d("DataBaseHelper", "DBKey=" + DBKey);*/ - try { //db.open(mCtx, mCtx.getPackageName(), DBKey); db.open(mCtx, mCtx.getPackageName()); @@ -108,6 +99,18 @@ public class DataBaseHelper { } catch (IOException e) { e.printStackTrace(); } + + //If the passphrase is empty, generate a random passphrase and recreate database + if(DBKey.equals("")) { + DBKey = Global.randomString(DB_KEY_LENGTH); + prefs.setDBKey(DBKey); + + /*mCtx.deleteDatabase(DBName); + SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(mCtx.getDatabasePath("swadroid_db_crypt"), DBKey, null); + database.close();*/ + } + + //Log.d("DataBaseHelper", "DBKey=" + DBKey); } /** @@ -207,16 +210,16 @@ public class DataBaseHelper { ent.getInt(params.getSecond())); } else if(table.equals(Global.DB_TABLE_NOTIFICATIONS)) { o = new SWADNotification(ent.getInt("id"), - ent.getString("eventType"), + Crypto.decrypt(DBKey, ent.getString("eventType")), ent.getLong("eventTime"), - ent.getString("userSurname1"), - ent.getString("userSurname2"), - ent.getString("userFirstname"), - ent.getString("userPhoto"), - ent.getString("location"), - ent.getString("summary"), + Crypto.decrypt(DBKey, ent.getString("userSurname1")), + Crypto.decrypt(DBKey, ent.getString("userSurname2")), + Crypto.decrypt(DBKey, ent.getString("userFirstname")), + Crypto.decrypt(DBKey, ent.getString("userPhoto")), + Crypto.decrypt(DBKey, ent.getString("location")), + Crypto.decrypt(DBKey, ent.getString("summary")), ent.getInt("status"), - ent.getString("content")); + Crypto.decrypt(DBKey, ent.getString("content"))); } else if(table.equals(Global.DB_TABLE_TEST_QUESTIONS)) { id = ent.getInt("id"); PairTable q = (PairTable)getRow(Global.DB_TABLE_TEST_QUESTIONS_COURSE, "qstCod", Long.toString(id)); @@ -709,16 +712,16 @@ public class DataBaseHelper { String status = String.valueOf(n.getStatus()); ent.setValue("id", n.getId()); - ent.setValue("eventType", n.getEventType()); + ent.setValue("eventType", Crypto.encrypt(DBKey, n.getEventType())); ent.setValue("eventTime", eventTime); - ent.setValue("userSurname1", n.getUserSurname1()); - ent.setValue("userSurname2", n.getUserSurname2()); - ent.setValue("userFirstname", n.getUserFirstName()); - ent.setValue("userPhoto", n.getUserPhoto()); - ent.setValue("location", n.getLocation()); - ent.setValue("summary", n.getSummary()); + ent.setValue("userSurname1", Crypto.encrypt(DBKey, n.getUserSurname1())); + ent.setValue("userSurname2", Crypto.encrypt(DBKey, n.getUserSurname2())); + ent.setValue("userFirstname", Crypto.encrypt(DBKey, n.getUserFirstName())); + ent.setValue("userPhoto", Crypto.encrypt(DBKey, n.getUserPhoto())); + ent.setValue("location", Crypto.encrypt(DBKey, n.getLocation())); + ent.setValue("summary", Crypto.encrypt(DBKey, n.getSummary())); ent.setValue("status", status); - ent.setValue("content", n.getContent()); + ent.setValue("content", Crypto.encrypt(DBKey, n.getContent())); ent.save(); } @@ -1664,6 +1667,27 @@ public class DataBaseHelper { rows.get(i).delete(); } } + + /** + * Encrypts the notifications data + */ + public void encryptNotifications() { + List rows = db.getEntityList(Global.DB_TABLE_NOTIFICATIONS); + + Iterator iter = rows.iterator(); + while (iter.hasNext()) { + Entity ent = iter.next(); + ent.setValue("eventType", Crypto.encrypt(DBKey, ent.getString("eventType"))); + ent.setValue("userSurname1", Crypto.encrypt(DBKey, ent.getString("userSurname1"))); + ent.setValue("userSurname2", Crypto.encrypt(DBKey, ent.getString("userSurname2"))); + ent.setValue("userFirstname", Crypto.encrypt(DBKey, ent.getString("userSurname2"))); + ent.setValue("userPhoto", Crypto.encrypt(DBKey, ent.getString("userPhoto"))); + ent.setValue("location", Crypto.encrypt(DBKey, ent.getString("location"))); + ent.setValue("summary", Crypto.encrypt(DBKey, ent.getString("summary"))); + ent.setValue("content", Crypto.encrypt(DBKey, ent.getString("content"))); + ent.save(); + } + } /** * Empty table from database diff --git a/SWADroid/src/es/ugr/swad/swadroid/model/Test.java b/SWADroid/src/es/ugr/swad/swadroid/model/Test.java index 3c9eef63..401ac32f 100644 --- a/SWADroid/src/es/ugr/swad/swadroid/model/Test.java +++ b/SWADroid/src/es/ugr/swad/swadroid/model/Test.java @@ -316,11 +316,11 @@ public class Test extends Model { s = buff.toString().trim(); //Remove accents - s = s.replace('‡', 'a'); - s = s.replace('Ž', 'e'); - s = s.replace('’', 'i'); - s = s.replace('—', 'o'); - s = s.replace('œ', 'u'); + s = s.replace('á', 'a'); + s = s.replace('é', 'e'); + s = s.replace('í', 'i'); + s = s.replace('ó', 'o'); + s = s.replace('ú', 'u'); return s; } diff --git a/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationItem.java b/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationItem.java index edc2ed52..8b2c3960 100644 --- a/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationItem.java +++ b/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationItem.java @@ -18,11 +18,11 @@ */ package es.ugr.swad.swadroid.modules.notifications; -import es.ugr.swad.swadroid.DownloadImageTask; import es.ugr.swad.swadroid.Global; -import es.ugr.swad.swadroid.MenuActivity; import es.ugr.swad.swadroid.R; +import es.ugr.swad.swadroid.gui.MenuActivity; import es.ugr.swad.swadroid.modules.Messages; +import es.ugr.swad.swadroid.utils.DownloadImageTask; import android.content.Intent; import android.os.Bundle; import android.util.Log; diff --git a/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/Notifications.java b/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/Notifications.java index 775c7085..dc228922 100644 --- a/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/Notifications.java +++ b/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/Notifications.java @@ -50,7 +50,6 @@ import android.widget.Toast; import es.ugr.swad.swadroid.Global; import es.ugr.swad.swadroid.R; -import es.ugr.swad.swadroid.model.DataBaseHelper; import es.ugr.swad.swadroid.model.SWADNotification; import es.ugr.swad.swadroid.modules.Module; @@ -169,8 +168,7 @@ public class Notifications extends Module { setContentView(R.layout.list_items); this.findViewById(R.id.courseSelectedText).setVisibility(View.GONE); - this.findViewById(R.id.groupSpinner).setVisibility(View.GONE); - + this.findViewById(R.id.groupSpinner).setVisibility(View.GONE); image = (ImageView)this.findViewById(R.id.moduleIcon); image.setBackgroundResource(R.drawable.notif); @@ -187,6 +185,7 @@ public class Notifications extends Module { dbCursor = dbHelper.getDb().getCursor(Global.DB_TABLE_NOTIFICATIONS, selection, orderby); startManagingCursor(dbCursor); adapter = new NotificationsCursorAdapter(this, dbCursor); + adapter.setDBKey(prefs.getDBKey()); list = (ListView)this.findViewById(R.id.listItems); list.setAdapter(adapter); diff --git a/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationsCursorAdapter.java b/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationsCursorAdapter.java index 697ff4e5..8b006271 100644 --- a/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationsCursorAdapter.java +++ b/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationsCursorAdapter.java @@ -22,14 +22,12 @@ import java.util.Date; import es.ugr.swad.swadroid.Global; import es.ugr.swad.swadroid.R; -import es.ugr.swad.swadroid.modules.Messages; +import es.ugr.swad.swadroid.utils.Crypto; import android.content.Context; -import android.content.Intent; import android.database.Cursor; import android.text.Html; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.CursorAdapter; import android.widget.ImageView; @@ -41,8 +39,9 @@ import android.widget.TextView; * */ public class NotificationsCursorAdapter extends CursorAdapter { + protected Context ctx; private boolean [] contentVisible; - Context ctx; + private String DBKey; /** * Constructor @@ -75,7 +74,6 @@ public class NotificationsCursorAdapter extends CursorAdapter { @Override public void bindView(View view, Context context, Cursor cursor) { - final Context ctx = context; final Long notificationCode = cursor.getLong(cursor.getColumnIndex("id")); final String userPhoto = cursor.getString(cursor.getColumnIndex("userPhoto")); long unixTime; @@ -86,7 +84,7 @@ public class NotificationsCursorAdapter extends CursorAdapter { java.text.DateFormat dateShortFormat = android.text.format.DateFormat.getDateFormat(context); java.text.DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(context); int numRows = cursor.getCount(); - + if(contentVisible.length == 0) { contentVisible = new boolean[numRows]; } @@ -107,17 +105,17 @@ public class NotificationsCursorAdapter extends CursorAdapter { /* OnClickListener replyMessageListener = new OnClickListener() { public void onClick(View v) { - Intent activity = new Intent(ctx.getApplicationContext(), Messages.class); + Intent activity = new Intent(context.getApplicationContext(), Messages.class); activity.putExtra("notificationCode", notificationCode); activity.putExtra("summary", summary.getText().toString()); - ctx.startActivity(activity); + context.startActivity(activity); } };*/ if(eventType != null) { eventCode.setText(notificationCode.toString()); - eventUserPhoto.setText(userPhoto); - type = cursor.getString(cursor.getColumnIndex("eventType")); + eventUserPhoto.setText(Crypto.decrypt(DBKey, userPhoto)); + type = Crypto.decrypt(DBKey, cursor.getString(cursor.getColumnIndex("eventType"))); //messageReplyButton.setVisibility(View.GONE); if(type.equals("examAnnouncement")) @@ -165,9 +163,9 @@ public class NotificationsCursorAdapter extends CursorAdapter { } if(eventSender != null){ sender = ""; - senderFirstname = cursor.getString(cursor.getColumnIndex("userFirstname")); - senderSurname1 = cursor.getString(cursor.getColumnIndex("userSurname1")); - senderSurname2 = cursor.getString(cursor.getColumnIndex("userSurname2")); + senderFirstname = Crypto.decrypt(DBKey, cursor.getString(cursor.getColumnIndex("userFirstname"))); + senderSurname1 = Crypto.decrypt(DBKey, cursor.getString(cursor.getColumnIndex("userSurname1"))); + senderSurname2 = Crypto.decrypt(DBKey, cursor.getString(cursor.getColumnIndex("userSurname2"))); //Empty fields checking if(senderFirstname.compareTo(Global.NULL_VALUE)!=0) @@ -180,10 +178,10 @@ public class NotificationsCursorAdapter extends CursorAdapter { eventSender.setText(sender); } if(location != null) { - location.setText(Html.fromHtml(cursor.getString(cursor.getColumnIndex("location")))); + location.setText(Html.fromHtml(Crypto.decrypt(DBKey, cursor.getString(cursor.getColumnIndex("location"))))); } if(summary != null){ - summaryText = cursor.getString(cursor.getColumnIndex("summary")); + summaryText = Crypto.decrypt(DBKey, cursor.getString(cursor.getColumnIndex("summary"))); //Empty field checking if(summaryText.compareTo(Global.NULL_VALUE)==0) @@ -192,7 +190,7 @@ public class NotificationsCursorAdapter extends CursorAdapter { summary.setText(Html.fromHtml(summaryText)); } if((content != null)){ - contentText = cursor.getString(cursor.getColumnIndex("content")); + contentText = Crypto.decrypt(DBKey, cursor.getString(cursor.getColumnIndex("content"))); //Empty field checking if(contentText.compareTo(Global.NULL_VALUE)==0) @@ -249,4 +247,12 @@ public class NotificationsCursorAdapter extends CursorAdapter { this.notifyDataSetChanged(); } }*/ + + /** + * Sets the database key + * @param key + */ + public void setDBKey(String key) { + DBKey = key; + } } diff --git a/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationsSyncAdapterService.java b/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationsSyncAdapterService.java index f727e9fe..0d24d511 100644 --- a/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationsSyncAdapterService.java +++ b/SWADroid/src/es/ugr/swad/swadroid/modules/notifications/NotificationsSyncAdapterService.java @@ -33,7 +33,6 @@ import org.ksoap2.serialization.SoapSerializationEnvelope; import org.ksoap2.transport.KeepAliveHttpsTransportSE; import org.xmlpull.v1.XmlPullParserException; -import es.ugr.swad.swadroid.Base64; import es.ugr.swad.swadroid.Global; import es.ugr.swad.swadroid.Preferences; import es.ugr.swad.swadroid.R; @@ -41,6 +40,7 @@ import es.ugr.swad.swadroid.model.DataBaseHelper; import es.ugr.swad.swadroid.model.SWADNotification; import es.ugr.swad.swadroid.model.User; import es.ugr.swad.swadroid.ssl.SecureConnection; +import es.ugr.swad.swadroid.utils.Base64; import android.accounts.Account; import android.accounts.OperationCanceledException; diff --git a/SWADroid/src/es/ugr/swad/swadroid/utils/Crypto.java b/SWADroid/src/es/ugr/swad/swadroid/utils/Crypto.java new file mode 100644 index 00000000..e0040d2e --- /dev/null +++ b/SWADroid/src/es/ugr/swad/swadroid/utils/Crypto.java @@ -0,0 +1,121 @@ +/* + * This file is part of SWADroid. + * + * Copyright (C) 2010 Juan Miguel Boyero Corral + * + * SWADroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SWADroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with SWADroid. If not, see . + */ + +package es.ugr.swad.swadroid.utils; + +import java.security.MessageDigest; +import java.security.SecureRandom; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +/** + * Cryptographic class for encription purposes. + * @author Juan Miguel Boyero Corral + */ +public class Crypto +{ + public static String encrypt(String seed, String cleartext) + { + try + { + byte[] rawKey = getRawKey(seed.getBytes()); + byte[] result = encrypt(rawKey, cleartext.getBytes()); + return Base64.encodeBytes(result); + } + catch(Exception e) + { + e.printStackTrace(); + } + return "error"; + } + + public static String decrypt(String seed, String encrypted) + { + try + { + byte[] rawKey = getRawKey(seed.getBytes()); + byte[] enc = Base64.decode(encrypted); + byte[] result = decrypt(rawKey, enc); + return new String(result); + } + catch(Exception e) + { + e.printStackTrace(); + } + return "error"; + } + + private static byte[] getRawKey(byte[] seed) throws Exception + { + KeyGenerator kgen = KeyGenerator.getInstance("AES"); + SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); + sr.setSeed(seed); + kgen.init(128, sr); + SecretKey skey = kgen.generateKey(); + byte[] raw = skey.getEncoded(); + return raw; + } + + private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception + { + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + byte[] encrypted = cipher.doFinal(clear); + return encrypted; + } + + private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception + { + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + byte[] decrypted = cipher.doFinal(encrypted); + return decrypted; + } + + public static final String md5(final String s) + { + try + { + MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); + digest.update(s.getBytes()); + byte messageDigest[] = digest.digest(); + + StringBuffer hexString = new StringBuffer(); + for(int i=0; i