Android 資料加密演算法總結
阿新 • • 發佈:2019-01-30
安卓中,不管是內網還是外網,資料的傳輸首要考慮就是安全問題,尤其是使用者資訊,以及各種密碼等敏感資訊。
所以說,對資料的加密是很有必要的,尤其是當下物聯網蓬勃發展的今天,資料安全尤為重要。
因此本人總結了一下安卓中幾種加密方式的實現:
③ AES:對稱加密演算法,為了防止網路(包含內網和外網)傳遞AES加密的祕文時導致的資料丟失(last block incomplete in decryption),一般需對祕文做base64處理,加密時候需要新增一個類似bcprov-jdk的庫和兩個policy檔案,解密則不需要,
由於客戶端攜帶了金鑰,靜態的金鑰存在於客戶端總是不安全的,那麼可以考慮ECDH公鑰交換協商隨機金鑰,基點或者全部邏輯加密演算法寫在加殼的SO裡,此隨機金鑰可作為資料aes加密的金鑰,這樣可保證安全,當然亦可考慮使用非對稱加密演算法。
AES128和AES256主要區別是金鑰長度不同(分別是128bits,256bits)、加密處理輪數不同(分別是10輪,14輪),後者強度高於前者;
具體到專案中需根據服務端具體情況進行修改,關於資料加密的總結就這麼多。
所以說,對資料的加密是很有必要的,尤其是當下物聯網蓬勃發展的今天,資料安全尤為重要。
因此本人總結了一下安卓中幾種加密方式的實現:
① MD5:一種不可逆的加密演算法,常用於只需加密無需解密的資料上,比如使用者密碼,也常用來保證資料的完整性,因為資料被篡改後,其加密後的MD5也會隨之改變,對比篡改前的MD5可確定資料是否完整;
② DES:對稱加密演算法;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的金鑰 * * @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;
}
具體到專案中需根據服務端具體情況進行修改,關於資料加密的總結就這麼多。