詳解Android安全防護(hù)之加密算法
摘要
也許有些開發(fā)者或者企業(yè)覺(jué)得。我們公司的app,數(shù)據(jù)量這些少,會(huì)有那個(gè)黑客吃飽了沒(méi)事做來(lái)破解啊。又不是支付寶,或者其他那些用戶量很多的應(yīng)用。如果是這樣想的話,那只能說(shuō)目光短淺了。
Android應(yīng)用常用的加密算法
如果說(shuō)按加密的內(nèi)容是否可以還原,可以分為可逆加密和非可逆加密。
非可逆加密:也就是說(shuō)加密后的數(shù)據(jù)是不能還原成原來(lái)的數(shù)據(jù)。比如MD5加密 加密一個(gè)密碼:123456 加密后成: afabsbfbabf437hfbbff73(結(jié)果并不一定是這個(gè),只是舉例)。也就是說(shuō)加密后的結(jié)果afabsbfbabf437hfbbff73是不能夠在解密出123456這個(gè)值的。
可逆加密:可逆加密有一個(gè)公鑰和一個(gè)私鑰,通過(guò)公鑰進(jìn)行數(shù)據(jù)的加密,通過(guò)私鑰進(jìn)行解密。代表有:RSA,AES。
對(duì)稱加密和非對(duì)稱加密:可逆加密根據(jù)其使用加解密是否使用同一個(gè)密鑰又分為對(duì)稱加密(加解密使用同一個(gè)密鑰)和非對(duì)稱加密(加解密的密鑰分開)
MD5
MD5的特點(diǎn):
1、壓縮性:任意長(zhǎng)度的數(shù)據(jù),算出來(lái)的MD5值的長(zhǎng)度都是固定。
2、容易計(jì)算性:從原始數(shù)據(jù)計(jì)算出MD5值是很容易的。
3、抗修改性:愿數(shù)據(jù)只要有一點(diǎn)點(diǎn)的改動(dòng),得到的MD5差別都是很大的。
4、強(qiáng)抗碰撞性:從原數(shù)據(jù)計(jì)算出來(lái)的MD5,想要找到一個(gè)具有相同的MD5,非常難。
MD5的應(yīng)用場(chǎng)景:
1、一致性驗(yàn)證(比如下載某個(gè)文件,不知道文件是否下載完成,可以MD5進(jìn)行校驗(yàn)。加密文件比較耗時(shí),需要放到子線程中)
2、密碼的存儲(chǔ)(如登陸注冊(cè)這些,賬號(hào)密碼會(huì)保存到sp中,直接就保存到賬號(hào)密碼的MD5值就好了。這樣也可以避免服務(wù)器權(quán)限者知道這個(gè)密碼)
MD5的簡(jiǎn)單使用
先寫一個(gè)MD5的工具類
package com.example.huangjialin.md5test;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Utils {
public static String md5(String content) {
byte[] hash = null;
try {
hash = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
StringBuilder stringBuilder = new StringBuilder(hash.length * 2);
for (byte b: hash) {
if ((b & 0xFF) < 0x10){
stringBuilder.append("0");
}
stringBuilder.append(Integer.toHexString(b & 0xFF));
}
return stringBuilder.toString();
}
}
簡(jiǎn)單的解釋一下上面的,首先是通過(guò)MessageDigest.getInstance(“MD5”)來(lái)獲取到MessageDigest這個(gè)類,這個(gè)類是java自帶的一個(gè)加密類,然后通過(guò)調(diào)用digest()方法來(lái)的獲取到加密后的字節(jié)數(shù)組。該方法傳入的參數(shù)是byte[] input 所以還需要將字符串轉(zhuǎn)化為byte[]。得到加密后的字節(jié)數(shù)組以后,將他們轉(zhuǎn)換成16禁止的字符串,然后拼接起來(lái)就可以了。
然后直接調(diào)用:
/**
* MD5加密
*/
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String md5_123456abc = Utils.md5("123456abc");
String md5_huangjialin = Utils.md5("huangjialin");
Log.i("huangjialin"," md5_123456abc算出的MD5值是: " + md5_123456abc);
Log.i("huangjialin"," md5_huangjialin算出的MD5值是: " + md5_huangjialin);
}
});
得出的結(jié)果:
09-20 15:33:12.208 7352-7352/com.example.huangjialin.md5test I/huangjialin: md5_123456abc算出的MD5值是: df10ef8509dc176d733d59549e7dbfaf
09-20 15:33:12.208 7352-7352/com.example.huangjialin.md5test I/huangjialin: md5_huangjialin算出的MD5值是: 08e768954478c8669619d7d087db0070
這里說(shuō)一句題外話:Log輸出日志有很多種如Log.i();Log.d()等等,但是現(xiàn)在有些手機(jī)廠商直接就把等級(jí)較低的日志給屏蔽掉,所以有些日志輸出在有些手機(jī)可以看到,有些手機(jī)沒(méi)有看到。解決辦法就是換輸出等級(jí)較高的就OK了。
RSA
RSA是現(xiàn)在比較流行的一種非對(duì)稱加密的,它需要一對(duì)密鑰(公鑰和私鑰)公鑰進(jìn)行加密,私鑰進(jìn)行解密。
RSA的加密原理
1、隨機(jī)選擇兩個(gè)大的質(zhì)數(shù)P和Q,P不等于Q,計(jì)算出結(jié)果:N = P*Q;
2、選擇一個(gè)大于1,小于N的自然數(shù)E,E必須和(P-1)*(Q-1)互素。
3、用公式計(jì)算出D:D*E = mod(P-1)*(Q-1)
4、銷毀P和Q
最終得到的N,E就是公鑰,D就是私鑰了。
RSA加解密步驟
1、甲方生成密鑰對(duì)(公鑰和私鑰,公鑰用來(lái)加密數(shù)據(jù),私鑰自己保留,用來(lái)解密數(shù)據(jù))
2、甲方使用私鑰加密數(shù)據(jù),然后用私鑰對(duì)加密后的數(shù)據(jù)簽名,并把這些放送給乙方,乙方使用公鑰,簽名來(lái)驗(yàn)證帶解密數(shù)據(jù)是否有效,如果有效就使用公鑰對(duì)數(shù)據(jù)進(jìn)行解密
3、乙方使用公鑰加密數(shù)據(jù),向甲方發(fā)送經(jīng)過(guò)加密后的數(shù)據(jù),甲方或者加密數(shù)據(jù)后,就可以通過(guò)私鑰進(jìn)行解密了。
RSA使用場(chǎng)景
項(xiàng)目中一些敏感的數(shù)據(jù),比如身份證號(hào),銀行卡,等相關(guān)信息可通過(guò)加密后在傳給服務(wù)器,服務(wù)器使用私鑰進(jìn)行解密。
RSA密鑰對(duì)生成
RSA的密鑰對(duì)生成方式有兩種
/*
初始化KeyPairGenerator類,并獲取到公鑰和私鑰
*/
byte[] publicKeyByte;
byte[] prvateKtyByte;
public void getKey() {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(“RSA”);//KeyPairGenerator類是java專門提供生成密鑰對(duì)的一個(gè)類。
keyPairGenerator.initialize(1024); //設(shè)置密鑰對(duì)的大小
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();//獲取私鑰
PublicKey publicKey = keyPair.getPublic();//獲取公鑰
prvateKtyByte = privateKey.getEncoded();//私鑰對(duì)應(yīng)的字節(jié)數(shù)組
publicKeyByte = publicKey.getEncoded(); //公鑰對(duì)應(yīng)的字節(jié)數(shù)組
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
當(dāng)然上面這種生成密鑰對(duì)的方式,基本很少會(huì)在項(xiàng)目中使用使用,用得比較多的還是第二中方式。
第二種是通過(guò)OpenSSl工具生成密鑰對(duì)
這種生成密鑰對(duì)的方式需要安裝OpenSSl。這里就不說(shuō)具體怎么安裝了。這里簡(jiǎn)單的說(shuō)一下生成密鑰對(duì)所需要的一些命令
使用命令生成私鑰:
genrsa -out rsa_private_key.pem 1024
這條命令是讓openssl隨機(jī)生成一份私鑰,長(zhǎng)度為1024
使用命令生成公鑰:
rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
命令成功以后,就會(huì)在openSSL下的bin目錄下生成公鑰和私鑰,然后就可以進(jìn)行加密和解密了。
加密
/**
* 加密
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public byte[] encryption(String content) {
byte[] result = null;
try {
Cipher cipher = Cipher.getInstance("RSA");
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyByte);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
result = cipher.doFinal(content.getBytes());
Log.i("huangjialin", "----> " + Base64.getEncoder().encodeToString(result));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return result;
}
解密
/**
* 解密
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public void decryption() {
Cipher cipher = null;
try {
cipher = Cipher.getInstance("RSA");
//私鑰需要通過(guò)PKCS8EncodedKeySpec來(lái)讀取
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(prvateKtyByte);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//生成私鑰
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//String content = "123456";
byte[] input = encryption("123456");
byte[] result = cipher.doFinal(input);
Log.i("huangjialin", "--解密--> " + new String(result));
//Assert.assertTrue(content.equals(new String(result)));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
當(dāng)然上面的代碼是我寫測(cè)試用的,真正項(xiàng)目中,還得封裝好,把它弄成工具類,進(jìn)行調(diào)用。
AES
AES是一個(gè)對(duì)稱加密,也就是說(shuō)使用AES進(jìn)行加密和解密,他們使用的密鑰都是一樣的。AES加密算法是密碼學(xué)中的高級(jí)加密標(biāo)準(zhǔn),又稱Rijndael加密法,是美國(guó)聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)。這個(gè)標(biāo)準(zhǔn)用來(lái)替代原先的DES,已經(jīng)被多方分析并使用。同時(shí)AES他的算法加密強(qiáng)度大,執(zhí)行效率很高。
AES使用場(chǎng)景
1、由于AES是對(duì)稱加密,加解密都是使用同一個(gè)密鑰,所以說(shuō)在項(xiàng)目中一些敏感的數(shù)據(jù)需要保存到本地??梢韵韧珹ES的密鑰進(jìn)行加密,需要用的使用,將數(shù)據(jù)取出來(lái)再進(jìn)行解密。
2、可以進(jìn)行對(duì)一些敏感數(shù)據(jù)進(jìn)行加密,然后在傳遞給服務(wù)器。
AES使用
在Android7.0之前可以這樣獲取到密鑰
private SecretKey generateKey(String seed) throws Exception {
// 獲取秘鑰生成器
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
// 通過(guò)種子初始化
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "Crypto");
secureRandom.setSeed(seed.getBytes("UTF-8"));
keyGenerator.init(128, secureRandom);
// 生成秘鑰并返回
return keyGenerator.generateKey();
}
但是在Android7.0之后就不支持了,移除了Crypto。當(dāng)然也這種獲取密鑰方式在7.0之后Google也給出了解決方案,但是官方并不建議這樣來(lái)獲取。具體的可以看這里。https://android-developers.googleblog.com/2016/06/security-crypto-provider-deprecated-in.html
官方給出的是另一種方式,并不需要獲取密鑰,而是定義密碼的形式。
package com.example.huangjialin.md5test;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class MainActivity extends AppCompatActivity {
private EditText edittext;
private Button button, jiami, jiemi;
private TextView textView;
private SecretKey secretKey;
private byte[] bytes;
private String content = "huangjialin,我是要加密的數(shù)據(jù)";
String password = "huangji黃家磷";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edittext = findViewById(R.id.edittext);
button = findViewById(R.id.button);
textView = findViewById(R.id.textview);
jiami = findViewById(R.id.jiami);
jiemi = findViewById(R.id.jiemi);
Log.i("huagjialin", "--加密的數(shù)據(jù)-- > " + content);
/**
* 獲取密鑰
*/
/* button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
secretKey = generateKey("huangjiain");
} catch (Exception e) {
e.printStackTrace();
}
}
});*/
/**
* 加密
*/
jiami.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
bytes = encrypt(content, password);
String str = new String(bytes);
Log.i("huagjialin", "--加密后的數(shù)據(jù)-- > " + Base64.decode(str,Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
}
}
});
/**
* 解密
*/
jiemi.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
byte[] by = decrypt(bytes, password);
String string = new String(by);
Log.i("huagjialin", "--解密后的數(shù)據(jù)-- > " + string);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* 另一種加密形式
*/
private byte[] encrypt(String content, String password) throws Exception {
// 創(chuàng)建AES秘鑰
SecretKeySpec key = new SecretKeySpec(password.getBytes(), "AES/CBC/PKCS5PADDING");
// 創(chuàng)建密碼器
Cipher cipher = Cipher.getInstance("AES");
// 初始化加密器
cipher.init(Cipher.ENCRYPT_MODE, key);
// 加密
return cipher.doFinal(content.getBytes("UTF-8"));
}
/**
* 解密
*/
private byte[] decrypt(byte[] content, String password) throws Exception {
// 創(chuàng)建AES秘鑰
SecretKeySpec key = new SecretKeySpec(password.getBytes(), "AES/CBC/PKCS5PADDING");
// 創(chuàng)建密碼器
Cipher cipher = Cipher.getInstance("AES");
// 初始化解密器
cipher.init(Cipher.DECRYPT_MODE, key);
// 解密
return cipher.doFinal(content);
}
}
09-20 21:12:36.394 15933-15933/com.example.huangjialin.md5test I/huagjialin: --加密的數(shù)據(jù)-- > huangjialin,我是要加密的數(shù)據(jù)
09-20 21:12:39.561 15933-15933/com.example.huangjialin.md5test I/huagjialin: --加密后的數(shù)據(jù)-- > [B@d62495e
09-20 21:12:41.829 15933-15933/com.example.huangjialin.md5test I/huagjialin: --解密后的數(shù)據(jù)-- > huangjialin,我是要加密的數(shù)據(jù)
以上就是我們比較常用的幾種加密的一些內(nèi)容。
以上就是詳解Android安全防護(hù)之加密算法的詳細(xì)內(nèi)容,更多關(guān)于Android安全防護(hù)之加密算法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android簡(jiǎn)易文件管理器實(shí)例(列表式文件目錄)
下面小編就為大家?guī)?lái)一篇android簡(jiǎn)易文件管理器實(shí)例(列表式文件目錄)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
Android操作Excel文件的功能實(shí)現(xiàn)
本篇文章主要介紹了Android操作Excel文件的功能實(shí)現(xiàn),Android中操作Excel文件導(dǎo)出報(bào)表時(shí)主要采用開源庫(kù)jxl,有興趣的可以了解一下。2017-03-03
Android通過(guò)交互實(shí)現(xiàn)貝塞爾曲線的繪制
本篇我們將介紹簡(jiǎn)單的交互式繪圖,通過(guò)獲取觸控位置來(lái)設(shè)定貝塞爾曲線的控制點(diǎn),從而實(shí)現(xiàn)交互式繪制曲線,感興趣的小伙伴可以了解一下2022-05-05
使用RecyclerView添加Header和Footer的方法
RecyclerView雖然作為L(zhǎng)istView的替代者有著較好的性能提升,但是ListView的一些常用功能卻沒(méi)有提供,比如我們平時(shí)會(huì)經(jīng)常用到的addHeaderView,addFooterView,既然RecyclerView沒(méi)有提供這個(gè)方法,我們應(yīng)該如何為列表添加頭部和底部呢,接下來(lái)通過(guò)本文給大家介紹2016-03-03
Android使用MulticastSocket實(shí)現(xiàn)多點(diǎn)廣播圖片
這篇文章主要為大家詳細(xì)介紹了Android使用MulticastSocket實(shí)現(xiàn)多點(diǎn)廣播圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
Android OpenGLES2.0等腰直角三角形和彩色的三角形(三)
這篇文章主要為大家詳細(xì)介紹了Android OpenGLES2.0等腰直角三角形和彩色的三角形,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
Android使用Handler實(shí)現(xiàn)打地鼠游戲
這篇文章主要為大家詳細(xì)介紹了Android使用Handler實(shí)現(xiàn)打地鼠游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android中檢測(cè)當(dāng)前是否為主線程最可靠的解決方法
這篇文章主要介紹了Android中檢測(cè)當(dāng)前是否為主線程最可靠的解決方法,本文先是給出了最可靠的方法,然后給出了幾個(gè)實(shí)驗(yàn)例子,需要的朋友可以參考下2015-01-01

