# 安全的即时通信系统

# 1 系统设计

# 1.1 设计目标

本项目旨在实现一个安全的即时通信系统,主要关注点包括信息的加密传输、信息完整性的保障以及用户的隐私保护。系统的主要特点是:

新用户注册与登录功能,用户身份的安全验证,信息加密传输,采用公钥和对称密钥结合,密钥管理功能,信息完整性的校验,用户管理功能。

# 1.2 信息要求

用户注册与登录: 系统应允许新用户创建账户,并提供登录功能。注册过程中应收集必要的信息,如用户名、密码、电子邮件等。密码应通过哈希等安全手段存储。

即时消息交换: 用户应能够实时发送和接收文本消息。系统应支持多种消息类型,如文本、图片、文件等。

# 1.3 开发和运行环境选择

开发语言: 前台开发语言为 Flutter/Dart 以及 React+mui + 后台 python-Flask 框架

开发工具:andriodStudio,pycharm

后台数据库:Mysql

运行环境: MacOS 操作系统,ios 系统

# 2 网络安全相关设计

# 2.1 网络安全相关原理论述

在即时通信系统中,网络安全是至关重要的。本系统采用高级加密标准(AES)进行数据加密,以保证信息的机密性和完整性。AES 是一种广泛使用的对称加密算法,提供了强大的安全性。下面是一些关键概念:

对称加密: 在对称加密中,相同的密钥用于加密和解密数据。这意味着发送方和接收方必须共享同一个密钥。

AES 密钥: AES 密钥是一个特定长度的位序列,用于 AES 加密算法。在我们的系统中,密钥将被动态生成,并且在每次会话开始时更新。

传输向量(IV): 初始化向量(IV)是 AES 加密中使用的一个随机数,用于保证即使相同的数据被加密多次,每次生成的密文也是不同的。这提高了安全性,因为它防止了某些类型的攻击。

密钥交换: 由于 AES 是对称加密,因此需要安全地传输密钥。我们的系统将采用密钥交换协议来安全地共享密钥。

img

# 2.2 相应功能概要设计

AES 密钥相关功能设计

功能 描述
AES 密钥生成 系统将动态生成 AES 密钥。每次用户会话开始时,都会生成一个新的密钥,以确保通信的安全性。
传输向量 每个加密的消息都会附带一个传输向量,以确保即使相同的消息被重复加密,也会产生不同的密文。
加密过程 使用生成的 AES 密钥和传输向量对用户的信息进行加密,确保在传输过程中信息的机密性。
解密过程 接收方使用相同的 AES 密钥和传输向量对收到的信息进行解密,以还原原始消息。
密钥和 IV 管理 密钥和初始化向量不在服务器上存储,且每次打开页面都会重新生成,增加了系统的安全性。

# 3AES 加密系统(AES 密钥模块)详细设计

# 3.1 AES 密钥传输过程:

AES密钥传输流程图

# 3.2 AES 密钥使用过程:密钥使用过程(加密解密)

# 3.3 AES 模块代码

# 3.3.1AES 密钥生成

获取好友的对话密钥,如果不存在则生成

// 生成 AES 密钥
  Uint8List _generateAESKey() {
    var secureRandom = SecureRandom("Fortuna")..seed(KeyParameter(Uint8List(32)));
    var key = Uint8List(16); // 128 位
    for (int i = 0; i < key.length; i++) {
      key[i] = secureRandom.nextUint8();
    }
    return key;
  }
  // 获取或生成 AES 密钥
  Future<Uint8List> getOrGenerateAESKey(int friendId) async {
    if (_aesKeys.containsKey(friendId)) {
      return _aesKeys[friendId]!;
    } else {
      var aesKey = _generateAESKey();
      _aesKeys[friendId] = aesKey;
      await _storage.write(key: 'aes_key_$friendId', value: base64.encode(aesKey));
      print("已生成AESK:");
      print(aesKey);
      return aesKey;
    }
  }

# 3.3.2 密钥传输

# 1. 从 PEM 中解析 RSA 公钥
  • 先消除 PEM 格式:
List<int> decodePEM(String pem) {
    // 去除 PEM 字符串中的头部和尾部,以及换行符
    var startsWith = [
      '-----BEGIN PUBLIC KEY-----',
      '-----BEGIN RSA PUBLIC KEY-----'
    ];
    var endsWith = [
      '-----END PUBLIC KEY-----',
      '-----END RSA PUBLIC KEY-----'
    ];
    bool isOpenPgp = pem.contains('-----BEGIN PGP PUBLIC KEY BLOCK-----');
    if (isOpenPgp) {
      startsWith.add('-----BEGIN PGP PUBLIC KEY BLOCK-----');
      endsWith.add('-----END PGP PUBLIC KEY BLOCK-----');
    }
    pem = pem.replaceAll('\r\n', '').replaceAll('\r', '').replaceAll('\n', '');
    String? start;
    for (String s in startsWith) {
      if (pem.startsWith(s)) start = s;
    }
    String? end;
    for (String s in endsWith) {
      if (pem.endsWith(s)) end = s;
    }
    if (start == null || end == null) {
      throw Exception('Invalid PEM');
    }
    pem = pem.substring(start.length, pem.length - end.length);
    // 将 Base64 字符串解码为二进制数据
    return base64.decode(pem);
  }
  • 得到字符串之后再提取 RSA 公钥:
