AES加密

AES(Advanced Encrypt Security)加密属于对称加密,即密钥既用来加密也用来解密。所以密钥必须保存在一个非常安全的地方,如果丢失,攻击者就可以利用其进行数据解密。

AES加密属于分组加密,即一次处理若干个数据位(128bit);128bit的明文会产生同样长度的密文。一次处理一个数据位的称为流加密。

AES加密的密钥可以从0-256bit。

当然,Android还支持其他的加密方式,如 Camellia、Blowfish、Twofish;每一种加密的分组长度及密钥长度都不同。但是实现加密与解密的步骤与AES加密基本一致。


下面展示了使用AES进行加密和解密的过程:


package com.example.aesdemo;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;

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.SecretKeySpec;

public class Crypto {
	
	private static final int KEYSIZE = 128;
	
	public static byte[] encrypt(byte[] key,byte[] data){
		
		SecretKeySpec secretKeySpec=new SecretKeySpec(key, "AES");
		Cipher cipher;
		byte[] cipherText=null;
		
		try {
			cipher=Cipher.getInstance("AES"); // create a new Cipher
			//initialize cipher
			cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); 
			//encrypt or decrypt according the opmode when initialize
			cipherText=cipher.doFinal(data);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return cipherText;
	}
	
	public static byte[] decrypt(byte[] key,byte[] data){
		
		SecretKeySpec secretKeySpec=new SecretKeySpec(key, "AES");
		Cipher cipher;
		byte[] cipherText=null;
		
		try {
			cipher=Cipher.getInstance("AES");
			cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
			cipherText=cipher.doFinal(data);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return cipherText;
	}

	public static byte[] generateKey(byte[] randomNumberSeed){
		
		SecretKey secretKey=null;
		
		try {
			KeyGenerator keyGenerator=KeyGenerator.getInstance("AES");
			SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
			random.setSeed(randomNumberSeed);
			keyGenerator.init(KEYSIZE, random);
			secretKey=keyGenerator.generateKey();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		return secretKey.getEncoded();
	}

}


其中,

SecretKey  对称密钥。使用getEncoded()将密钥转化为字节数组。

SecretKeySpec:此类通过字节数组生成一个SecretKey:new SecretKeySpec(key,"AES")。

SecureRandom  强加密随机生成器。SecureRandom.getInstance("SHA1PRNG"),生成使用SHA1PRNG算法的加密随机生成器。random.setSeed(randomNumberSeed);重新设置此随机对象的种子。给定种子补充而不是取代现有的种子。因此,保证重复调用不会降低随机性。

KeyGenerator 对程密钥生成器。可以使用KeyGenerator.getInstance("AES")进行初始化。

keyGenerator.init(KEYSIZE, random); 初始化KeyGenerator,keysize是密钥长度,random即随机生成器。

通过keyGenerator.generateKey();来获取密钥。

Cipher  用于加密和解密的类。

Cipher.init();用来初始化。下面列举部分初始化方法:

public final void init(int opmode,Key key)  throwsInvalidKeyException

opmode:传入初始化Cipher的操作:加密、解密、密钥包装或密钥解包

key:密钥

public final void init(int opmode,Key key,SecureRandom random) throws InvalidKeyException :

random : 随机源,可用于参数生成。

public final void init(int opmode, Key key,AlgorithmParameterSpec params)throwsInvalidKeyException,InvalidAlgorithmParameterException

params :初始化向量。


上文的代码中使用的密钥长度为128bit,但在实际应用中推荐使用算法支持的最大密钥长度,这样可以使得对你的密钥的暴力破解变得更难。


我们知道分组加密是对明文按照固定长度的分组进行加密,这就会存在一个数据填充的问题。有两种数据填充的方法:

1、补零:位数不够的用0补充。

2、PKCS5/7:取需要填充的位数作为填充内容。如,还有10位要填充,则填充内容位0A。


分组密码有以下集中模式:

ECB模式(电子密码本):一个明文数据块加密成一个密文数据块,下一个明文数据块加密成下一个密文数据块;这种加密比较简单,而且,如果存在相同的密文数据块就很容易推断出使用了ECB加密,这样破解者就只用解密部分密文,而不必解密整个密文。

CBC模式(密码分组链接):使用一个初始化向量与第一组明文进行异或操作,然后进行加密,然后再将密文作为向量与下一组明文进行异或操作。这样保证了每组密文依赖它前面的所有明文。

PCBC(填充密码分组)

CFB(密码反馈)

OFB(输出反馈)


下面介绍使用CBC模式和PKCS5Padding进行填充的加密和解密,其中,系统默认使用的是 AES/CBC/PKCS5Padding:

package com.dream.cipherdemo;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import android.content.Context;
import android.util.Log;

public class Crypto {
	private static final String TAG = Crypto.class.getSimpleName();
	private static final String transformation = "AES/CBC/PKCS5Padding";
	private static final String algorithm = "AES";
	private Context context;

	public Crypto(Context context) {
		super();
		this.context = context;
	}

	private byte[] cipher(int mode,byte[] data){
		byte[] result=null;
		KeyManager keyManager=new KeyManager(context);
		SecretKeySpec secretKeySpec=new SecretKeySpec(keyManager.getKey(), 
				algorithm);
		IvParameterSpec params=new IvParameterSpec(keyManager.getIv());

		try {
			Cipher cipher=Cipher.getInstance(transformation);
			cipher.init(mode, secretKeySpec, params);
			result=cipher.doFinal(data);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidAlgorithmParameterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		return result;		
	}
	
	public byte[] encrypt(byte[] data){
		return cipher(Cipher.ENCRYPT_MODE, data);
	}
	
	public byte[] decrypt(byte[] data){
		return cipher(Cipher.DECRYPT_MODE, data);
	}

}

我这里将密钥和向量分别保存在文件中,密文保存在另一个文件中,这样虽然存在直接攻击的危险,但是可以使我们免受非直接攻击。

源码下载










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值