fix:调整新的中继协议中的上报签名逻辑

This commit is contained in:
liyi 2024-12-02 15:43:59 +08:00
parent a92ae906ef
commit 7a4fbe19d8
4 changed files with 221 additions and 60 deletions

View File

@ -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/payload_type_constant.dart';
import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart';
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
class MessageCommand {
/// 线
static List<int> goOnlineRelay() {
static List<int> goOnlineRelay({
required String FromPeerId,
required String ToPeerId,
}) {
String serializedBytesString = ScpMessage(
ProtocolFlag: ProtocolFlagConstant.scp01,
MessageType: MessageTypeConstant.Req,
MessageId: 1,
SpTotal: 0,
SpIndex: 0,
FromPeerId: 'ToPeerId',
ToPeerId: 'ToPeerId',
FromPeerId: FromPeerId,
ToPeerId: ToPeerId,
Payload: 'hello',
PayloadCRC: 55230,
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(
ProtocolFlag: ProtocolFlagConstant.scp01,
MessageType: MessageTypeConstant.Req,
MessageId: 1,
SpTotal: 0,
SpIndex: 0,
FromPeerId: 'FromPeerId',
ToPeerId: 'ToPeerId',
FromPeerId: FromPeerId,
ToPeerId: ToPeerId,
Payload: 'hello',
PayloadCRC: 55230,
PayloadLength: 5,
@ -73,4 +81,9 @@ class MessageCommand {
}
return bytes;
}
static int calculationCrc(payload){
var checkSumResult = Crc32.calculate(payload);
return checkSumResult;
}
}

View File

@ -144,85 +144,135 @@ class ScpMessage {
return bytesToHexString;
}
//
static ScpMessage deserialize(List<int> bytes) {
final message = ScpMessage();
int offset = 0;
// ProtocolFlag (4 bytes)
if (bytes.length - offset >= 4) {
message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4));
offset += 4;
} else {
throw FormatException("Invalid ProtocolFlag length");
}
// MessageType (1 byte)
if (bytes.length - offset >= 1) {
message.MessageType = bytes[offset];
offset += 1;
} else {
throw FormatException("Invalid MessageType length");
}
// MessageId (2 bytes, little-endian)
if (bytes.length - offset >= 2) {
message.MessageId = (bytes[offset + 1] << 8) | bytes[offset];
offset += 2;
} else {
throw FormatException("Invalid MessageId length");
}
// SpTotal (1 byte)
if (bytes.length - offset >= 1) {
message.SpTotal = bytes[offset];
offset += 1;
} else {
throw FormatException("Invalid SpTotal length");
}
// SpIndex (1 byte)
if (bytes.length - offset >= 1) {
message.SpIndex = bytes[offset];
offset += 1;
} else {
throw FormatException("Invalid SpIndex length");
}
// FromPeerId (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;
} else {
throw FormatException("Invalid FromPeerId length");
}
// ToPeerId (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;
} else {
throw FormatException("Invalid ToPeerId length");
}
// PayloadType (2 bytes, little-endian)
if (bytes.length - offset >= 2) {
message.PayloadType = (bytes[offset + 1] << 8) | bytes[offset];
offset += 2;
} else {
throw FormatException("Invalid PayloadType length");
}
// PayloadCRC (2 bytes, little-endian)
if (bytes.length - offset >= 2) {
message.PayloadCRC = (bytes[offset + 1] << 8) | bytes[offset];
offset += 2;
} else {
throw FormatException("Invalid PayloadCRC length");
}
// PayloadLength (4 bytes, big-endian)
// PayloadLength (4 bytes, little-endian)
if (bytes.length - offset >= 4) {
// PayloadLength对应的4个字节
print('PayloadLength bytes: ${bytes.sublist(offset, offset + 4)}');
message.PayloadLength = (bytes[offset] |
(bytes[offset + 1] << 8) |
(bytes[offset + 2] << 16) |
(bytes[offset + 3] << 24));
(bytes[offset + 1] << 8) |
(bytes[offset + 2] << 16) |
(bytes[offset + 3] << 24)); // little-endian
offset += 4;
} else {
throw FormatException("Invalid PayloadLength length");
}
// Payload ()
if (message.PayloadLength != null &&
bytes.length - offset >= message.PayloadLength!) {
message.Payload =
utf8.decode(bytes.sublist(offset, offset + message.PayloadLength!));
if (message.PayloadLength != null && bytes.length - offset >= message.PayloadLength!) {
message.Payload = utf8.decode(bytes.sublist(offset, 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;
}
// 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) {
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');

View File

@ -2,11 +2,17 @@ import 'dart:async';
import 'dart:io';
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/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:pointycastle/export.dart' as pc;
import 'package:star_lock/app_settings/app_settings.dart';
import 'package:star_lock/flavors.dart';
import 'package:star_lock/network/start_chart_api.dart';
@ -55,11 +61,11 @@ class StartChartManage {
String ToPeerId = ''; // ID
String FromPeerId = ''; // ID
bool _isLoginSuccessfulToStartChart = false; //
final String echoPeerId = '3phX8Ng2cZHz5NtP8xAf6nYy2z1BYytoejgjoHrWMGhH';
//
Future<void> init() async {
//
//
await _clientRegister();
//
@ -70,12 +76,6 @@ class StartChartManage {
// udp服务
await _onlineRelayService();
//
_sendHeartbeatMessage();
// 线
await _sendOnlineMessage();
}
///
@ -85,6 +85,8 @@ class StartChartManage {
await _requestStarChartRegisterNode();
await _saveStarChartRegisterNodeToStorage(requestStarChartRegisterNode);
_log(text: '获取到星图注册节点信息:$requestStarChartRegisterNode');
FromPeerId = requestStarChartRegisterNode.peer!.id ?? '';
ToPeerId = echoPeerId;
}
//
@ -98,12 +100,11 @@ class StartChartManage {
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}');
}
_log(text: '中继信息----》${relayInfoEntity}');
}
void closeUdpSocket() {
@ -138,6 +139,7 @@ class StartChartManage {
_log(text: '收到消息---> 长度:${dg?.data?.length}, 数据:${dg?.data}');
if (dg?.data != null) {
final deserialize = ScpMessage.deserialize(dg!.data);
_log(text: '=============${bytesToHex(dg!.data)}');
_log(text: 'Udp收到结构体数据---》$deserialize');
}
} catch (e) {
@ -156,15 +158,19 @@ class StartChartManage {
reportInformationData: data,
);
if (response.statusCode == 200) {
// TODO
_log(text: '星图登录成功');
// 线
await _sendOnlineMessage();
//
_sendHeartbeatMessage();
}
}
// 线
Future<void> _sendOnlineMessage() async {
// 线
final message = MessageCommand.goOnlineRelay();
final message = MessageCommand.goOnlineRelay(
FromPeerId: FromPeerId, ToPeerId: ToPeerId);
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) {
_log(text: '心跳已经开始了. 请勿重复发送心跳包消息');
return;
@ -188,7 +197,10 @@ class StartChartManage {
seconds: heartbeatIntervalTime,
),
(Timer timer) async {
final List<int> message = MessageCommand.heartbeatMessage();
final List<int> message = MessageCommand.heartbeatMessage(
FromPeerId: FromPeerId,
ToPeerId: relayPeerId,
);
await _sendMessage(message: message);
},
);
@ -248,15 +260,16 @@ class StartChartManage {
//
Future<ReportInformationData> _makeReportInformationData() async {
//
int currentTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
//
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
//
final publicKey = await getPublicKey();
//
final privateKey = await getPrivateKey();
//
final sign = await _generateSign(
currentTimestamp: currentTimestamp,
currentTimestamp: relayInfoEntity!.time ?? 0,
privateKeyHex: privateKey,
);
@ -264,8 +277,7 @@ class StartChartManage {
final List<ListenAddrData> 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 ?? '',
@ -278,7 +290,7 @@ class StartChartManage {
public_key: publicKey,
listen_addr: listenAddrDataList,
relay_service: relayServiceData,
time: currentTimestamp,
time: relayInfoEntity.time ?? 0,
sign: sign,
);
@ -289,7 +301,7 @@ class StartChartManage {
Future<List<ListenAddrData>> _makeListenAddrDataList() async {
final List<ListenAddrData> listenAddrDataList = [];
final List<String> localIp = await _getAllIpAddresses();
//
// ip地址
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
if (relayInfoEntity != null && relayInfoEntity.client_addr != null) {
listenAddrDataList.add(
@ -313,7 +325,7 @@ class StartChartManage {
return listenAddrDataList ?? [];
}
/// ip
/// IP
Future<List<String>> _getAllIpAddresses() async {
final List<String> ipAddresses = [];
try {
@ -324,9 +336,21 @@ class StartChartManage {
for (final interface in interfaces) {
for (final address in interface.addresses) {
if (address.address.isNotEmpty &&
!IpConstant.reportExcludeIp.contains(address.address)) {
ipAddresses.add(address.address);
// IP
String ipAddress = 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 {
String resultSign = '';
try {
// 1.
Uint8List signData = Uint8List(4);
ByteData.view(signData.buffer)
.setUint32(0, currentTimestamp, Endian.little);
// 1. 32
Uint8List signData = encodeTimestampToLittleEndianBytes(currentTimestamp);
// 2. SHA-256
Digest hash = sha256.convert(signData);
// 2.
List<int> privateKeyBytes = hexToBytes(privateKeyHex);
// 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;
// 3. PEM
final pemPrivateKey =
convertToPemPrivateKey(privateKeyBytes, isPKCS8: true);
// 4.
var result = await fastRsa.RSA
.signPKCS1v15Bytes(signData, fastRsa.Hash.SHA256, pemPrivateKey);
resultSign = hex.encode(result);
} catch (e) {
_log(text: '❌--->上报信息生成签名时出现错误: $e');
e.printError();
}
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
pc.RSAPrivateKey loadPrivateKey(String privateKeyHex) {
//

View File

@ -250,7 +250,7 @@ dependencies:
crc32_checksum: ^0.0.2
cryptography: ^2.7.0
asn1lib: ^1.0.0
fast_rsa: ^3.6.6
dependency_overrides:
#强制设置google_maps_flutter_ios 为 2.5.2