import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; import 'package:pointycastle/asn1/asn1_parser.dart'; import 'package:pointycastle/asn1/primitives/asn1_integer.dart'; import 'package:pointycastle/asn1/primitives/asn1_sequence.dart'; import 'package:pointycastle/asymmetric/api.dart'; import 'package:pointycastle/digests/sha256.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/flavors.dart'; import 'package:star_lock/network/start_chart_api.dart'; import 'package:star_lock/talk/startChart/command/message_command.dart'; import 'package:star_lock/talk/startChart/constant/ip_constant.dart'; import 'package:star_lock/talk/startChart/constant/listen_addr_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/relay_info_entity.dart'; import 'package:star_lock/talk/startChart/entity/report_information_data.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/entity/star_chart_register_node_entity.dart'; import 'package:star_lock/tools/deviceInfo_utils.dart'; import 'package:star_lock/tools/storage.dart'; import 'package:uuid/uuid.dart'; import 'dart:convert'; import 'dart:typed_data'; import 'package:crypto/crypto.dart'; import 'package:pointycastle/export.dart' as pc; // 为 Pointy Castle 添加命名空间 import 'package:asn1lib/asn1lib.dart' as asn1lib; // Prefix for asn1lib class StartChartManage { // 私有构造函数,防止外部直接new对象 StartChartManage._internal(); // 单例对象 static final StartChartManage _instance = StartChartManage._internal(); // 工厂构造函数,返回单例对象 factory StartChartManage() { return _instance; } // 产品昵称 final String _productName = F.navTitle; RawDatagramSocket? _udpSocket; late String remoteHost = ''; // 远程主机地址(服务器返回) late int remotePort = 0; // 远程主机端口(服务器返回) final int localPort = 62289; // 本地端口 String localPublicHost = ''; // 本地公网ip地址 int heartbeatIntervalTime = 1; // 心跳包间隔时间(s) Timer? _heartBeatTimer; // 心跳包定时器 bool _heartBeatTimerRunning = false; // 心跳包定时任务发送状态 String ToPeerId = ''; // 对端ID String FromPeerId = ''; // 我的ID bool _isLoginSuccessfulToStartChart = false; // 是否在星图登录成功 // 星图服务初始化 Future init() async { // 客户端注册 await _clientRegister(); // 中继查询 await _relayQuery(); // 上报 await reportInformation(); // 初始化udp服务 await _onlineRelayService(); // 发送心跳消息 _sendHeartbeatMessage(); // 发送送上线消息 await _sendOnlineMessage(); } /// 客户端注册 Future _clientRegister() async { _log(text: '开始注册客户端'); final StarChartRegisterNodeEntity requestStarChartRegisterNode = await _requestStarChartRegisterNode(); await _saveStarChartRegisterNodeToStorage(requestStarChartRegisterNode); _log(text: '获取到星图注册节点信息:$requestStarChartRegisterNode'); } // 中继查询 Future _relayQuery() async { final RelayInfoEntity relayInfoEntity = await StartChartApi.to.relayQueryInfo(); _saveRelayInfoEntityToStorage(relayInfoEntity); if (relayInfoEntity.client_addr != null) { localPublicHost = relayInfoEntity.client_addr!; } if (relayInfoEntity.relay_list?.length != 0) { final data = relayInfoEntity.relay_list?[0]; FromPeerId = data?.peerID ?? ''; final parseUdpUrl = _parseUdpUrl(data?.listenAddr ?? ''); remoteHost = parseUdpUrl['host'] ?? ''; remotePort = parseUdpUrl['port'] ?? ''; _log(text: '中继信息----》${data}'); } } void closeUdpSocket() { if (_udpSocket != null) { _udpSocket?.close(); } } // 初始化udp Future _onlineRelayService() async { var addressIListenFrom = InternetAddress.anyIPv4; RawDatagramSocket.bind(addressIListenFrom, localPort) .then((RawDatagramSocket socket) { _udpSocket = socket; /// 广播功能 _udpSocket!.broadcastEnabled = true; /// 设置数据接收回调 _onReceiveData(_udpSocket!); }).catchError((error) { _log(text: 'Failed to bind UDP socket: $error'); }); } // 接收返回的数据 void _onReceiveData(RawDatagramSocket socket) { socket.listen((RawSocketEvent event) { if (event == RawSocketEvent.read) { Datagram? dg = socket.receive(); try { _log(text: '收到消息---> 长度:${dg?.data?.length}, 数据:${dg?.data}'); if (dg?.data != null) { final deserialize = ScpMessage.deserialize(dg!.data); _log(text: 'Udp收到结构体数据---》$deserialize'); } } catch (e) { _log(text: '❌ Udp ----> $e'); } } }); } // 上报信息至发现服务 Future reportInformation() async { _log(text: '上报信息至发现服务'); // 构建参数 ReportInformationData data = await _makeReportInformationData(); final response = await StartChartApi.to.reportInformation( reportInformationData: data, ); if (response.statusCode == 200) { // TODO 登录成功之后的逻辑 _log(text: '星图登录成功'); } } // 发送上线消息 Future _sendOnlineMessage() async { // 组装上线消息 final message = MessageCommand.goOnlineRelay(); await _sendMessage(message: message); } // 发送回声测试消息 void sendEchoMessage({required String ToPeerId}) async { final message = MessageCommand.echoMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, ); await _sendMessage(message: message); } // 发送心跳包消息 void _sendHeartbeatMessage() { if (_heartBeatTimerRunning) { _log(text: '心跳已经开始了. 请勿重复发送心跳包消息'); return; } _heartBeatTimer ??= Timer.periodic( Duration( seconds: heartbeatIntervalTime, ), (Timer timer) async { final List message = MessageCommand.heartbeatMessage(); await _sendMessage(message: message); }, ); _heartBeatTimerRunning = true; } // 停止定时发送心跳包 void stopHeartbeat() { _heartBeatTimer?.cancel(); _heartBeatTimer = null; // 清除定时器引用 _heartBeatTimerRunning = false; _log(text: '发送心跳包结束'); } // 发送消息 Future _sendMessage({required List message}) async { _log(text: '发送给中继的消息体:${message},序列化之后的数据:【${bytesToHex(message)}】'); var result = await _udpSocket?.send( message, InternetAddress(remoteHost), remotePort); if (result != message.length) { AppLog.log('❌Udp ----> send data error $result ${message.length}'); _udpSocket = null; } } // 请求注册节点 Future _requestStarChartRegisterNode() async { // 获取设备信息 final Map deviceInfo = await _getDeviceInfo(); // 发送注册节点请求 final StarChartRegisterNodeEntity response = await StartChartApi.to.starChartRegisterNode( product: _productName, model: '${deviceInfo['brand']}_${deviceInfo['model']}', name: '${deviceInfo['id']}', unique: deviceInfo['id'] ?? Uuid().v1(), ); return response; } // 保存星图注册节点信息至缓存 Future _saveStarChartRegisterNodeToStorage( StarChartRegisterNodeEntity starChartRegisterNodeEntity) async { if (starChartRegisterNodeEntity != null) { await Storage.saveStarChartRegisterNodeInfo(starChartRegisterNodeEntity); _log(text: '注册成功'); } } // 保存星图中继服务器信息至缓存 Future _saveRelayInfoEntityToStorage( RelayInfoEntity relayInfoEntity) async { if (relayInfoEntity != null) { await Storage.saveRelayInfo(relayInfoEntity); } } // 构造上报信息数据参数 Future _makeReportInformationData() async { // 获取当前时间戳 int currentTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; // 获取公钥 final publicKey = await getPublicKey(); // 获取私钥 final privateKey = await getPrivateKey(); // 生成签名 final sign = await _generateSign( currentTimestamp: currentTimestamp, privateKeyHex: privateKey, ); // 获取本机所有ip地址和中继返回的外网地址 final List listenAddrDataList = await _makeListenAddrDataList(); // 从缓存中获取中继信息 final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo(); final RelayServiceData relayServiceData = RelayServiceData( name: relayInfoEntity?.relay_list?[0].name ?? '', listen_addr: relayInfoEntity?.relay_list?[0].listenAddr ?? '', peers_max: relayInfoEntity?.relay_list?[0].peerMax ?? 0, peers_current: relayInfoEntity?.relay_list?[0].peerCurrent ?? 0, ); ReportInformationData data = ReportInformationData( id: FromPeerId, public_key: publicKey, listen_addr: listenAddrDataList, relay_service: relayServiceData, time: currentTimestamp, sign: sign, ); return data; } // 获取本机所有ip地址和中继返回的外网地址 Future> _makeListenAddrDataList() async { final List listenAddrDataList = []; final List localIp = await _getAllIpAddresses(); // 从缓存中获取中继信息 final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo(); if (relayInfoEntity != null && relayInfoEntity.client_addr != null) { listenAddrDataList.add( ListenAddrData( type: ListenAddrTypeConstant.relay, address: IpConstant.udpUrl + relayInfoEntity.client_addr! + ':' + localPort.toString(), ), ); } localIp.forEach((element) { listenAddrDataList.add( ListenAddrData( type: ListenAddrTypeConstant.local, address: IpConstant.udpUrl + element + ':' + localPort.toString(), ), ); }); return listenAddrDataList ?? []; } /// 获取本机所有ip Future> _getAllIpAddresses() async { final List ipAddresses = []; try { final List interfaces = await NetworkInterface.list( includeLoopback: true, type: InternetAddressType.any, ); for (final interface in interfaces) { for (final address in interface.addresses) { if (address.address.isNotEmpty && !IpConstant.reportExcludeIp.contains(address.address)) { ipAddresses.add(address.address); } } } } catch (e) { _log(text: '❌--->获取本机IP时出现错误: $e'); } return ipAddresses ?? []; } void _log({required String text}) { AppLog.log('$_productName=====${text}'); } /// 获取设备信息 Future> _getDeviceInfo() async { final Map deviceInfo = await DeviceInfoUtils.getDeviceInfo(); return deviceInfo; } /// 解析 UDP URL 并提取 IP 地址和端口号 Map _parseUdpUrl(String url) { // 使用正则表达式匹配 IP 地址和端口号 final regex = RegExp(r'udp://(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)') .firstMatch(url); if (regex != null) { final ip = regex.group(1); final portStr = regex.group(2); final port = int.tryParse(portStr ?? ''); if (ip != null && port != null) { return {'host': ip, 'port': port}; } } throw FormatException('无法解析 URL 格式: $url'); } String bytesToHex(List bytes) { return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(''); } // 生成签名sing Future _generateSign({ required int currentTimestamp, required String privateKeyHex, }) async { String resultSign = ''; try { // 1. 获取当前时间戳并编码为小端字节序 Uint8List signData = Uint8List(4); ByteData.view(signData.buffer) .setUint32(0, currentTimestamp, Endian.little); // 2. 对时间戳数据计算 SHA-256 哈希值 Digest hash = sha256.convert(signData); // 3. 使用 RSA 私钥进行签名 (需要提供 RsaPrivateKey) pc.RSAPrivateKey privateKey = loadPrivateKey(privateKeyHex); // 需要加载你的 RSA 私钥 Uint8List signature = rsaSign(privateKey, hash.bytes); // 4. 将签名结果转换为十六进制字符串 String hexSignature = signature .map((byte) => byte.toRadixString(16).padLeft(2, '0')) .join(); resultSign = hexSignature; } catch (e) { _log(text: '❌--->上报信息生成签名时出现错误: $e'); } return resultSign ?? ''; } /// 自定义 PEM 格式的 RSA 私钥解析器 pc.RSAPrivateKey loadPrivateKey(String privateKeyHex) { // 将十六进制字符串转换为字节数组 final uint8list = Uint8List.fromList(hexToBytes(privateKeyHex)); try { // 使用 asn1lib 的 ASN1Parser 解析 final asn1Parser = asn1lib.ASN1Parser(uint8list); final topLevelSeq = asn1Parser.nextObject() as asn1lib.ASN1Sequence; final modulus = bytesToBigInt( (topLevelSeq.elements[1] as asn1lib.ASN1Integer).valueBytes()); final privateExponent = bytesToBigInt( (topLevelSeq.elements[3] as asn1lib.ASN1Integer).valueBytes()); final p = bytesToBigInt( (topLevelSeq.elements[4] as asn1lib.ASN1Integer).valueBytes()); final q = bytesToBigInt( (topLevelSeq.elements[5] as asn1lib.ASN1Integer).valueBytes()); return pc.RSAPrivateKey(modulus, privateExponent, p, q); } catch (e) { // 如果发生解码错误,打印错误信息 print("Error decoding private key: $e"); rethrow; } } // 解析对端数据 Future analyzeInformationOtherEnd() async { await StartChartApi.to.analyzeInformationOtherEnd(peerId: ToPeerId); } // 将十六进制字符串转换为字节数组 List hexToBytes(String hex) { return List.generate(hex.length ~/ 2, (i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16)); } BigInt bytesToBigInt(Uint8List bytes) { return BigInt.parse( bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(), radix: 16, ); } /// 使用 RSA 私钥进行 PKCS#1 v1.5 签名 Uint8List rsaSign(pc.RSAPrivateKey privateKey, List data) { final signer = pc.RSASigner(pc.SHA256Digest(), '06052b24030203') ..init(true, pc.PrivateKeyParameter(privateKey)); return signer.generateSignature(Uint8List.fromList(data)).bytes; } Future getPublicKey() async { // 从缓存中获取星图注册节点信息 final StarChartRegisterNodeEntity? starChartRegisterNodeInfo = await Storage.getStarChartRegisterNodeInfo(); return starChartRegisterNodeInfo?.peer?.publicKey ?? ''; } Future getPrivateKey() async { // 从缓存中获取星图注册节点信息 final StarChartRegisterNodeEntity? starChartRegisterNodeInfo = await Storage.getStarChartRegisterNodeInfo(); return starChartRegisterNodeInfo?.peer?.privateKey ?? ''; } }