Android encryption / decryption using AES
有没有一个很好的例子,如何加密和解密图像和其他文件与AES在Android上?
警告:此答案包含不安全的代码(使用sha1prng进行密钥派生,并在ecb模式下使用aes)相反,在CBC或GCM模式(GCM提供隐私和完整性)中,使用PBKDF2WITHHMACSHA1进行密钥派生和AES。
您可以使用如下函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } |
像这样调用它们:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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); |
号
这应该有效,我现在在一个项目中使用类似的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 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"); } } |
如nacho.l所述,使用带有hmacsha1派生的pbkdf2,因为它更安全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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]; } } |
号
旧问题,但我升级了支持Android的回答,之前和之后4.2,并考虑到所有最近的变化,根据Android开发者博客。
另外,我在Github回购协议上留下了一个工作示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | 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); } } |
如果您正在加密文本文件,那么下面的测试/示例可能很有用。它执行以下操作:
最后缓冲一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | // 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 "); 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"); } } |
。
在Android中加密/解密AES
1 2 3 |
encrypt function
号
1 2 3 4 5 6 7 8 9 10 11 | 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
号
1 2 3 4 5 6 7 8 9 10 11 | 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在C中加密/解密#
1 2 3 4 5 6 7 | static void Main(string[] args) { string enc = encryptAES("0123000000000215","keykey"); string dec = decryptAES(enc,"keykey"); Console.ReadKey(); } |
。
encrypt function
号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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
号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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
号
1 2 3 4 5 6 | public static byte[] CalculateMD5Hash(string input) { MD5 md5 = MD5.Create(); byte[] inputBytes = Encoding.Unicode.GetBytes(input); return md5.ComputeHash(inputBytes); } |
号
对于aes/cbc/pkcs7加密/解密,只需复制并粘贴以下代码,并用自己的secretkey和iv替换secretkey和iv。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import android.util.Base64; public class CryptoHandler { String SecretKey ="xxxxxxxxxxxxxxxxxxxx"; String IV ="xxxxxxxxxxxxxxxx"; private static CryptoHandler instance = null; public static CryptoHandler getInstance() { if (instance == null) { instance = new CryptoHandler(); } return instance; } public String encrypt(String message) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, UnsupportedEncodingException, InvalidAlgorithmParameterException { byte[] srcBuff = message.getBytes("UTF8"); //here using substring because AES takes only 16 or 24 or 32 byte of key SecretKeySpec skeySpec = new SecretKeySpec(SecretKey.substring(0,32).getBytes(),"AES"); IvParameterSpec ivSpec = new IvParameterSpec(IV.substring(0,16).getBytes()); Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); ecipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec); byte[] dstBuff = ecipher.doFinal(srcBuff); String base64 = Base64.encodeToString(dstBuff, Base64.DEFAULT); return base64; } public String decrypt(String encrypted) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { SecretKeySpec skeySpec = new SecretKeySpec(SecretKey.substring(0,32).getBytes(),"AES"); IvParameterSpec ivSpec = new IvParameterSpec(IV.substring(0,16).getBytes()); Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); ecipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec); byte[] raw = Base64.decode(encrypted, Base64.DEFAULT); byte[] originalBytes = ecipher.doFinal(raw); String original = new String(originalBytes,"UTF8"); return original; } } |
号
在Android上执行AES加密的简单API。这是与aescrypt库ruby和obj-c相对应的android(具有相同的默认值):
https://github.com/scottyab/aescrypt-android
将Bouncy Castle添加到Android项目:https://mvnrepository.com/artifact/org.BouncyCastle/bcprov-jdk16/1.45
在您的主要活动中添加此行:
1 2 3 |
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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); } } |
。