您的位置:首页 > 移动开发 > Android开发

Android AES 文件加密解密

2016-03-30 12:31 597 查看
几番折磨终有结果,现将Demo整理出来。。。

[java] view
plain copy







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));

}

}

1. 其实我的Demo中只用到了AESCipher和getRawKey两个方法。若要返回字符串,一定要注意编码,如

[html] view
plain copy







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字节时,加密后数据长度不变。

[html] view
plain copy







算法/模式/填充 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

[java] view
plain copy







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

[html] view
plain copy







<?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>

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

转载地址:/article/2421320.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: