Android安全防護防護———加密演算法
摘要
這篇文章本來早就應該寫了,但是由於專案一直開發新的需求,就拖後了。現在有時間了,必須得寫了。現在Android應用程式對安全防範這方面要求越來越高了。特別是金融行業,如果金融app沒有沒有做好相應安全處理,那些很容易被一些Hacker(黑客)所攻擊。並不是說做了這些安全防範,這個應用就百分之百的安全的。只是說能夠儘可能加大破解難度。也許有些開發者或者企業覺得。我們公司的app,資料量這些少,會有那個黑客吃飽了沒事做來破解啊。又不是支付寶,或者其他那些使用者量很多的應用。如果是這樣想的話,那隻能說目光短淺了。
Android應用常用的加密演算法
如果說按加密的內容是否可以還原,可以分為可逆加密和非可逆加密。
非可逆加密:也就是說加密後的資料是不能還原成原來的資料。比如MD5加密 加密一個密碼:123456 加密後成: afabsbfbabf437hfbbff73(結果並不一定是這個,只是舉例)。也就是說加密後的結果afabsbfbabf437hfbbff73是不能夠在解密出123456這個值的。
可逆加密:可逆加密有一個公鑰和一個私鑰,通過公鑰進行資料的加密,通過私鑰進行解密。代表有:RSA,AES。
對稱加密和非對稱加密:可逆加密根據其使用加解密是否使用同一個金鑰又分為對稱加密(加解密使用同一個金鑰)和非對稱加密(加解密的金鑰分開)
MD5
MD5的特點:
1、壓縮性:任意長度的資料,算出來的MD5值的長度都是固定。
2、容易計算性:從原始資料計算出MD5值是很容易的。
3、抗修改性:願資料只要有一點點的改動,得到的MD5差別都是很大的。
4、強抗碰撞性:從原資料計算出來的MD5,想要找到一個具有相同的MD5,非常難。
MD5的應用場景:
1、一致性驗證(比如下載某個檔案,不知道檔案是否下載完成,可以MD5進行校驗。加密檔案比較耗時,需要放到子執行緒中)
2、密碼的儲存(如登陸註冊這些,賬號密碼會儲存到sp中,直接就儲存到賬號密碼的MD5值就好了。這樣也可以避免伺服器許可權者知道這個密碼)
MD5的簡單使用
先寫一個MD5的工具類
1 package com.example.huangjialin.md5test; 2 3 import java.io.UnsupportedEncodingException; 4 import java.security.MessageDigest; 5 import java.security.NoSuchAlgorithmException; 6 7 /** 8* Created by huangjialin on 2018/9/4. 9*/ 10 11 public class Utils { 12 13public static String md5(String content) { 14byte[] hash = null; 15try { 16hash = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8")); 17} catch (NoSuchAlgorithmException e) { 18e.printStackTrace(); 19} catch (UnsupportedEncodingException e) { 20e.printStackTrace(); 21} 22 23StringBuilder stringBuilder = new StringBuilder(hash.length * 2); 24for (byte b: hash) { 25if ((b & 0xFF) < 0x10){ 26stringBuilder.append("0"); 27 28} 29stringBuilder.append(Integer.toHexString(b & 0xFF)); 30 31} 32return stringBuilder.toString(); 33} 34 35 }
簡單的解釋一下上面的,首先是通過MessageDigest.getInstance(“MD5”)來獲取到MessageDigest這個類,這個類是java自帶的一個加密類,然後通過呼叫digest()方法來的獲取到加密後的位元組陣列。該方法傳入的引數是byte[] input 所以還需要將字串轉化為byte[]。得到加密後的位元組陣列以後,將他們轉換成16禁止的字串,然後拼接起來就可以了。
然後直接呼叫:
1 /** 2 3* MD5加密 4 5*/ 6 7button.setOnClickListener(new View.OnClickListener() { 8 9@Override 10 11public void onClick(View v) { 12 13 14 15String md5_123456abc = Utils.md5("123456abc"); 16 17String md5_huangjialin = Utils.md5("huangjialin"); 18 19Log.i("huangjialin","md5_123456abc算出的MD5值是:" + md5_123456abc); 20 21Log.i("huangjialin","md5_huangjialin算出的MD5值是:" + md5_huangjialin); 22 23} 24 25});
得出的結果:
1 09-20 15:33:12.208 7352-7352/com.example.huangjialin.md5test I/huangjialin:md5_123456abc算出的MD5值是:df10ef8509dc176d733d59549e7dbfaf 2 3 09-20 15:33:12.208 7352-7352/com.example.huangjialin.md5test I/huangjialin:md5_huangjialin算出的MD5值是:08e768954478c8669619d7d087db0070
這裡說一句題外話:Log輸出日誌有很多種如Log.i();Log.d()等等,但是現在有些手機廠商直接就把等級較低的日誌給遮蔽掉,所以有些日誌輸出在有些手機可以看到,有些手機沒有看到。解決辦法就是換輸出等級較高的就OK了。
RSA
RSA是現在比較流行的一種非對稱加密的,它需要一對金鑰(公鑰和私鑰)公鑰進行加密,私鑰進行解密。
RSA的加密原理
1、隨機選擇兩個大的質數P和Q,P不等於Q,計算出結果:N = P*Q;
2、選擇一個大於1,小於N的自然數E,E必須和(P-1)*(Q-1)互素。
3、用公式計算出D:D*E = mod(P-1)*(Q-1)
4、銷燬P和Q
最終得到的N,E就是公鑰,D就是私鑰了。
RSA加解密步驟
1、甲方生成金鑰對(公鑰和私鑰,公鑰用來加密資料,私鑰自己保留,用來解密資料)
2、甲方使用私鑰加密資料,然後用私鑰對加密後的資料簽名,並把這些放送給乙方,乙方使用公鑰,簽名來驗證帶解密資料是否有效,如果有效就使用公鑰對資料進行解密
3、乙方使用公鑰加密資料,向甲方傳送經過加密後的資料,甲方或者加密資料後,就可以通過私鑰進行解密了。
RSA使用場景
專案中一些敏感的資料,比如身份證號,銀行卡,等相關資訊可通過加密後在傳給伺服器,伺服器使用私鑰進行解密。
RSA金鑰對生成
RSA的金鑰對生成方式有兩種
1 /* 2初始化KeyPairGenerator類,並獲取到公鑰和私鑰 3*/ 4byte[] publicKeyByte; 5byte[] prvateKtyByte; 6 7public void getKey() { 8try { 9KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(“RSA”);//KeyPairGenerator類是java專門提供生成金鑰對的一個類。 10keyPairGenerator.initialize(1024); //設定金鑰對的大小 11KeyPair keyPair = keyPairGenerator.generateKeyPair(); 12PrivateKey privateKey = keyPair.getPrivate();//獲取私鑰 13PublicKey publicKey = keyPair.getPublic();//獲取公鑰 14prvateKtyByte = privateKey.getEncoded();//私鑰對應的位元組陣列 15publicKeyByte = publicKey.getEncoded(); //公鑰對應的位元組陣列 16 17} catch (NoSuchAlgorithmException e) { 18e.printStackTrace(); 19} 20}
當然上面這種生成金鑰對的方式,基本很少會在專案中使用使用,用得比較多的還是第二中方式。
第二種是通過OpenSSl工具生成金鑰對
這種生成金鑰對的方式需要安裝OpenSSl。這裡就不說具體怎麼安裝了。這裡簡單的說一下生成金鑰對所需要的一些命令
使用命令生成私鑰:
1 genrsa -out rsa_private_key.pem 1024
這條命令是讓openssl隨機生成一份私鑰,長度為1024
使用命令生成公鑰:
1 rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
命令成功以後,就會在openSSL下的bin目錄下生成公鑰和私鑰,然後就可以進行加密和解密了。
加密
1 /** 2* 加密 3*/ 4 5 6@RequiresApi(api = Build.VERSION_CODES.O) 7public byte[] encryption(String content) { 8byte[] result = null; 9try { 10Cipher cipher = Cipher.getInstance("RSA"); 11X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyByte); 12KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 13PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); 14cipher.init(Cipher.ENCRYPT_MODE, publicKey); 15result = cipher.doFinal(content.getBytes()); 16Log.i("huangjialin", "---->" + Base64.getEncoder().encodeToString(result)); 17} catch (NoSuchAlgorithmException e) { 18e.printStackTrace(); 19} catch (NoSuchPaddingException e) { 20e.printStackTrace(); 21} catch (InvalidKeySpecException e) { 22e.printStackTrace(); 23} catch (InvalidKeyException e) { 24e.printStackTrace(); 25} catch (BadPaddingException e) { 26e.printStackTrace(); 27} catch (IllegalBlockSizeException e) { 28e.printStackTrace(); 29} 30return result; 31}
解密
1 /** 2* 解密 3*/ 4 5@RequiresApi(api = Build.VERSION_CODES.O) 6 7public void decryption() { 8 9Cipher cipher = null; 10try { 11cipher = Cipher.getInstance("RSA"); 12//私鑰需要通過PKCS8EncodedKeySpec來讀取 13PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(prvateKtyByte); 14KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 15//生成私鑰 16PrivateKey privateKey = keyFactory.generatePrivate(keySpec); 17cipher.init(Cipher.DECRYPT_MODE, privateKey); 18//String content = "123456"; 19byte[] input = encryption("123456"); 20byte[] result = cipher.doFinal(input); 21Log.i("huangjialin", "--解密-->" + new String(result)); 22//Assert.assertTrue(content.equals(new String(result))); 23 24 25} catch (NoSuchAlgorithmException e) { 26e.printStackTrace(); 27} catch (NoSuchPaddingException e) { 28e.printStackTrace(); 29} catch (BadPaddingException e) { 30e.printStackTrace(); 31} catch (IllegalBlockSizeException e) { 32e.printStackTrace(); 33} catch (InvalidKeyException e) { 34e.printStackTrace(); 35} catch (InvalidKeySpecException e) { 36e.printStackTrace(); 37} 38 39 40}
當然上面的程式碼是我寫測試用的,真正專案中,還得封裝好,把它弄成工具類,進行呼叫。
AES
AES是一個對稱加密,也就是說使用AES進行加密和解密,他們使用的金鑰都是一樣的。AES加密演算法是密碼學中的高階加密標準,又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析並使用。同時AES他的演算法加密強度大,執行效率很高。
AES使用場景
1、由於AES是對稱加密,加解密都是使用同一個金鑰,所以說在專案中一些敏感的資料需要儲存到本地。可以先同AES的金鑰進行加密,需要用的使用,將資料取出來再進行解密。
2、可以進行對一些敏感資料進行加密,然後在傳遞給伺服器。
AES使用
在Android7.0之前可以這樣獲取到金鑰
1 private SecretKey generateKey(String seed) throws Exception { 2// 獲取祕鑰生成器 3KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); 4// 通過種子初始化 5SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "Crypto"); 6 7secureRandom.setSeed(seed.getBytes("UTF-8")); 8keyGenerator.init(128, secureRandom); 9// 生成祕鑰並返回 10return keyGenerator.generateKey(); 11}
但是在Android7.0之後就不支援了,移除了Crypto。當然也這種獲取金鑰方式在7.0之後Google也給出瞭解決方案,但是官方並不建議這樣來獲取。具體的可以看這裡。ofollow,noindex" target="_blank">https://android-developers.googleblog.com/2016/06/security-crypto-provider-deprecated-in.html
官方給出的是另一種方式,並不需要獲取金鑰,而是定義密碼的形式。
1 package com.example.huangjialin.md5test; 2 3 import android.os.Bundle; 4 import android.support.v7.app.AppCompatActivity; 5 import android.util.Base64; 6 import android.util.Log; 7 import android.view.View; 8 import android.widget.Button; 9 import android.widget.EditText; 10 import android.widget.TextView; 11 12 import javax.crypto.Cipher; 13 import javax.crypto.SecretKey; 14 import javax.crypto.spec.SecretKeySpec; 15 16 public class MainActivity extends AppCompatActivity { 17private EditText edittext; 18private Button button, jiami, jiemi; 19private TextView textView; 20private SecretKey secretKey; 21private byte[] bytes; 22private String content = "huangjialin,我是要加密的資料"; 23String password = "huangji黃家磷"; 24 25@Override 26protected void onCreate(Bundle savedInstanceState) { 27super.onCreate(savedInstanceState); 28setContentView(R.layout.activity_main); 29 30edittext = findViewById(R.id.edittext); 31button = findViewById(R.id.button); 32textView = findViewById(R.id.textview); 33jiami = findViewById(R.id.jiami); 34jiemi = findViewById(R.id.jiemi); 35 36Log.i("huagjialin", "--加密的資料-- > " + content); 37 38/** 39* 獲取金鑰 40*/ 41/* button.setOnClickListener(new View.OnClickListener() { 42@Override 43public void onClick(View v) { 44try { 45secretKey = generateKey("huangjiain"); 46} catch (Exception e) { 47e.printStackTrace(); 48} 49 50} 51});*/ 52 53 54/** 55* 加密 56*/ 57 58jiami.setOnClickListener(new View.OnClickListener() { 59@Override 60public void onClick(View v) { 61try { 62bytes = encrypt(content, password); 63String str = new String(bytes); 64Log.i("huagjialin", "--加密後的資料-- > " + Base64.decode(str,Base64.DEFAULT)); 65} catch (Exception e) { 66e.printStackTrace(); 67} 68 69} 70}); 71 72/** 73* 解密 74*/ 75 76jiemi.setOnClickListener(new View.OnClickListener() { 77@Override 78public void onClick(View v) { 79try { 80byte[] by = decrypt(bytes, password); 81String string = new String(by); 82Log.i("huagjialin", "--解密後的資料-- > " + string); 83} catch (Exception e) { 84e.printStackTrace(); 85} 86} 87}); 88 89 90} 91 92 93 94 95 96 97/** 98* 另一種加密形式 99*/ 100private byte[] encrypt(String content, String password) throws Exception { 101// 建立AES祕鑰 102SecretKeySpec key = new SecretKeySpec(password.getBytes(), "AES/CBC/PKCS5PADDING"); 103// 建立密碼器 104Cipher cipher = Cipher.getInstance("AES"); 105// 初始化加密器 106cipher.init(Cipher.ENCRYPT_MODE, key); 107// 加密 108return cipher.doFinal(content.getBytes("UTF-8")); 109} 110 111 112/** 113* 解密 114*/ 115private byte[] decrypt(byte[] content, String password) throws Exception { 116// 建立AES祕鑰 117SecretKeySpec key = new SecretKeySpec(password.getBytes(), "AES/CBC/PKCS5PADDING"); 118// 建立密碼器 119Cipher cipher = Cipher.getInstance("AES"); 120// 初始化解密器 121cipher.init(Cipher.DECRYPT_MODE, key); 122// 解密 123return cipher.doFinal(content); 124} 125 126 }
1 09-20 21:12:36.394 15933-15933/com.example.huangjialin.md5test I/huagjialin: --加密的資料-- > huangjialin,我是要加密的資料 2 09-20 21:12:39.561 15933-15933/com.example.huangjialin.md5test I/huagjialin: --加密後的資料-- > [B@d62495e 3 09-20 21:12:41.829 15933-15933/com.example.huangjialin.md5test I/huagjialin: --解密後的資料-- > huangjialin,我是要加密的資料
以上就是我們比較常用的幾種加密的一些內容。好了,這篇內容就到這,文中如果錯誤,麻煩大神指教,共同進步