在移动端的开发中,数据安全的问题一直是大家备受关注的,数据加密技术也受到了大家的青睐。项目中也用到了一些,在这里学习总结下,完善下自己的知识,也分享给大家,一起交流!(末尾也会说下自己在项目中的使用)
对称加密算法是较传统的加密体制,即通信双方在加/解密过程中使用他们共享的单一密钥,鉴于其算法简单和加密速度快的优点,目前仍然在使用,但是安全性方面就差一点可能。最常用的对称密码算法是DES算法,而DES密钥长度较短,已经不适合当今分布式开放网络对数据加密安全性的要求。一种新的基于Rijndael算法(自己脑补,本人也不太熟悉)对称高级数据加密标准AES取代了数据加密标准DES,弥补了DES的缺陷,目前使用比较多一点
非对称加密由于加/解密钥不同(公钥加密,私钥解密),密钥管理简单,得到了很广泛的应用。RSA是非对称加密系统最著名的公钥密码算法。但是由于RSA算法进行的都是大数计算,使得RSA最快的情况也比AES慢上倍,这是RSA最大的缺陷。但是其安全性较高,这也是大家比较喜欢的地方吧!
Base64其实并不是安全领域的加密算法,因为它的加密解密算法都是公开的,典型的防菜鸟不防程序猿的例子哈哈,Base64编码本质上是一种将二进制数据转成文本数据的方案。用处就是将一些不适合传输的数据内容进行编码来适合传输。
字符串进行Base64编码
- String encodedString = Base64.encodeToString("wenwen".getBytes(), Base64.DEFAULT);
- 第一个参数就是字节数组
字符串进行Base64解码
- String decodedString = new String(Base64.decode(encodedString, Base64.DEFAULT));
- decodedString就是wenwen
它是一种单向加密算法,只能加密、无法解密。多用于密码的存储等等。对于MD5的安全性,网上有关MD5解密的网站数不胜数,破解机制采用穷举法,就是手机所有可能的MD5值跑字典。所以常常采用对数据进行多次MD5加密或者采取加盐(就是加一段独有的字符串在进行加密)的操作。
- public static String md5(String string) {
- if (TextUtils.isEmpty(string)) {
- return "";
- }
- MessageDigest md5 = null;
- try {
- md5 = MessageDigest.getInstance("MD5");
- byte[] bytes = md5.digest(string.getBytes());
- String result = "";
- for (byte b: bytes) {
- String temp = Integer.toHexString(b & 0xff);
- if (temp.length() == 1) {
- temp = "0" + temp;
- }
- result += temp;
- }
- return result;
- } catch(NoSuchAlgorithmException e) {
- e.printStackTrace();
- }
- return "";
- }
对称加密秘钥是唯一的,加密解密都是一个秘钥。AES速度上占优于RSA,但是只有一个秘钥,安全性较低一些。
- //常量介绍
- private final static String HEX = "0123456789ABCDEF";
- //AES是加密方式 CBC是工作模式 PKCS5Padding是填充模式
- private static final String CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";
- //AES 加密
- private static final String AES = "AES";
- // SHA1PRNG 强随机种子算法, 要区别4.2以上版本的调用方法
- private static final String SHA1PRNG = "SHA1PRNG";
- //生成随机数,可以当做动态的密钥 加密和解密的密钥必须一致,不然将不能解密
- public static String generateKey() {
- try {
- SecureRandom localSecureRandom = SecureRandom.getInstance(SHA1PRNG);
- byte[] bytes_key = new byte[20];
- localSecureRandom.nextBytes(bytes_key);
- String str_key = toHex(bytes_key);
- return str_key;
- } catch(Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- // 对密钥进行处理
- private static byte[] getRawKey(byte[] seed) throws Exception {
- KeyGenerator kgen = KeyGenerator.getInstance(AES);
- //for android
- SecureRandom sr = null;
- // 在4.2以上版本中,SecureRandom获取方式发生了改变
- if (android.os.Build.VERSION.SDK_INT >= 17) {
- sr = SecureRandom.getInstance(SHA1PRNG, "Crypto");
- } else {
- sr = SecureRandom.getInstance(SHA1PRNG);
- }
- // for Java
- // secureRandom = SecureRandom.getInstance(SHA1PRNG);
- sr.setSeed(seed);
- kgen.init(128, sr); //256 bits or 128 bits,192bits
- //AES中128位密钥版本有10个加密循环,192比特密钥版本有12个加密循环,256比特密钥版本则有14个加密循环。
- SecretKey skey = kgen.generateKey();
- byte[] raw = skey.getEncoded();
- return raw;
- }
- //加密
- public static String encrypt(String key, String cleartext) {
- if (TextUtils.isEmpty(cleartext)) {
- return cleartext;
- }
- try {
- byte[] result = encrypt(key, cleartext.getBytes());
- return Base64Encoder.encode(result);
- } catch(Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- private static byte[] encrypt(String key, byte[] clear) throws Exception {
- byte[] raw = getRawKey(key.getBytes());
- SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
- Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
- cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
- byte[] encrypted = cipher.doFinal(clear);
- return encrypted;
- }
- /*
- * 解密
- */
- public static String decrypt(String key, String encrypted) {
- if (TextUtils.isEmpty(encrypted)) {
- return encrypted;
- }
- try {
- byte[] enc = Base64Decoder.decodeToBytes(encrypted);
- byte[] result = decrypt(key, enc);
- return new String(result);
- } catch(Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- /*
- * 解密
- */
- private static byte[] decrypt(String key, byte[] encrypted) throws Exception {
- byte[] raw = getRawKey(key.getBytes());
- SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
- Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
- cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
- byte[] decrypted = cipher.doFinal(encrypted);
- return decrypted;
- }
- }
RSA算法是最流行的公钥密码算法,使用长度可以变化的密钥。RSA是第一个既能用于数据加密也能用于数字签名的算法。它在很多密码协议中都有应用,如SSL和S/MIME。RSA算法是基于大质数的因数分解的公匙体系。简单的讲,就是两个很大的质数,一个作为公钥,另一个作为私钥,如用其中一个加密,则用另一个解密。密钥长度从40到2048位可变,密钥越长,加密效果越好,但加密解密的开销也大。所以他在加密的速度上回小于AES等对称加密。
- //全局变量
- public static final String RSA = "RSA";// 非对称加密密钥算法
- public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";//加密填充方式
- public static final int DEFAULT_KEY_SIZE = 2048;//秘钥默认长度
- public static final byte[] DEFAULT_SPLIT = "#PART#".getBytes(); // 当要加密的内容超过bufferSize,则采用partSplit进行分块加密
- public static final int DEFAULT_BUFFERSIZE = (DEFAULT_KEY_SIZE / 8) - 11;// 当前秘钥支持加密的最大字节数
- /**
- * 随机生成RSA密钥对
- * @param keyLength 密钥长度,范围:512~2048 一般1024
- */
- public static KeyPair generateRSAKeyPair(int keyLength) {
- try {
- KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
- kpg.initialize(keyLength);
- return kpg.genKeyPair();
- } catch(NoSuchAlgorithmException e) {
- e.printStackTrace();
- return null;
- }
- }
- /**
- * 用公钥对字符串进行加密
- * @param data 原文
- */
- public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
- // 得到公钥
- X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
- KeyFactory kf = KeyFactory.getInstance(RSA);
- PublicKey keyPublic = kf.generatePublic(keySpec);
- // 加密数据
- Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
- cp.init(Cipher.ENCRYPT_MODE, keyPublic);
- return cp.doFinal(data);
- }
- /**
- * 使用私钥进行解密
- */
- public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception {
- // 得到私钥
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
- KeyFactory kf = KeyFactory.getInstance(RSA);
- PrivateKey keyPrivate = kf.generatePrivate(keySpec);
- // 解密数据
- Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
- cp.init(Cipher.DECRYPT_MODE, keyPrivate);
- byte[] arr = cp.doFinal(encrypted);
- return arr;
- }
- /**
- * 私钥加密
- * @param data 待加密数据
- * @param privateKey 密钥
- * @return byte[] 加密数据
- */
- public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey) throws Exception {
- // 得到私钥
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
- KeyFactory kf = KeyFactory.getInstance(RSA);
- PrivateKey keyPrivate = kf.generatePrivate(keySpec);
- // 数据加密
- Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
- cipher.init(Cipher.ENCRYPT_MODE, keyPrivate);
- return cipher.doFinal(data);
- }
- /**
- * 公钥解密
- *
- * @param data 待解密数据
- * @param publicKey 密钥
- * @return byte[] 解密数据
- */
- public static byte[] decryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
- // 得到公钥
- X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
- KeyFactory kf = KeyFactory.getInstance(RSA);
- PublicKey keyPublic = kf.generatePublic(keySpec);
- // 数据解密
- Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
- cipher.init(Cipher.DECRYPT_MODE, keyPublic);
- return cipher.doFinal(data);
- }
关于加密填充方式:之前以为上面这些操作就能实现rsa加解密,以为万事大吉了,呵呵,这事还没完,悲剧还是发生了,Android这边加密过的数据,服务器端死活解密不了,原来android系统的RSA实现是”RSA/None/NoPadding”,而标准JDK实现是”RSA/None/PKCS1Padding” ,这造成了在android机上加密后无法在服务器上解密的原因,所以在实现的时候这个一定要注意。
实现分段加密:搞定了填充方式之后又自信的认为万事大吉了,可是意外还是发生了,RSA非对称加密内容长度有限制,1024位key的最多只能加密127位数据,否则就会报错(javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes) ,RSA 是常用的非对称加密算法。最近使用时却出现了“不正确的长度”的异常,研究发现是由于待加密的数据超长所致。RSA 算法规定:待加密的字节数不能超过密钥的长度值除以 8 再减去 11(即:KeySize / 8 - 11),而加密后得到密文的字节数,正好是密钥的长度值除以 8(即:KeySize / 8)。
- /**
- * 用公钥对字符串进行分段加密
- *
- */
- public static byte[] encryptByPublicKeyForSpilt(byte[] data, byte[] publicKey) throws Exception {
- int dataLen = data.length;
- if (dataLen <= DEFAULT_BUFFERSIZE) {
- return encryptByPublicKey(data, publicKey);
- }
- List < Byte > allBytes = new ArrayList < Byte > (2048);
- int bufIndex = 0;
- int subDataLoop = 0;
- byte[] buf = new byte[DEFAULT_BUFFERSIZE];
- for (int i = 0; i < dataLen; i++) {
- buf[bufIndex] = data[i];
- if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) {
- subDataLoop++;
- if (subDataLoop != 1) {
- for (byte b: DEFAULT_SPLIT) {
- allBytes.add(b);
- }
- }
- byte[] encryptBytes = encryptByPublicKey(buf, publicKey);
- for (byte b: encryptBytes) {
- allBytes.add(b);
- }
- bufIndex = 0;
- if (i == dataLen - 1) {
- buf = null;
- } else {
- buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)];
- }
- }
- }
- byte[] bytes = new byte[allBytes.size()]; {
- int i = 0;
- for (Byte b: allBytes) {
- bytes[i++] = b.byteValue();
- }
- }
- return bytes;
- }
- /**
- * 使用私钥分段解密
- *
- */
- public static byte[] decryptByPrivateKeyForSpilt(byte[] encrypted, byte[] privateKey) throws Exception {
- int splitLen = DEFAULT_SPLIT.length;
- if (splitLen <= 0) {
- return decryptByPrivateKey(encrypted, privateKey);
- }
- int dataLen = encrypted.length;
- List < Byte > allBytes = new ArrayList < Byte > (1024);
- int latestStartIndex = 0;
- for (int i = 0; i < dataLen; i++) {
- byte bt = encrypted[i];
- boolean isMatchSplit = false;
- if (i == dataLen - 1) {
- // 到data的最后了
- byte[] part = new byte[dataLen - latestStartIndex];
- System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
- byte[] decryptPart = decryptByPrivateKey(part, privateKey);
- for (byte b: decryptPart) {
- allBytes.add(b);
- }
- latestStartIndex = i + splitLen;
- i = latestStartIndex - 1;
- } else if (bt == DEFAULT_SPLIT[0]) {
- // 这个是以split[0]开头
- if (splitLen > 1) {
- if (i + splitLen < dataLen) {
- // 没有超出data的范围
- for (int j = 1; j < splitLen; j++) {
- if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
- break;
- }
- if (j == splitLen - 1) {
- // 验证到split的最后一位,都没有break,则表明已经确认是split段
- isMatchSplit = true;
- }
- }
- }
- } else {
- // split只有一位,则已经匹配了
- isMatchSplit = true;
- }
- }
- if (isMatchSplit) {
- byte[] part = new byte[i - latestStartIndex];
- System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
- byte[] decryptPart = decryptByPrivateKey(part, privateKey);
- for (byte b: decryptPart) {
- allBytes.add(b);
- }
- latestStartIndex = i + splitLen;
- i = latestStartIndex - 1;
- }
- }
- byte[] bytes = new byte[allBytes.size()]; {
- int i = 0;
- for (Byte b: allBytes) {
- bytes[i++] = b.byteValue();
- }
- }
- return bytes;
- }
- /**
- * 私钥分段加密
- * @param data 要加密的原始数据
- * @param privateKey 秘钥
- */
- public static byte[] encryptByPrivateKeyForSpilt(byte[] data, byte[] privateKey) throws Exception {
- int dataLen = data.length;
- if (dataLen <= DEFAULT_BUFFERSIZE) {
- return encryptByPrivateKey(data, privateKey);
- }
- List < Byte > allBytes = new ArrayList < Byte > (2048);
- int bufIndex = 0;
- int subDataLoop = 0;
- byte[] buf = new byte[DEFAULT_BUFFERSIZE];
- for (int i = 0; i < dataLen; i++) {
- buf[bufIndex] = data[i];
- if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) {
- subDataLoop++;
- if (subDataLoop != 1) {
- for (byte b: DEFAULT_SPLIT) {
- allBytes.add(b);
- }
- }
- byte[] encryptBytes = encryptByPrivateKey(buf, privateKey);
- for (byte b: encryptBytes) {
- allBytes.add(b);
- }
- bufIndex = 0;
- if (i == dataLen - 1) {
- buf = null;
- } else {
- buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)];
- }
- }
- }
- byte[] bytes = new byte[allBytes.size()]; {
- int i = 0;
- for (Byte b: allBytes) {
- bytes[i++] = b.byteValue();
- }
- }
- return bytes;
- }
- /**
- * 公钥分段解密
- *
- * @param encrypted 待解密数据
- * @param publicKey 密钥
- */
- public static byte[] decryptByPublicKeyForSpilt(byte[] encrypted, byte[] publicKey) throws Exception {
- int splitLen = DEFAULT_SPLIT.length;
- if (splitLen <= 0) {
- return decryptByPublicKey(encrypted, publicKey);
- }
- int dataLen = encrypted.length;
- List < Byte > allBytes = new ArrayList < Byte > (1024);
- int latestStartIndex = 0;
- for (int i = 0; i < dataLen; i++) {
- byte bt = encrypted[i];
- boolean isMatchSplit = false;
- if (i == dataLen - 1) {
- // 到data的最后了
- byte[] part = new byte[dataLen - latestStartIndex];
- System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
- byte[] decryptPart = decryptByPublicKey(part, publicKey);
- for (byte b: decryptPart) {
- allBytes.add(b);
- }
- latestStartIndex = i + splitLen;
- i = latestStartIndex - 1;
- } else if (bt == DEFAULT_SPLIT[0]) {
- // 这个是以split[0]开头
- if (splitLen > 1) {
- if (i + splitLen < dataLen) {
- // 没有超出data的范围
- for (int j = 1; j < splitLen; j++) {
- if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
- break;
- }
- if (j == splitLen - 1) {
- // 验证到split的最后一位,都没有break,则表明已经确认是split段
- isMatchSplit = true;
- }
- }
- }
- } else {
- // split只有一位,则已经匹配了
- isMatchSplit = true;
- }
- }
- if (isMatchSplit) {
- byte[] part = new byte[i - latestStartIndex];
- System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
- byte[] decryptPart = decryptByPublicKey(part, publicKey);
- for (byte b: decryptPart) {
- allBytes.add(b);
- }
- latestStartIndex = i + splitLen;
- i = latestStartIndex - 1;
- }
- }
- byte[] bytes = new byte[allBytes.size()]; {
- int i = 0;
- for (Byte b: allBytes) {
- bytes[i++] = b.byteValue();
- }
- }
- return bytes;
- }
在实际开发中,不是太重要的数据用一种加密方式感觉就可以了。但是比较重要的数据建议用多种加密方式结合的方式,比如我用的RSA+AES加密。他主要解决了两个问题:1.RSA加解密速度慢,不适合大量数据文件加密 2.AES加密速度很快,但是安全性没有RSA加密方式的安全。其主要思想就是服务端生成公钥私钥,并提供接口将公钥给android端,android端生成AES秘钥,并用AES秘钥对大量数据进行加密(解决RSA加解密速度慢的问题),然后用调用接口拿到的RSA公钥对自己生成AES秘钥进行加密,客户端将得到的秘钥和通过AES加密的数据发送给服务器。(秘钥可以放在请求头中,数据放在请求体中,这个随意了)。服务拿到你的秘钥和数据后,用私钥加密得到AES秘钥,再通过秘钥得到发送的数据就好了。
这次的学习也是学习的其他大神的总结,非常感谢他们。在这里记录下来,供自己学习也想让更多的人看到,帮助更多的人。
http://blog.csdn.net/randyjiawenjie/article/details/6587986
http://blog.csdn.net/axi295309066/article/details/52491077
http://blog.csdn.net/qq_26685493/ gongyongfeng的博客
http://www.cnblogs.com/whoislcj/ 李总写代码
来源: http://blog.csdn.net/say_from_wen/article/details/77870849