fix:调整新的中继协议中的上报签名逻辑
This commit is contained in:
parent
a92ae906ef
commit
7a4fbe19d8
@ -1,19 +1,24 @@
|
|||||||
|
import 'package:crc32_checksum/crc32_checksum.dart';
|
||||||
import 'package:star_lock/talk/startChart/constant/message_type_constant.dart';
|
import 'package:star_lock/talk/startChart/constant/message_type_constant.dart';
|
||||||
import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart';
|
import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart';
|
||||||
import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart';
|
import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart';
|
||||||
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
||||||
|
|
||||||
class MessageCommand {
|
class MessageCommand {
|
||||||
|
|
||||||
/// 客户端去中继上线命令
|
/// 客户端去中继上线命令
|
||||||
static List<int> goOnlineRelay() {
|
static List<int> goOnlineRelay({
|
||||||
|
required String FromPeerId,
|
||||||
|
required String ToPeerId,
|
||||||
|
}) {
|
||||||
String serializedBytesString = ScpMessage(
|
String serializedBytesString = ScpMessage(
|
||||||
ProtocolFlag: ProtocolFlagConstant.scp01,
|
ProtocolFlag: ProtocolFlagConstant.scp01,
|
||||||
MessageType: MessageTypeConstant.Req,
|
MessageType: MessageTypeConstant.Req,
|
||||||
MessageId: 1,
|
MessageId: 1,
|
||||||
SpTotal: 0,
|
SpTotal: 0,
|
||||||
SpIndex: 0,
|
SpIndex: 0,
|
||||||
FromPeerId: 'ToPeerId',
|
FromPeerId: FromPeerId,
|
||||||
ToPeerId: 'ToPeerId',
|
ToPeerId: ToPeerId,
|
||||||
Payload: 'hello',
|
Payload: 'hello',
|
||||||
PayloadCRC: 55230,
|
PayloadCRC: 55230,
|
||||||
PayloadLength: 5,
|
PayloadLength: 5,
|
||||||
@ -46,15 +51,18 @@ class MessageCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 心跳消息
|
// 心跳消息
|
||||||
static List<int> heartbeatMessage() {
|
static List<int> heartbeatMessage({
|
||||||
|
required String FromPeerId,
|
||||||
|
required String ToPeerId,
|
||||||
|
}) {
|
||||||
ScpMessage message = ScpMessage(
|
ScpMessage message = ScpMessage(
|
||||||
ProtocolFlag: ProtocolFlagConstant.scp01,
|
ProtocolFlag: ProtocolFlagConstant.scp01,
|
||||||
MessageType: MessageTypeConstant.Req,
|
MessageType: MessageTypeConstant.Req,
|
||||||
MessageId: 1,
|
MessageId: 1,
|
||||||
SpTotal: 0,
|
SpTotal: 0,
|
||||||
SpIndex: 0,
|
SpIndex: 0,
|
||||||
FromPeerId: 'FromPeerId',
|
FromPeerId: FromPeerId,
|
||||||
ToPeerId: 'ToPeerId',
|
ToPeerId: ToPeerId,
|
||||||
Payload: 'hello',
|
Payload: 'hello',
|
||||||
PayloadCRC: 55230,
|
PayloadCRC: 55230,
|
||||||
PayloadLength: 5,
|
PayloadLength: 5,
|
||||||
@ -73,4 +81,9 @@ class MessageCommand {
|
|||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int calculationCrc(payload){
|
||||||
|
var checkSumResult = Crc32.calculate(payload);
|
||||||
|
return checkSumResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,85 +144,135 @@ class ScpMessage {
|
|||||||
return bytesToHexString;
|
return bytesToHexString;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 反序列化方法
|
|
||||||
static ScpMessage deserialize(List<int> bytes) {
|
static ScpMessage deserialize(List<int> bytes) {
|
||||||
final message = ScpMessage();
|
final message = ScpMessage();
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
// ProtocolFlag (4 bytes)
|
// ProtocolFlag (4 bytes)
|
||||||
if (bytes.length - offset >= 4) {
|
if (bytes.length - offset >= 4) {
|
||||||
message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4));
|
message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4));
|
||||||
offset += 4;
|
offset += 4;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid ProtocolFlag length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageType (1 byte)
|
// MessageType (1 byte)
|
||||||
if (bytes.length - offset >= 1) {
|
if (bytes.length - offset >= 1) {
|
||||||
message.MessageType = bytes[offset];
|
message.MessageType = bytes[offset];
|
||||||
offset += 1;
|
offset += 1;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid MessageType length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageId (2 bytes, little-endian)
|
// MessageId (2 bytes, little-endian)
|
||||||
if (bytes.length - offset >= 2) {
|
if (bytes.length - offset >= 2) {
|
||||||
message.MessageId = (bytes[offset + 1] << 8) | bytes[offset];
|
message.MessageId = (bytes[offset + 1] << 8) | bytes[offset];
|
||||||
offset += 2;
|
offset += 2;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid MessageId length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpTotal (1 byte)
|
// SpTotal (1 byte)
|
||||||
if (bytes.length - offset >= 1) {
|
if (bytes.length - offset >= 1) {
|
||||||
message.SpTotal = bytes[offset];
|
message.SpTotal = bytes[offset];
|
||||||
offset += 1;
|
offset += 1;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid SpTotal length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpIndex (1 byte)
|
// SpIndex (1 byte)
|
||||||
if (bytes.length - offset >= 1) {
|
if (bytes.length - offset >= 1) {
|
||||||
message.SpIndex = bytes[offset];
|
message.SpIndex = bytes[offset];
|
||||||
offset += 1;
|
offset += 1;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid SpIndex length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromPeerId (字符串,长度固定为44字节)
|
// FromPeerId (字符串,长度固定为44字节)
|
||||||
if (bytes.length - offset >= 44) {
|
if (bytes.length - offset >= 44) {
|
||||||
message.FromPeerId = utf8.decode(bytes.sublist(offset, offset + 44));
|
message.FromPeerId = utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
|
||||||
offset += 44;
|
offset += 44;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid FromPeerId length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToPeerId (字符串,长度固定为44字节)
|
// ToPeerId (字符串,长度固定为44字节)
|
||||||
if (bytes.length - offset >= 44) {
|
if (bytes.length - offset >= 44) {
|
||||||
message.ToPeerId = utf8.decode(bytes.sublist(offset, offset + 44));
|
message.ToPeerId = utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
|
||||||
offset += 44;
|
offset += 44;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid ToPeerId length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayloadType (2 bytes, little-endian)
|
// PayloadType (2 bytes, little-endian)
|
||||||
if (bytes.length - offset >= 2) {
|
if (bytes.length - offset >= 2) {
|
||||||
message.PayloadType = (bytes[offset + 1] << 8) | bytes[offset];
|
message.PayloadType = (bytes[offset + 1] << 8) | bytes[offset];
|
||||||
offset += 2;
|
offset += 2;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid PayloadType length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayloadCRC (2 bytes, little-endian)
|
// PayloadCRC (2 bytes, little-endian)
|
||||||
if (bytes.length - offset >= 2) {
|
if (bytes.length - offset >= 2) {
|
||||||
message.PayloadCRC = (bytes[offset + 1] << 8) | bytes[offset];
|
message.PayloadCRC = (bytes[offset + 1] << 8) | bytes[offset];
|
||||||
offset += 2;
|
offset += 2;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid PayloadCRC length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayloadLength (4 bytes, big-endian)
|
// PayloadLength (4 bytes, little-endian)
|
||||||
if (bytes.length - offset >= 4) {
|
if (bytes.length - offset >= 4) {
|
||||||
|
// 打印PayloadLength对应的4个字节
|
||||||
|
print('PayloadLength bytes: ${bytes.sublist(offset, offset + 4)}');
|
||||||
|
|
||||||
message.PayloadLength = (bytes[offset] |
|
message.PayloadLength = (bytes[offset] |
|
||||||
(bytes[offset + 1] << 8) |
|
(bytes[offset + 1] << 8) |
|
||||||
(bytes[offset + 2] << 16) |
|
(bytes[offset + 2] << 16) |
|
||||||
(bytes[offset + 3] << 24));
|
(bytes[offset + 3] << 24)); // 修正为 little-endian
|
||||||
offset += 4;
|
offset += 4;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid PayloadLength length");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Payload (字符串,转换为字节)
|
// Payload (字符串,转换为字节)
|
||||||
if (message.PayloadLength != null &&
|
if (message.PayloadLength != null && bytes.length - offset >= message.PayloadLength!) {
|
||||||
bytes.length - offset >= message.PayloadLength!) {
|
message.Payload = utf8.decode(bytes.sublist(offset, offset + message.PayloadLength!));
|
||||||
message.Payload =
|
|
||||||
utf8.decode(bytes.sublist(offset, offset + message.PayloadLength!));
|
|
||||||
offset += message.PayloadLength!;
|
offset += message.PayloadLength!;
|
||||||
|
} else {
|
||||||
|
throw FormatException("Invalid Payload or PayloadLength");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证PayloadCRC
|
||||||
|
// if (message.Payload != null) {
|
||||||
|
// var crcBytes = List<int>.from(utf8.encode(message.Payload!));
|
||||||
|
// var calculatedCrc = _calculateCrc16(crcBytes);
|
||||||
|
// if (calculatedCrc != message.PayloadCRC) {
|
||||||
|
// throw FormatException("PayloadCRC verification failed. Expected: ${message.PayloadCRC}, Actual: $calculatedCrc");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
// CRC-16 计算函数(示例实现,可能需要根据具体协议调整)
|
||||||
|
static int _calculateCrc16(List<int> data) {
|
||||||
|
const poly = 0x8005;
|
||||||
|
int crc = 0xFFFF;
|
||||||
|
|
||||||
|
for (final b in data) {
|
||||||
|
crc ^= b << 8;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if ((crc & 0x8000) != 0) {
|
||||||
|
crc = (crc << 1) ^ poly;
|
||||||
|
} else {
|
||||||
|
crc <<= 1;
|
||||||
|
}
|
||||||
|
crc &= 0xFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static String bytesToHex(List<int> bytes) {
|
static String bytesToHex(List<int> bytes) {
|
||||||
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');
|
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');
|
||||||
|
|||||||
@ -2,11 +2,17 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
import 'package:cryptography/cryptography.dart';
|
||||||
|
import 'package:encrypt/encrypt.dart';
|
||||||
|
import 'package:fast_rsa/fast_rsa.dart' as fastRsa;
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:pointycastle/asn1/asn1_parser.dart';
|
import 'package:pointycastle/asn1/asn1_parser.dart';
|
||||||
import 'package:pointycastle/asn1/primitives/asn1_integer.dart';
|
import 'package:pointycastle/asn1/primitives/asn1_integer.dart';
|
||||||
import 'package:pointycastle/asn1/primitives/asn1_sequence.dart';
|
import 'package:pointycastle/asn1/primitives/asn1_sequence.dart';
|
||||||
import 'package:pointycastle/asymmetric/api.dart';
|
import 'package:pointycastle/asymmetric/api.dart';
|
||||||
import 'package:pointycastle/digests/sha256.dart';
|
import 'package:pointycastle/digests/sha256.dart';
|
||||||
|
import 'package:pointycastle/export.dart' as pc;
|
||||||
import 'package:star_lock/app_settings/app_settings.dart';
|
import 'package:star_lock/app_settings/app_settings.dart';
|
||||||
import 'package:star_lock/flavors.dart';
|
import 'package:star_lock/flavors.dart';
|
||||||
import 'package:star_lock/network/start_chart_api.dart';
|
import 'package:star_lock/network/start_chart_api.dart';
|
||||||
@ -55,11 +61,11 @@ class StartChartManage {
|
|||||||
String ToPeerId = ''; // 对端ID
|
String ToPeerId = ''; // 对端ID
|
||||||
String FromPeerId = ''; // 我的ID
|
String FromPeerId = ''; // 我的ID
|
||||||
|
|
||||||
bool _isLoginSuccessfulToStartChart = false; // 是否在星图登录成功
|
final String echoPeerId = '3phX8Ng2cZHz5NtP8xAf6nYy2z1BYytoejgjoHrWMGhH';
|
||||||
|
|
||||||
// 星图服务初始化
|
// 星图服务初始化
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
// 客户端注册
|
// 节点注册
|
||||||
await _clientRegister();
|
await _clientRegister();
|
||||||
|
|
||||||
// 中继查询
|
// 中继查询
|
||||||
@ -70,12 +76,6 @@ class StartChartManage {
|
|||||||
|
|
||||||
// 初始化udp服务
|
// 初始化udp服务
|
||||||
await _onlineRelayService();
|
await _onlineRelayService();
|
||||||
|
|
||||||
// 发送心跳消息
|
|
||||||
_sendHeartbeatMessage();
|
|
||||||
|
|
||||||
// 发送送上线消息
|
|
||||||
await _sendOnlineMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 客户端注册
|
/// 客户端注册
|
||||||
@ -85,6 +85,8 @@ class StartChartManage {
|
|||||||
await _requestStarChartRegisterNode();
|
await _requestStarChartRegisterNode();
|
||||||
await _saveStarChartRegisterNodeToStorage(requestStarChartRegisterNode);
|
await _saveStarChartRegisterNodeToStorage(requestStarChartRegisterNode);
|
||||||
_log(text: '获取到星图注册节点信息:$requestStarChartRegisterNode');
|
_log(text: '获取到星图注册节点信息:$requestStarChartRegisterNode');
|
||||||
|
FromPeerId = requestStarChartRegisterNode.peer!.id ?? '';
|
||||||
|
ToPeerId = echoPeerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 中继查询
|
// 中继查询
|
||||||
@ -98,12 +100,11 @@ class StartChartManage {
|
|||||||
|
|
||||||
if (relayInfoEntity.relay_list?.length != 0) {
|
if (relayInfoEntity.relay_list?.length != 0) {
|
||||||
final data = relayInfoEntity.relay_list?[0];
|
final data = relayInfoEntity.relay_list?[0];
|
||||||
FromPeerId = data?.peerID ?? '';
|
|
||||||
final parseUdpUrl = _parseUdpUrl(data?.listenAddr ?? '');
|
final parseUdpUrl = _parseUdpUrl(data?.listenAddr ?? '');
|
||||||
remoteHost = parseUdpUrl['host'] ?? '';
|
remoteHost = parseUdpUrl['host'] ?? '';
|
||||||
remotePort = parseUdpUrl['port'] ?? '';
|
remotePort = parseUdpUrl['port'] ?? '';
|
||||||
_log(text: '中继信息----》${data}');
|
|
||||||
}
|
}
|
||||||
|
_log(text: '中继信息----》${relayInfoEntity}');
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeUdpSocket() {
|
void closeUdpSocket() {
|
||||||
@ -138,6 +139,7 @@ class StartChartManage {
|
|||||||
_log(text: '收到消息---> 长度:${dg?.data?.length}, 数据:${dg?.data}');
|
_log(text: '收到消息---> 长度:${dg?.data?.length}, 数据:${dg?.data}');
|
||||||
if (dg?.data != null) {
|
if (dg?.data != null) {
|
||||||
final deserialize = ScpMessage.deserialize(dg!.data);
|
final deserialize = ScpMessage.deserialize(dg!.data);
|
||||||
|
_log(text: '=============${bytesToHex(dg!.data)}');
|
||||||
_log(text: 'Udp收到结构体数据---》$deserialize');
|
_log(text: 'Udp收到结构体数据---》$deserialize');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -156,15 +158,19 @@ class StartChartManage {
|
|||||||
reportInformationData: data,
|
reportInformationData: data,
|
||||||
);
|
);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
// TODO 登录成功之后的逻辑
|
|
||||||
_log(text: '星图登录成功');
|
_log(text: '星图登录成功');
|
||||||
|
// 发送送上线消息
|
||||||
|
await _sendOnlineMessage();
|
||||||
|
// 发送心跳消息
|
||||||
|
_sendHeartbeatMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送上线消息
|
// 发送上线消息
|
||||||
Future<void> _sendOnlineMessage() async {
|
Future<void> _sendOnlineMessage() async {
|
||||||
// 组装上线消息
|
// 组装上线消息
|
||||||
final message = MessageCommand.goOnlineRelay();
|
final message = MessageCommand.goOnlineRelay(
|
||||||
|
FromPeerId: FromPeerId, ToPeerId: ToPeerId);
|
||||||
await _sendMessage(message: message);
|
await _sendMessage(message: message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +184,10 @@ class StartChartManage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 发送心跳包消息
|
// 发送心跳包消息
|
||||||
void _sendHeartbeatMessage() {
|
void _sendHeartbeatMessage() async {
|
||||||
|
// 从缓存中获取中继信息
|
||||||
|
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
|
||||||
|
final String relayPeerId = relayInfoEntity?.relay_list?[0].peerID ?? '';
|
||||||
if (_heartBeatTimerRunning) {
|
if (_heartBeatTimerRunning) {
|
||||||
_log(text: '心跳已经开始了. 请勿重复发送心跳包消息');
|
_log(text: '心跳已经开始了. 请勿重复发送心跳包消息');
|
||||||
return;
|
return;
|
||||||
@ -188,7 +197,10 @@ class StartChartManage {
|
|||||||
seconds: heartbeatIntervalTime,
|
seconds: heartbeatIntervalTime,
|
||||||
),
|
),
|
||||||
(Timer timer) async {
|
(Timer timer) async {
|
||||||
final List<int> message = MessageCommand.heartbeatMessage();
|
final List<int> message = MessageCommand.heartbeatMessage(
|
||||||
|
FromPeerId: FromPeerId,
|
||||||
|
ToPeerId: relayPeerId,
|
||||||
|
);
|
||||||
await _sendMessage(message: message);
|
await _sendMessage(message: message);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -248,15 +260,16 @@ class StartChartManage {
|
|||||||
|
|
||||||
// 构造上报信息数据参数
|
// 构造上报信息数据参数
|
||||||
Future<ReportInformationData> _makeReportInformationData() async {
|
Future<ReportInformationData> _makeReportInformationData() async {
|
||||||
// 获取当前时间戳
|
// 从缓存中获取中继信息
|
||||||
int currentTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
|
||||||
|
|
||||||
// 获取公钥
|
// 获取公钥
|
||||||
final publicKey = await getPublicKey();
|
final publicKey = await getPublicKey();
|
||||||
// 获取私钥
|
// 获取私钥
|
||||||
final privateKey = await getPrivateKey();
|
final privateKey = await getPrivateKey();
|
||||||
// 生成签名
|
// 生成签名
|
||||||
final sign = await _generateSign(
|
final sign = await _generateSign(
|
||||||
currentTimestamp: currentTimestamp,
|
currentTimestamp: relayInfoEntity!.time ?? 0,
|
||||||
privateKeyHex: privateKey,
|
privateKeyHex: privateKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -264,8 +277,7 @@ class StartChartManage {
|
|||||||
final List<ListenAddrData> listenAddrDataList =
|
final List<ListenAddrData> listenAddrDataList =
|
||||||
await _makeListenAddrDataList();
|
await _makeListenAddrDataList();
|
||||||
|
|
||||||
// 从缓存中获取中继信息
|
//
|
||||||
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
|
|
||||||
final RelayServiceData relayServiceData = RelayServiceData(
|
final RelayServiceData relayServiceData = RelayServiceData(
|
||||||
name: relayInfoEntity?.relay_list?[0].name ?? '',
|
name: relayInfoEntity?.relay_list?[0].name ?? '',
|
||||||
listen_addr: relayInfoEntity?.relay_list?[0].listenAddr ?? '',
|
listen_addr: relayInfoEntity?.relay_list?[0].listenAddr ?? '',
|
||||||
@ -278,7 +290,7 @@ class StartChartManage {
|
|||||||
public_key: publicKey,
|
public_key: publicKey,
|
||||||
listen_addr: listenAddrDataList,
|
listen_addr: listenAddrDataList,
|
||||||
relay_service: relayServiceData,
|
relay_service: relayServiceData,
|
||||||
time: currentTimestamp,
|
time: relayInfoEntity.time ?? 0,
|
||||||
sign: sign,
|
sign: sign,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -289,7 +301,7 @@ class StartChartManage {
|
|||||||
Future<List<ListenAddrData>> _makeListenAddrDataList() async {
|
Future<List<ListenAddrData>> _makeListenAddrDataList() async {
|
||||||
final List<ListenAddrData> listenAddrDataList = [];
|
final List<ListenAddrData> listenAddrDataList = [];
|
||||||
final List<String> localIp = await _getAllIpAddresses();
|
final List<String> localIp = await _getAllIpAddresses();
|
||||||
// 从缓存中获取中继信息
|
// 从缓存中获取中继信息,取出返回的客户端ip地址
|
||||||
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
|
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
|
||||||
if (relayInfoEntity != null && relayInfoEntity.client_addr != null) {
|
if (relayInfoEntity != null && relayInfoEntity.client_addr != null) {
|
||||||
listenAddrDataList.add(
|
listenAddrDataList.add(
|
||||||
@ -313,7 +325,7 @@ class StartChartManage {
|
|||||||
return listenAddrDataList ?? [];
|
return listenAddrDataList ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取本机所有ip
|
/// 获取本机所有 IP 地址
|
||||||
Future<List<String>> _getAllIpAddresses() async {
|
Future<List<String>> _getAllIpAddresses() async {
|
||||||
final List<String> ipAddresses = [];
|
final List<String> ipAddresses = [];
|
||||||
try {
|
try {
|
||||||
@ -324,9 +336,21 @@ class StartChartManage {
|
|||||||
|
|
||||||
for (final interface in interfaces) {
|
for (final interface in interfaces) {
|
||||||
for (final address in interface.addresses) {
|
for (final address in interface.addresses) {
|
||||||
if (address.address.isNotEmpty &&
|
// 获取原始 IP 地址
|
||||||
!IpConstant.reportExcludeIp.contains(address.address)) {
|
String ipAddress = address.address;
|
||||||
ipAddresses.add(address.address);
|
|
||||||
|
// 解码 URL 编码的字符串
|
||||||
|
ipAddress = Uri.decodeFull(ipAddress);
|
||||||
|
|
||||||
|
// 移除 IPv6 地址中的接口标识符(如果有)
|
||||||
|
if (ipAddress.contains('%')) {
|
||||||
|
ipAddress = ipAddress.split('%').first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保 IP 地址不为空且不在排除列表中
|
||||||
|
if (ipAddress.isNotEmpty &&
|
||||||
|
!IpConstant.reportExcludeIp.contains(ipAddress)) {
|
||||||
|
ipAddresses.add(ipAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,30 +400,104 @@ class StartChartManage {
|
|||||||
}) async {
|
}) async {
|
||||||
String resultSign = '';
|
String resultSign = '';
|
||||||
try {
|
try {
|
||||||
// 1. 获取当前时间戳并编码为小端字节序
|
// 1. 将 32 位时间戳以小端字节序编码为二进制数据
|
||||||
Uint8List signData = Uint8List(4);
|
Uint8List signData = encodeTimestampToLittleEndianBytes(currentTimestamp);
|
||||||
ByteData.view(signData.buffer)
|
|
||||||
.setUint32(0, currentTimestamp, Endian.little);
|
|
||||||
|
|
||||||
// 2. 对时间戳数据计算 SHA-256 哈希值
|
// 2.将十六进制字符串转换为字节数组
|
||||||
Digest hash = sha256.convert(signData);
|
List<int> privateKeyBytes = hexToBytes(privateKeyHex);
|
||||||
|
|
||||||
// 3. 使用 RSA 私钥进行签名 (需要提供 RsaPrivateKey)
|
// 3.将私钥转换为 PEM 格式
|
||||||
pc.RSAPrivateKey privateKey =
|
final pemPrivateKey =
|
||||||
loadPrivateKey(privateKeyHex); // 需要加载你的 RSA 私钥
|
convertToPemPrivateKey(privateKeyBytes, isPKCS8: true);
|
||||||
Uint8List signature = rsaSign(privateKey, hash.bytes);
|
// 4.签名
|
||||||
|
var result = await fastRsa.RSA
|
||||||
// 4. 将签名结果转换为十六进制字符串
|
.signPKCS1v15Bytes(signData, fastRsa.Hash.SHA256, pemPrivateKey);
|
||||||
String hexSignature = signature
|
resultSign = hex.encode(result);
|
||||||
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
|
|
||||||
.join();
|
|
||||||
resultSign = hexSignature;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log(text: '❌--->上报信息生成签名时出现错误: $e');
|
_log(text: '❌--->上报信息生成签名时出现错误: $e');
|
||||||
|
e.printError();
|
||||||
}
|
}
|
||||||
return resultSign ?? '';
|
return resultSign ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将 32 位时间戳以小端字节序编码为二进制数据
|
||||||
|
Uint8List encodeTimestampToLittleEndianBytes(int timestamp) {
|
||||||
|
// 创建一个 4 字节的 ByteData 对象
|
||||||
|
ByteData byteData = ByteData(4);
|
||||||
|
|
||||||
|
// 将 32 位时间戳写入 ByteData,使用小端字节序
|
||||||
|
byteData.setUint32(0, timestamp, Endian.little);
|
||||||
|
|
||||||
|
// 将 ByteData 转换为 Uint8List
|
||||||
|
Uint8List bytes = byteData.buffer.asUint8List();
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字节数组转换为 PEM 格式的公钥
|
||||||
|
String convertToPemPublicKey(List<int> publicKeyBytes,
|
||||||
|
{bool isPKCS8 = true}) {
|
||||||
|
// 将字节数组转换为Base64编码的字符串
|
||||||
|
String base64PublicKey = base64Encode(publicKeyBytes);
|
||||||
|
// 添加PEM格式的头尾标签
|
||||||
|
String pemHeader;
|
||||||
|
String pemFooter;
|
||||||
|
if (isPKCS8) {
|
||||||
|
// 添加PEM格式的头尾标签
|
||||||
|
pemHeader = "-----BEGIN PUBLIC KEY-----";
|
||||||
|
pemFooter = "-----END PUBLIC KEY-----";
|
||||||
|
} else {
|
||||||
|
// 添加PEM格式的头尾标签
|
||||||
|
pemHeader = "-----BEGIN RSA PUBLIC KEY-----";
|
||||||
|
pemFooter = "-----END RSA PUBLIC KEY-----";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将Base64字符串分行为每行64个字符
|
||||||
|
const lineLength = 64;
|
||||||
|
List<String> lines = [];
|
||||||
|
for (int i = 0; i < base64PublicKey.length; i += lineLength) {
|
||||||
|
int end = (i + lineLength < base64PublicKey.length)
|
||||||
|
? i + lineLength
|
||||||
|
: base64PublicKey.length;
|
||||||
|
lines.add(base64PublicKey.substring(i, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组合成完整的PEM格式字符串
|
||||||
|
return "$pemHeader\n${lines.join('\n')}\n$pemFooter";
|
||||||
|
}
|
||||||
|
|
||||||
|
String convertToPemPrivateKey(List<int> privateKeyBytes,
|
||||||
|
{bool isPKCS8 = true}) {
|
||||||
|
// 将字节数组转换为Base64编码的字符串
|
||||||
|
String base64PrivateKey = base64Encode(privateKeyBytes);
|
||||||
|
|
||||||
|
// 添加PEM格式的头尾标签
|
||||||
|
String pemHeader;
|
||||||
|
String pemFooter;
|
||||||
|
|
||||||
|
if (isPKCS8) {
|
||||||
|
pemHeader = "-----BEGIN PRIVATE KEY-----";
|
||||||
|
pemFooter = "-----END PRIVATE KEY-----";
|
||||||
|
} else {
|
||||||
|
pemHeader = "-----BEGIN RSA PRIVATE KEY-----";
|
||||||
|
pemFooter = "-----END RSA PRIVATE KEY-----";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将Base64字符串分行为每行64个字符
|
||||||
|
const lineLength = 64;
|
||||||
|
List<String> lines = []; // 用于存储每一行
|
||||||
|
|
||||||
|
for (int i = 0; i < base64PrivateKey.length; i += lineLength) {
|
||||||
|
int end = (i + lineLength < base64PrivateKey.length)
|
||||||
|
? i + lineLength
|
||||||
|
: base64PrivateKey.length;
|
||||||
|
lines.add(base64PrivateKey.substring(i, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组合成完整的PEM格式字符串
|
||||||
|
return "$pemHeader\n${lines.join('\n')}\n$pemFooter";
|
||||||
|
}
|
||||||
|
|
||||||
/// 自定义 PEM 格式的 RSA 私钥解析器
|
/// 自定义 PEM 格式的 RSA 私钥解析器
|
||||||
pc.RSAPrivateKey loadPrivateKey(String privateKeyHex) {
|
pc.RSAPrivateKey loadPrivateKey(String privateKeyHex) {
|
||||||
// 将十六进制字符串转换为字节数组
|
// 将十六进制字符串转换为字节数组
|
||||||
|
|||||||
@ -250,7 +250,7 @@ dependencies:
|
|||||||
crc32_checksum: ^0.0.2
|
crc32_checksum: ^0.0.2
|
||||||
cryptography: ^2.7.0
|
cryptography: ^2.7.0
|
||||||
asn1lib: ^1.0.0
|
asn1lib: ^1.0.0
|
||||||
|
fast_rsa: ^3.6.6
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
#强制设置google_maps_flutter_ios 为 2.5.2
|
#强制设置google_maps_flutter_ios 为 2.5.2
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user