So I'm using the code below for encrypting/decrypting string values that can be stored in the device preferences but I know need to add a method for encrypting/decrypting an ArrayList that can also be stored in the preferences as well (so I'm guessing the encryption needs to convert the arraylist to a string and then the decryption needs to convert that string back to the arraylist). Since I'm pretty new to Android/Java I'm having a hard time figuring out how to do this so any help would be appreciated.
public String encrypt(String key, String data) {
if (key == null || data == null)
return null;
try {
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(charsetName));
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm);
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
byte[] dataBytes = data.getBytes(charsetName);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.encodeToString(cipher.doFinal(dataBytes), base64Mode);
} catch (Exception e) {
return null;
}
}
public String decrypt(String key, String data) {
if (key == null || data == null)
return null;
try {
byte[] dataBytes = Base64.decode(data, base64Mode);
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(charsetName));
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm);
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] dataBytesDecrypted = (cipher.doFinal(dataBytes));
return new String(dataBytesDecrypted);
} catch (Exception e) {
return null;
}
}
Serialize the ArrayList<LinkedHashMap> to Json before sending the input to encrypt()
public static String ArrayListToString(ArrayList<LinkedHashMap> list) {
Gson serializer = new Gson();
return serializer.toJson(list);
}
then do deserializing after decrypting.
You need to reference Google-Gson in your project.
Related
I am currently working on a drawing based project , i had stored the values as on json format and store in file , but i want encrypt the json by using the key and decrypt the json the by same key.
Stringify your json using String resultString = JSON.stringify() and encrypt your resultString using the following method
public class EncryptUtils {
public static SecretKey generateKey(String mySecret)
throws NoSuchAlgorithmException, InvalidKeySpecException
{
return secret = new SecretKeySpec(mySecret.getBytes(), "AES");
}
public static byte[] encryptMsg(String message, SecretKey secret)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException
{
/* Encrypt the message. */
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8"));
return cipherText;
}
public static String decryptMsg(byte[] cipherText, SecretKey secret)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException
{
/* Decrypt the message, given derived encContentValues and initialization vector. */
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
String decryptString = new String(cipher.doFinal(cipherText), "UTF-8");
return decryptString;
}
}
String mySecret="mySecretKeyString";
String secretKey = EncryptUtils.generateKey(mySecret);
String encryptedStr = EncryptUtils.encryptMsg(jsonResultString, secretKey));
String decryptedStr = EncryptUtils.decryptMsg(encryptedStr.getBytes("UTF-8"), secretKey));
finally you could get the JSON data using following method
try {
JSONObject obj = new JSONObject(decryptedString);
Log.d("My App", obj.toString());
} catch (Throwable t) {
Log.e("My App", "Could not parse malformed JSON: \"" + json + "\"");
}
Do not use this as some kind of security measurement.
The encryption mechanism in this post is a One-time pad, which means that the secret key can be easily recovered by an attacker using 2 encrypted messages. XOR 2 encrypted messages and you get the key. That simple!
public class EncryptUtils {
public static final String DEFAULT_ENCODING = "UTF-8";
static BASE64Encoder enc = new BASE64Encoder();
static BASE64Decoder dec = new BASE64Decoder();
public static String base64encode(String text) {
try {
return enc.encode(text.getBytes(DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
return null;
}
}//base64encode
public static String base64decode(String text) {
try {
return new String(dec.decodeBuffer(text), DEFAULT_ENCODING);
} catch (IOException e) {
return null;
}
}//base64decode
public static void main(String[] args) {
String txt = "some text to be encrypted";
String key = "key phrase used for XOR-ing";
System.out.println(txt + " XOR-ed to: " + (txt = xorMessage(txt, key)));
String encoded = base64encode(txt);
System.out.println(" is encoded to: " + encoded + " and that is decoding to: " + (txt = base64decode(encoded)));
System.out.print("XOR-ing back to original: " + xorMessage(txt, key));
}
public static String xorMessage(String message, String key) {
try {
if (message == null || key == null) return null;
char[] keys = key.toCharArray();
char[] mesg = message.toCharArray();
int ml = mesg.length;
int kl = keys.length;
char[] newmsg = new char[ml];
for (int i = 0; i < ml; i++) {
newmsg[i] = (char)(mesg[i] ^ keys[i % kl]);
}//for i
return new String(newmsg);
} catch (Exception e) {
return null;
}
}//xorMessage
}
I'm looking to encrypt my access token that is generated when the user inputs a username and password. The question I'm asking here is what are the different ways of doing this and what would the community think is the best method to do so. I've looked at Cryptography and Keystore but not sure if these are the right way to go? I'm a noob to encryption so any documentation would also be very helpful.
Thank you very much :D
Please try with android SecretKeySpec class
for encryption
public void setPassCode(String value) throws Exception {
try {
SecretKeySpec sks = null;
sks = getEncryptKey();
byte[] userLatENC=null;
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE,sks );
userLatENC = c.doFinal(value.getBytes());
passCode = Base64.encodeToString(userLatENC, Base64.DEFAULT);
} catch (Exception e) {
throw e;
}
}
for decryption
public String getPassCode() throws Exception {
SecretKeySpec sks = null;
try {
String encVal = "pass_code";
if (encVal.isEmpty()) {
return encVal;
}
sks = getDecryptKey();
byte[] latDEC=null;
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, sks);
latDEC = c.doFinal(Base64.decode(encVal, Base64.DEFAULT));
return new String(latDEC);
} catch (Exception e) {
throw e;
}
}
Get Encrypted Key
private SecretKeySpec getEncryptKey() throws Exception{
SecretKeySpec sks = null;
SecretKey key =null;
byte[] keyToSave;
try {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed("any data used as random seed".getBytes());
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128, sr);
key= kg.generateKey();
keyToSave =key.getEncoded();
sks = new SecretKeySpec(keyToSave, "AES");
ks.load(null,null);
ks.setKeyEntry("aliasKey",key,null, null);
FileOutputStream ksout = context.openFileOutput("keystore_android", Context.MODE_PRIVATE);
ks.store(ksout, null);
ksout.close();
return sks;
} catch (Exception e) {
throw e;
}
}
For Decrypted Key
private SecretKeySpec getDecryptKey() throws Exception{
SecretKeySpec sks = null;
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream fis = null;
fis = context.openFileInput("keystore_android");
keyStore.load(fis,null);
sks=new SecretKeySpec((keyStore.getKey("aliasKey", null)).getEncoded(), "AES");
return sks;
} catch (Exception e) {
throw e;
}
}
Encryption is working fine. When I try to decrypt I receive error "pad block corrupted"(on c3des.doFinal). What am I doing wrong? I use web services (C#) and encryption and decryption is working fine.
public String encryptText(String plainText) throws Exception {
byte[] plaintext = plainText.getBytes("UTF-8");//input
byte[] tdesKeyData = "sdfsdgf3q453qdsg".getBytes("UTF-8");
Cipher c3des = Cipher.getInstance("DESede/ECB/PKCS7Padding");
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
c3des.init(Cipher.ENCRYPT_MODE, myKey);
byte[] cipherText = c3des.doFinal(plaintext);
String encryptedString = Base64.encodeToString(cipherText,
Base64.DEFAULT);
return encryptedString;
}
public String decrypt(String tekst) throws Exception {
byte[] tekst2 = tekst.getBytes("UTF-8");
byte[] tdesKeyData = "sdfsdgf3q453qdsg".getBytes("UTF-8");
Cipher c3des = Cipher.getInstance("DESede/ECB/PKCS7Padding");
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
String decryptedString="";
try {
c3des.init(Cipher.DECRYPT_MODE, myKey);
byte[] cipherText = c3des.doFinal(tekst2);
decryptedString = Base64.decode(cipherText, Base64.DEFAULT).toString();
}
catch(Exception e)
{
decryptedString=e.getMessage();
}
return decryptedString;
}
I want to store an encrypted Profile object in SharedPreferences using gson.
Here's my code:
public void saveProfile(Profile newProfile) {
try {
Log.i(C.TAG, newProfile.toString());
SharedPreferences.Editor editor = prefs.edit();
String profileJSONfied = new Gson().toJson(newProfile);
Log.i(C.TAG, profileJSONfied);
byte[] cleartext = profileJSONfied.getBytes(HTTP.UTF_8);
Log.i(C.TAG, cleartext.toString());
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
String encrypedProfile = Base64.encodeToString(cipher.doFinal(cleartext), Base64.DEFAULT);
Log.i(C.TAG, encrypedProfile);
editor.putString(PROFILE, encrypedProfile);
editor.commit();
profile = newProfile;
} catch (Exception e) {
Log.i(C.TAG, e.getMessage());
}
}
public Profile loadProfile() {
try {
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, key);
Log.i(C.TAG, prefs.getString(PROFILE, null));
// byte[] plainTextProfileBytes = Base64.decode(cipher.doFinal(prefs.getString(PROFILE, null).getBytes(HTTP.UTF_8)), Base64.DEFAULT);
byte[] plainTextProfileBytes = Base64.decode(prefs.getString(PROFILE, null).getBytes(HTTP.UTF_8), Base64.DEFAULT);
Log.i(C.TAG, new String(plainTextProfileBytes, HTTP.UTF_8));
profile = new Gson().fromJson(new String(plainTextProfileBytes, HTTP.UTF_8), PROFILE_TYPE);
Log.i(C.TAG, profile.toString());
} catch (Exception e) {
Log.i(C.TAG, e.getMessage());
}
return profile;
}
Here's an output example (ordered by Log's order):
saveProfile:
Profile#4146a1d8
{"email":"aaa","firstName":"aaa","lastName":"aaa","postal":"aaa", etc etc...}
[B#414819b0
+nLS7XhRoIFPBeC11/h6mMz6hFfc8js03QJ8VwVZH+dPBeC11/h6mJ448CLGPNzz+bU669XpAI8VXchYQJr7mgDwHpeoSrP4BMACydjKpC8Q9atbk9xz6HNqDpNOiqaa75hFM+r9pzm55/E2E2tdjz4s5OzNNppAPzmtS69tZAZLPuYt1kvnJehHa6fDt2o5UCv6VukCwvVgt+UDcCqCKvF22Iv6vdMXWTcm
At this point I think everything went as expected. The problem lies below, decipher operation
loadProfile:(backward process)
+nLS7XhRoIFPBeC11/h6mMz6hFfc8js03QJ8VwVZH+dPBeC11/h6mJ448CLGPNzz+bU669XpAI8VXchYQJr7mgDwHpeoSrP4BMACydjKpC8Q9atbk9xz6HNqDpNOiqaa75hFM+r9pzm55/E2E2tdjz4s5OzNNppAPzmtS69tZAZLPuYt1kvnJehHa6fDt2o5UCv6VukCwvVgt+UDcCqCKvF22Iv6vdMXWTcm
�r��xQ��O���z���W��;4�|WY�O���z��8�"�<���:������]�X#�������J�����ʤ/�[��s�sj�N����E3��9���6k]�>,���6�#?9�K�mdK>�-�K�%�Gk�÷j9P+�V���`��p*�*�v؋��Y7&���-A
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 21
If I instead of:
byte[] plainTextProfileBytes = Base64.decode(prefs.getString(PROFILE,null).getBytes(HTTP.UTF_8), Base64.DEFAULT);
I use:
byte[] plainTextProfileBytes = Base64.decode(cipher.doFinal(prefs.getString(PROFILE, null).getBytes(HTTP.UTF_8)), Base64.DEFAULT);
The error will be:
pad block corrupted
What am I missing here?
Thanks for your time.
So, I've found a solution!
For starters I've changed a bit of my code. I've created a myPBEkey class with two methods, encrypt and decrypt, both return a Cipher object with the respective "opmode":
Then I've changed my saveProfile and loadProfile methos code to:
public void saveProfile(Profile newProfile) {
try {
SharedPreferences.Editor editor = prefs.edit();
String profileJSONfied = new Gson().toJson(newProfile);
byte[] encryptedProfile = pbeKey.encrypt().doFinal(profileJSONfied.getBytes(HTTP.UTF_8));
byte[] encryptedProfileBase64 = Base64.encode(encryptedProfile, Base64.DEFAULT);
editor.putString(PROFILE, new String(encryptedProfileBase64, HTTP.UTF_8));
editor.commit();
profile = newProfile;
} catch (Exception e) {
Log.i(C.TAG, e.getMessage());
}
}
public Profile loadProfile() {
if (profile == null) {
try {
byte[] decodedProfileBase64 = Base64.decode(prefs.getString(PROFILE, null), Base64.DEFAULT);
byte[] plainTextProfileBytes = pbeKey.decrypt().doFinal(decodedProfileBase64);
profile = new Gson().fromJson(new String(plainTextProfileBytes, HTTP.UTF_8), PROFILE_TYPE);
} catch (Exception e) {
Log.i(C.TAG, e.getMessage());
}
return profile;
I think what solved the problem was separating the encrypt/decrypt from Base64 encode/decode, so first we encrypt and then encode the encrypted byte[] and finally store it. The same goes when decrypting, first we decode the encrypted base64 profile and then decrypt the decoded byte[]. Voilà!
Thanks for your time, hope it helps you.
First of all, I've reviewed all the entries on the forum, and I still can not find a solution to my problem.
I have to measure the time it takes to encode and decode a text with DES, and make a comparison with other algorithms.
When I run the code, I have this error: BadPaddingException: pad block corrupted. When I debug, the code fails in this line:
byte [] plaintext = cipher.doFinal (cipherBytes);
I use class Base64 to encode/decode String <--> byte[]
Any idea?
thanks
private static final String CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";
private static int KEY_LENGTH = 64;
public static SecretKey deriveKeyDES() {
try {
long start = System.currentTimeMillis();
KeyGenerator kgen = KeyGenerator.getInstance("DES");
kgen.init(KEY_LENGTH);
SecretKey result = kgen.generateKey();
long elapsed = System.currentTimeMillis() - start;
return result;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
public static String encrypt(String plaintext, SecretKey key) {
try {
long start = System.currentTimeMillis();
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = cipher.doFinal(plaintext.getBytes("UTF-8"));
long elapsed = System.currentTimeMillis() - start;
return toBase64(cipherText);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static String toBase64(byte[] bytes) {
return Base64.encodeToString(bytes, Base64.NO_WRAP).trim();
}
public static String decrypt(String ciphertext, SecretKey key) {
try {
byte[] cipherBytes = fromBase64(ciphertext);
long start = System.currentTimeMillis();
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
cipher.update(cipherBytes);
// This is where I get exception
byte[] plaintext = cipher.doFinal(cipherBytes);
String plainrStr = new String(plaintext, "UTF-8").trim();
long elapsed = System.currentTimeMillis() - start;
return plainrStr;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static byte[] fromBase64(String base64) {
return Base64.decode(base64, Base64.NO_WRAP);
}
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
cipher.update(cipherBytes);
// byte[] plaintext = cipher.doFinal(cipherBytes);
// ^-- You shouldn't pass cipherBytes twice.
// v-- instead use the parameter-less method:
byte[] plaintext = cipher.doFinal();
Padding exception occur when the last cipher text block does not compute to valid plain text. This would happen if last ciphertext block is corrupted or the key is incorrect. For CBC mode it would also happen if the second to last ciphertext was altered (but you are using ECB mode encryption).
In your case, the deriveKeyDES() is always generating a random key. Although we didn't get the actual calls to the security methods, I would presume you use a different key for encryption and decryption. In that case there is a very high chance that the resulting plain text does not contain valid padding bytes.
Rasmus answer certainly points to an error in your code, and it would screw up your timings and return a the plain text two times, but it would not remove the BadPaddingException.
I had the same problem in one source code, and IllegalBlockSizeException in another one.
Solved this two problems by return encoding data like:
public String encrypt(String input) {
try {
byte[] inputBytes = input.getBytes("UTF-8");
byte[] enc = encryptCipher.doFinal(inputBytes);
// and problem was in return encoding. That's how i fixed it
return Base64.encodeToString(enc,Base64.DEFAULT);
.....
}
}
Give u a code for decrypt:
public String decrypt(String input) {
try {
byte[] dec = Base64.decode(input.getBytes(), Base64.DEFAULT);
//here had exception
byte[] utf8 = decryptCipher.doFinal(dec);
return new String(utf8,"UTF8");
} catch (IOException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
I should submit, that had BadPaddingException and IllegalBlockSizeException
only in decrypt method byte[] utf8 = decryptCipher.doFinal(dec); (u had exeption in the same place: byte[] plaintext = cipher.doFinal(cipherBytes);), but real wrong is in encrypt method(return value)
That's why i recommend u to use that code in encrypt method:
return Base64.encodeToString(enc,Base64.DEFAULT);
P.S Tried to a give full answer on your question.