安卓中,不管是内网还是外网,数据的传输首要考虑就是安全问题,尤其是用户信息,以及各种密码等敏感信息。
所以说,对数据的加密是很有必要的,尤其是当下物联网蓬勃发展的今天,数据安全尤为重要。
因此本人总结了一下安卓中几种加密方式的实现:
③ AES:对称加密算法,为了防止网络(包含内网和外网)传递AES加密的秘文时导致的数据丢失(last block incomplete in decryption),一般需对秘文做base64处理,加密时候需要添加一个类似bcprov-jdk的库和两个policy文件,解密则不需要,
由于客户端携带了密钥,静态的密钥存在于客户端总是不安全的,那么可以考虑ECDH公钥交换协商随机密钥,基点或者全部逻辑加密算法写在加壳的SO里,此随机密钥可作为数据aes加密的密钥,这样可保证安全,当然亦可考虑使用非对称加密算法。
AES128和AES256主要区别是密钥长度不同(分别是128bits,256bits)、加密处理轮数不同(分别是10轮,14轮),后者强度高于前者;
具体到项目中需根据服务端具体情况进行修改,关于数据加密的总结就这么多。
所以说,对数据的加密是很有必要的,尤其是当下物联网蓬勃发展的今天,数据安全尤为重要。
因此本人总结了一下安卓中几种加密方式的实现:
① 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;
}
具体到项目中需根据服务端具体情况进行修改,关于数据加密的总结就这么多。