-
-
Save codesnipers/3906447e730376f2e076bf16d857c463 to your computer and use it in GitHub Desktop.
Encryptor and Decryptor for data encryption.decryption using the Android KeyStore.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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