Skip to content

Instantly share code, notes, and snippets.

@praslnx8
Created May 24, 2018 09:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save praslnx8/0676c3402437950552f697e82a2aaa4e to your computer and use it in GitHub Desktop.
Save praslnx8/0676c3402437950552f697e82a2aaa4e to your computer and use it in GitHub Desktop.
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import android.util.Log;
import java.security.KeyPair;
import java.security.SecureRandom;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class SecureEncryptor {
private static final String SECURE_PREF = "secure_pref";
private static final String ENCRYPTED_MASTER_PREF_KEY = "encrypted_master_pref_key";
private static SecureEncryptor instance;
public static SecureEncryptor getInstance(Context context) {
if(instance == null) {
instance = new SecureEncryptor(context);
}
return instance;
}
private SecretKey secretKey = null;
/**
* Base64 encoding settings used for generated code verifiers.
*/
private static final int PKCE_BASE64_ENCODE_SETTINGS =
Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE;
private SecureEncryptor(Context context) {
createOrLoadMasterKey(context);
}
private void createOrLoadMasterKey(Context context) {
KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.getInstance();
KeyPair keyPair = keyStoreWrapper.getAndroidKeyStoreAsymmetricKeyPair("versa");
if(keyPair == null) {
Log.i("XDFCE", "keypair is null. Create a new one");
keyPair = keyStoreWrapper.loadAndroidAssymetricKeys(context, "versa");
}
if(keyPair == null) {
Log.i("XDFCE", "Create new keypair failed. Aborting!!!");
return;
}
String encryptedMasterKey = getSavedEncryptedMasterKey(context);
byte[] decryptedMasterKey;
if(encryptedMasterKey == null) {
byte[] newRandomMasterKey = generateSecureRandom();
encryptedMasterKey = CipherWrapper.getInstance().encryptWithPublicKey(newRandomMasterKey, keyPair.getPublic());
decryptedMasterKey = newRandomMasterKey;
if(encryptedMasterKey != null) {
saveEncryptedMasterKey(context, encryptedMasterKey);
Log.i("XDFCE", encryptedMasterKey);
} else {
Log.i("XDFCE", "Unable to encryptWithPublicKey");
}
} else {
Log.i("XDFCE", "Decrytping...");
decryptedMasterKey = CipherWrapper.getInstance().decryptWithPrivate(encryptedMasterKey, keyPair.getPrivate());
}
if(decryptedMasterKey != null) {
Log.i("XDFCE", Base64.encodeToString(decryptedMasterKey, Base64.DEFAULT));
Log.i("XDFCE", new String(decryptedMasterKey));
secretKey = new SecretKeySpec(decryptedMasterKey, "AES");
}
}
private byte[] generateSecureRandom() {
SecureRandom secureRandom = new SecureRandom();
byte[] randomBytes = new byte[16];
secureRandom.nextBytes(randomBytes);
return randomBytes;
}
@Nullable
private String getSavedEncryptedMasterKey(@Nonnull Context context) {
SharedPreferences sp = context.getSharedPreferences(SECURE_PREF, Context.MODE_PRIVATE);
return sp.getString(ENCRYPTED_MASTER_PREF_KEY, null);
}
private void saveEncryptedMasterKey(@Nonnull Context context, @Nonnull String encryptedMasterKey) {
SharedPreferences sp = context.getSharedPreferences(SECURE_PREF, Context.MODE_PRIVATE);
SharedPreferences.Editor edit = sp.edit();
edit.putString(ENCRYPTED_MASTER_PREF_KEY, encryptedMasterKey);
edit.apply();
}
@Nullable
public String encryptMsg(@Nonnull String messageToBeEncrypted)
{
if(secretKey != null) {
return CipherWrapper.getInstance().encryptMsg(messageToBeEncrypted, secretKey);
}
return null;
}
@Nullable
public String decryptMsg(@Nonnull String encryptedMessage)
{
if(secretKey != null) {
return CipherWrapper.getInstance().decryptMsg(encryptedMessage, secretKey);
}
return null;
}
}
import android.content.Context;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.support.annotation.RequiresApi;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.Calendar;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.security.auth.x500.X500Principal;
public class KeyStoreWrapper {
@Nullable private KeyStore keyStore;
private static final String KEYSTORE_NAME = "AndroidKeyStore";
private static KeyStoreWrapper instance;
public static KeyStoreWrapper getInstance() {
if(instance == null) {
instance = new KeyStoreWrapper();
}
return instance;
}
private KeyStoreWrapper() {
keyStore = getKeyStoreInstance();
}
@Nullable
private KeyStore getKeyStoreInstance() {
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance(KEYSTORE_NAME);
try {
if(keyStore != null) {
keyStore.load(null);
}
} catch (IOException | NoSuchAlgorithmException | CertificateException e) {
e.printStackTrace();
}
} catch (KeyStoreException e) {
e.printStackTrace();
}
return keyStore;
}
@Nullable
KeyPair loadAndroidAssymetricKeys(@Nonnull Context context, @Nonnull String alias) {
KeyPairGenerator generator = null;
try {
generator = KeyPairGenerator.getInstance("RSA", KEYSTORE_NAME);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
initGeneratorWithKeyGenParameterSpec(generator, alias);
} else {
initGeneratorWithKeyPairGeneratorSpec(context, generator, alias);
}
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
e.printStackTrace();
}
if(generator != null) {
return generator.generateKeyPair();
}
return null;
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void initGeneratorWithKeyGenParameterSpec(@Nonnull KeyPairGenerator generator, @Nonnull String alias) {
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT|KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
try {
generator.initialize(builder.build());
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
private void initGeneratorWithKeyPairGeneratorSpec(@Nonnull Context context, @Nonnull KeyPairGenerator generator, @Nonnull String alias) {
Calendar startDate = Calendar.getInstance();
Calendar endDate = Calendar.getInstance();
endDate.add(Calendar.YEAR, 25);
KeyPairGeneratorSpec.Builder builder = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setSerialNumber(BigInteger.ONE)
.setSubject(new X500Principal("CN=${alias} CA Certificate"))
.setStartDate(startDate.getTime())
.setEndDate(endDate.getTime());
try {
generator.initialize(builder.build());
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
@Nullable
KeyPair getAndroidKeyStoreAsymmetricKeyPair(@Nonnull String alias) {
if(keyStore != null) {
try {
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, null);
PublicKey publicKey = null;
Certificate certificate = keyStore.getCertificate(alias);
if(certificate != null) {
publicKey = certificate.getPublicKey();
}
if(privateKey != null && publicKey != null) {
return new KeyPair(publicKey, privateKey);
}
} catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
return null;
}
void removeAndroidKeyStoreKey(@Nonnull String alias) {
if(keyStore != null) {
try {
keyStore.deleteEntry(alias);
} catch (KeyStoreException e) {
e.printStackTrace();
}
}
}
}
import android.util.Base64;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class CipherWrapper {
private static final String TRANSFORMATION_ASYMMETRIC = "RSA/ECB/PKCS1Padding";
private static final String AES = "AES/CBC/PKCS5Padding";
private static CipherWrapper instance;
public static CipherWrapper getInstance() {
if(instance == null) {
instance = new CipherWrapper();
}
return instance;
}
private Cipher asymCipher;
private Cipher symCipher;
{
try {
asymCipher = Cipher.getInstance(TRANSFORMATION_ASYMMETRIC);
symCipher = Cipher.getInstance(AES);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
}
@Nullable
String encryptWithPublicKey(@Nonnull byte[] data, @Nonnull Key publicKey) {
try {
asymCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = asymCipher.doFinal(data);
return Base64.encodeToString(bytes, Base64.DEFAULT);
} catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
@Nullable
byte[] decryptWithPrivate(@Nonnull String data, @Nonnull Key privateKey) {
try {
asymCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encryptedData = Base64.decode(data, Base64.DEFAULT);
byte[] decodedData = asymCipher.doFinal(encryptedData);
return decodedData;
} catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
@Nullable
String encryptMsg(@Nonnull String message, @Nonnull SecretKey secret)
{
/* Encrypt the message. */
try {
byte[] IV = new byte[16];
symCipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(IV));
byte[] bytes = symCipher.doFinal(message.getBytes());
return Base64.encodeToString(bytes, Base64.DEFAULT);
} catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return null;
}
@Nullable
String decryptMsg(@Nonnull String cipherText, @Nonnull SecretKey secret)
{
/* Decrypt the message, given derived encContentValues and initialization vector. */
try {
byte[] IV = new byte[16];
symCipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(IV));
byte[] decode = Base64.decode(cipherText, Base64.NO_WRAP);
byte[] data = symCipher.doFinal(decode);
return new String(data, "UTF-8");
} catch (InvalidKeyException | BadPaddingException | UnsupportedEncodingException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment