Android external file encryption and decryption and application practice

Android external file encryption and decryption and application practice

There is such an application scenario. When we put some important files in the asset folder, we can directly get the file by decompressing the .apk. We don’t want some files involving important information to be decompiled. At this time, we need to encrypt the file first, and then put it in the resource directory in Android, and then decrypt it when used.

In modern cryptography, the security of encryption systems is based on keys rather than algorithms. Now I will introduce a complete set of encryption, decryption and application processes. I think this encryption process is very reliable in terms of practicality and security. It is also a commonly used practice on the market. The core logic is actually quite simple. After all, the most difficult part of the encryption and decryption algorithm implementation is ready-made. Part of our company has also used this process, which is of course more complicated than what I am talking about.

1. Introduction

It mainly involves the application of the following algorithms: RSA, AES, and Base64 encoding. The basic idea is to use [AES algorithm + AES key] to encrypt files. In order to ensure the security of the key, the AES key will be encrypted using [RSA algorithm + RSA private key].

If you are not familiar with these algorithms, you can take a look at the article "Common Encryption Methods and Application Scenarios" by our boss. It is enough to know the general principles and usage methods, because the algorithms are ready-made in Java and can be used directly.

Android encryption and decryption flow chart.png

After arranging the process, it is the above flowchart, which is divided into three parts:

  • The first part is to encapsulate the encryption process into a small tool and use the encryption tool to encrypt the file;
  • The second part is to encapsulate the decryption process into a small decryption tool, and use the decryption tool to decrypt our files so as to make relevant modifications;
  • The purpose of the third block is to put the encrypted files and the AES algorithm keys for encryption and decryption into the Android resource file for specific use.

One thing to add is the public and private keys of the RSA algorithm. From the third block, we can see that the RSA public and private keys are not placed in the resource file. In fact, if you think about it, you will know that if the encrypted file, the AES key for encryption and decryption, and the RSA key used to encrypt the AES key are all placed in the folder, there will be no security (Note: the encryption and decryption algorithm can be modified to be unique to your company, which is what our company does). Therefore, in order to ensure security, our RSA public and private keys are dynamically obtained through the code in the application signature (.keystore signature file). If you are interested, you can read this article: [Extract private keys and certificates from Java Keystore files].

2. Block 1: Encryption tool for encryption

The Java interface development of the tool is implemented through the Java swing package. Those who are interested in swing can refer to this article Introduction to Java Swing Graphical Interface Development, which explains it in great detail.

At the beginning, there is no AES key, so we need to generate a secure key, so generate a random AES key and save it. The operation page interface of the encryption tool is:

encryption

2.1. Generate a random key

Generating a random key is mainly divided into several steps:

  • Generate a random number as a seed using UUID.randomUUID();
  • The seed is provided to the KeyGenerator to generate the AES key. As long as the AES key generated by the seed is consistent;
  • Obtain the public and private keys required by the RSA algorithm through application signature;
  • RSA encrypts the AES key with the private key;

Because the generated key is byte[], it is displayed on the interface through Base64 encoding.

  1. /**  
  2. * Generate a random key  
  3. */  
  4. private void randomKey() {  
  5. try {  
  6. //Generate a random number as a seed  
  7. String uuid = UUID.randomUUID().toString();  
  8. byte[] seed = uuid.getBytes( "UTF-8" );  
  9. //Generate AES key  
  10. byte[] rawkey = AES.getRawKey(seed);  
  11. //Get the key pair for application signing  
  12. KeyPair pair = SignKey.getSignKeyPair();  
  13. //Encrypt the AES key using the RSA private key  
  14. byte[] key = RSA.encrypt(rawkey, pair.getPrivate());  
  15. //Base64 encoding into a string for display  
  16. String base64Key = Base64.encode( key );  
  17. mKeyText.setText(base64Key);  
  18. } catch (Exception e) {  
  19. e.printStackTrace();  
  20. }  
  21. }  
  22. AES.getRawKey(seed) mainly uses the AES key generator to generate a 128-bit key. The specific implementation is:  
  23. /**
  24.   * Generate a key stream encrypted with the AES algorithm. This key will be encrypted again with the key of the application signature {@link SignKey}  
  25. */  
  26. public   static byte[] getRawKey(byte[] seed) throws Exception {  
  27. KeyGenerator kgen = KeyGenerator.getInstance( "AES" );  
  28. SecureRandom sr = SecureRandom.getInstance( "SHA1PRNG" );  
  29. sr.setSeed(seed);  
  30. //192 and 256 bits may not be available  
  31. kgen.init(128, sr);  
  32. SecretKey skey = kgen.generateKey();  
  33. return skey.getEncoded();  
  34. }

