How to Encrypt/Decrypt String in Android

Easy way to Encrypt/Decrypt string in Android

You can use Cipher for this.

This class provides the functionality of a cryptographic cipher for encryption and decryption. It forms the core of the Java Cryptographic Extension (JCE) framework.

Sample of encryption and decryption:

public static SecretKey generateKey() 
throws NoSuchAlgorithmException, InvalidKeySpecException
{
return secret = new SecretKeySpec(password.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;
}

To encrypt:

SecretKey secret = generateKey();
EncUtil.encryptMsg(String toEncrypt, secret))

To decrypt:

EncUtil.decryptMsg(byte[] toDecrypt, secret))

Android String Encryption/Decryption

This line is in error:

plainText.setText(new String(cipher.doFinal(plainText.getText().toString().getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));

If we break it apart, we have something like

byte [] cipherBytes = cipher.doFinal(plainText.getText().toString().getBytes(StandardCharsets.UTF_8));
plainText.setText(new String(cipherBytes, StandardCharsets.UTF_8);

The problem is that cipherBytes is a sequence of arbitrary bytes rather than the characters of a string. The String constructor will silently replace invalid characters with something else, a process which corrupts the data.

If you want to display the cipher bytes or otherwise send it to a character oriented channel you must encode it. Typically encodings are base64 or hex. To decrypt the String you must then decode it to bytes first and then decrypt it.

Example:

byte [] cipherBytes = cipher.doFinal(plainText.getText().toString().getBytes(StandardCharsets.UTF_8));
plainText.setText(Base64.encodeToString(cipherBytes, Base64.DEFAULT));

And on decrypt:

byte[] cipherBytes = Base64.decode(plainText.getText().toString(), Base64.DEFAULT);
byte[] decrypted = cipher.doFinal(cipherBytes);

String encryption using Jetpack Security in Android

I would take your signature:

public String encrypt(String data) and keep it that way but choose an approach:

  1. Is the data small enough that using Secure Shared Preferences enough to store something? (not the best idea due to the issues with Shared Pref.)

  2. Can you keep the data in a File (temporary) and then return that?

You can do either, the difference shouldn't be too big since you're likely going to have some form of class YourCryptoImplementation where you're going to perform all this...

Using Shared Preferences

You can have a couple of methods (sorry, In Kotlin because it's shorter and I've already used similar code):

private fun getEncryptedPreferences() = 
EncryptedSharedPreferences.create("your_shared_preferences", advancedKeyAlias,
context, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)

You're going to wonder what advancedKeyAlias is. That's just a private var advancedKeyAlias: String but the actual value... will be something like:

    init {
val advancedSpec = KeyGenParameterSpec.Builder("your_master_key_name",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT).apply {
setBlockModes(KeyProperties.BLOCK_MODE_GCM)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
setKeySize(256)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val hasStrongBox = context.packageManager.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE)
if (hasStrongBox)
setIsStrongBoxBacked(true)
}
}.build()
advancedKeyAlias = MasterKeys.getOrCreate(advancedSpec)
}

So, now in your init() of this class, you ensure you have your key alias created.

You can use it to encrypt or decrypt.

Back to our SharedPref. example:

Let's say you want to store a string, you can offer:

    fun encryptToSharedPref(String data) {
getEncryptedPrefs().edit().putString("the_key_you_want_to_use", data).apply()
}

And to "read" the value:

fun getValueFromSharedPreferencesWith(key: String) = getEncryptedPreferences().getString(key, null)

That would work, if the strings fit in SharedPref and if you don't care about other Shared Preferences issues...

What about FILES?

Not a huge difference, but assuming you're in the same class (that is, the advancedKeyAlias exists).

