安卓中,不管是内网还是外网,数据的传输首要考虑就是安全问题,尤其是用户信息,以及各种密码等敏感信息。
所以说,对数据的加密是很有必要的,尤其是当下物联网蓬勃发展的今天,数据安全尤为重要。


因此本人总结了一下安卓中几种加密方式的实现:

① MD5:一种不可逆的加密算法,常用于只需加密无需解密的数据上,比如用户密码,也常用来保证数据的完整性,因为数据被篡改后,其加密后的MD5也会随之改变,对比篡改前的MD5可确定数据是否完整;

public final static String getMD5String(String s) {

		final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
				'9', 'A', 'B', 'C', 'D', 'E', 'F' };

		if (s == null)
			return null;

		try {
			MessageDigest mdInst = MessageDigest.getInstance("MD5");
			mdInst.update(s.getBytes());
			byte[] md = mdInst.digest();
			int len = md.length;
			StringBuilder buf = new StringBuilder(len * 2);
			for (int j = 0; j < len; j++) {
				buf.append(HEX_DIGITS[(md[j] >> 4) & 0x0f]);
				buf.append(HEX_DIGITS[md[j] & 0x0f]);
			}
			return buf.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}


② DES:对称加密算法;

/**
     * 返回可逆算法DES的密钥
     *
     * @param key 前8字节将被用来生成密钥。
     * @return 生成的密钥
     * @throws Exception
     */
    public static Key getDESKey(byte[] key) throws Exception {
        DESKeySpec des = new DESKeySpec(key);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        return keyFactory.generateSecret(des);
    }

    /**
     * 根据指定的密钥及算法,将字符串进行解密。
     *
     * @param data      要进行解密的数据,它是由原来的byte[]数组转化为字符串的结果。
     * @param key       密钥。
     * @param algorithm 算法--"DES/CBC/PKCS5Padding"
     * @return 解密后的结果。它由解密后的byte[]重新创建为String对象。如果解密失败,将返回null。
     * @throws Exception
     */
    public static String decrypt(String data, Key key, String algorithm)
            throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key);
        String result = new String(cipher.doFinal(StringUtils
                .hexStringToByteArray(data)), "utf8");
        return result;
    }

    /**
     * 根据指定的密钥及算法对指定字符串进行可逆加密。
     *
     * @param data      要进行加密的字符串。
     * @param key       密钥。
     * @param algorithm 算法。
     * @return 加密后的结果将由byte[]数组转换为16进制表示的数组。如果加密过程失败,将返回null。
     */
    public static String encrypt(String data, Key key, String algorithm)
            throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return StringUtils.byteArrayToHexString(cipher.doFinal(data
                .getBytes("utf8")));
    }
    /**
     * byte[]数组转换为16进制的字符串
     *
     * @param data 要转换的字节数组
     * @return 转换后的结果
     */
    public static final String byteArrayToHexString(byte[] data) {
        StringBuilder sb = new StringBuilder(data.length * 2);
        for (byte b : data) {
            int v = b & 0xff;
            if (v < 16) {
                sb.append('0');
            }
            sb.append(Integer.toHexString(v));
        }
        return sb.toString().toUpperCase(Locale.getDefault());
    }

    /**
     * 16进制表示的字符串转换为字节数组
     *
     * @param s 16进制表示的字符串
     * @return byte[] 字节数组
     */
    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] d = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            // 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个进制字节
            d[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character
                    .digit(s.charAt(i + 1), 16));
        }
        return d;
    }



③ AES:对称加密算法,为了防止网络(包含内网和外网)传递AES加密的秘文时导致的数据丢失(last block incomplete in decryption),一般需对秘文做base64处理,加密时候需要添加一个类似bcprov-jdk的库和两个policy文件,解密则不需要,

由于客户端携带了密钥,静态的密钥存在于客户端总是不安全的,那么可以考虑ECDH公钥交换协商随机密钥,基点或者全部逻辑加密算法写在加壳的SO里,此随机密钥可作为数据aes加密的密钥,这样可保证安全,当然亦可考虑使用非对称加密算法。

AES128和AES256主要区别是密钥长度不同(分别是128bits,256bits)、加密处理轮数不同(分别是10轮,14轮),后者强度高于前者;