SignKey.getSignKeyPair() is used to obtain the public and private keys required for the RSA algorithm. It comes from our application signature. Everyone should be familiar with it. Application packaging and uploading requires signature packaging.

keystore

Java provides an API to obtain the private key and certificate of the testkey.keystore file (generate one yourself using studio), and put the testkey.keystore file in the directory:

  1. /**  
  2. * Author:xishuang  
  3. * Date :2018.05.06  
  4. * Des: Read the key pair and certificate according to the imported application signature  
  5. */  
  6. public class SignKey {  
  7. // Application signature  
  8. private static final String keystoreName = "testkey.keystore" ;  
  9. private static final String keystorePassword = "123456" ;  
  10. //Alias ​​of application signature  
  11. private static final String alias = "key0" ;  
  12. private static final String aliasPassword = "123456" ;  
  13. /**  
  14. * Get the signed key pair to encrypt the key  
  15. */  
  16. public   static KeyPair getSignKeyPair() {  
  17. try {  
  18. File storeFile = new File(keystoreName);  
  19. if (!storeFile.exists()) {  
  20. throw new IllegalArgumentException( "Signature file has not been set yet!" );  
  21. }  
  22. String keyStoreType = "JKS" ;  
  23. char [] keystorepasswd = keystorePassword.toCharArray();  
  24. char [] keyaliaspasswd = aliasPassword.toCharArray();  
  25. KeyStore keystore = KeyStore.getInstance(keyStoreType);  
  26. keystore.load (new FileInputStream(storeFile), keystorepasswd);  
  27. //Get the private key  
  28. Key   key = keystore.getKey(alias, keyaliaspasswd);  
  29. if ( key instanceof PrivateKey) {  
  30. //Get the public key  
  31. Certificate cert = keystore.getCertificate(alias);  
  32. PublicKey publicKey = cert.getPublicKey();  
  33. ///Public and private keys are stored in KeyPair
  34.   return new KeyPair(publicKey, (PrivateKey) key );  
  35. }  
  36. } catch (Exception e) {  
  37. e.printStackTrace();  
  38. }  
  39. return   null ;  
  40. }  
  41. }

The parameters required to get the testkey.keystore are the same as those required for signing the packaged application, and are obtained through the keystore class provided by java. Then the AES key is encrypted with the testkey.keystore private key just obtained, and then converted into a string through Base64 for display. The encoding is converted only for displaying the key.

Key Base64 encoding.png

2.2. Exporting Keys

Export the key to a file and import it directly next time to decrypt the file. To export the key, you need to convert the Base64 key string in the text box into Byte[] using Base64 before saving it.

  1. byte[] key = Base64.decode(base64Key);  
  2. // Output the raw key  
  3. File keyFile = new File(dir, "testkey.dat" );  
  4. FileOutputStream fos = new FileOutputStream(keyFile);

2.3 Encrypted Files

The key is already available, and the AES algorithm is ready-made. Just call the API to encrypt:

  1. private static final String AES = "AES" ;  
  2. /**  
  3. * AES algorithm to encrypt files  
  4. *  
  5. * @param rawKey AES key  
  6. * @param fromFile The file to be encrypted  
  7. * @param toFile encrypted file  
  8. */  
  9. public   static void encryptFile(byte[] rawKey, File fromFile, File toFile) throws Exception {  
  10. if (!fromFile.exists()) {  
  11. throw new NullPointerException( "File does not exist" );  
  12. }  
  13. if (toFile.exists()) {  
  14. toFile.delete ();  
  15. }  
  16. SecretKeySpec skeySpec = new SecretKeySpec(rawKey, AES);  
  17. Cipher cipher = Cipher.getInstance(AES);
  18.   //Encryption mode  
  19. cipher.init(Cipher.ENCRYPT_MODE, skeySpec);  
  20. FileInputStream fis = new FileInputStream(fromFile);  
  21. FileOutputStream fos = new FileOutputStream(toFile, true );  
  22. byte[] buffer = new byte[512 * 1024 - 16];  
  23. int offset;
  24. //Use encryption stream to encrypt  
  25. CipherInputStream bis = new CipherInputStream(fis, cipher);  
  26. while ((offset = bis. read (buffer)) != -1) {
  27.   fos.write(buffer, 0, offset);  
  28. fos.flush();  
  29. }  
  30. fos.close ();  
  31. fis.close ();  
  32. }

