android 5.0 AES加密实现

可以参考的资料:http://blog.youkuaiyun.com/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也不太适用:

  1. // 加密算法,加密模式和填充方式三部分或指定加密算  
  2.  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:


    
    

    
    

    
     
     

    
     
     

        
      
      
    
     
     

    
     
     
    
     
     

    
     
     

    
     
     

    
     
     


    
    
   
   

最近,我忙里偷闲地将原先的AES 4.2 版 加密解密软件作了一次较大的改动。并将其发布在此与大家分享。 在AES 5.0加密解密软件中在原先AES 4.2 版 加密解密软件的基础上作出了以下改进: 1、修改了几次信息提示的错误。 2、对软件的速度进行了再一次的提升,一般况下其速度不会低于16MB/s(兆每秒)。 3、新增了直接将文件或文件夹拖动到编辑框或列表框中来设置相应的路径的功能,以方便用户使用。 4、新增了更换皮肤这一功能,当然您也可以在选择Skins目录下的其它皮肤作为本软件运行时的皮肤。 5、新增了加密解密字符串中加密字符串后可以将所得到的密文导出到文本文档的功能,以利用所得到的密文来作为密钥或是初始向量来对文件进行加密解密,使得加密解密字符串这一功能可以得到充分的利用。 6、新增了自动关联文件类型(共两种文件类型:“.cryp”、“.key”)的功能(在Vista 下需要以管理员权限来运行软件才能真正地关联文件类型)。使得文件的相应的文件在您的电脑里不再是以未知类型文件的图标来显示;当然,您可以更换skins目录下的“CrypedFileIcon.ico”与“KeyFileIcon.ico”文件来更改图标。使您可以在Windows系统的资料管理器下双击“.cryp”的文件时会自动打开软件并切换到加密解密单文件的页面同时设置好“待解密文件路径”与“解密后文件路径”这两个参数。 当然,本版本软件保存了本系列软件的早前版的所有功能。 注: 1、若在您的机器上无法关联文件类型的话,是因为您在进行本软件时所使用的用户的权限不够,无法在注册表的HKEY_CLASSES_ROOT根键下写入文件类型的注册信息,在Vista 下需要以管理员权限来运行软件才能真正地关联文件类型。 2、本软件所使用的几个图标与皮肤均来自网上,原作者拥有该类文件的版权。 3、本版本软件在运行时需要与本软件同一目录里的Skins目录下的动态链接库“Skin.dll”的支持;否则,本版本的软件将无法运行。 4、最早的版本(从2.0开始) AES 加密解密软件可在http://www.mathmagic.cn/bbs/read.php?tid=10054下载获得,且先前的4.1与4.2版 AES 加密解密软件可在http://www.mathmagic.cn/bbs/read.php?tid=10489&fpage=2下载获得。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值