
前言
作为一个Android开发者,通常都要对一些重要的内容进行加密,以免数据被他人窃取,造成不可必要的损失。加密方式有很多种,在这里我只介绍一种,也是Android开发中经常用到的对称加密算法AES。
AES发展史和算法
关于AES的发展和算法详解,大家有兴趣的话可以参阅这篇文章:http://blog.csdn.net/lrwwll/article/details/78069013
什么是AES?
AES是高级加密标准(英语:Advanced Encryption Standard,缩写:AES)。
在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
对称加密和非对称加密
- 对称加密
所谓的对称加密就是采用单钥密码系统的加密方式,同一个密钥可以同时用作数据的加密与解密,由于其速度快,对称加密通常在消息发送方需要加密大量数据时使用,也被称作单密钥加密,在对称加密算法中,数据发信方将明文(原始数据)和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。- 非对称加密
非对称加密在加密的过程中使用一对密钥,而不像对称加密只使用一个单独的密钥。一对密钥中一个用于加密,另一个用来解密。如用A加密,则用B解密;如果用B加密,则要用A解密。
在JAVA中如何使用?
在Java中使用AES进行加解密的逻辑大致如下:
- 生成/获取密钥
- 加/解密
下面来看看详细的实现过程:
生成密钥
-
构建KeyGenerator对象
在Java Api文档的javax.cropto包下专门为开发者提供了一套用于加密操作的类和接口,此包下有一个类KeyGenerator,此类提供了(对称)密钥生成器的功能,并通过静态方法getInstance(...)
来获取该对象,代码如下:
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
该静态方法中需要传入指定加密算法的名称,当然,还有其他算法。例如:
Blowfish、DES、DESede、HmacMD5或HmacSHA1等,这些算法都可以实现加密。
-
初始化密钥生成器
keyGenerator.init(128, new SecureRandom(password.getBytes()));
通过查看接口文档,我发现此方法有三个,这里我直接使用参数最多的方法。
首先看第一个参数keysize
,看名字就知道此参数指定了密钥的长度(单位:bit),而不同算法的密钥长度是有规定的,AES可以是128,192或256,但是JDK加密库对密钥长度限制在128位以下,所以这里直接传128,传其它则会抛出异常;如果需要在AES中使用128位以上密钥加密时,必须下载扩展的加密库包,下载地址:
http://blog.csdn.net/shangpusp/article/details/7416603
第二个参数要传一个SecureRandom
对象,此对象实现了真正意义上的随机,在它的构造方法中可以指定传一个字节数组,这里称为种子,其实就是生成安全随机数序列的初始值,用来生成用户指定的随机数序列,同一个种子,生成的随机数序列是一模一样的。
-
生成AES专用密钥
SecretKey secretKey = keyGenerator.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec aesKeySpec = new SecretKeySpec(enCodeFormat, "AES");
上面的generateKey(...)
方法主要用来生成一个新的对称密钥,而下面的 SecretKeySpec
则是它的实现类,这里我指定生成了一个AES专用密钥,参数传入此密钥的字节数组格式和密钥的名称(名字自己定),下面是密钥生成的完整代码:
/**
*
* 生成AES密钥
* @param password 指定随机源的种子
* @return
*/
public static SecretKeySpec createKey(String password) {
try {
//指定加密算法的名称为AES,
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
//初始化密钥生成器,指定密钥的长度(单位:bit),
//SecureRandom是生成安全随机数序列
SecureRandom secureRandom = new SecureRandom(password.getBytes());
keyGenerator.init(128, secureRandom);
//生成原始对称密钥
SecretKey secretKey = keyGenerator.generateKey();
//返回编码格式的密钥
byte[] enCodeFormat = secretKey.getEncoded();
//根据字节数组生成AES专用密钥
SecretKeySpec aesKeySpec = new SecretKeySpec(enCodeFormat, "AES");
//返回AES密钥
return aesKeySpec;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
加/解密
-
加密
/**
* 明文加密
*
* @param key AES密钥
* @param content 加密前的原内容
* @return
*/
public static String encrypt(SecretKeySpec key, String content) {
try {
//根据指定算法生成密码器
Cipher ciper = Cipher.getInstance("AES");
//初始化密码器,
// 第一个参数为密码的操作模式:加密(ENCRYPT_MODE),解密(DECRYPT_MODE)
//第二个参数为AES密钥
ciper.init(Cipher.ENCRYPT_MODE,key);
//获取加密内容的字节数组
byte[] contentBytes = content.getBytes("utf-8");
byte[] aesBytes = ciper.doFinal(contentBytes);
//为了避免解密时数据丢失,将加密后的内容进行Base64编码后再返回
String aesContent = Base64.encodeToString(aesBytes,Base64.DEFAULT);
//将加密后的密文转为字符串返回
return aesContent;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
-
解密
/**
* 密文解密
*
* @param key AES密钥
* @param content 加密后的内容
* @return
*/
public static String decrypt(SecretKeySpec key, String content) {
try {
//根据指定算法生成密码器
Cipher cipher = Cipher.getInstance("AES");
//初始化密码器,
// 第一个参数为密码的操作模式:加密(ENCRYPT_MODE),解密(DECRYPT_MODE)
//第二个参数为AES密钥
cipher.init(Cipher.DECRYPT_MODE, key);
//先将密文进行Base64解码
byte[] aesBytes = Base64.decode(content,Base64.DEFAULT);
//将密文进行解密
byte[] contentBytes = cipher.doFinal(aesBytes);
//将解密后的内容转成字符串并返回
return new String(contentBytes,"utf-8");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
上面就是加解密的代码了,不过需要注意加解密的密钥,也就是上面这两个方法中的第一个参数SecretKeySpec
必须是同一个,否则解密会失败。另外也可以根据自己的需求将其保存在本地。
最后在Activity中使用这三个方法:
public class MainActivity extends AppCompatActivity {
private TextView oldView, encryView, decryView;
private SecretKeySpec key; //密钥
private String password = "123456";//随机源种子
private String content = "Hello,玩安卓";//原始内容
private String encryContent;//加密后
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
oldView = (TextView) findViewById(R.id.old_content);
encryView = (TextView) findViewById(R.id.encry_content);
decryView = (TextView) findViewById(R.id.decry_content);
oldView.setText("原内容 : " + content);
//生成密钥
key = AesEncryptionUtils.createKey(password);
findViewById(R.id.btn_encry).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//加密
encryContent = AesEncryptionUtils.encrypt(key,content);
encryView.setText("加密后 :" + encryContent);
}
});
findViewById(R.id.btn_decry).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//解密
String decryContent = AesEncryptionUtils.decrypt(key,encryContent);
decryView.setText("解密后 :" + decryContent);
}
});
}
}
效果展示:

结语
如果大家还有什么问题,可以在下面留言,另外推荐一个安卓技术交流群:591683946,大家可以互相学习,共同进步。
网友评论