Skip to content

Instantly share code, notes, and snippets.

@Heilum
Last active October 13, 2017 02:20
Show Gist options
  • Save Heilum/675de27e6ae59fd0bf20c49b87e72ed3 to your computer and use it in GitHub Desktop.
Save Heilum/675de27e6ae59fd0bf20c49b87e72ed3 to your computer and use it in GitHub Desktop.
Use AES/RSA encryption algorithm to make a KeyChain on Android
package com.swordfishsoft.security;
import android.content.Context;
import android.content.SharedPreferences;
import android.security.KeyPairGeneratorSpec;
import android.security.keystore.KeyProperties;
import android.util.Base64;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Calendar;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;
/**
* Created by chenwanfei on 10/10/2017.
*
*
*
* How to use:
  SecurityDataStore sds = new SecurityDataStore(aActivity);
sds.store("test","abc中国");
String value = sds.retrieveString("test");
Log.e("abc","value="+value);
sds.removeKey("test");
inspired by => https://medium.com/@ericfu/securely-storing-secrets-in-an-android-application-501f030ae5a3
*
*/
public class SecurityDataStore {
private static final String AndroidKeyStore = "AndroidKeyStore";
private static final String KEY_ALIAS = "RSA";
private KeyStore mKeyStore;
private static final String RSA_MODE = "RSA/ECB/PKCS1Padding";
private Context mContext;
private static final String AES_MODE = "AES/ECB/PKCS7Padding";
private static final String RSA_PROVIDER = "AndroidKeyStoreBCWorkaround";
public SecurityDataStore(Context context) {
try {
mKeyStore = KeyStore.getInstance(AndroidKeyStore);
mKeyStore.load(null);
// Generate the RSA key pairs
if (!mKeyStore.containsAlias(KEY_ALIAS)) {
// Generate a key pair for encryption
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
//start.add(Calendar.YEAR, -30);
end.add(Calendar.YEAR, 30);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(KEY_ALIAS)
.setSubject(new X500Principal("CN=" + KEY_ALIAS))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, AndroidKeyStore);
kpg.initialize(spec);
kpg.generateKeyPair();
mContext = context;
}
} catch (Exception e) {
Log.e("123","init SecurityDataStore fail",e);
mKeyStore = null;
}
}
private byte[] rsaEncrypt(byte[] secret){
if (mKeyStore != null){
try{
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) mKeyStore.getEntry(KEY_ALIAS, null);
// Encrypt the text
Cipher inputCipher = Cipher.getInstance(RSA_MODE, RSA_PROVIDER);
inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inputCipher);
cipherOutputStream.write(secret);
cipherOutputStream.close();
byte[] vals = outputStream.toByteArray();
return vals;
}catch (Exception e){
Log.e("123","init SecurityDataStore fail",e);
return null;
}
}else{
return null;
}
}
private byte[] rsaDecrypt(byte[] encrypted) {
if (mKeyStore != null){
try{
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)mKeyStore.getEntry(KEY_ALIAS, null);
Cipher output = Cipher.getInstance(RSA_MODE, RSA_PROVIDER);
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(encrypted), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}
byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
return bytes;
}catch (Exception e){
Log.e("123","init SecurityDataStore fail",e);
return null;
}
}else{
return null;
}
}
private String dataKeyForKey(String key){
return key +"_data";
}
private String encryptedAESKeyForKey(String key){
return key+"_encrypted_AESKey";
}
public boolean store(String key,String value){
return store(key,value.getBytes(StandardCharsets.UTF_8));
}
public void removeKey(String key){
SharedPreferences pref = mContext.getSharedPreferences(AndroidKeyStore, Context.MODE_PRIVATE);
SharedPreferences.Editor edit = pref.edit();
edit.remove(dataKeyForKey(key));
edit.remove(encryptedAESKeyForKey(key));
edit.commit();
}
public String retrieveString(String key){
byte[] data = retrieve(key);
if (data != null){
return new String(data, StandardCharsets.UTF_8);
}else{
return null;
}
}
public byte[] retrieve(String key){
SharedPreferences pref = mContext.getSharedPreferences(AndroidKeyStore, Context.MODE_PRIVATE);
String enryptedAESKeyBase64 = pref.getString(encryptedAESKeyForKey(key),null);
String dataBase64 = pref.getString(dataKeyForKey(key),null);
if (enryptedAESKeyBase64 != null && dataBase64 != null){
byte[] encryptedAESKey = Base64.decode(enryptedAESKeyBase64,Base64.DEFAULT);
byte[] encryptedData = Base64.decode(dataBase64,Base64.DEFAULT);
byte[] decryptedAESKey = rsaDecrypt(encryptedAESKey);
if (decryptedAESKey != null && encryptedData != null){
try{
Cipher c = Cipher.getInstance(AES_MODE, "BC");
c.init(Cipher.DECRYPT_MODE,new SecretKeySpec(decryptedAESKey, "AES"));
byte[] decodedBytes = c.doFinal(encryptedData);
return decodedBytes;
}catch (Exception e){
Log.e("123","retrieve fail",e);
return null;
}
}else{
return null;
}
}else{
return null;
}
}
public boolean store(String key,byte[] value){
try{
byte[] AESkey = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(AESkey);
Cipher c = Cipher.getInstance(AES_MODE, "BC");
c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(AESkey, "AES"));
byte[] encodedBytes = c.doFinal(value);
String encryptedBase64Encoded = Base64.encodeToString(encodedBytes, Base64.DEFAULT);
SharedPreferences pref = mContext.getSharedPreferences(AndroidKeyStore, Context.MODE_PRIVATE);
SharedPreferences.Editor edit = pref.edit();
edit.putString(dataKeyForKey(key),encryptedBase64Encoded);
byte[] encryptedAESKey = rsaEncrypt(AESkey);
String enryptedKeyB64 = Base64.encodeToString(encryptedAESKey, Base64.DEFAULT);
edit.putString(encryptedAESKeyForKey(key),enryptedKeyB64);
edit.commit();
return true;
}catch (Exception e){
Log.e("123","store fail",e);
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment