美文网首页
客户端与服务端数据加密传输方案

客户端与服务端数据加密传输方案

作者: 若风丶 | 来源:发表于2019-10-20 22:40 被阅读0次

前言

从前一篇网络安全基础要点知识介绍中可以知道,在网络通信中,通信传输数据容易被截取或篡改,如果在传输用户隐私数据过程中,被不法分子截取或篡改,就可能导致用户受到伤害,比如被诈骗,所以对客户端与服务端的传输数据加密,是网络通信中必不可少的。

数据加密方案

首先,客户端与服务端商量好数据加密协议,对传输数据做到安全保护。

安全保护至少需要有下面两点:

采用HTTPS协议

采用公钥密码体制RSA算法对数据加密

现在安全是保证了,但还要考虑到性能问题,由于RSA算法对数据加密时运算速度慢,所以直接把所有传输数据都用RSA加密,会导致网络通信慢,这对用户将是不好的体验。由于对称密钥密码体制中的AES运算速度快且安全性高,所以结合AES对传输数据加密是非常好的方案。

下面是对客户端与服务端通信数据加密比较通用的方案:

客户端生成AES密钥,并保存AES密钥

客户端用AES密钥对请求传输数据进行加密

客户端使用RSA公钥对AES密钥加密,然后把值放到自定义的一个请求头中

客户端向服务端发起请求

服务端拿到自定义的请求头值,然后使用RSA私钥解密,拿到AES密钥

服务端使用AES密钥对请求数据解密

服务端对响应数据使用AES密钥加密

服务端向客户端发出响应

客户端拿到服务端加密数据,并使用之前保存的AES密钥解密

注意:传输数据使用AES密钥加密,RSA公钥对AES密钥加密。RSA公钥和私钥由服务端生成,公钥放在客户端,私钥放在服务端。公钥私钥要私密保护,不能随便给人。

具体流程图如下:

上面网络通信过程是安全的,可以保证通信数据即使被截取了,也无法获得任何有效信息;即使被篡改了,也无法被客户端和服务端验证通过。

数据加密细节

AES加解密

生成AES密钥和使用AES密钥加密、解密,有下面重要的几点:

1.密钥长度的选择:AES能支持的密钥长度可以为128,192,256位(也即16,24,32个字节),这里选择128位。

2.算法/模式/填充的选择:

算法/模式/填充 字节加密后数据长度 不满16字节加密后长度

AES/CBC/NoPadding 16 不支持

AES/CBC/PKCS5Padding 32 16

AES/CBC/ISO10126Paddind 32 16

AES/CFB/NoPadding 16 原始数据长度

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 原始数据长度

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。

下面为具体代码实现:

    private final int AES_KEY_LENGTH = 16;//密钥长度16字节,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  被加密的数据

    * @return AES加密后的数据

    */

    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  被解密的数据

    * @return AES解密后的数据

    */

    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

现在AES密钥和AES加密、解密都有了,在通常情况下,还会对加密、解密过程进行Base64 编码、解码。

Base64编码,选择 URL_SAFE 标识,也就是 “-” 和 “_” 会被替换为 “+” 和 “/”,:

    /**

    * 对数据进行Base64编码,使用的是{@link android.util.Base64},而且flags需要使用 {@link android.util.Base64#URL_SAFE,android.util#Base64.NO_PADDING,android.util.Base64#NO_WRAP}。

    *

    * @param input 来源数据

    * @return Base64编码的数据

    */

    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解码,和编码对应:

    /**

    * 对数据进行Base64解码,使用的是{@link android.util.Base64},而且flags需要使用 {@link android.util.Base64#URL_SAFE,android.util.Base64#NO_WRAP},主要是为了和Base64加密对应。

    *

    * @param str 需要解码的数据

    * @return Base64解码后的数据

    */

    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私钥解密的流程,所以需要和服务端沟通商量好。

具体代码实现:

    private final String RSA_PUB_KEY = "服务端给的公钥";

    private final String RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding";

    /**

    * 公钥加密

    *

    * @param data          要加密的数据

    * @param key            公钥

    * @param transformation 算法/模式/填充

    * @return 加密后的数据

    */

    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.为了保证网络通信中的通信数据安全,首先采用HTTPS协议和公钥密钥体制中的RSA加密。

2.因为是RSA运算速度慢,所以采用运算速度快且安全性高的对称密钥密码体制中的AES对所

有传输数据进行加密,然后再用RSA对AES密钥加密,这样既能保证安全又能保证性能。

3.RSA公钥和私钥由服务端生成,公钥放在客户端,私钥放在服务端。

4.数据加密后采用Base64编码,数据解密前采用Base64解码。

5.编码格式同一采用UTF-8

————————————————

版权声明:本文为CSDN博主「麦田里的守望者-Jiang」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/wangjiang_qianmo/article/details/88073848

相关文章

  • 客户端与服务端数据加密传输方案

    前言 从前一篇网络安全基础要点知识介绍中可以知道,在网络通信中,通信传输数据容易被截取或篡改,如果在传输用户隐私数...

  • uni-app 海报

    海报方案 目前主要的方案有: 服务端生成 客户端生成 服务端 + 客户端 一、客户端生成方案对比 1.1 爸妈搜海...

  • 周报 第一期

    一、 概念 客户端:客户端与服务端对应,发送请求接受数据并将接受的数据展示出来。服务端:接受客户端发来的请求,并会...

  • http接口幂等性

    客户端与服务端进行两次通信来check 1、客户端请求服务端获取token 2、客户端用token提交数据给服务端...

  • Netty应答服务

    需求:服务端接收客户端发送的数据,并将数据返回给客户端 客户端代码 客户端handler 服务端代码 服务端han...

  • WebSocket:即时通信

    遵循TCP协议,客户端与服务端全双工通信 之前服务端是不能向客户端发数据,必须客户端轮询,多次向服务端发送请求,服...

  • RSA和AES双向加密(Android 和 Java)

    专注于Android开发,分享经验总结,欢迎加入 Github 接口数据加密 方案:客户端请求时数据加密,服务端解...

  • Retrofit自定义Converter数据异常处理攻略

    开发中客户端时常与服务端打交道,客户端请求参数到服务端,服务端返回响应数据到客户端,开发中用的比较多的就是Json...

  • Netty空闲检测之读空闲

    客户端与服务端通信的时候,服务端如何感知到客户端下线.客户端可以每4秒向服务端发送一个数据,服务端每5秒进行空闲检...

  • 2021-09-27 网络编程(TCP传输2)

    演示TCP传输的客户端和服务端的互访需求:客户端给服务端发送数据,服务端收到数据后,给客户端反馈信息

网友评论

      本文标题:客户端与服务端数据加密传输方案

      本文链接:https://www.haomeiwen.com/subject/twfzmctx.html