几番折磨终有结果,现将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 <span style="color:#FF0000;">AESCipher</span>(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[] <span style="color:#FF0000;">getRawKey</span>(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));
- }
- }
- 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
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;
- @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<Void, Void, Boolean> {
- 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
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/textView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="请输入文件名" />
- <EditText
- android:id="@+id/file_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="请输入文件名" >
- <requestFocus />
- </EditText>
- <EditText
- android:id="@+id/new_file_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="请输入新的文件名" >
- </EditText>
- <Button
- android:id="@+id/encrypt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="加密" />
- <Button
- android:id="@+id/decrypt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="解密" />
- <TextView
- android:id="@+id/message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="TextView" />
- </LinearLayout>
注:许多代码来自网络,但已记不清最初来自哪里了。
转载地址:http://blog.csdn.net/zjclugger/article/details/34838447