· 7 years ago · Dec 29, 2018, 08:04 PM
1public final class EncryptionHelper {
2 private static final int TAG_LENGTH_BIT = 128;
3 private static final String AES_MODE = "AES/GCM/NoPadding";
4 private static final String KEY_STORE_TYPE = "AndroidKeyStore";
5 private static final String SIGNATURE_ALGORITHM = "SHA512withRSA";
6 private static final String PROVIDER = "AndroidOpenSSL";
7 private static final String TAG = EncryptionHelper.class.getSimpleName();
8 private final Context context;
9 private final KeyStore keyStore;
10 private static final int INITIALIZATION_VECTOR_LENGTH = 12;
11 private static final int CERTIFICATE_LIFE_TIME_YEARS = 1;
12 private static final String RSA_MODE = "RSA/ECB/PKCS1Padding";
13
14 public EncryptionHelper(Context context) {
15 this.context = context;
16 keyStore = createKeyStore();
17 }
18
19
20 private KeyStore createKeyStore() {
21 KeyStore keyStore = null;
22 try {
23 keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
24 keyStore.load(null);
25 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
26 e.printStackTrace();
27 }
28 return keyStore;
29 }
30
31 private java.security.Key getSecretKey(String alias) throws Exception {
32 return keyStore.getKey(alias, null);
33 }
34
35 @TargetApi(Build.VERSION_CODES.M)
36 private void generateKeyPairAES(String alias) {
37 try {
38 if (!keyStore.containsAlias(alias)) {
39 KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE_TYPE);
40 keyGenerator.init(
41 new KeyGenParameterSpec.Builder(alias,
42 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
43 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
44 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
45 .setRandomizedEncryptionRequired(false)
46 .build());
47 keyGenerator.generateKey();
48 }
49 } catch (KeyStoreException | InvalidAlgorithmParameterException
50 | NoSuchAlgorithmException | NoSuchProviderException e) {
51 e.printStackTrace();
52 }
53 }
54
55 public String createSignature(String alias, String value) {
56 if (value == null || value.isEmpty() || alias == null || alias.isEmpty()) {
57 return null;
58 }
59
60 try {
61
62 generateKeyPairRSA(alias);
63 KeyStore.Entry entry = keyStore.getEntry(alias, null);
64 if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
65 Log.w(TAG, "Not an instance of a PrivateKeyEntry");
66 return null;
67 }
68 Signature s = Signature.getInstance(SIGNATURE_ALGORITHM);
69 s.initSign(((KeyStore.PrivateKeyEntry) entry).getPrivateKey());
70 s.update(value.getBytes());
71 byte[] signature = s.sign();
72 return Base64.encodeToString(signature, Base64.DEFAULT);
73 } catch (NoSuchAlgorithmException
74 | InvalidKeyException | KeyStoreException | SignatureException
75 | UnrecoverableEntryException e) {
76 e.printStackTrace();
77 return null;
78 }
79 }
80
81 public boolean matchBySignature(String alias, String input, String signatureStr) {
82 if (input == null || input.isEmpty()
83 || alias == null || alias.isEmpty()
84 || signatureStr == null || signatureStr.isEmpty()
85 ) {
86 return false;
87 }
88
89 try {
90
91 byte[] data = input.getBytes();
92 byte[] signature;
93 try {
94 // The signature is going to be examined as a byte array,
95 // not as a base64 encoded string.
96 signature = Base64.decode(signatureStr, Base64.DEFAULT);
97 } catch (IllegalArgumentException e) {
98 // signatureStr wasn't null, but might not have been encoded
99 // properly.
100 // It's not a valid Base64 string.
101 return false;
102 }
103
104 KeyStore.Entry entry = keyStore.getEntry(alias, null);
105 if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
106 Log.w(TAG, "Not an instance of a PrivateKeyEntry");
107 return false;
108 }
109 Signature s = Signature.getInstance(SIGNATURE_ALGORITHM);
110 s.initVerify(((KeyStore.PrivateKeyEntry) entry).getCertificate());
111 s.update(data);
112 return s.verify(signature);
113 } catch (NoSuchAlgorithmException
114 | InvalidKeyException | KeyStoreException | SignatureException
115 | UnrecoverableEntryException e) {
116 e.printStackTrace();
117 return false;
118 }
119 }
120
121
122 @RequiresApi(api = Build.VERSION_CODES.M)
123 private void generateKeyPairRSA(String alias) {
124 try {
125 if (!keyStore.containsAlias(alias)) {
126 // Generate a key pair for encryption
127 Calendar start = Calendar.getInstance();
128 Calendar end = Calendar.getInstance();
129 end.add(Calendar.YEAR, CERTIFICATE_LIFE_TIME_YEARS);
130 KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
131 .setAlias(alias)
132 .setSubject(new X500Principal("CN=" + alias))
133 .setSerialNumber(BigInteger.TEN)
134 .setStartDate(start.getTime())
135 .setEndDate(end.getTime())
136 .build();
137 KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEY_STORE_TYPE);
138 kpg.initialize(spec);
139 kpg.generateKeyPair();
140 }
141 } catch (KeyStoreException | InvalidAlgorithmParameterException
142 | NoSuchAlgorithmException | NoSuchProviderException e) {
143 e.printStackTrace();
144 }
145 }
146
147 private String encryptStringAES(String alias, String text) {
148 if (alias == null || alias.isEmpty()
149 || text == null || text.isEmpty()) {
150 return null;
151 }
152
153 if (alias.length() < INITIALIZATION_VECTOR_LENGTH) {
154 throw new IllegalArgumentException("alias must be longer then " + INITIALIZATION_VECTOR_LENGTH + " characters");
155 }
156
157 byte[] input = text.getBytes();
158 Cipher c;
159 try {
160 generateKeyPairAES(alias);
161 Key secretKey = getSecretKey(alias);
162
163 c = Cipher.getInstance(AES_MODE);
164 c.init(Cipher.ENCRYPT_MODE,
165 secretKey,
166 new GCMParameterSpec(TAG_LENGTH_BIT, alias.substring(0, INITIALIZATION_VECTOR_LENGTH).getBytes()));
167 byte[] encodedBytes = c.doFinal(input);
168 return Base64.encodeToString(encodedBytes, Base64.DEFAULT);
169 } catch (Exception e) {
170 e.printStackTrace();
171 return null;
172 }
173 }
174
175 private String decryptStringAES(String alias, String text) {
176 if (alias == null || alias.isEmpty()
177 || text == null || text.isEmpty()) {
178 return null;
179 }
180
181 if (alias.length() < INITIALIZATION_VECTOR_LENGTH) {
182 throw new IllegalArgumentException("alias must be longer then " + INITIALIZATION_VECTOR_LENGTH + " characters");
183 }
184 Cipher c;
185 byte[] encoded = Base64.decode(text.getBytes(), Base64.DEFAULT);
186 try {
187 Key secretKey = getSecretKey(alias);
188 c = Cipher.getInstance(AES_MODE);
189 c.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LENGTH_BIT, alias.substring(0, INITIALIZATION_VECTOR_LENGTH).getBytes()));
190 byte[] decodedBytes = c.doFinal(encoded);
191 return new String(decodedBytes, "UTF-8");
192 } catch (Exception e) {
193 e.printStackTrace();
194 return null;
195 }
196 }
197
198 String encryptString(String alias, String text) {
199 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
200 return encryptStringAES(alias, text);
201 } else {
202 return rsaEncrypt(alias, text);
203 }
204 }
205
206 String getSignatureString(String alias, String text) {
207 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
208 return encryptStringAES(alias, text);
209 } else {
210 return rsaEncrypt(alias, text);
211 }
212 }
213
214 String decryptString(String alias, String text) {
215 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
216 return decryptStringAES(alias, text);
217 } else {
218 return rsaDecrypt(alias, text);
219 }
220 }
221
222 private byte[] rsaEncrypt(byte[] secret, String alias) throws Exception {
223 KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
224 // Encrypt the text
225 Cipher inputCipher = Cipher.getInstance(RSA_MODE, PROVIDER);
226 inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());
227
228 byte[] vals = inputCipher.doFinal(secret);
229 return vals;
230 }
231
232 private byte[] rsaDecrypt(byte[] encrypted, String alias) throws Exception {
233 KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
234 Cipher output = Cipher.getInstance(RSA_MODE, PROVIDER);
235 output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
236 CipherInputStream cipherInputStream = new CipherInputStream(
237 new ByteArrayInputStream(encrypted), output);
238 ArrayList<Byte> values = new ArrayList<>();
239 int nextByte;
240 while ((nextByte = cipherInputStream.read()) != -1) {
241 values.add((byte) nextByte);
242 }
243
244 byte[] bytes = new byte[values.size()];
245 for (int i = 0; i < bytes.length; i++) {
246 bytes[i] = values.get(i).byteValue();
247 }
248 return bytes;
249 }
250
251
252 @RequiresApi(api = Build.VERSION_CODES.M)
253 private String rsaEncrypt(String alias, String text) {
254 try {
255 generateKeyPairRSA(alias);
256 KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
257 RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();
258
259 Cipher inCipher = Cipher.getInstance(RSA_MODE, PROVIDER);
260 inCipher.init(Cipher.ENCRYPT_MODE, publicKey);
261
262 byte[] vals = inCipher.doFinal(text.getBytes());
263 return Base64.encodeToString(vals, Base64.DEFAULT);
264 } catch (Exception e) {
265 e.printStackTrace();
266 return null;
267 }
268 }
269
270 private String rsaDecrypt(String alias, String text) {
271 try {
272 KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
273 PrivateKey privateKey = privateKeyEntry.getPrivateKey();
274
275 Cipher output = Cipher.getInstance(RSA_MODE);
276 output.init(Cipher.DECRYPT_MODE, privateKey);
277
278 CipherInputStream cipherInputStream = new CipherInputStream(
279 new ByteArrayInputStream(Base64.decode(text, Base64.DEFAULT)), output);
280 ArrayList<Byte> values = new ArrayList<>();
281 int nextByte;
282 while ((nextByte = cipherInputStream.read()) != -1) {
283 values.add((byte) nextByte);
284 }
285
286 byte[] bytes = new byte[values.size()];
287 for (int i = 0; i < bytes.length; i++) {
288 bytes[i] = values.get(i);
289 }
290 return new String(bytes, 0, bytes.length, "UTF-8");
291
292 } catch (Exception e) {
293 e.printStackTrace();
294 return null;
295 }
296 }
297}