/** 
         * 密钥算法 
         * java6支持56位密钥,bouncycastle支持64位 
         * */ 
        public static final String KEY_ALGORITHM="AES";  
           
        /** 
         * 加密/解密算法/工作模式/填充方式 
         *  
         * JAVA6 支持PKCS5PADDING填充方式 
         * Bouncy castle支持PKCS7Padding填充方式 
         * */ 
        public static final String CIPHER_ALGORITHM="AES/ECB/PKCS7Padding";  
           
        /** 
         *  
         * 生成密钥,java6只支持56位密钥,bouncycastle支持64位密钥 
         * @return byte[] 二进制密钥 
         * */ 
        public static byte[] initkey() throws Exception{  
            return new byte[] { 0x08, 0x08, 0x04, 0x0b, 0x02, 0x0f, 0x0b, 0x0c,
                    0x01, 0x03, 0x09, 0x07, 0x0c, 0x03, 0x07, 0x0a, 0x04, 0x0f,
                    0x06, 0x0f, 0x0e, 0x09, 0x05, 0x01, 0x0a, 0x0a, 0x01, 0x09,
                    0x06, 0x07, 0x09, 0x0d };
        }
        //Base64:byte[]→String
    	public static String encryptBASE64(byte[] key) throws Exception {
    		return (new BASE64Encoder()).encodeBuffer(key);
    	}
    	//Base64:String→byte[]
    	public static byte[] decryptBASE64(String key) throws Exception {
    		return (new BASE64Decoder()).decodeBuffer(key);
    	}
        /** 
         * 转换密钥 
         * @param key 二进制密钥 
         * @return Key 密钥 
         * */ 
        public static Key toKey(byte[] key) throws Exception{ 
            //生成密钥  
            SecretKey secretKey=new SecretKeySpec(key,KEY_ALGORITHM);  
            return secretKey;  
        }  
           
        /** 
         * 加密数据 
         * @param data 待加密数据 
         * @param key 密钥 
         * @return byte[] 加密后的数据 
         * */ 
        public static byte[] encrypt(byte[] data,byte[] key) throws Exception{  
            //还原密钥  
            Key k=toKey(key);  
            /** 
             * 实例化 
             * 使用 PKCS7PADDING 填充方式,按如下方式实现,就是调用bouncycastle组件实现 
             * Cipher.getInstance(CIPHER_ALGORITHM,"BC") 
             */ 
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM, "BC");  
            //初始化,设置为加密模式  
            cipher.init(Cipher.ENCRYPT_MODE, k);  
            //执行操作  
            return cipher.doFinal(data);  
        }
       /** 
         * 解密数据 
         * @param data 待解密数据 
         * @param key 密钥 
         * @return byte[] 解密后的数据 
         * */ 
        public static byte[] decrypt(byte[] data,byte[] key) throws Exception{  
            //欢迎密钥  
            Key k =toKey(key);  
            /** 
             * 实例化 
             * 使用 PKCS7PADDING 填充方式,按如下方式实现,就是调用bouncycastle组件实现 
             * Cipher.getInstance(CIPHER_ALGORITHM,"BC") 
             */ 
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM);  
            //初始化,设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, k);  
            //执行操作  
            return cipher.doFinal(data);  
        }


④ RSA:非对称加密算法,RSA的安全性依赖于大数的分解,公钥和私钥都是两个大素数(大于100的十进制位)的函数。公钥对数据进行加密后传输,接收方用私钥进行解密,而从一个公钥和密文推断出明文的难度等同于分解两个大素数的积。由于进行的都是大数计算,使得RSA最快的情况也比DES慢上100倍,无论是软件还是硬件实现。速度一直是RSA的缺陷。一般来说只用于少量数据加密。