Select the file and encrypt it using the AES algorithm and AES key. The final result is as follows. If there is no key that can decrypt it, I lose.

Before and after encryption

3. Block 2: Decryption by decryption tool

There is actually no need to explain the decryption process, because the decryption process is the reverse process of the encryption process.

This decryption is not used in the application, but to facilitate us to update the encrypted file. The file must be decrypted before modifying it.

Decryption

3.1. Import AES key

This key is the key we generated earlier. After importing it, use the RSA public key signed by the application to decrypt the AES key:

  1. //Get the encrypted key raw key    
  2. String keyStr = mKeyText.getText();  
  3. byte[] key = Base64.decode(keyStr);  
  4. //Get the application signature key pair and public key to decrypt the raw key    
  5. KeyPair keypair = SignKey.getSignKeyPair();  
  6. byte[] rawkey = RSA.decrypt( key , keypair.getPublic());  
  7. //Use raw key to decrypt the file  
  8. AES.decryptFile(rawkey, fromFile, toFile);

3.2 Decrypting Files

After getting the pure version of the AES key, you can directly call the AES algorithm to decrypt the file:

  1. /**  
  2. * AES algorithm decrypts files  
  3. *  
  4. * @param rawKey AES key  
  5. * @param fromFile the encrypted file  
  6. * @param toFile decrypted file  
  7. */  
  8. public   static void decryptFile(byte[] rawKey, File fromFile, File toFile) throws Exception {  
  9. if (!fromFile.exists()) {  
  10. throw new NullPointerException( "File does not exist" );  
  11. }  
  12. if (toFile.exists()) {  
  13. toFile.delete ();  
  14. }  
  15. SecretKeySpec skeySpec = new SecretKeySpec(rawKey, AES);  
  16. Cipher cipher = Cipher.getInstance(AES);  
  17. //Decryption mode  
  18. cipher.init(Cipher.DECRYPT_MODE, skeySpec);  
  19. FileInputStream fis = new FileInputStream(fromFile);  
  20. FileOutputStream fos = new FileOutputStream(toFile, true );  
  21. byte[] buffer = new byte[512 * 1024 + 16];  
  22. int offset;  
  23. //Use the decryption stream to decrypt  
  24. CipherInputStream cipherInputStream = new CipherInputStream(fis, cipher);  
  25. while ((offset = cipherInputStream. read (buffer)) != -1) {  
  26. fos.write(buffer, 0, offset);  
  27. fos.flush();  
  28. }  
  29. fos.close ();  
  30. fis.close ();  
  31. }

Compared with the AES encryption process, it can be found that it is just switching the AES algorithm mode.

3. Block 3: Decrypting files in Android applications

To decrypt the file, you need to add the encrypted AES key to the resource folder, which is the key derived above, and the encrypted file. The premise for correct decryption is that your application signature is the same as the signature used to encrypt the file.

Resource Files

3.1. Decrypting the AES key

The main difference between decrypting files in Android applications and decrypting files in Java tools lies in the acquisition of RSA keys. In Java tools, the application signature testkey.keystore is owned by the developer, and all the information in it can be obtained. In Android, applications are released to the application market, and anyone can download our package. The application signature can only obtain its public key through the API provided by Android.

  1. /**  
  2. * Author:xishuang  
  3. * Date :2018.05.06  
  4. * Des: Application signature reading tool class  
  5. */  
  6. public class SignKey {  
  7. /**  
  8. * Get the signature of the current application  
  9. *  
  10. * @param context context  
  11. */  
  12. public   static byte[] getSign(Context context) {  
  13. PackageManager pm = context.getPackageManager();  
  14. try {  
  15. PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);  
  16. Signature[] signatures = info.signatures;  
  17. if (signatures != null ) {  
  18. return signatures[0].toByteArray();  
  19. }  
  20. } catch (NameNotFoundException e) {  
  21. e.printStackTrace();  
  22. }  
  23. return   null ;  
  24. }  
  25. /**  
  26. * Get the public key based on the signature  
  27. */  
  28. public   static PublicKey getPublicKey(byte[] signature) {  
  29. try {  
  30. CertificateFactory certFactory = CertificateFactory  
  31. .getInstance( "X.509" );  
  32. X509Certificate cert = (X509Certificate) certFactory  
  33. .generateCertificate(new ByteArrayInputStream(signature));  
  34. return cert.getPublicKey();  
  35. } catch (CertificateException e) {  
  36. e.printStackTrace();  
  37. }
  38.   return   null ;  
  39. }  
  40. }