You're going to have a getEncryptedFile helper method:

    private fun getEncryptedFile(file: File) = EncryptedFile.Builder(file, context, advancedKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build()

And you can decrypt a file like:

  fun decryptFile(file: File): FileInputStream {
return getEncryptedFile(file).openFileInput()
}

Very simple, and you can obviously use it like

val rawData = yourCryptoClassAbove.decryptFile(File("path/to/file").readBytes()

val decryptedString = String(rawData)

Now to encrypt a file, you can use a FileOutputStream, that is a stream that outputs the bytes directly to a file... in our case, an encrypted file.

E.g.:

    fun encryptFile(bytes: ByteArray, file: File) {
var outputStream: FileOutputStream? = null
try {
outputStream = getEncryptedFile(file).openFileOutput().apply {
write(bytes)
}
} catch (exception: IOException) {
Log.e(TAG, "output file already exists, please use a new file", exception)
} finally {
outputStream?.close()
}
}

You receive a ByteArray though, but that's not hard to obtain if you have a string...

var dataToEncrypt = ... //any "String"
yourCryptoClassAbove.encryptFile(File("path/to/file", dataToEncrypt.toByteArray())

And that's basically most of what you'd likely need. Obviously, you can have any method to generate your "advancedKey".

I don't know if this would help you, but it would certainly abstract the complexity of encrypting away from your code using it.

Disclaimer: some of these is code I've used, some is just "pseudo code" that gives you an Idea what I had in mind.

Encrypt/decrypt in android in plaintext

Here i added the sample code for you. Please try and let me know.

import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import android.util.Base64;

public class EncodeDecodeAES {

private final static String HEX = "0123456789ABCDEF";
private final static String key = "encryptionKey"
private final static int JELLY_BEAN_4_2 = 17;

private final static byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

public static String encrypt(String cleartext) throws Exception {
return encrypt(key,cleartext);
}

public static String decrypt(String cleartext) throws Exception {
return decrypt(key,cleartext);
}

public static String encrypt(String seed, String cleartext) throws Exception {

byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
String fromHex = toHex(result);
String base64 = new String(Base64.encodeToString(fromHex.getBytes(), 0));
return base64;
}

public static String decrypt(String seed, String encrypted) throws Exception {

byte[] seedByte = seed.getBytes();
System.arraycopy(seedByte, 0, key, 0, ((seedByte.length < 16) ? seedByte.length : 16));
String base64 = new String(Base64.decode(encrypted, 0));
byte[] rawKey = getRawKey(seedByte);
byte[] enc = toByte(base64);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}

public static byte[] encryptBytes(String seed, byte[] cleartext) throws Exception {

byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext);
return result;
}

public static byte[] decryptBytes(String seed, byte[] encrypted) throws Exception {

byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = decrypt(rawKey, encrypted);
return result;

}

private static byte[] getRawKey(byte[] seed) throws Exception {

KeyGenerator kgen = KeyGenerator.getInstance("AES"); // , "SC");
SecureRandom sr = null;
if (android.os.Build.VERSION.SDK_INT >= JELLY_BEAN_4_2) {
sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
} else {
sr = SecureRandom.getInstance("SHA1PRNG");
}
sr.setSeed(seed);
try {
kgen.init(256, sr);
// kgen.init(128, sr);
} catch (Exception e) {
// Log.w(LOG, "This device doesn't suppor 256bits, trying 192bits.");
try {
kgen.init(192, sr);
} catch (Exception e1) {
// Log.w(LOG, "This device doesn't suppor 192bits, trying 128bits.");
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"); // /ECB/PKCS7Padding", "SC");
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"); // /ECB/PKCS7Padding", "SC");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}

public static String toHex(String txt) {
return toHex(txt.getBytes());
}

public static String fromHex(String hex) {

return new String(toByte(hex));
}

public static byte[] toByte(String hexString) {

int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
return result;
}

public static String toHex(byte[] buf) {

if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}

private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}

}

How to call in your activity :

Encryption:

 String Name = EncodeDecodeAES.encrypt("John Smith");
Log.d("Encrypted value",Name);

Decryption:

String Name = EncodeDecodeAES.decrypt("NzhEMTlBMUExRTU5OEVDMDBEMDI5NkVDMTlBQzZGNkI=");
Log.d("Decrypted value",Name);

Hope it should helpful for you.

Fast and simple String encrypt/decrypt in JAVA

Java - encrypt / decrypt user name and password from a configuration file

Code from above link

DESKeySpec keySpec = new DESKeySpec("Your secret Key phrase".getBytes("UTF8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);
sun.misc.BASE64Encoder base64encoder = new BASE64Encoder();
sun.misc.BASE64Decoder base64decoder = new BASE64Decoder();
.........

// ENCODE plainTextPassword String
byte[] cleartext = plainTextPassword.getBytes("UTF8");

Cipher cipher = Cipher.getInstance("DES"); // cipher is not thread safe
cipher.init(Cipher.ENCRYPT_MODE, key);
String encryptedPwd = base64encoder.encode(cipher.doFinal(cleartext));
// now you can store it
......

// DECODE encryptedPwd String
byte[] encrypedPwdBytes = base64decoder.decodeBuffer(encryptedPwd);

Cipher cipher = Cipher.getInstance("DES");// cipher is not thread safe
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainTextPwdBytes = (cipher.doFinal(encrypedPwdBytes));

How to encrypt and decrypt the json string in android using key?

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
}

Encrypt/Decrypt String Kotlin

To encode your ciphertext use base 64 or hexadecimals. The Java API contains a Base64 class, so you're probably best off using that.

byte[]#toString doesn't do what you expect it to do; it simply returns a representation of the byte array reference, not the contents of the byte array.


Besides that:

  • don't use SecureRandom to derive a key, try and lookup PBKDF2;
  • explicitly use a mode of operation such as "AES/GCM/NoPadding";
  • use a unique IV, and a random IV if you decide to use CBC (usually insecure);
  • don't use toByteArray without explicitly selecting a character encoding for the message.


Related Topics



Leave a reply



Submit