android 加解密的速度和什么有关系,Android 加解密类Cipher

近日在做一个关于短信及文件加解密的小项目,查看了一些Android加解密方面的知识。关于加解密这部分以前完全没有接触过,所以网上乱翻了一天对于什么DES,AES,RSA,BASE64,MD5之类的还是懵懵懂懂,这里也就不再来说它们的原理了,实在是我自己也没弄懂。写这篇博客时也就大致了解了一下Cipher类,并实现了一个简单的AES编解码工具类,当然是用的都是些最简单的默认模式,更详细的知识还有待进一步的学习。 此博文主要是复习记录一下一天学习所得,加深印象。

Android的Cipher类其实是来自于Java的加密环境JCE,查看官方的文档就可以看到: javax.crypto.Cipher。当然据说这个类也不在官方的正式JDK里面,而且有些解密方法在Android中不可用(这部分不敢下结论,加密的东西我也刚刚开始了解)。下面就来具体看一下实现AES加密的过程吧。

Step1:实现一个简单XML文件做测试界面,实现一个EditText,两个Button,一个TextView显示结果:

android:id="@+id/et_input"

android:layout_width="match_parent"

android:layout_height="wrap_content" />

android:id="@+id/btn_encrypt"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="加密"/>

android:id="@+id/btn_decrypt"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="解密"/>

android:id="@+id/tv_output"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

Step2:Activity代码就不贴出来了,无非就是获得上面4个控件,然后监听加密、解密两个按钮执行加解密工具类的加解密方法。

Step3:实现加解密工具类,这也是此篇博文的关键。下面来一步一步的分析:

先来看看官方给出的加密大致流程:Use a secure random number generator,, to initialize any cryptographic keys,.  这句话大致意思就是使用一个安全的随机数来产生一个密匙。当然这个密匙是我们用来加密使用的。把这段话转换成一段代码如下:

private static byte[] getRawKey(byte[] seed) throws NoSuchAlgorithmException{

SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); // 获得一个随机数,传入的参数为默认方式。

sr.setSeed(seed);  // 设置一个种子,这个种子一般是用户设定的密码。也可以是其它某个固定的字符串

KeyGenerator keyGen = KeyGenerator.getInstance("AES");  // 获得一个key生成器(AES加密模式)

keyGen.init(128, sr);      // 设置密匙长度128位

SecretKey key = keyGen.generateKey();  // 获得密匙

byte[] raw = key.getEncoded();   // 返回密匙的byte数组供加解密使用

return raw;

}

当我们通过上面的方法获得密匙后,就可以使用Cypher提供的方法来进行加解密操作了。注意Cypher无法直接获得实例,只能通过getInstance方法来获得实例:

This class provides access to implementations of cryptographic ciphers for encryption and decryption. Cipher classes can not be instantiated directly, one has to call the Cipher's getInstance method with the name of a requested transformation, optionally with a provider. A transformation specifies an operation (or a set of operations) as a string in the form:

"algorithm/mode/padding"or

"algorithm"

这里指出在获得Cypher实例时需要使用getInstance方法,且必须传入一个参数,这个参数包括加密算法,加密模式和填充方式三部分,当然也可以只指定加密算法,从而使用默认的模式和填充方式。此次我的设计传入"AES"参数,那么系统会默认使用"AES/CFB8/NoPadding"来做为这三部分的设置参数。当获得Cypher实例以后,就可以调用它的一些方法来进行加解密,具体实现如下:

private static byte[] encry(byte[] raw, byte[] input) throws Exception {  // 加密

SecretKeySpec keySpec = new SecretKeySpec(raw, "AES"); // 根据上一步生成的密匙指定一个密匙(密匙二次加密?)

Cipher cipher = Cipher.getInstance("AES");  // 获得Cypher实例对象

cipher.init(Cipher.ENCRYPT_MODE, keySpec);  // 初始化模式为加密模式,并指定密匙

byte[] encode = cipher.doFinal(input);  // 执行加密操作。 input为需要加密的byte数组

return encode;                         // 返回加密后的密文(byte数组)

}

private static byte[] decry(byte[] raw, byte[] encode) throws Exception{ // 解密

SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");

Cipher cipher = Cipher.getInstance("AES");

cipher.init(Cipher.DECRYPT_MODE, keySpec);   // 解密的的方法差不多,只是这里的模式不一样

byte[] decode = cipher.doFinal(encode);  // 加解密都通过doFinal方法来执行最终的实际操作

return decode;

}

最后再对上述两个方法进行一次封装,提供对外的接口即可完成此工具类的简单功能。加解密工具要求实现字符串明文输入,字符串密文输出,下面来看看具体实现代码:

public static String decryptString(String seed,byte[] encode) throws Exception{

byte[] raw = getRawKey(seed.getBytes());

byte[] decode = decry(raw, encode);

return new String(decode);

}

public static byte[] encryptString(String seed, String input) throws Exception{

byte[] raw = getRawKey(seed.getBytes());

byte[] encode = encry(raw, input.getBytes());

// return new String(encode);  // 这里返回加密后的密文字符串是有问题的,后面再解释。

returnencode;

}

上面代码中基本实现了需求,只不过加密时输出的是byte数组,解密时输入也是byte数组。当然这样对称设计在同一个应用里是不会出现问题的,不过传输时大家都习惯将byte数组转成字符串的格式,而在byte转字符串时会因为字符编码的原因出现一些问题,下面就来看看如何来解决加解密使用字符串密文异常问题。

大家都知道,计算机存储字符使用ASCII码形式,当出现中文等字符时将无法使用一个8位的字符来表示,国际通用标准UNICODE使用16位来表示,但不同语种已经实现了大量不同的编码格式,而我们的加密算法是针对byte数组来进行的,加密后的byte数组将无法使用某种字符编码来转成字符串,所以这里将加密后的byte数组转换成字符串时将出现问题。

这里再举一个简单的例子来说明会出现何种问题,大家可以使用下面这段代码来跟踪一下具体实现效果:

byte[] b = -120;

String str = new String(b);

byte[] bytes = str.getBytes();

大家执行一下这段代码,看看bytes得到是什么? 我想不用我说,现在应该都知道为什么转换会出现问题了吧。这对这种情况,我们需要在byte与String转换时进行一个byte转hex,以及hex转byte的中转操作。具体代码如下:

private static String bytesToHexString(byte[] src){

StringBuilder stringBuilder = new StringBuilder("");

if (src == null || src.length <= 0) {

return null;

}

for (int i = 0; i < src.length; i++) {

int v = src[i] & 0xFF;

String hv = Integer.toHexString(v);

if (hv.length() < 2) {

stringBuilder.append(0);

}

stringBuilder.append(hv);

}

return stringBuilder.toString();

}

private static byte[] hexStringToBytes(String hexString) {

if (hexString == null || hexString.equals("")) {

return null;

}

hexString = hexString.toUpperCase();

int length = hexString.length() / 2;

char[] hexChars = hexString.toCharArray();

byte[] d = new byte[length];

for (int i = 0; i < length; i++) {

int pos = i * 2;

d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));

}

return d;

}

private static byte charToByte(char c) {

return (byte) "0123456789ABCDEF".indexOf(c);

}

到这里就完美解决了转换问题,把上述前面封装的两个接口中标注红色的部分分别进行转换即可达到进出皆为String的目的。 AES算法的简单实现到这里就完成了,关于加解密方面的知识后续还需要抽时间再好好补一下。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值