安卓中,不管是內網還是外網,數據的傳輸首要考慮就是安全問題,尤其是用戶信息,以及各種密碼等敏感信息。
所以說,對數據的加密是很有必要的,尤其是當下物聯網蓬勃發展的今天,數據安全尤爲重要。
因此本人總結了一下安卓中幾種加密方式的實現:
③ 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;
}
具體到項目中需根據服務端具體情況進行修改,關於數據加密的總結就這麼多。