· 7 years ago · May 29, 2018, 02:38 AM
1import android.content.Context;
2import android.content.SharedPreferences;
3import android.util.Base64;
4import android.util.Log;
5
6import java.security.KeyPair;
7import java.security.SecureRandom;
8
9import javax.annotation.Nonnull;
10import javax.annotation.Nullable;
11import javax.crypto.SecretKey;
12import javax.crypto.spec.SecretKeySpec;
13
14public class SecureEncryptor {
15
16 private static final String SECURE_PREF = "secure_pref";
17
18 private static final String ENCRYPTED_MASTER_PREF_KEY = "encrypted_master_pref_key";
19
20 private static SecureEncryptor instance;
21
22 public static SecureEncryptor getInstance(Context context) {
23
24 if(instance == null) {
25 instance = new SecureEncryptor(context);
26 }
27
28 return instance;
29 }
30
31 private SecretKey secretKey = null;
32
33 /**
34 * Base64 encoding settings used for generated code verifiers.
35 */
36 private static final int PKCE_BASE64_ENCODE_SETTINGS =
37 Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE;
38
39 private SecureEncryptor(Context context) {
40 createOrLoadMasterKey(context);
41 }
42
43 private void createOrLoadMasterKey(Context context) {
44
45 KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.getInstance();
46
47 KeyPair keyPair = keyStoreWrapper.getAndroidKeyStoreAsymmetricKeyPair("versa");
48
49 if(keyPair == null) {
50 Log.i("XDFCE", "keypair is null. Create a new one");
51 keyPair = keyStoreWrapper.loadAndroidAssymetricKeys(context, "versa");
52 }
53
54 if(keyPair == null) {
55 Log.i("XDFCE", "Create new keypair failed. Aborting!!!");
56 return;
57 }
58
59 String encryptedMasterKey = getSavedEncryptedMasterKey(context);
60
61 byte[] decryptedMasterKey;
62 if(encryptedMasterKey == null) {
63 byte[] newRandomMasterKey = generateSecureRandom();
64
65
66 encryptedMasterKey = CipherWrapper.getInstance().encryptWithPublicKey(newRandomMasterKey, keyPair.getPublic());
67
68 decryptedMasterKey = newRandomMasterKey;
69
70 if(encryptedMasterKey != null) {
71 saveEncryptedMasterKey(context, encryptedMasterKey);
72 Log.i("XDFCE", encryptedMasterKey);
73 } else {
74 Log.i("XDFCE", "Unable to encryptWithPublicKey");
75 }
76 } else {
77 Log.i("XDFCE", "Decrytping...");
78
79 decryptedMasterKey = CipherWrapper.getInstance().decryptWithPrivate(encryptedMasterKey, keyPair.getPrivate());
80 }
81
82 if(decryptedMasterKey != null) {
83 Log.i("XDFCE", Base64.encodeToString(decryptedMasterKey, Base64.DEFAULT));
84 Log.i("XDFCE", new String(decryptedMasterKey));
85 secretKey = new SecretKeySpec(decryptedMasterKey, "AES");
86 }
87 }
88
89 private byte[] generateSecureRandom() {
90
91 SecureRandom secureRandom = new SecureRandom();
92
93 byte[] randomBytes = new byte[16];
94 secureRandom.nextBytes(randomBytes);
95 return randomBytes;
96 }
97
98
99 @Nullable
100 private String getSavedEncryptedMasterKey(@Nonnull Context context) {
101 SharedPreferences sp = context.getSharedPreferences(SECURE_PREF, Context.MODE_PRIVATE);
102 return sp.getString(ENCRYPTED_MASTER_PREF_KEY, null);
103 }
104
105 private void saveEncryptedMasterKey(@Nonnull Context context, @Nonnull String encryptedMasterKey) {
106 SharedPreferences sp = context.getSharedPreferences(SECURE_PREF, Context.MODE_PRIVATE);
107 SharedPreferences.Editor edit = sp.edit();
108 edit.putString(ENCRYPTED_MASTER_PREF_KEY, encryptedMasterKey);
109 edit.apply();
110 }
111
112 @Nullable
113 public String encryptMsg(@Nonnull String messageToBeEncrypted)
114 {
115 if(secretKey != null) {
116 return CipherWrapper.getInstance().encryptMsg(messageToBeEncrypted, secretKey);
117 }
118
119 return null;
120 }
121
122 @Nullable
123 public String decryptMsg(@Nonnull String encryptedMessage)
124 {
125 if(secretKey != null) {
126 return CipherWrapper.getInstance().decryptMsg(encryptedMessage, secretKey);
127 }
128
129 return null;
130 }
131}
132
133
134
135import android.content.Context;
136import android.os.Build;
137import android.security.KeyPairGeneratorSpec;
138import android.security.keystore.KeyGenParameterSpec;
139import android.security.keystore.KeyProperties;
140import android.support.annotation.RequiresApi;
141
142import java.io.IOException;
143import java.math.BigInteger;
144import java.security.InvalidAlgorithmParameterException;
145import java.security.KeyPair;
146import java.security.KeyPairGenerator;
147import java.security.KeyStore;
148import java.security.KeyStoreException;
149import java.security.NoSuchAlgorithmException;
150import java.security.NoSuchProviderException;
151import java.security.PrivateKey;
152import java.security.PublicKey;
153import java.security.UnrecoverableKeyException;
154import java.security.cert.Certificate;
155import java.security.cert.CertificateException;
156import java.util.Calendar;
157
158import javax.annotation.Nonnull;
159import javax.annotation.Nullable;
160import javax.security.auth.x500.X500Principal;
161
162public class KeyStoreWrapper {
163
164 @Nullable private KeyStore keyStore;
165
166 private static final String KEYSTORE_NAME = "AndroidKeyStore";
167
168 private static KeyStoreWrapper instance;
169
170 public static KeyStoreWrapper getInstance() {
171 if(instance == null) {
172 instance = new KeyStoreWrapper();
173 }
174 return instance;
175 }
176
177 private KeyStoreWrapper() {
178 keyStore = getKeyStoreInstance();
179 }
180
181 @Nullable
182 private KeyStore getKeyStoreInstance() {
183 KeyStore keyStore = null;
184 try {
185 keyStore = KeyStore.getInstance(KEYSTORE_NAME);
186 try {
187 if(keyStore != null) {
188 keyStore.load(null);
189 }
190 } catch (IOException | NoSuchAlgorithmException | CertificateException e) {
191 e.printStackTrace();
192 }
193 } catch (KeyStoreException e) {
194 e.printStackTrace();
195 }
196 return keyStore;
197 }
198
199 @Nullable
200 KeyPair loadAndroidAssymetricKeys(@Nonnull Context context, @Nonnull String alias) {
201 KeyPairGenerator generator = null;
202 try {
203 generator = KeyPairGenerator.getInstance("RSA", KEYSTORE_NAME);
204
205 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
206 initGeneratorWithKeyGenParameterSpec(generator, alias);
207 } else {
208 initGeneratorWithKeyPairGeneratorSpec(context, generator, alias);
209 }
210 } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
211 e.printStackTrace();
212 }
213
214 if(generator != null) {
215 return generator.generateKeyPair();
216 }
217
218 return null;
219 }
220
221 @RequiresApi(api = Build.VERSION_CODES.M)
222 private void initGeneratorWithKeyGenParameterSpec(@Nonnull KeyPairGenerator generator, @Nonnull String alias) {
223 KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT|KeyProperties.PURPOSE_DECRYPT)
224 .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
225 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
226
227 try {
228 generator.initialize(builder.build());
229 } catch (InvalidAlgorithmParameterException e) {
230 e.printStackTrace();
231 }
232 }
233
234 private void initGeneratorWithKeyPairGeneratorSpec(@Nonnull Context context, @Nonnull KeyPairGenerator generator, @Nonnull String alias) {
235 Calendar startDate = Calendar.getInstance();
236 Calendar endDate = Calendar.getInstance();
237 endDate.add(Calendar.YEAR, 25);
238
239 KeyPairGeneratorSpec.Builder builder = new KeyPairGeneratorSpec.Builder(context)
240 .setAlias(alias)
241 .setSerialNumber(BigInteger.ONE)
242 .setSubject(new X500Principal("CN=${alias} CA Certificate"))
243 .setStartDate(startDate.getTime())
244 .setEndDate(endDate.getTime());
245
246 try {
247 generator.initialize(builder.build());
248 } catch (InvalidAlgorithmParameterException e) {
249 e.printStackTrace();
250 }
251 }
252
253 @Nullable
254 KeyPair getAndroidKeyStoreAsymmetricKeyPair(@Nonnull String alias) {
255 if(keyStore != null) {
256 try {
257 PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, null);
258 PublicKey publicKey = null;
259 Certificate certificate = keyStore.getCertificate(alias);
260 if(certificate != null) {
261 publicKey = certificate.getPublicKey();
262 }
263
264 if(privateKey != null && publicKey != null) {
265 return new KeyPair(publicKey, privateKey);
266 }
267
268 } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException e) {
269 e.printStackTrace();
270 }
271 }
272
273 return null;
274 }
275
276 void removeAndroidKeyStoreKey(@Nonnull String alias) {
277 if(keyStore != null) {
278 try {
279 keyStore.deleteEntry(alias);
280 } catch (KeyStoreException e) {
281 e.printStackTrace();
282 }
283 }
284 }
285
286
287}
288
289
290import android.util.Base64;
291
292import java.io.UnsupportedEncodingException;
293import java.security.InvalidAlgorithmParameterException;
294import java.security.InvalidKeyException;
295import java.security.Key;
296import java.security.NoSuchAlgorithmException;
297
298import javax.annotation.Nonnull;
299import javax.annotation.Nullable;
300import javax.crypto.BadPaddingException;
301import javax.crypto.Cipher;
302import javax.crypto.IllegalBlockSizeException;
303import javax.crypto.NoSuchPaddingException;
304import javax.crypto.SecretKey;
305import javax.crypto.spec.IvParameterSpec;
306
307public class CipherWrapper {
308
309 private static final String TRANSFORMATION_ASYMMETRIC = "RSA/ECB/PKCS1Padding";
310 private static final String AES = "AES/CBC/PKCS5Padding";
311
312 private static CipherWrapper instance;
313
314 public static CipherWrapper getInstance() {
315 if(instance == null) {
316 instance = new CipherWrapper();
317 }
318 return instance;
319 }
320
321 private Cipher asymCipher;
322 private Cipher symCipher;
323
324 {
325 try {
326 asymCipher = Cipher.getInstance(TRANSFORMATION_ASYMMETRIC);
327 symCipher = Cipher.getInstance(AES);
328 } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
329 e.printStackTrace();
330 }
331 }
332
333 @Nullable
334 String encryptWithPublicKey(@Nonnull byte[] data, @Nonnull Key publicKey) {
335 try {
336 asymCipher.init(Cipher.ENCRYPT_MODE, publicKey);
337 byte[] bytes = asymCipher.doFinal(data);
338 return Base64.encodeToString(bytes, Base64.DEFAULT);
339 } catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
340 e.printStackTrace();
341 }
342
343 return null;
344 }
345
346 @Nullable
347 byte[] decryptWithPrivate(@Nonnull String data, @Nonnull Key privateKey) {
348 try {
349 asymCipher.init(Cipher.DECRYPT_MODE, privateKey);
350 byte[] encryptedData = Base64.decode(data, Base64.DEFAULT);
351 byte[] decodedData = asymCipher.doFinal(encryptedData);
352 return decodedData;
353 } catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
354 e.printStackTrace();
355 }
356
357 return null;
358 }
359
360
361 @Nullable
362 String encryptMsg(@Nonnull String message, @Nonnull SecretKey secret)
363 {
364 /* Encrypt the message. */
365 try {
366 byte[] IV = new byte[16];
367 symCipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(IV));
368 byte[] bytes = symCipher.doFinal(message.getBytes());
369 return Base64.encodeToString(bytes, Base64.DEFAULT);
370 } catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
371 e.printStackTrace();
372 }
373
374 return null;
375 }
376
377 @Nullable
378 String decryptMsg(@Nonnull String cipherText, @Nonnull SecretKey secret)
379 {
380 /* Decrypt the message, given derived encContentValues and initialization vector. */
381 try {
382 byte[] IV = new byte[16];
383 symCipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(IV));
384 byte[] decode = Base64.decode(cipherText, Base64.NO_WRAP);
385 byte[] data = symCipher.doFinal(decode);
386 return new String(data, "UTF-8");
387 } catch (InvalidKeyException | BadPaddingException | UnsupportedEncodingException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
388 e.printStackTrace();
389 }
390
391 return null;
392 }
393}