After obtaining the public key of the application signature testkey.keystore, the process is basically the same as the operation in the Java tool, using the RSA public key to decrypt the AES key.

  1. private static final String SIMPLE_KEY_DATA = "testkey.dat" ;  
  2. /**  
  3. * Get the decrypted file encryption key  
  4. */  
  5. private static byte[] getRawKey(Context context) throws Exception {  
  6. //Get the application's signing key  
  7. byte[] sign = SignKey.getSign(context);  
  8. PublicKey pubKey = SignKey.getPublicKey(sign);  
  9. //Get the key for the encrypted file  
  10. InputStream keyis = context.getAssets(). open (SIMPLE_KEY_DATA);  
  11. byte[] key = getData(keyis);  
  12. //Decryption key  
  13. return RSA.decrypt( key , pubKey);  
  14. }

Finally, use the decrypted AES key to decrypt the file.

3.2. AES key decryption file

Get the file stream of the encrypted file through the resource manager, and use the AES algorithm to decrypt the file stream using the AES key.

  1. /**  
  2. * Get the decrypted file stream  
  3. */  
  4. public   static InputStream onObtainInputStream(Context context) {  
  5. try {  
  6. AssetManager assetmanager = context.getAssets();  
  7. InputStream is = assetmanager.open ( "encrypt_test.txt" ) ;  
  8. byte[] rawkey = getRawKey(context);  
  9. //Using the decryption stream, the data will be decrypted before being written to the underlying OutputStream  
  10. SecretKeySpec skeySpec = new SecretKeySpec(rawkey, "AES" );  
  11. Cipher cipher = Cipher.getInstance( "AES" );  
  12. cipher.init(Cipher.DECRYPT_MODE, skeySpec);  
  13. return new CipherInputStream( is , cipher);  
  14. } catch (Exception e) {  
  15. e.printStackTrace();  
  16. }  
  17. return   null ;  
  18. }

After getting the encrypted file stream, the purpose is achieved, which can be parsed into a string and displayed:

  1. private void inputData() {  
  2. InputStream in = DecryptUtil.onObtainInputStream(this);  
  3. try {  
  4. BufferedReader reader = new BufferedReader(new InputStreamReader( in , "GBK" ));  
  5. StringBuilder sb = new StringBuilder();  
  6. String line;  
  7. while ((line = reader.readLine()) != null ) {  
  8. sb.append(line + "\n" );  
  9. }  
  10. contentTv.setText(sb.toString());  
  11. } catch (IOException e) {  
  12. e.printStackTrace();  
  13. finally  
  14. try {  
  15. in .close () ;  
  16. } catch (IOException e) {  
  17. e.printStackTrace();  
  18. }  
  19. }  
  20. }

The example effect diagram is as follows. Please pay attention to the content in the red box. Because I am too lazy to create a new project, I tested it with the original project:

Effect

Currently, the tool uses the encryption and decryption algorithms that are common on the market. You can change the algorithm, such as DES or other symmetric and asymmetric algorithms, or even your own modified algorithms. If you want to run the example demonstration:

QQ screenshot 20180508234849.png

Just run the java file and you can open the encryption and decryption tool. The encryption and decryption tool interface is a small part extracted from our toolkit. After all, writing the interface is very annoying. Thanks to our great god who wrote such a tool many years ago.

<<:  Android 9.0 is now available! Xiaomi, OnePlus, and OV are the first to release it

>>:  Google announces Android version market share in May: More people are eating "Oreo"

Recommend

Microsoft and Touch Technology invest heavily in Windows Game Contest

Recently, Chukong Technology and Microsoft jointl...

Two tips to make your article interesting and not original

Believe that the copy you write is actually prett...

How to use the Golden Circle Rule to write a marketing promotion plan?

Double 11, Double 12, Christmas Eve and other fes...

iPhone after-sales adjustment: Apple finally compromises

Apple is promoting safer and more compliant iPhon...

Observation on content products of e-commerce operation platform!

With the rise of short video platforms, the trend...

Taro cross-terminal solution for Ctrip’s mini-program ecosystem

​Author|Ctrip's front-end framework team prov...

iOS understands multithreading from a practical perspective

Preface I believe that many developers have more ...

How many times can I sign up for the 2020 Daily Specials in a month?

Daily Specials is a platform provided by Taobao w...

The entire iPhone 13 series no longer supports China Telecom's 2G/3G network

A few days ago, Apple’s official support website ...