· 6 years ago · Feb 25, 2020, 02:16 PM
1import android.content.Context;
2import android.os.Build;
3import android.security.KeyPairGeneratorSpec;
4import android.security.keystore.KeyGenParameterSpec;
5import android.security.keystore.KeyProperties;
6import android.util.Base64;
7import java.io.ByteArrayInputStream;
8import java.io.ByteArrayOutputStream;
9import java.io.IOException;
10import java.math.BigInteger;
11import java.security.InvalidAlgorithmParameterException;
12import java.security.InvalidKeyException;
13import java.security.KeyPairGenerator;
14import java.security.KeyStore;
15import java.security.KeyStoreException;
16import java.security.NoSuchAlgorithmException;
17import java.security.NoSuchProviderException;
18import java.security.ProviderException;
19import java.security.UnrecoverableEntryException;
20import java.security.cert.CertificateException;
21import java.security.interfaces.RSAPublicKey;
22import java.security.spec.AlgorithmParameterSpec;
23import java.util.ArrayList;
24import java.util.Calendar;
25import java.util.GregorianCalendar;
26import javax.crypto.Cipher;
27import javax.crypto.CipherInputStream;
28import javax.crypto.CipherOutputStream;
29import javax.crypto.NoSuchPaddingException;
30import javax.security.auth.x500.X500Principal;
31
32public class AndroidKeystoreHelper {
33
34 private static final String CIPHER_TYPE = "RSA/ECB/PKCS1Padding";
35 private static final String CIPHER_PROVIDER_OPENSSL = "AndroidOpenSSL";
36 private static final String CIPHER_PROVIDER_ANDROID = "AndroidKeyStoreBCWorkaround";
37 private static final String ALGORITHM = "RSA";
38 private static final String PRINCIPLE_PREFIX = "CN=";
39 private static final String KEYSTORE = "AndroidKeyStore";
40 private static final String CHARSET = "UTF-8";
41 public static final String ALIAS = "dQYuRFcVP8be6pgrTsFzGCSr";
42
43
44 private Context context;
45 private KeyStore keyStore;
46
47 public AndroidKeystoreHelper(Context context) throws KeyStoreException, NoSuchAlgorithmException, IOException, CertificateException {
48 this.context = context;
49 initKeystore();
50 }
51
52 private void initKeystore() throws KeyStoreException, NoSuchAlgorithmException, IOException, CertificateException {
53 keyStore = KeyStore.getInstance(KEYSTORE);
54 keyStore.load(null);
55 }
56
57
58 public String encryptString(String alias, String plainText) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, IOException, UnrecoverableEntryException, KeyStoreException, InvalidAlgorithmParameterException {
59 String encryptedText;
60 createKeys(context, alias);
61 KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
62 RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();
63
64 Cipher inCipher = getCipher();
65 inCipher.init(Cipher.ENCRYPT_MODE, publicKey);
66
67 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
68 CipherOutputStream cipherOutputStream = new CipherOutputStream(
69 outputStream, inCipher);
70 cipherOutputStream.write(plainText.getBytes(CHARSET));
71 cipherOutputStream.close();
72
73 byte[] vals = outputStream.toByteArray();
74 encryptedText = (Base64.encodeToString(vals, Base64.DEFAULT));
75
76 return encryptedText;
77 }
78
79
80 public String decryptString(String alias, String encryptedText) throws ProviderException,UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IOException {
81 String decryptedText;
82 KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
83
84 Cipher output = getCipher();
85 output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
86
87 CipherInputStream cipherInputStream = new CipherInputStream(
88 new ByteArrayInputStream(Base64.decode(encryptedText, Base64.DEFAULT)), output);
89 ArrayList<Byte> values = new ArrayList<>();
90 int nextByte;
91 while ((nextByte = cipherInputStream.read()) != -1) {
92 values.add((byte) nextByte);
93 }
94
95 byte[] bytes = new byte[values.size()];
96 for (int i = 0; i < bytes.length; i++) {
97 bytes[i] = values.get(i);
98 }
99
100 decryptedText = (new String(bytes, 0, bytes.length, CHARSET));
101
102 return decryptedText;
103 }
104
105 /**
106 * Creates RSA Public Private Key pair against given alias.<p></p>
107 * Key will be accessible using this alias only.
108 *
109 * @param context app context
110 * @param alias alias against which this RSA pair will be generated.
111 * @throws NoSuchProviderException
112 * @throws NoSuchAlgorithmException
113 * @throws InvalidAlgorithmParameterException
114 * @throws KeyStoreException
115 */
116 private void createKeys(Context context, String alias) throws ProviderException,NoSuchProviderException,
117 NoSuchAlgorithmException, InvalidAlgorithmParameterException, KeyStoreException {
118
119 int serial = 1337;
120
121 if (!keyStore.containsAlias(alias)) {
122 Calendar start = new GregorianCalendar();
123 Calendar end = new GregorianCalendar();
124 end.add(Calendar.YEAR, 100);
125 KeyPairGenerator kpGenerator = KeyPairGenerator
126 .getInstance(ALGORITHM,
127 KEYSTORE);
128 AlgorithmParameterSpec spec;
129
130 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
131 // Below Android M, use the KeyPairGeneratorSpec.Builder.
132
133 spec = new KeyPairGeneratorSpec.Builder(context)
134 // You'll use the alias later to retrieve the key. It's a key for the key!
135 .setAlias(alias)
136 // The subject used for the self-signed certificate of the generated pair
137 .setSubject(new X500Principal(PRINCIPLE_PREFIX + alias))
138 // The serial number used for the self-signed certificate of the
139 // generated pair.
140 .setSerialNumber(BigInteger.valueOf(serial))
141 // Date range of validity for the generated pair.
142 .setStartDate(start.getTime())
143 .setEndDate(end.getTime())
144 .build();
145
146
147 } else {
148 // On Android M or above, use the KeyGenparameterSpec.Builder and specify permitted
149 // properties and restrictions of the key.
150 spec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_DECRYPT)
151 .setCertificateSubject(new X500Principal(PRINCIPLE_PREFIX + alias))
152 .setDigests(KeyProperties.DIGEST_SHA256)
153 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
154 .setCertificateSerialNumber(BigInteger.valueOf(serial))
155 .setCertificateNotBefore(start.getTime())
156 .setCertificateNotAfter(end.getTime())
157 .build();
158 }
159
160 kpGenerator.initialize(spec);
161
162 kpGenerator.generateKeyPair();
163 }
164 }
165
166 // get Cipher object depending on API level
167 private Cipher getCipher() {
168 try {
169 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // below android m
170 // error in android 6: InvalidKeyException: Need RSA private or public key
171 return Cipher.getInstance(CIPHER_TYPE, CIPHER_PROVIDER_OPENSSL);
172 } else {
173 // error in android 5: NoSuchProviderException: Provider not available: AndroidKeyStoreBCWorkaround
174 return Cipher.getInstance(CIPHER_TYPE, CIPHER_PROVIDER_ANDROID);
175 }
176 } catch (Exception exception) {
177 throw new RuntimeException("Failed to get an instance of Cipher", exception);
178 }
179 }
180
181}