android encryption/decryption with AES

2019-01-01 14:12发布

问题:

Is there a good example of how to encrypt/decrypt Image and other files with AES on Android? I\'m working on a project which needs to encrypt/decrypt data but I\'m not really sure how to do it.

回答1:

Warning: This answer contains code you should not use as it is insecure (using SHA1PRNG for key derivation and using AES in ECB mode)

Instead, use PBKDF2WithHmacSHA1 for key derivation and AES in CBC or GCM mode (GCM provides both privacy and integrity)

You could use functions like these:

private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
    SecretKeySpec skeySpec = new SecretKeySpec(raw, \"AES\");
    Cipher cipher = Cipher.getInstance(\"AES\");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(clear);
    return encrypted;
}

private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
    SecretKeySpec skeySpec = new SecretKeySpec(raw, \"AES\");
    Cipher cipher = Cipher.getInstance(\"AES\");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] decrypted = cipher.doFinal(encrypted);
    return decrypted;
}

And invoke them like this:

ByteArrayOutputStream baos = new ByteArrayOutputStream();  
bm.compress(Bitmap.CompressFormat.PNG, 100, baos); // bm is the bitmap object   
byte[] b = baos.toByteArray();  

byte[] keyStart = \"this is a key\".getBytes();
KeyGenerator kgen = KeyGenerator.getInstance(\"AES\");
SecureRandom sr = SecureRandom.getInstance(\"SHA1PRNG\");
sr.setSeed(keyStart);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();    

// encrypt
byte[] encryptedData = encrypt(key,b);
// decrypt
byte[] decryptedData = decrypt(key,encryptedData);

This should work, I use similar code in a project right now.



回答2:

import java.security.AlgorithmParameters;
import java.security.SecureRandom;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

class SecurityUtils {

  private static final byte[] salt = { (byte) 0xA4, (byte) 0x0B, (byte) 0xC8,
      (byte) 0x34, (byte) 0xD6, (byte) 0x95, (byte) 0xF3, (byte) 0x13 };

  private static int BLOCKS = 128;

  public static byte[] encryptAES(String seed, String cleartext)
      throws Exception {
    byte[] rawKey = getRawKey(seed.getBytes(\"UTF8\"));
    SecretKeySpec skeySpec = new SecretKeySpec(rawKey, \"AES\");
    Cipher cipher = Cipher.getInstance(\"AES\");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    return cipher.doFinal(cleartext.getBytes(\"UTF8\"));
  }

  public static byte[] decryptAES(String seed, byte[] data) throws Exception {
    byte[] rawKey = getRawKey(seed.getBytes(\"UTF8\"));
    SecretKeySpec skeySpec = new SecretKeySpec(rawKey, \"AES\");
    Cipher cipher = Cipher.getInstance(\"AES\");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    return cipher.doFinal(data);
  }

  private static byte[] getRawKey(byte[] seed) throws Exception {
    KeyGenerator kgen = KeyGenerator.getInstance(\"AES\");
    SecureRandom sr = SecureRandom.getInstance(\"SHA1PRNG\");
    sr.setSeed(seed);
    kgen.init(BLOCKS, sr); // 192 and 256 bits may not be available
    SecretKey skey = kgen.generateKey();
    byte[] raw = skey.getEncoded();
    return raw;
  }

  private static byte[] pad(byte[] seed) {
    byte[] nseed = new byte[BLOCKS / 8];
    for (int i = 0; i < BLOCKS / 8; i++)
      nseed[i] = 0;
    for (int i = 0; i < seed.length; i++)
      nseed[i] = seed[i];

    return nseed;
  }

  public static byte[] encryptPBE(String password, String cleartext)
      throws Exception {
    SecretKeyFactory factory = SecretKeyFactory
        .getInstance(\"PBKDF2WithHmacSHA1\");
    KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1024, 256);
    SecretKey tmp = factory.generateSecret(spec);
    SecretKey secret = new SecretKeySpec(tmp.getEncoded(), \"AES\");

    Cipher cipher = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();
    byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
    return cipher.doFinal(cleartext.getBytes(\"UTF-8\"));
  }

  public static String decryptPBE(SecretKey secret, String ciphertext,
      byte[] iv) throws Exception {
    Cipher cipher = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
    return new String(cipher.doFinal(ciphertext.getBytes()), \"UTF-8\");
  }

}


回答3:

Old question but I upgrade the answers supporting Android prior and post 4.2 and considering all recent changes according to Android developers blog

Plus I leave a working example on my github repo.

import java.nio.charset.Charset;
import java.security.AlgorithmParameters;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;


/*
* This software is provided \'as-is\', without any express or implied
* warranty.  In no event will Google be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, as long as the origin is not misrepresented.
* 
* @author: Ricardo Champa
* 
*/

public class MyCipher {

    private final static String ALGORITHM = \"AES\";
    private String mySecret;

    public MyCipher(String mySecret){
        this.mySecret = mySecret;
    }

    public MyCipherData encryptUTF8(String data){
        try{
            byte[] bytes = data.toString().getBytes(\"utf-8\");
            byte[] bytesBase64 = Base64.encodeBase64(bytes);
            return encrypt(bytesBase64);
        }
        catch(Exception e){
            MyLogs.show(e.getMessage());
            return null;
        }

    }

    public String decryptUTF8(byte[] encryptedData, IvParameterSpec iv){
        try {
            byte[] decryptedData = decrypt(encryptedData, iv);
            byte[] decodedBytes = Base64.decodeBase64(decryptedData);
            String restored_data = new String(decodedBytes, Charset.forName(\"UTF8\"));
            return restored_data;
        } catch (Exception e) {
            MyLogs.show(e.getMessage());;
            return null;
        }
    }

    //AES
    private MyCipherData encrypt(byte[] raw, byte[] clear) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITHM);
        Cipher cipher = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");
        //solved using PRNGFixes class
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] data = cipher.doFinal(clear);

        AlgorithmParameters params = cipher.getParameters();
        byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
        return new MyCipherData(data, iv);
    }

    private byte[] decrypt(byte[] raw, byte[] encrypted, IvParameterSpec iv) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITHM);
        Cipher cipher = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;
    }

    private byte[] getKey() throws Exception{
        byte[] keyStart = this.mySecret.getBytes(\"utf-8\");
        KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM);

        SecureRandom sr = SecureRandom.getInstance(\"SHA1PRNG\", \"Crypto\");
        //      if (android.os.Build.VERSION.SDK_INT >= 17) {
        //          sr = SecureRandom.getInstance(\"SHA1PRNG\", \"Crypto\");
        //      } else {
        //          sr = SecureRandom.getInstance(\"SHA1PRNG\");
        //      }
        sr.setSeed(keyStart);
        kgen.init(128, sr); // 192 and 256 bits may not be available
        SecretKey skey = kgen.generateKey();
        byte[] key = skey.getEncoded();
        return key;

    }
    ////////////////////////////////////////////////////////////
    private MyCipherData encrypt(byte[] data) throws Exception{
        return encrypt(getKey(),data);
    }
    private byte[] decrypt(byte[] encryptedData, IvParameterSpec iv) throws Exception{
        return decrypt(getKey(),encryptedData, iv);
    }
}


回答4:

If you are encrypting a text file, then the following test/sample may be useful. It does the following:

  1. Create a byte stream,
  2. wraps that with AES encryption,
  3. wrap it next with text processing
  4. and lastly buffers it

    // AESdemo
    
    public class AESdemo extends Activity {
        boolean encryptionIsOn = true;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_aesdemo);
            // needs <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />
            String homeDirName = Environment.getExternalStorageDirectory().getAbsolutePath() +
                    \"/\" + getPackageName();
            File file = new File(homeDirName, \"test.txt\");
            byte[] keyBytes = getKey(\"password\");
    
            try {
                File dir = new File(homeDirName);
                if (!dir.exists())
                    dir.mkdirs();
                if (!file.exists())
                    file.createNewFile();
    
                OutputStreamWriter osw;
    
                if (encryptionIsOn) {
                    Cipher cipher = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");
                    SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, \"AES\");
                    IvParameterSpec ivParameterSpec = new IvParameterSpec(keyBytes);
                    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
    
                    FileOutputStream fos = new FileOutputStream(file);
                    CipherOutputStream cos = new CipherOutputStream(fos, cipher);
                    osw = new OutputStreamWriter(cos, \"UTF-8\");
                }
                else    // not encryptionIsOn
                    osw = new FileWriter(file);
    
                BufferedWriter out = new BufferedWriter(osw);
                out.write(\"This is a test\\n\");
                out.close();
            }
            catch (Exception e) {
                System.out.println(\"Encryption Exception \"+e);
            }
    
            ///////////////////////////////////
            try {
                InputStreamReader isr;
    
                if (encryptionIsOn) {
                    Cipher cipher = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");
                    SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, \"AES\");
                    IvParameterSpec ivParameterSpec = new IvParameterSpec(keyBytes);
                    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
    
                    FileInputStream fis = new FileInputStream(file);
                    CipherInputStream cis = new CipherInputStream(fis, cipher);
                    isr = new InputStreamReader(cis, \"UTF-8\");
                }
                else
                    isr = new FileReader(file);
    
                BufferedReader in = new BufferedReader(isr);
                String line = in.readLine();
                System.out.println(\"Text read: <\"+line+\">\");
                in.close();
            }
            catch (Exception e) {
                System.out.println(\"Decryption Exception \"+e);
            }
        }
    
        private byte[] getKey(String password) throws UnsupportedEncodingException {
            String key = \"\";
            while (key.length() < 16)
                key += password;
            return key.substring(0, 16).getBytes(\"UTF-8\");
        }
    }
    


回答5:

As mentioned by Nacho.L PBKDF2WithHmacSHA1 derivation is used as it is more secured.

import android.util.Base64;

import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AESEncyption {

    private static final int pswdIterations = 10;
    private static final int keySize = 128;
    private static final String cypherInstance = \"AES/CBC/PKCS5Padding\";
    private static final String secretKeyInstance = \"PBKDF2WithHmacSHA1\";
    private static final String plainText = \"sampleText\";
    private static final String AESSalt = \"exampleSalt\";
    private static final String initializationVector = \"8119745113154120\";

    public static String encrypt(String textToEncrypt) throws Exception {

        SecretKeySpec skeySpec = new SecretKeySpec(getRaw(plainText, AESSalt), \"AES\");
        Cipher cipher = Cipher.getInstance(cypherInstance);
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(initializationVector.getBytes()));
        byte[] encrypted = cipher.doFinal(textToEncrypt.getBytes());
        return Base64.encodeToString(encrypted, Base64.DEFAULT);
    }

    public static String decrypt(String textToDecrypt) throws Exception {

        byte[] encryted_bytes = Base64.decode(textToDecrypt, Base64.DEFAULT);
        SecretKeySpec skeySpec = new SecretKeySpec(getRaw(plainText, AESSalt), \"AES\");
        Cipher cipher = Cipher.getInstance(cypherInstance);
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(initializationVector.getBytes()));
        byte[] decrypted = cipher.doFinal(encryted_bytes);
        return new String(decrypted, \"UTF-8\");
    }

    private static byte[] getRaw(String plainText, String salt) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance(secretKeyInstance);
            KeySpec spec = new PBEKeySpec(plainText.toCharArray(), salt.getBytes(), pswdIterations, keySize);
            return factory.generateSecret(spec).getEncoded();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return new byte[0];
    }

}


回答6:

Simple API to perform AES encryption on Android. This is the Android counterpart to the AESCrypt library Ruby and Obj-C (with the same defaults):

https://github.com/scottyab/AESCrypt-Android



回答7:

AES encrypt/decrypt in android

String encData= encrypt(\"keykey\".getBytes(\"UTF-16LE\"), (\"0123000000000215\").getBytes(\"UTF-16LE\"));

String decData= decrypt(\"keykey\",Base64.decode(encData.getBytes(\"UTF-16LE\"), Base64.DEFAULT));

encrypt function

private static String encrypt(byte[] key, byte[] clear) throws Exception
    {
        MessageDigest md = MessageDigest.getInstance(\"md5\");
        byte[] digestOfPassword = md.digest(key);

        SecretKeySpec skeySpec = new SecretKeySpec(digestOfPassword, \"AES\");
        Cipher cipher = Cipher.getInstance(\"AES/ECB/PKCS7Padding\");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(clear);
        return Base64.encodeToString(encrypted,Base64.DEFAULT);
    }

decrypt function

private static String decrypt(String key, byte[] encrypted) throws Exception
    {
        MessageDigest md = MessageDigest.getInstance(\"md5\");
        byte[] digestOfPassword = md.digest(key.getBytes(\"UTF-16LE\"));

        SecretKeySpec skeySpec = new SecretKeySpec(digestOfPassword, \"AES\");
        Cipher cipher = Cipher.getInstance(\"AES/ECB/PKCS7Padding\");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        return new String(decrypted, \"UTF-16LE\");
    }

AES encrypt/decrypt in c#

 static void Main(string[] args)
        {
            string enc = encryptAES(\"0123000000000215\", \"keykey\");
            string dec = decryptAES(enc, \"keykey\");

            Console.ReadKey();
        }

encrypt function

 public static string encryptAES(string input, string key)
        {
            var plain = Encoding.Unicode.GetBytes(input);

            // 128 bits
            AesCryptoServiceProvider provider = new AesCryptoServiceProvider();
            provider.KeySize = 128;
            provider.Mode = CipherMode.ECB;
            provider.Padding = PaddingMode.PKCS7;

            provider.Key = CalculateMD5Hash(key);

            var enc = provider.CreateEncryptor().TransformFinalBlock(plain, 0, plain.Length);
            return Convert.ToBase64String(enc);
        }

decrypt function

public static string decryptAES(string encryptText, string key)
{
    byte[] enc = Convert.FromBase64String(encryptText);
    // 128 bits
    AesCryptoServiceProvider provider = new AesCryptoServiceProvider();
    provider.KeySize = 128;
    provider.Mode = CipherMode.ECB;
    provider.Padding = PaddingMode.PKCS7;

    provider.Key = CalculateMD5Hash(key);


    var dec = provider.CreateDecryptor().TransformFinalBlock(enc, 0, enc.Length);
    return Encoding.Unicode.GetString(dec);
}

create md5

 public static byte[] CalculateMD5Hash(string input)
        {
            MD5 md5 = MD5.Create();
            byte[] inputBytes = Encoding.Unicode.GetBytes(input);
            return md5.ComputeHash(inputBytes);
        }


回答8:

To add bouncy castle to Android project: https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk16/1.45

Add this line in your Main Activity:

static {
    Security.addProvider(new BouncyCastleProvider());
}

public class AESHelper {

    private static final String TAG = \"AESHelper\";

    public static byte[] encrypt(byte[] data, String initVector, String key) {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(\"UTF-8\"));
            Cipher c = Cipher.getInstance(\"AES/CBC/PKCS5PADDING\");
            SecretKeySpec k = new SecretKeySpec(Base64.decode(key, Base64.DEFAULT), \"AES\");
            c.init(Cipher.ENCRYPT_MODE, k, iv);
            return c.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public static byte[] decrypt(byte[] data, String initVector, String key) {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(\"UTF-8\"));
            Cipher c = Cipher.getInstance(\"AES/CBC/PKCS5PADDING\");
            SecretKeySpec k = new SecretKeySpec(Base64.decode(key, Base64.DEFAULT), \"AES\");
            c.init(Cipher.DECRYPT_MODE, k, iv);
            return c.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public static String keyGenerator() throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(\"AES\");
        keyGenerator.init(192);

        return Base64.encodeToString(keyGenerator.generateKey().getEncoded(),
                Base64.DEFAULT);
    }
}