Skip to content

Instantly share code, notes, and snippets.

@codesnipers
Forked from JosiasSena/DeCryptor.java
Last active March 9, 2021 15:15
Show Gist options
  • Save codesnipers/3906447e730376f2e076bf16d857c463 to your computer and use it in GitHub Desktop.
Save codesnipers/3906447e730376f2e076bf16d857c463 to your computer and use it in GitHub Desktop.
Encryptor and Decryptor for data encryption.decryption using the Android KeyStore.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hanjintransportation.hanjindelivery.util.secure.SampleActivity">
<EditText
android:id="@+id/ed_text_to_encrypt"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_encrypted_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ed_text_to_encrypt" />
<TextView
android:id="@+id/tv_decrypted_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_encrypted_text" />
<TextView
android:id="@+id/tv_key"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_decrypted_text" />
<Button
android:id="@+id/btn_encrypt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:onClick="onClick"
android:text="암호화"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_key" />
<Button
android:id="@+id/btn_decrypt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="복호화"
android:onClick="onClick"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_encrypt" />
<Button
android:id="@+id/btn_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:onClick="onClick"
android:text="지움"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_decrypt" />
</androidx.constraintlayout.widget.ConstraintLayout>
public class DeCryptor {
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
private KeyStore keyStore;
Context context;
DeCryptor(Context context) throws CertificateException, NoSuchAlgorithmException, KeyStoreException,
IOException {
this.context = context;
initKeyStore();
}
private void initKeyStore() throws KeyStoreException, CertificateException,
NoSuchAlgorithmException, IOException {
keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
}
String decrypt(final String alias, final byte[] encryptedData)
throws UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException,
NoSuchPaddingException, InvalidKeyException, IOException,
BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
String IV = PrefUtils.getString(context, "IV");
if (null != IV && !IV.isEmpty()) {
byte[] encryptionIv = Base64.decode(IV, Base64.DEFAULT);
Log.e("Decrypter", "IV : " + IV + " IV size " + encryptionIv.length);
final GCMParameterSpec spec = new GCMParameterSpec(128, encryptionIv);
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(alias), spec);
return new String(cipher.doFinal(encryptedData), StandardCharsets.UTF_8);
} else {
return "{}";
}
}
private SecretKey getSecretKey(final String alias) throws NoSuchAlgorithmException,
UnrecoverableEntryException, KeyStoreException {
return ((KeyStore.SecretKeyEntry) keyStore.getEntry(alias, null)).getSecretKey();
}
}
public class EnCryptor {
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
Context context;
EnCryptor(Context context) {
this.context = context;
}
@RequiresApi(api = Build.VERSION_CODES.M)
public byte[] encrypt(final String alias, final String textToEncrypt)
throws NoSuchAlgorithmException,
NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IOException,
InvalidAlgorithmParameterException, BadPaddingException,
IllegalBlockSizeException {
final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(alias));
String IV = Base64.encodeToString(cipher.getIV(), Base64.DEFAULT);
PrefUtils.setParam(context, "IV", IV);
return (cipher.doFinal(textToEncrypt.getBytes(StandardCharsets.UTF_8)));
}
@RequiresApi(api = Build.VERSION_CODES.M)
@NonNull
private SecretKey getSecretKey(final String alias) throws NoSuchAlgorithmException,
NoSuchProviderException, InvalidAlgorithmParameterException {
final KeyGenerator keyGenerator = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
keyGenerator.init(new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
return keyGenerator.generateKey();
}
}
public class PrefUtils {
private static final String PREF_DEFALUT_STIRNG = "";
private static final Boolean PREF_DEFAULT_BOOLEAN = Boolean.FALSE;
private static final Integer PREF_DEFALUT_INTEGER = 0;
private static final Float PREF_DEFAULT_FLOAT = 0f;
private static final Long PREF_DEFAULT_LONG = 0l;
private static SharedPreferences.Editor getDefalutEditor(Context context){
return PreferenceManager.getDefaultSharedPreferences(context).edit();
}
public static SharedPreferences getDefaultSharedPrefs(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
public static void setParam(Context context , String key, Object object){
if(object==null){
return;
}
String type = object.getClass().getSimpleName();
SharedPreferences.Editor editor = getDefalutEditor(context);
if("String".equals(type)){
editor.putString(key, (String)object);
}
else if("Integer".equals(type)){
editor.putInt(key, (Integer)object);
}
else if("Boolean".equals(type)){
editor.putBoolean(key, (Boolean)object);
}
else if("Float".equals(type)){
editor.putFloat(key, (Float)object);
}
else if("Long".equals(type)){
editor.putLong(key, (Long)object);
}
editor.commit();
}
public static void setParamArray(Context context, String key, HashSet<String> mSet) {
SharedPreferences.Editor editor = getDefalutEditor(context);
editor.putStringSet(key, mSet);
editor.commit();
}
public static Set<String> getStringArray(Context context, String key) {
return getDefaultSharedPrefs(context).getStringSet(key, null);
}
public static String getString(Context context, String key) {
return getDefaultSharedPrefs(context).getString(key, PREF_DEFALUT_STIRNG);
}
public static String getString(Context context, String key, String default_value) {
return getDefaultSharedPrefs(context).getString(key, default_value);
}
public static boolean getBoolean(Context context, String key) {
return getDefaultSharedPrefs(context).getBoolean(key, PREF_DEFAULT_BOOLEAN);
}
public static int getInt(Context context, String key) {
return getDefaultSharedPrefs(context).getInt(key, PREF_DEFALUT_INTEGER);
}
public static long getLong(Context context, String key) {
return getDefaultSharedPrefs(context).getLong(key, PREF_DEFAULT_LONG);
}
public static long getLong(Context context, String key, long default_long) {
return getDefaultSharedPrefs(context).getLong(key, PREF_DEFAULT_LONG);
}
public static float getFloat(Context context, String key) {
return getDefaultSharedPrefs(context).getFloat(key, PREF_DEFAULT_FLOAT);
}
public static boolean contains(Context context, String key) {
return getDefaultSharedPrefs(context).contains(key);
}
public static void remove(Context context, String key)
{
SharedPreferences sp = getDefaultSharedPrefs(context);
SharedPreferences.Editor editor = sp.edit();
editor.remove(key);
SharedPreferencesCompat.apply(editor);
}
public static void clear(Context context)
{
SharedPreferences sp = getDefaultSharedPrefs(context);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
SharedPreferencesCompat.apply(editor);
}
public static Map<String, ?> getAll(Context context)
{
SharedPreferences sp = getDefaultSharedPrefs(context);
return sp.getAll();
}
private static class SharedPreferencesCompat
{
private static final Method sApplyMethod = findApplyMethod();
@SuppressWarnings({ "unchecked", "rawtypes" })
private static Method findApplyMethod()
{
try
{
Class clz = SharedPreferences.Editor.class;
return clz.getMethod("apply");
} catch (NoSuchMethodException e)
{
Dlog.e(e.getMessage());
}
return null;
}
public static void apply(SharedPreferences.Editor editor)
{
try
{
if (sApplyMethod != null)
{
sApplyMethod.invoke(editor);
return;
}
} catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) {
Dlog.e(e.getMessage());
}
editor.commit();
}
}
}
public class SampleUsage extends AppCompatActivity {
private static final String SAMPLE_ALIAS = "MYALIAS";
private EnCryptor encryptor;
private DeCryptor decryptor;
private EditText edTextToEncrypt;
private TextView tvEncryptedText;
private TextView tvDecryptedText;
private TextView tvKey;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
edTextToEncrypt = findViewById(R.id.ed_text_to_encrypt);
tvEncryptedText = findViewById(R.id.tv_encrypted_text);
tvDecryptedText = findViewById(R.id.tv_decrypted_text);
tvKey = findViewById(R.id.tv_key);
initKey();
}
private void initKey() {
encryptor = new EnCryptor(this);
try {
decryptor = new DeCryptor(this);
} catch (CertificateException | NoSuchAlgorithmException | KeyStoreException |
IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
public void onClick(View view) {
final int id = view.getId();
switch (id) {
case R.id.btn_encrypt:
encryptText();
break;
case R.id.btn_decrypt:
decryptText();
break;
case R.id.btn_clear:
clear();
break;
}
}
private void decryptText() {
tvDecryptedText.setText("");
try {
String encryptedText = PrefUtils.getString(this, "sec");
byte[] inputEncryptedJSONFromFile = Base64.decode(encryptedText, Base64.DEFAULT);
String temp = decryptor.decrypt(SAMPLE_ALIAS,inputEncryptedJSONFromFile);
tvDecryptedText.setText(temp);
} catch (UnrecoverableEntryException | NoSuchAlgorithmException |
KeyStoreException | NoSuchPaddingException |
IOException | InvalidKeyException e) {
Dlog.e("decryptData() called with: " + e.getMessage());
} catch (IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void encryptText() {
try {
final byte[] encryptedText = encryptor
.encrypt(SAMPLE_ALIAS, edTextToEncrypt.getText().toString());
String encrytText = Base64.encodeToString(encryptedText, Base64.DEFAULT);
PrefUtils.setParam(this, "sec", encrytText);
tvEncryptedText.setText(encrytText);
} catch (NoSuchAlgorithmException | NoSuchProviderException |
IOException | NoSuchPaddingException | InvalidKeyException e) {
Dlog.e("onClick() called with: " + e.getMessage());
} catch (InvalidAlgorithmParameterException |
IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
}
private void clear() {
tvEncryptedText.setText("");
tvDecryptedText.setText("");
PrefUtils.clear(this);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment