前言
從前一篇網(wǎng)絡安全基礎要點知識介紹中可以知道刽射,在網(wǎng)絡通信中,通信傳輸數(shù)據(jù)容易被截取或篡改幸乒,如果在傳輸用戶隱私數(shù)據(jù)過程中,被不法分子截取或篡改唇牧,就可能導致用戶受到傷害罕扎,比如被詐騙,所以對客戶端與服務端的傳輸數(shù)據(jù)加密丐重,是網(wǎng)絡通信中必不可少的腔召。
數(shù)據(jù)加密方案
首先,客戶端與服務端商量好數(shù)據(jù)加密協(xié)議扮惦,對傳輸數(shù)據(jù)做到安全保護臀蛛。
安全保護至少需要有下面兩點:
采用HTTPS協(xié)議
采用公鑰密碼體制RSA算法對數(shù)據(jù)加密
現(xiàn)在安全是保證了,但還要考慮到性能問題崖蜜,由于RSA算法對數(shù)據(jù)加密時運算速度慢浊仆,所以直接把所有傳輸數(shù)據(jù)都用RSA加密,會導致網(wǎng)絡通信慢豫领,這對用戶將是不好的體驗抡柿。由于對稱密鑰密碼體制中的AES運算速度快且安全性高,所以結合AES對傳輸數(shù)據(jù)加密是非常好的方案等恐。
下面是對客戶端與服務端通信數(shù)據(jù)加密比較通用的方案:
客戶端生成AES密鑰洲劣,并保存AES密鑰
客戶端用AES密鑰對請求傳輸數(shù)據(jù)進行加密
客戶端使用RSA公鑰對AES密鑰加密,然后把值放到自定義的一個請求頭中
客戶端向服務端發(fā)起請求
服務端拿到自定義的請求頭值课蔬,然后使用RSA私鑰解密囱稽,拿到AES密鑰
服務端使用AES密鑰對請求數(shù)據(jù)解密
服務端對響應數(shù)據(jù)使用AES密鑰加密
服務端向客戶端發(fā)出響應
客戶端拿到服務端加密數(shù)據(jù),并使用之前保存的AES密鑰解密
注意:傳輸數(shù)據(jù)使用AES密鑰加密二跋,RSA公鑰對AES密鑰加密粗悯。RSA公鑰和私鑰由服務端生成,公鑰放在客戶端同欠,私鑰放在服務端。公鑰私鑰要私密保護横缔,不能隨便給人铺遂。
具體流程圖如下:
上面網(wǎng)絡通信過程是安全的,可以保證通信數(shù)據(jù)即使被截取了茎刚,也無法獲得任何有效信息襟锐;即使被篡改了,也無法被客戶端和服務端驗證通過膛锭。
數(shù)據(jù)加密細節(jié)
AES加解密
生成AES密鑰和使用AES密鑰加密粮坞、解密蚊荣,有下面重要的幾點:
1.密鑰長度的選擇:AES能支持的密鑰長度可以為128,192,256位(也即16,24,32個字節(jié)),這里選擇128位莫杈。
2.算法/模式/填充的選擇:
算法/模式/填充 字節(jié)加密后數(shù)據(jù)長度 不滿16字節(jié)加密后長度
AES/CBC/NoPadding 16 不支持
AES/CBC/PKCS5Padding 32 16
AES/CBC/ISO10126Paddind 32 16
AES/CFB/NoPadding 16 原始數(shù)據(jù)長度
AES/CFB/PKCS5Padding 32 16
AES/CFB/ISO10126Padding 32 16
AES/ECB/NoPadding 16 不支持
AES/ECB/PKCS5Padding 32 16
AES/ECB/ISO10126Padding 32 16
AES/ECB/ISO10126Padding 32 16
AES/OFB/NoPadding 16 原始數(shù)據(jù)長度
AES/OFB/PKCS5Padding 32 16
AES/OFB/ISO10126Padding 32 16
AES/PCBC/NoPadding 16 不支持
AES/PCBC/PKCS5Padding 32 16
AES/PCBC/ISO10126Padding 32 16
這里選擇AES/CBC/PKCS5Padding互例。
3.添加向量 IvParameterSpec:增強算法強度。
4.編碼格式選擇:UTF-8筝闹。
下面為具體代碼實現(xiàn):
? ? private final int AES_KEY_LENGTH = 16;//密鑰長度16字節(jié)媳叨,128位
? ? private final String AES_ALGORITHM = "AES";//算法名字
? ? private final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding";//算法/模式/填充
? ? private final String AES_IV = "0112030445060709";//使用CBC模式,需要一個向量iv关顷,可增加加密算法的強度
? ? private final String AES_STRING = "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLOP";
? ? private final Charset UTF_8 = Charset.forName("UTF-8");//編碼格式
? ? /**
? ? * 使用AES加密
? ? *
? ? * @param aesKey AES Key
? ? * @param data? 被加密的數(shù)據(jù)
? ? * @return AES加密后的數(shù)據(jù)
? ? */
? ? private byte[] encodeAES(byte[] aesKey, String data) {
? ? ? ? if (aesKey == null || aesKey.length != AES_KEY_LENGTH) {
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? SecretKeySpec keySpec = new SecretKeySpec(aesKey, AES_ALGORITHM);
? ? ? ? try {
? ? ? ? ? ? Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
? ? ? ? ? ? IvParameterSpec iv = new IvParameterSpec(AES_IV.getBytes(UTF_8));
? ? ? ? ? ? cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
? ? ? ? ? ? return cipher.doFinal(data.getBytes(UTF_8));
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? Log.d(TAG, e.getMessage(), e);
? ? ? ? }
? ? ? ? return null;
? ? }
? ? /**
? ? * 使用AES解密
? ? *
? ? * @param aesKey AES Key
? ? * @param data? 被解密的數(shù)據(jù)
? ? * @return AES解密后的數(shù)據(jù)
? ? */
? ? private String decodeAES(byte[] aesKey, byte[] data) {
? ? ? ? if (aesKey == null || aesKey.length != AES_KEY_LENGTH) {
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? SecretKeySpec keySpec = new SecretKeySpec(aesKey, AES_ALGORITHM);
? ? ? ? try {
? ? ? ? ? ? Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
? ? ? ? ? ? IvParameterSpec iv = new IvParameterSpec(AES_IV.getBytes(UTF_8));
? ? ? ? ? ? cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
? ? ? ? ? ? return new String(cipher.doFinal(data), UTF_8);
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? Log.d(TAG, e.getMessage(), e);
? ? ? ? }
? ? ? ? return null;
? ? }
? ? private int getRandom(int count) {
? ? ? ? return (int) Math.round(Math.random() * (count));
? ? }
? ? /**
? ? * 生成AES key
? ? *
? ? * @return AES key
? ? */
? ? private String initAESKey() {
? ? ? ? StringBuilder sb = new StringBuilder();
? ? ? ? int len = AES_STRING.length();
? ? ? ? for (int i = 0; i < AES_KEY_LENGTH; i++) {
? ? ? ? ? ? sb.append(AES_STRING.charAt(getRandom(len - 1)));
? ? ? ? }
? ? ? ? return sb.toString();
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
現(xiàn)在AES密鑰和AES加密糊秆、解密都有了,在通常情況下议双,還會對加密痘番、解密過程進行Base64 編碼、解碼平痰。
Base64編碼汞舱,選擇 URL_SAFE 標識,也就是 “-” 和 “_” 會被替換為 “+” 和 “/”觉增,:
? ? /**
? ? * 對數(shù)據(jù)進行Base64編碼兵拢,使用的是{@link android.util.Base64},而且flags需要使用 {@link android.util.Base64#URL_SAFE,android.util#Base64.NO_PADDING,android.util.Base64#NO_WRAP}逾礁。
? ? *
? ? * @param input 來源數(shù)據(jù)
? ? * @return Base64編碼的數(shù)據(jù)
? ? */
? ? private String encodeBase64(byte[] input) {
? ? ? ? return new String(Base64.encode(input, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP), UTF_8);
? ? }
1
2
3
4
5
6
7
8
9
10
Base64解碼说铃,和編碼對應:
? ? /**
? ? * 對數(shù)據(jù)進行Base64解碼,使用的是{@link android.util.Base64}嘹履,而且flags需要使用 {@link android.util.Base64#URL_SAFE,android.util.Base64#NO_WRAP}腻扇,主要是為了和Base64加密對應。
? ? *
? ? * @param str 需要解碼的數(shù)據(jù)
? ? * @return Base64解碼后的數(shù)據(jù)
? ? */
? ? private byte[] decodeBase64(String str) {
? ? ? ? return Base64.decode(str.getBytes(UTF_8), Base64.URL_SAFE | Base64.DEFAULT);
? ? }
1
2
3
4
5
6
7
8
9
RSA公鑰加密
RSA公鑰是從服務端拿到的砾嫉,這個公鑰不能被泄漏幼苛,必須做到安全保護。
使用RSA公鑰加密焕刮,也有幾個重要點:
1.拿到的公鑰是Base64 編碼后的舶沿,所以首先需要對公鑰Base64解碼。
2.算法/模式/填充的選擇:RSA/ECB/PKCS1Padding
3.編碼格式選擇:UTF-8配并。
注意:使用RSA公鑰加密的流程對應的就是服務端使用RSA私鑰解密的流程括荡,所以需要和服務端溝通商量好。
具體代碼實現(xiàn):
? ? private final String RSA_PUB_KEY = "服務端給的公鑰";
? ? private final String RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
? ? /**
? ? * 公鑰加密
? ? *
? ? * @param data? ? ? ? ? 要加密的數(shù)據(jù)
? ? * @param key? ? ? ? ? ? 公鑰
? ? * @param transformation 算法/模式/填充
? ? * @return 加密后的數(shù)據(jù)
? ? */
? ? public byte[] encryptByPublicKey(byte[] data, String key, String transformation)
? ? ? ? ? ? throws GeneralSecurityException {
? ? ? ? byte[] keyBytes = Base64.decode(key.getBytes(UTF_8), Base64.NO_WRAP);
? ? ? ? X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
? ? ? ? KeyFactory keyFactory = KeyFactory.getInstance("RSA");
? ? ? ? PublicKey pubKey = keyFactory.generatePublic(keySpec);
? ? ? ? Cipher cipher = Cipher.getInstance(transformation);
? ? ? ? cipher.init(Cipher.ENCRYPT_MODE, pubKey);
? ? ? ? return cipher.doFinal(data);
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
總結
1.為了保證網(wǎng)絡通信中的通信數(shù)據(jù)安全溉旋,首先采用HTTPS協(xié)議和公鑰密鑰體制中的RSA加密畸冲。
2.因為是RSA運算速度慢,所以采用運算速度快且安全性高的對稱密鑰密碼體制中的AES對所
有傳輸數(shù)據(jù)進行加密,然后再用RSA對AES密鑰加密邑闲,這樣既能保證安全又能保證性能算行。
3.RSA公鑰和私鑰由服務端生成,公鑰放在客戶端苫耸,私鑰放在服務端州邢。
4.數(shù)據(jù)加密后采用Base64編碼,數(shù)據(jù)解密前采用Base64解碼鲸阔。
5.編碼格式同一采用UTF-8
————————————————
版權聲明:本文為CSDN博主「麥田里的守望者-Jiang」的原創(chuàng)文章偷霉,遵循 CC 4.0 BY-SA 版權協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明褐筛。
原文鏈接:https://blog.csdn.net/wangjiang_qianmo/article/details/88073848