前言:
对于AES加密,在博文 AES 加密 已经有了详细说明,这边博文将其用Android 实现。
更多的加密可以看:
实例:
所需公共类 BlockEncryptionHelper 如下:
public class BlockEncryptionHelper {
/**
* get a random secret key.
* must use the same key for encrypting and decrypting.
*/
private static SecretKeySpec getRandomSecretKeySpec(String alg, String key) {
try {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(key.getBytes("UTF-8"));
KeyGenerator gen = KeyGenerator.getInstance(alg);
gen.init(128, sr);
SecretKey secKey = gen.generateKey();
return new SecretKeySpec(secKey.getEncoded(), alg);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* get a stationary secret key.
* must use the same key for encryption and decryption.
*/
private static SecretKey getSecretKey(String alg, String key) {
try {
byte[] rawKey = key.getBytes("UTF-8");
return new SecretKeySpec(rawKey, alg);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static Cipher getCipher(int mode, String alg, String key) {
if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
return null;
SecretKey secretKey = getSecretKey(alg, key);
if (secretKey == null)
return null;
if (TextUtils.equals(alg, "DES") || TextUtils.equals(alg, "DESede")) {
key = key.substring(0, 8);
}
try {
IvParameterSpec ivp = new IvParameterSpec(key.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance(alg + "/CBC/PKCS5Padding");
cipher.init(mode, secretKey, ivp);
return cipher;
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static byte[] encrypt(byte[] rawValue, Cipher cipher) {
if (cipher == null) {
return null;
}
byte[] encrypted = null;
try {
encrypted = cipher.doFinal(rawValue);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return encrypted;
}
}
其中AES 的加密模式是CBC,填充模式的PKCS5Padding。最终通过cipher.doFinal 进入加密、解密功能。
另外,这里用到的Cipher 是针对部分双向加密的算法接口,具体的双向加密可以看 数据加密 ---- 总篇。
算法处理的过程提炼到BlockEncryption 类中:
public abstract class BlockEncryption {
private static final String TAG = "BlockEncryption";
private Cipher mEncrytCipher = null;
private Cipher mDecrytCipher = null;
private Context mContext;
private EncryptionCallback mCallback;
// whether delete the source file.
private boolean mDeleteSrcFile = true;
private static final int BUFFER_SIZE = 4096;
public BlockEncryption() {
}
public BlockEncryption(Context context, boolean deleteSrcFile, EncryptionCallback callback) {
mContext = context;
mDeleteSrcFile = deleteSrcFile;
mCallback = callback;
}
private int roundUpBlockSize(int bufferSize) {
return bufferSize + getBlockSize();
}
public abstract String getAlgorithm();
public abstract Cipher getCipher(int mode);
/**
* Set the block size for padding.
* Different size for different algorithm. e.g 16 bytes for AES.
*/
protected abstract int getBlockSize();
/**
* Different algorithms may use different buffer size.
*/
protected int getBufferSize() {
return BUFFER_SIZE;
}
private byte[] encrypt(byte[] rawData, Cipher cipher) {
return BlockEncryptionHelper.encrypt(rawData, cipher);
}
/**
* Decrypt files with algorithm AES or DES. When {@value destPath} is null or empty, save the file
* into current directory or into original directory.
*/
public boolean decryptFile(String srcPath, String destPath) {
Log.d(TAG, "==== decryptFile, srcPath = " + srcPath + ", destPath = " + destPath);
File srcFile = new File(srcPath);
if (!FileOperation.isFileExists(srcFile)) {
return false;
}
if (mDecrytCipher == null) {
mDecrytCipher = getCipher(Cipher.DECRYPT_MODE);
}
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
File destFile = null;
...
try {
fileInputStream = new FileInputStream(srcFile);
...
// decrypt file
int cnt;
byte[] buf = new byte[roundUpBlockSize(getBufferSize())];
while ((cnt = fileInputStream.read(buf)) >= 0) {
byte[] rawData = buf;
if (cnt != buf.length)
rawData = FileOperation.subBytes(buf, 0, cnt);
byte[] decryptData = encrypt(rawData, mDecrytCipher);
if (decryptData != null)
fileOutputStream.write(decryptData, 0, decryptData.length);
else
return false;
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
...
...
}
...
return true;
}
/**
* Encrypt file with algorithm AES or DES. When {@value destPath} is null or empty, create a new
* file in current directory, and the file name is random with 32 characters.
*/
public boolean encryptFile(String srcPath, String destPath) {
Log.d(TAG, "==== encryptFile, srcPath = " + srcPath + ", destPath = " + destPath);
File srcFile = new File(srcPath);
if (!FileOperation.isFileExists(srcFile)) {
return false;
}
if (mEncrytCipher == null) {
mEncrytCipher = getCipher(Cipher.ENCRYPT_MODE);
}
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
...
try {
fileInputStream = new FileInputStream(srcFile);
fileOutputStream = new FileOutputStream(destFile);
// encrypt file
int cnt;
byte[] buf = new byte[getBufferSize()];
while ((cnt = fileInputStream.read(buf)) >= 0) {
byte[] rawData = buf;
if (cnt != buf.length)
rawData = FileOperation.subBytes(buf, 0, cnt);
byte[] encryptData = encrypt(rawData, mEncrytCipher);
fileOutputStream.write(encryptData, 0, encryptData.length);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} finally {
...
...
}
...
return true;
}
public String strEncryption(String src) {
byte[] plainText = src.getBytes();
if (mEncrytCipher == null) {
mEncrytCipher = getCipher(Cipher.ENCRYPT_MODE);
}
byte[] cipherText = encrypt(plainText, mEncrytCipher);
try {
return new String(Base64.encode(cipherText, Base64.DEFAULT), "UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public String strDecryption(String src) {
byte[] cipherText = null;
try {
cipherText = Base64.decode(src.getBytes("UTF-8"), Base64.DEFAULT);
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (mDecrytCipher == null) {
mDecrytCipher = getCipher(Cipher.DECRYPT_MODE);
}
byte[] plainText = encrypt(cipherText, mDecrytCipher);
try {
return new String(plainText, "utf-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
注意其中的几个抽象函数,都是跟算法相关的,所以抽象出来再具体的算法中具体定义。
针对AES 算法,抽象函数实现的定义在AESEncryption 类中:
public class AESEncryption extends BlockEncryption{
private static final String DEFAULT_KEY = "shiftencrypt0926";
public static final String ALGORITHM = "AES";
private static final int AES_BLOCK_SIZE = 16;
public AESEncryption(Context context, boolean deleteSrcFile, EncryptionCallback callback) {
super(context, deleteSrcFile, callback);
}
@Override
public Cipher getCipher(int mode) {
return BlockEncryptionHelper.getCipher(mode, ALGORITHM, DEFAULT_KEY);
}
@Override
public String getAlgorithm() {
return ALGORITHM;
}
@Override
protected int getBlockSize() {
return AES_BLOCK_SIZE;
}
}
这里的block size 很重要,在算法中需要引进数据的取整,在数据不够块大小(AES 块大小为128位,详细看AES加密算法的详细介绍与实现)需要填充,所以最后的数据加密都是以128位(16字节)为单位。
测试用的Activity (Button 实现onClick 监听)中实现如下:
private void testEncryptionAES() {
Button eaes = (Button) findViewById(R.id.encrypt_aes);
eaes.setOnClickListener(this);
Button daes = (Button) findViewById(R.id.decrypt_aes);
daes.setOnClickListener(this);
}
private void aesEncryption() {
if (mFileEncryption != null) {
Log.d(TAG, "==== test file, encryption with aes ...");
String filePath = "/storage/emulated/0/hehe.png";
String destPath = "/storage/emulated/0/2.png";
mFileEncryption.setAlgorithm(FileEncryption.ALG_MODE_AES);
mFileEncryption.start(FileEncryption.MODE_ENCRYPT, filePath, destPath);
Log.d(TAG, "==== test file, encryption end");
}
}
private void aesDecryption() {
if (mFileEncryption != null) {
Log.d(TAG, "==== test file, decryption with aes ...");
String filePath = "/storage/emulated/0/hehe.png";
String destPath = "/storage/emulated/0/2.png";
mFileEncryption.setAlgorithm(FileEncryption.ALG_MODE_AES);
mFileEncryption.start(FileEncryption.MODE_DECRYPT, destPath, filePath);
Log.d(TAG, "==== test file, decryption end");
}
}