浅谈android数据存储加密

来源:互联网 发布:安广网络含山分公司 编辑:程序博客网 时间:2024/06/04 17:55

写在开头

在移动端的开发中,数据安全的问题一直是大家备受关注的,数据加密技术也受到了大家的青睐。项目中也用到了一些,在这里学习总结下,完善下自己的知识,也分享给大家,一起交流!(末尾也会说下自己在项目中的使用)

  • 按可逆性:加密可分为可逆算法不可逆算法
  • 按对称性:加密可分为对称算法非对称算法

一般的加密分为以下几种,下面会分别简单讲解原理和其使用方法:

  • Base64编码算法 (可逆)
  • MD5加密 (不可逆)(还有一个sha1值,可能做过支付宝的会比较熟悉)
  • Des加密 (对称,可逆)
  • Aes加密 (对称,可逆)
  • Rsa加密(非对称,可逆)

浅谈对称非对称

对称

对称加密算法是较传统的加密体制,即通信双方在加/解密过程中使用他们共享的单一密钥,鉴于其算法简单加密速度快的优点,目前仍然在使用,但是安全性方面就差一点可能。最常用的对称密码算法是DES算法,而DES密钥长度较短,已经不适合当今分布式开放网络对数据加密安全性的要求。一种新的基于Rijndael算法(自己脑补,本人也不太熟悉)对称高级数据加密标准AES取代了数据加密标准DES,弥补了DES的缺陷,目前使用比较多一点

非对称

非对称加密由于加/解密钥不同(公钥加密,私钥解密),密钥管理简单,得到了很广泛的应用。RSA是非对称加密系统最著名的公钥密码算法。但是由于RSA算法进行的都是大数计算,使得RSA最快的情况也比AES慢上倍,这是RSA最大的缺陷。但是其安全性较高,这也是大家比较喜欢的地方吧!

算法讲解及使用

Base64算法

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值跑字典。所以常常采用对数据进行多次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)

对称加密秘钥是唯一的,加密解密都是一个秘钥。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算法是最流行的公钥密码算法,使用长度可以变化的密钥。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所遇问题

关于加密填充方式:之前以为上面这些操作就能实现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/ 李总写代码