From 3a27c83c62a7fc5de141234749802e4cad747907 Mon Sep 17 00:00:00 2001 From: liyi Date: Fri, 29 Nov 2024 14:18:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E8=B0=83=E6=95=B4=E6=96=B0=E7=9A=84?= =?UTF-8?q?=E4=B8=AD=E7=BB=A7=E5=8D=8F=E8=AE=AE=E4=B8=AD=E7=9A=84=E4=B8=8A?= =?UTF-8?q?=E6=8A=A5=E7=AD=BE=E5=90=8D=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/login/login/starLock_login_page.dart | 8 +- lib/network/api.dart | 2 + lib/network/start_chart_api.dart | 11 ++ lib/talk/startChart/start_chart_manage.dart | 137 +++++++++++++++++--- lib/talk/udp/udp_reciverData.dart | 2 +- pubspec.yaml | 6 +- 6 files changed, 142 insertions(+), 24 deletions(-) diff --git a/lib/login/login/starLock_login_page.dart b/lib/login/login/starLock_login_page.dart index ec43b33b..ca3615b2 100755 --- a/lib/login/login/starLock_login_page.dart +++ b/lib/login/login/starLock_login_page.dart @@ -235,13 +235,13 @@ class _StarLockLoginPageState extends State { : null)), SubmitBtn( btnName: '发送上线请求', - onClick: () { + onClick: () async { // 注册星图节点信息 - StartChartManage().clientRegister(); + await StartChartManage().clientRegister(); // 查询中继信息 - StartChartManage().relayQuery(); + await StartChartManage().relayQuery(); // 发送上线请求 - StartChartManage().onlineRelayService(); + await StartChartManage().onlineRelayService(); }, ), SubmitBtn( diff --git a/lib/network/api.dart b/lib/network/api.dart index 686e9b5b..deb1701b 100755 --- a/lib/network/api.dart +++ b/lib/network/api.dart @@ -289,4 +289,6 @@ abstract class Api { '/SL-A-1.0/relay/query'; // 星图--中继查询 final String reportInformationDataURL = '/SL-A-1.0/peer/login'; // 星图--信息上报 + final String analyzeInformationOtherEndURL = + '/SL-A-1.0/peer/nslookup'; // 星图--解析对端信息 } diff --git a/lib/network/start_chart_api.dart b/lib/network/start_chart_api.dart index 1a6901c8..26e148d6 100644 --- a/lib/network/start_chart_api.dart +++ b/lib/network/start_chart_api.dart @@ -55,4 +55,15 @@ class StartChartApi extends BaseProvider { isUserBaseUrl: false, ); } + + // 星图--解析对端信息 + Future analyzeInformationOtherEnd({ + required String peerId, + }) async { + final response = await get( + _startChartHost + analyzeInformationOtherEndURL.toUrl + '?id=$peerId', + isUnShowLoading: true, + isUserBaseUrl: false, + ); + } } diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index 0baa1f6a..e5330c81 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -1,12 +1,10 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:convert/convert.dart'; -import 'package:crypto/crypto.dart'; -import 'package:encrypt/encrypt.dart'; -import 'package:pointycastle/asymmetric/api.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:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/flavors.dart'; import 'package:star_lock/network/start_chart_api.dart'; @@ -20,6 +18,12 @@ 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(); @@ -225,7 +229,7 @@ class StartChartManage { // 生成签名 final sign = await _generateSign( currentTimestamp: currentTimestamp, - privateKey: privateKey, + privateKeyHex: privateKey, ); // 获取本机所有ip地址和中继返回的外网地址 @@ -341,28 +345,127 @@ class StartChartManage { // 生成签名sing Future _generateSign({ required int currentTimestamp, - required String privateKey, + required String privateKeyHex, }) async { String resultSign = ''; try { - // 2. 将时间戳编码为小端字节序(Little Endian) + // 1. 获取当前时间戳并编码为小端字节序 Uint8List signData = Uint8List(4); - signData.buffer - .asByteData() + ByteData.view(signData.buffer) .setUint32(0, currentTimestamp, Endian.little); - // 3. 使用 SHA-256 对 signData 进行哈希运算 - final sha256Hash = sha256.convert(signData); - var parser = RSAKeyParser(); - final RSAPrivateKey parsePrivateKey = - parser.parse('-----BEGIN RSA PRIVATE KEY-----\n' + privateKey) - as RSAPrivateKey; + // 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'); + _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; + } + + // 解析 RSA 私钥 + // RSAPrivateKey parseRSAPrivateKey1(Uint8List bytes) { + // // 这里假设私钥是以 PKCS#1 DER 编码的 + // ASN1Parser parser = ASN1Parser(bytes); + // ASN1Sequence seq = parser.nextObject() as ASN1Sequence; + // if (seq.elements == null || seq.elements!.length < 9) { + // throw ArgumentError("Invalid RSA private key"); + // } + // + // // 解析各个元素 + // ASN1Integer version = seq.elements![0] as ASN1Integer; + // ASN1Integer modulus = seq.elements![1] as ASN1Integer; + // // ASN1Integer publicExponent = seq.elements![2] as ASN1Integer; + // ASN1Integer privateExponent = seq.elements![3] as ASN1Integer; + // ASN1Integer p = seq.elements![2] as ASN1Integer; + // ASN1Integer q = seq.elements![4] as ASN1Integer; + // ASN1Integer dP = seq.elements![5] as ASN1Integer; + // ASN1Integer dQ = seq.elements![6] as ASN1Integer; + // ASN1Integer qInv = seq.elements![7] as ASN1Integer; + // + // // 将 ASN1Integer 转换为 BigInt + // BigInt modulusValue = _convertToBigInt(modulus); + // // BigInt publicExponentValue = _convertToBigInt(publicExponent); + // BigInt privateExponentValue = _convertToBigInt(privateExponent); + // BigInt pValue = _convertToBigInt(p); + // BigInt qValue = _convertToBigInt(q); + // BigInt dPValue = _convertToBigInt(dP); + // BigInt dQPValue = _convertToBigInt(dQ); + // BigInt qInvQPValue = _convertToBigInt(qInv); + // + // // 创建 RSAPrivateKey 对象 + // return RSAPrivateKey(modulusValue, privateExponentValue, pValue, qValue); + // } + // + // // 将 ASN1Integer 转换为 BigInt + // BigInt _convertToBigInt(ASN1Integer integer) { + // // 获取 ASN1Integer 的字节数据 + // Uint8List bytes = integer.valueBytes!; + // // 将字节数据转换为 BigInt + // return BigInt.parse(hex.encode(bytes), radix: 16); + // } + Future getPublicKey() async { // 从缓存中获取星图注册节点信息 final StarChartRegisterNodeEntity? starChartRegisterNodeInfo = diff --git a/lib/talk/udp/udp_reciverData.dart b/lib/talk/udp/udp_reciverData.dart index 5b99d14a..6dc9dd25 100755 --- a/lib/talk/udp/udp_reciverData.dart +++ b/lib/talk/udp/udp_reciverData.dart @@ -20,7 +20,7 @@ class CommandUDPReciverManager { if (dataSize < 4) { return; } - // AppLog.log('appReceiveUDPData:$data'); + AppLog.log('appReceiveUDPData:$data'); final Uint8List data1 = Uint8List.fromList(data); if (data1.length == 1) { diff --git a/pubspec.yaml b/pubspec.yaml index fcb79126..968ca02b 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -184,7 +184,7 @@ dependencies: #加密解密 encrypt: ^5.0.1 crypto: ^3.0.3 - pointycastle: ^3.3.0 + pointycastle: ^3.4.0 date_format: ^2.0.7 # 下拉刷新 @@ -248,7 +248,8 @@ dependencies: open_filex: ^4.4.0 crc32_checksum: ^0.0.2 - fast_rsa: ^3.6.6 + cryptography: ^2.7.0 + asn1lib: ^1.0.0 dependency_overrides: @@ -256,6 +257,7 @@ dependency_overrides: google_maps_flutter_ios: 2.5.2 flutter_plugin_android_lifecycle: 2.0.18 + dev_dependencies: flutter_test: sdk: flutter