几番折磨终有结果,现将Demo整理出来。。。

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));}}
1. 其实我的Demo中只用到了AESCipher和getRawKey两个方法。若要返回字符串,一定要注意编码,如

    public static String encrypt(String data, String key) throws Exception {              try {                  Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");                  SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");                  cipher.init(Cipher.ENCRYPT_MODE, keyspec);                  byte[] encrypted = cipher.doFinal(data.getBytes());                  return Base64.encodeToString(encrypted, Base64.DEFAULT);              } catch (Exception e) {                  e.printStackTrace();                  return null;              }          }                public static String desEncrypt(String data, String key) throws Exception {              try {                  byte[] encrypted1 = Base64.decode(data.getBytes(), Base64.DEFAULT);                  Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");                  SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");                  cipher.init(Cipher.DECRYPT_MODE, keyspec);                  byte[] original = cipher.doFinal(encrypted1);                  return new String(original, "UTF-8");              } catch (Exception e) {                  e.printStackTrace();                  return null;              }          }  

2. 如果先把一个文件转换成字节数组,然后再加密,最后生成文件,这样很大机率很会产生OOM,所以这里利用了FileChannel,一次读取一定的字节数,而后再进行加密解密,最后再通过Channel生成新文件。

3. 先前一直失败,其重点是对“填充模式”的应用,我最终使用了AES/CFB/NoPadding,当不满16字节时,加密后数据长度不变。

    算法/模式/填充                16字节加密后数据长度        不满16字节加密后长度      AES/CBC/NoPadding             16                          不支持      AES/CBC/PKCS5Padding          32                          16      AES/CBC/ISO10126Padding       32                          16      AES/CFB/NoPadding             16                          原始数据长度      AES/CFB/PKCS5Padding          32                          16      AES/CFB/ISO10126Padding       32                          16      AES/ECB/NoPadding             16                          不支持      AES/ECB/PKCS5Padding          32                          16      AES/ECB/ISO10126Padding       32                          16      AES/OFB/NoPadding             16                          原始数据长度      AES/OFB/PKCS5Padding          32                          16      AES/OFB/ISO10126Padding       32                          16      AES/PCBC/NoPadding            16                          不支持      AES/PCBC/PKCS5Padding         32                          16      AES/PCBC/ISO10126Padding      32                          16  
当原始数据长度为16的整数倍时,假如原始数据长度等于16*n,则使用NoPadding时加密后数据长度等于16*n,其它情况下加密数据长度等于16*(n+1)。在不足16的整数倍的情况下,假如原始数据长度等于16*n+m[其中m小于16],除了NoPadding填充之外的任何方 式,加密数据长度都等于16*(n+1);NoPadding填充情况下,CBC、ECB和PCBC三种模式是不支持的,CFB、OFB两种模式下则加密数据长度等于原始数据长度。

4. 文件大小不同,用时不定,所以把加密解密过程放到一个AsyncTask内进行

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;@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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 extendsAsyncTask {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;}@Overrideprotected 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;}@Overrideprotected void onPostExecute(Boolean result) {super.onPostExecute(result);String showMessage = "";if (mIsEncrypt) {showMessage = result ? "加密已完成" : "加密失败!";} else {showMessage = result ? "解密完成" : "解密失败!";}mShowMessage.setText(showMessage);}}}

main.xml

<?xml version="1.0" encoding="utf-8"?>                                

注:许多代码来自网络,但已记不清最初来自哪里了。


更多相关文章

  1. android之bundle传递数据--两个activities之间
  2. Android Network数据存储
  3. Android中JSON数据格式的简单使用
  4. android 数据库操作
  5. android 文件名长度限制
  6. 通过Intent在Activity|Service之间传递数据
  7. android读取usb设备数据
  8. Android SharedPreferences数据存储
  9. Android使用HttpURLConnection获取数据

随机推荐

  1. android客户端访问网络工具类
  2. Android中onSaveInstanceState和onRestor
  3. Android英语单词记忆程序源码
  4. Android 不能生成R文件,gen目录为空
  5. android代码混淆后gson无法解析问题
  6. android 闪屏页面
  7. android 之关卡锁的实现
  8. Android源码分析-消息队列和Looper
  9. android之PopUpWindow显示Listview(文件列
  10. android使用webview加载网页