RSAPublicKey parsePublicKeyFromPem(String pemString) {
    // 解码 PEM 字符串以获取二进制数据
    final publicKeyDER = Uint8List.fromList(decodePEM(pemString));
    // 解析 ASN.1 结构
    var asn1Parser = ASN1Parser(publicKeyDER);
    var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
    // 确保序列包含至少两个元素(模数和指数)
    if (topLevelSeq.elements.length < 2) {
      throw Exception('Invalid ASN.1 sequence length');
    }
    var modulus = topLevelSeq.elements[0] as ASN1Integer;
    var exponent = topLevelSeq.elements[1] as ASN1Integer;
    // 创建 RSAPublicKey 对象
    return RSAPublicKey(modulus.valueAsBigInteger!, exponent.valueAsBigInteger!);
}

# 2. 传输密钥

  • 使用 RSA 进行加密,发送 AES 密钥
Uint8List convertStringToUint8List(String str) {
    // 去除字符串两端的方括号,并分割字符串
    var parts = str.substring(1, str.length - 1).split(',');
    // 将分割得到的字符串数组转换为 int 数组
    List<int> intList = parts.map((part) => int.parse(part.trim())).toList();
    // 使用 int 数组创建 Uint8List
    return Uint8List.fromList(intList);
  }
  Future<String> encryptMessage(String message, String publicKeyPem) async {
    final publicKey = parsePublicKeyFromPem(publicKeyPem);
    final encrypter = en.Encrypter(en.RSA(publicKey: publicKey));
    final encrypted = encrypter.encrypt(message);
    return encrypted.base64;
  }
void sendKey(String message) async {
    // 等待获取好友的公钥
    String publicKey = await getFriendPublicKey(widget.friendId);
    // Encrypt the key
    String encryptedKey = await encryptMessage(message, publicKey);
    print("打印密钥: $message");
    print("打印密文");
    print(encryptedKey);
    // Send the encrypted key to the backend
    http.post(
      Uri.parse('http://localhost:5000/send_message'),
      headers: <String, String>{
        'Content-Type': 'application/json; charset=UTF-8',
      },
      body: jsonEncode(<String, dynamic>{
        'sender_id': widget.userId,
        'receiver_id': widget.friendId,
        'message': encryptedKey,
        'type': 'AESkey',
      }),
    );
    print("AESKey 已发送");
    // 将 AES 密钥保存在发送者的本地
    Uint8List aesKey = convertStringToUint8List(message);  // 假设 message 是 Uint8List 格式的密钥
    _userProvider.saveAESKeyForUser(widget.friendId, aesKey);
    print("密钥已经保存!!!");
  }

# 3. 信息加密

  • 之后发送信息使用 AES 密钥加密。
void sendMessage() async{
    final text = _messageController.text;
    if (text.isNotEmpty) {
      final newMessage = Message(content: text, isUserMessage: true);
      setState(() {
        messages.add(newMessage);
      });
      _messageController.clear();
      var aesKey = await Provider.of<UserProvider>(context, listen: false).getOrGenerateAESKey(widget.friendId);
      print("加密时候用的aes密钥:$aesKey");
      // 生成随机的 IV
      var iv = createSecureRandom().nextBytes(16);
      print("加密时候的初始向量:$iv");
      // 加密消息,并将 IV 附加到密文前面
      var encryptedMessage = base64.encode(iv) + base64.encode(encryptWithAES(text, aesKey, iv));
      Future.delayed(Duration(seconds: 2), () {
        if (mounted) {
          setState(() {
            newMessage.status = MessageStatus.sent;
          });
        }
      }).catchError((error) {
        if (mounted) {
          setState(() {
            newMessage.status = MessageStatus.failed;
          });
        }
      });
// 发送消息到后端
      http.post(
        Uri.parse('http://localhost:5000/send_message'),
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
        },
        body: jsonEncode(<String, dynamic>{
          'sender_id': widget.userId,
          'receiver_id': widget.friendId,
          'message': encryptedMessage,
          'type':'message',
        }),
      );
      _scrollToBottom();
    }
  }

AES 传输结果:

img

img

# 4 系统测试

点击软件聊天界面,观察是否有信息发送,且信息是否被正确加密解密

image-20240302180355223

image-20240302180508245

image-20240302201733746

image-20240302203136493

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

尘落 微信支付

微信支付

尘落 支付宝

支付宝

尘落 贝宝

贝宝