基于Flutter与Python的安全即时通信系统设计与实现
本文详细介绍了基于Flutter和Python构建安全即时通信系统的设计与实现过程,重点阐述了如何运用AES加密算法保障消息传输的机密性和完整性。
- 软件开发
- 网络安全
安全的即时通信系统
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是对称加密,因此需要安全地传输密钥。我们的系统将采用密钥交换协议来安全地共享密钥。

2.2相应功能概要设计
AES 密钥相关功能设计
| 功能 | 描述 |
|---|---|
| AES密钥生成 | 系统将动态生成AES密钥。每次用户会话开始时,都会生成一个新的密钥,以确保通信的安全性。 |
| 传输向量 | 每个加密的消息都会附带一个传输向量,以确保即使相同的消息被重复加密,也会产生不同的密文。 |
| 加密过程 | 使用生成的AES密钥和传输向量对用户的信息进行加密,确保在传输过程中信息的机密性。 |
| 解密过程 | 接收方使用相同的AES密钥和传输向量对收到的信息进行解密,以还原原始消息。 |
| 密钥和IV管理 | 密钥和初始化向量不在服务器上存储,且每次打开页面都会重新生成,增加了系统的安全性。 |
3AES加密系统(AES密钥模块)详细设计
3.1 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传输结果:


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




留言讨论
0 条留言
正在加载留言...