// 密钥对
	private KeyPair keyPair = null;

	/**
	 * 初始化密钥对
	 */
	public RSAUtil() {
		try {
			this.keyPair = this.generateKeyPair();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 生成密钥对
	 * 
	 * @return KeyPair
	 * @throws Exception
	 */
	private KeyPair generateKeyPair() throws Exception {
		try {
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			// 这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低
			final int KEY_SIZE = 1024; //1024-bit密钥----目前较流行
			keyPairGen.initialize(KEY_SIZE, new SecureRandom());
			KeyPair keyPair = keyPairGen.genKeyPair();
			return keyPair;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}

	}

	/**
	 * 生成公钥
	 * 
	 * @param modulus
	 * @param publicExponent
	 * @return RSAPublicKey
	 * @throws Exception
	 */
	private RSAPublicKey generateRSAPublicKey(byte[] modulus,
			byte[] publicExponent) throws Exception {

		KeyFactory keyFac = null;
		try {
			keyFac = KeyFactory.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			throw new Exception(ex.getMessage());
		}
		RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
				modulus), new BigInteger(publicExponent));
		try {
			return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
		} catch (InvalidKeySpecException ex) {
			throw new Exception(ex.getMessage());
		}

	}

	/**
	 * 生成私钥
	 * 
	 * @param modulus
	 * @param privateExponent
	 * @return RSAPrivateKey
	 * @throws Exception
	 */
	private RSAPrivateKey generateRSAPrivateKey(byte[] modulus,
			byte[] privateExponent) throws Exception {
		KeyFactory keyFac = null;
		try {
			keyFac = KeyFactory.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			throw new Exception(ex.getMessage());
		}
		RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(
				modulus), new BigInteger(privateExponent));
		try {
			return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
		} catch (InvalidKeySpecException ex) {
			throw new Exception(ex.getMessage());
		}
	}

	/**
	 * 加密
	 * 
	 * @param key
	 *            加密的密钥
	 * @param data
	 *            待加密的明文数据
	 * @return 加密后的数据
	 * @throws Exception
	 */
	public String encrypt(Key key, byte[] data) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, key);
			// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024 加密块大小为127
			// byte,加密后为128个byte;
			// 因此共有2个加密块,第一个127 byte第二个为1个byte
			int blockSize = cipher.getBlockSize();
			int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
					: data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize)
					cipher.doFinal(data, i * blockSize, blockSize, raw, i
							* outputSize);
				else
					cipher.doFinal(data, i * blockSize, data.length - i
							* blockSize, raw, i * outputSize);
				// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中
				// ,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。
				i++;
			}
			return encryptBASE64(raw);
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * 解密
	 * 
	 * @param key
	 *            解密的密钥
	 * @param raw
	 *            已经加密的数据
	 * @return 解密后的明文
	 * @throws Exception
	 */
	public byte[] decrypt(Key key, String enRsaStr) throws Exception {
		byte[]raw = decryptBASE64(enRsaStr);
		try {
			Cipher cipher = Cipher.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(cipher.DECRYPT_MODE, key);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;
			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * 返回公钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public RSAPublicKey getRSAPublicKey() throws Exception {

		// 获取公钥
		RSAPublicKey pubKey = (RSAPublicKey) keyPair.getPublic();
		// 获取公钥系数(字节数组形式)
		byte[] pubModBytes = pubKey.getModulus().toByteArray();
		// 返回公钥公用指数(字节数组形式)
		byte[] pubPubExpBytes = pubKey.getPublicExponent().toByteArray();
		// 生成公钥
		RSAPublicKey recoveryPubKey = this.generateRSAPublicKey(pubModBytes,
				pubPubExpBytes);
		return recoveryPubKey;
	}
	//key to Base64String
	public static String getBase64Key(Key key)
			throws Exception {
		return encryptBASE64(key.getEncoded());
	}

	//byte[]→String
	public static String encryptBASE64(byte[] key) throws Exception {
		return (new BASE64Encoder()).encodeBuffer(key);
	}
	//String→byte[]
	public static byte[] decryptBASE64(String key) throws Exception {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

	/**
	 * 获取私钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public RSAPrivateKey getRSAPrivateKey() throws Exception {

		// 获取私钥
		RSAPrivateKey priKey = (RSAPrivateKey) keyPair.getPrivate();
		// 返回私钥系数(字节数组形式)
		byte[] priModBytes = priKey.getModulus().toByteArray();
		// 返回私钥专用指数(字节数组形式)
		byte[] priPriExpBytes = priKey.getPrivateExponent().toByteArray();
		// 生成私钥
		RSAPrivateKey recoveryPriKey = this.generateRSAPrivateKey(priModBytes,
				priPriExpBytes);
		return recoveryPriKey;
	}
	   /**
     * 得到公钥
     * @param key 密钥字符串(经过base64编码)
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key) throws Exception {
          byte[] keyBytes;
          keyBytes = (new BASE64Decoder()).decodeBuffer(key);

          X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
          KeyFactory keyFactory = KeyFactory.getInstance("RSA");
          PublicKey publicKey = keyFactory.generatePublic(keySpec);
          return publicKey;
    }
    /**
     * 得到私钥
     * @param key 密钥字符串(经过base64编码)
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
          byte[] keyBytes;
          keyBytes = (new BASE64Decoder()).decodeBuffer(key);

          PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
          KeyFactory keyFactory = KeyFactory.getInstance("RSA");
          PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
          return privateKey;
    }



具体到项目中需根据服务端具体情况进行修改,关于数据加密的总结就这么多。