可以参考的资料:http://blog.csdn.net/zjclugger/article/details/34838447
前几天因需要研究了下android 5.0平台上使用AES加密,但在网上找了相关资料并按照相关代码实现后发现加密可以成功,但解密时会报错失败,
查找一大堆资料后发现 在android 4.2之后SHA1PRNG 强随机种子算法调用方法不同,可用以下方式区别调用
private
static
byte
[] getRawKey(
byte
[] seed)
throws
Exception {
KeyGenerator kgen = KeyGenerator.getInstance(
"AES"
);
// SHA1PRNG 强随机种子算法, 要区别4.2以上版本的调用方法
SecureRandom sr =
null
;
if
(android.os.Build.VERSION.SDK_INT >=
17
) {
sr = SecureRandom.getInstance(
"SHA1PRNG"
,
"Crypto"
);
}
else
{
sr = SecureRandom.getInstance(
"SHA1PRNG"
);
}
sr.setSeed(seed);
kgen.init(
256
, sr);
//256 bits or 128 bits,192bits
SecretKey skey = kgen.generateKey();
byte
[] raw = skey.getEncoded();
return
raw;
}
同时 需要注意的是Cipher中的加密模式和填充方式两个参数,因这两个参数不适配的话也会导致解密失败,
android 4.2以后以下方式获取Cipher也不太适用:
- // 加密算法,加密模式和填充方式三部分或指定加密算
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
经过验证可以在android4.2以上版本有效使用的代码如下(本人使用的是android 5.0平台):
package com.silead.fp.utils;
import android.annotation.SuppressLint;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* AES encryptor
*
* @author janning_ning
*
*/
public class AESEncryptor {
private static final String HEX = "0123456789ABCDEF";
/**
* encrypt the clear string
*
* @param seed
* the key for encryptting
* @param clearText
* the clear string that need to be encryptted
* @return
* @throws Exception
*/
public static String encrypt(String seed, String clearText)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, clearText.getBytes());
return toHex(result);
}
/**
* decrypt the encryptted string
*
* @param seed
* the key for decryptting
* @param encrypttedText
* the encryptted string that need to be decryptted
* @return
* @throws Exception
*/
public static String decrypt(String seed, String encrypttedText)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypttedText);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}
@SuppressLint("TrulyRandom")
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clearText)
throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clearText);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encryptedText)
throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encryptedText);
return decrypted;
}
public static String toHex(String txt) {
return toHex(txt.getBytes());
}
public static String fromHex(String hex) {
return new String(toByte(hex));
}
public static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++) {
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
16).byteValue();
}
return result;
}
public static String toHex(byte[] buf) {
if (buf == null) {
return "";
}
int len = buf.length;
StringBuffer result = new StringBuffer(2 * len);
for (int i = 0; i < len; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
以下是网上其他代码供参考:
package com.king.zjc;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import android.util.Log;
public class AESHelper {
public static final String TAG = AESHelper.class.getSimpleName();
Runtime mRuntime = Runtime.getRuntime();
@SuppressWarnings("resource")
public boolean AESCipher(int cipherMode, String sourceFilePath,
String targetFilePath, String seed) {
boolean result = false;
FileChannel sourceFC = null;
FileChannel targetFC = null;
try {
if (cipherMode != Cipher.ENCRYPT_MODE
&& cipherMode != Cipher.DECRYPT_MODE) {
Log.d(TAG,
"Operation mode error, should be encrypt or decrypt!");
return false;
}
Cipher mCipher = Cipher.getInstance("AES/CFB/NoPadding");
byte[] rawkey = getRawKey(seed.getBytes());
File sourceFile = new File(sourceFilePath);
File targetFile = new File(targetFilePath);
sourceFC = new RandomAccessFile(sourceFile, "r").getChannel();
targetFC = new RandomAccessFile(targetFile, "rw").getChannel();
SecretKeySpec secretKey = new SecretKeySpec(rawkey, "AES");
mCipher.init(cipherMode, secretKey, new IvParameterSpec(
new byte[mCipher.getBlockSize()]));
ByteBuffer byteData = ByteBuffer.allocate(1024);
while (sourceFC.read(byteData) != -1) {
// 通过通道读写交叉进行。
// 将缓冲区准备为数据传出状态
byteData.flip();
byte[] byteList = new byte[byteData.remaining()];
byteData.get(byteList, 0, byteList.length);
//此处,若不使用数组加密解密会失败,因为当byteData达不到1024个时,加密方式不同对空白字节的处理也不相同,从而导致成功与失败。
byte[] bytes = mCipher.doFinal(byteList);
targetFC.write(ByteBuffer.wrap(bytes));
byteData.clear();
}
result = true;
} catch (IOException | NoSuchAlgorithmException | InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException | BadPaddingException
| NoSuchPaddingException e) {
Log.d(TAG, e.getMessage());
} finally {
try {
if (sourceFC != null) {
sourceFC.close();
}
if (targetFC != null) {
targetFC.close();
}
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
}
return result;
}
/**
* 加密后的字符串
*
* @param seed
* @param clearText
* @return
*/
public String encrypt(String seed, String source) {
// Log.d(TAG, "加密前的seed=" + seed + ",内容为:" + clearText);
byte[] result = null;
try {
byte[] rawkey = getRawKey(seed.getBytes());
result = encrypt(rawkey, source.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
String content = toHex(result);
return content;
}
/**
* 解密后的字符串
*
* @param seed
* @param encrypted
* @return
*/
public String decrypt(String seed, String encrypted) {
byte[] rawKey;
try {
rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypted);
byte[] result = decrypt(rawKey, enc);
String coentn = new String(result);
return coentn;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 使用一个安全的随机数来产生一个密匙,密匙加密使用的
*
* @param seed
* @return
* @throws NoSuchAlgorithmException
*/
private byte[] getRawKey(byte[] seed) throws NoSuchAlgorithmException {
// 获得一个随机数,传入的参数为默认方式。
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
// 设置一个种子,一般是用户设定的密码
sr.setSeed(seed);
// 获得一个key生成器(AES加密模式)
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
// 设置密匙长度128位
keyGen.init(128, sr);
// 获得密匙
SecretKey key = keyGen.generateKey();
// 返回密匙的byte数组供加解密使用
byte[] raw = key.getEncoded();
return raw;
}
/**
* 结合密钥生成加密后的密文
*
* @param raw
* @param input
* @return
* @throws Exception
*/
private byte[] encrypt(byte[] raw, byte[] input) throws Exception {
// 根据上一步生成的密匙指定一个密匙
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Cipher cipher = Cipher.getInstance("AES");
// 加密算法,加密模式和填充方式三部分或指定加密算
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 初始化模式为加密模式,并指定密匙
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(
new byte[cipher.getBlockSize()]));
byte[] encrypted = cipher.doFinal(input);
return encrypted;
}
/**
* 根据密钥解密已经加密的数据
*
* @param raw
* @param encrypted
* @return
* @throws Exception
*/
private byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(
new byte[cipher.getBlockSize()]));
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public String toHex(String txt) {
return toHex(txt.getBytes());
}
public String fromHex(String hex) {
return new String(toByte(hex));
}
public byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
16).byteValue();
return result;
}
public String toHex(byte[] buf) {
if (buf == null || buf.length <= 0)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private void appendHex(StringBuffer sb, byte b) {
final String HEX = "0123456789ABCDEF";
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
MainActivity.java:
package com.king.zjc;
import javax.crypto.Cipher;
import com.hisense.ad.encryption.AESHelper;
import com.hisense.ad.encryption.R;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
@SuppressLint("SdCardPath")
public class MainActivity extends Activity {
private final String SDcardPath = "/mnt/sdcard/encry/";
private Button mEncryptButton;
private Button mDecryptButton;
private TextView mShowMessage;
private EditText mFileName;
private EditText mNewFileName;
private AESHelper mAESHelper;
private EncryptionOrDecryptionTask mTask = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAESHelper = new AESHelper();
mFileName = (EditText) findViewById(R.id.file_name);
mNewFileName = (EditText) findViewById(R.id.new_file_name);
mShowMessage = (TextView) findViewById(R.id.message);
mEncryptButton = (Button) findViewById(R.id.encrypt);
mDecryptButton = (Button) findViewById(R.id.decrypt);
mEncryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mShowMessage.setText("开始加密,请稍等...");
if (mTask != null) {
mTask.cancel(true);
}
mTask = new EncryptionOrDecryptionTask(true, SDcardPath
+ mFileName.getText(), SDcardPath, mNewFileName
.getText().toString(), "zjc");
mTask.execute();
}
});
mDecryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mShowMessage.setText("开始解密,请稍等...");
if (mTask != null) {
mTask.cancel(true);
}
mTask = new EncryptionOrDecryptionTask(false, SDcardPath
+ mFileName.getText(), SDcardPath, mNewFileName
.getText().toString(), "zjc");
mTask.execute();
}
});
}
// #######################
/**
* 加密解密
*/
private class EncryptionOrDecryptionTask extends
AsyncTask
{
private String mSourceFile = "";
private String mNewFilePath = "";
private String mNewFileName = "";
private String mSeed = "";
private boolean mIsEncrypt = false;
public EncryptionOrDecryptionTask(boolean isEncrypt, String sourceFile,
String newFilePath, String newFileName, String seed) {
this.mSourceFile = sourceFile;
this.mNewFilePath = newFilePath;
this.mNewFileName = newFileName;
this.mSeed = seed;
this.mIsEncrypt = isEncrypt;
}
@Override
protected Boolean doInBackground(Void... params) {
boolean result = false;
if (mIsEncrypt) {
result = mAESHelper.AESCipher(Cipher.ENCRYPT_MODE, mSourceFile,
mNewFilePath + mNewFileName, mSeed);
} else {
result = mAESHelper.AESCipher(Cipher.DECRYPT_MODE, mSourceFile,
mNewFilePath + mNewFileName, mSeed);
}
return result;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
String showMessage = "";
if (mIsEncrypt) {
showMessage = result ? "加密已完成" : "加密失败!";
} else {
showMessage = result ? "解密完成" : "解密失败!";
}
mShowMessage.setText(showMessage);
}
}
}
main.xml: