Created
May 24, 2018 09:23
-
-
Save praslnx8/0676c3402437950552f697e82a2aaa4e to your computer and use it in GitHub Desktop.
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
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