Skip to content

Commit

Permalink
CA Hash Name
Browse files Browse the repository at this point in the history
  • Loading branch information
wanghongenpin committed Oct 14, 2024
1 parent d5eda40 commit 65c8d24
Show file tree
Hide file tree
Showing 19 changed files with 219 additions and 109 deletions.
13 changes: 13 additions & 0 deletions ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,18 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)

target.build_configurations.each do |config|
# You can remove unused permissions here
# for more information: https://github.com/Baseflow/flutter-permission-handler/blob/main/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h
# e.g. when you don't need camera permission, just add 'PERMISSION_CAMERA=0'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.camera
'PERMISSION_CAMERA=1',
## dart: PermissionGroup.photos
# 'PERMISSION_PHOTOS=1',
]
end
end
end
36 changes: 18 additions & 18 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
593E01BCFF86ADFAC59E51D5 /* [CP] Embed Pods Frameworks */,
DCDB7DFA9CFA165C8AAD9F25 /* [CP] Copy Pods Resources */,
298BDEFE069E2E1C3876CA2D /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -476,6 +476,23 @@
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
298BDEFE069E2E1C3876CA2D /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
35A8CB519E229982B14B0197 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand Down Expand Up @@ -546,23 +563,6 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
DCDB7DFA9CFA165C8AAD9F25 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
E7E8C74F615A57D43D59596C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand Down
6 changes: 4 additions & 2 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,13 @@
"requestBlock": "Request Block",

"other": "Other",
"certHashName": "CA Hash Name",
"systemCertName": "System Certificate Name",
"qrCode": "QR Code",
"scanQrCode": "Scan QR Code",
"generateQRcode": "Generate QR Code",
"generateQrCode": "Generate",
"saveImage": "Save Image",
"selectImage": "Select Image",
"inputContent": "Input Content",
"errorCorrectLevel": "Error Correct Level"
"errorCorrectLevel": "Error Correct"
}
4 changes: 3 additions & 1 deletion lib/l10n/app_zh.arb
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,10 @@
"requestBlock": "请求屏蔽",

"other": "其他",
"certHashName": "证书Hash名称",
"systemCertName": "系统证书名称",
"qrCode": "二维码",
"generateQRcode": "生成二维码",
"generateQrCode": "生成二维码",
"scanQrCode": "扫描二维码",
"saveImage": "保存图片",
"selectImage": "选择图片",
Expand Down
43 changes: 17 additions & 26 deletions lib/network/util/cert/x509.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// ignore_for_file: constant_identifier_names, depend_on_referenced_packages

import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:basic_utils/basic_utils.dart';
Expand All @@ -15,25 +14,6 @@ import 'basic_constraints.dart';

/// @author wanghongen
/// 2023/7/26
void main() {
var caPem = File('assets/certs/ca.crt').readAsStringSync();
var certPath = 'assets/certs/ca.crt';
//生成 公钥和私钥
var caRoot = X509Utils.x509CertificateFromPem(caPem);
var subject = caRoot.subject;
// Add Issuer
var issuerSeq = ASN1Sequence();
for (var k in subject.keys) {
var s = X509Generate._identifier(k, subject[k]!);
issuerSeq.add(s);
}
var d = X509Generate.x509NameHashOld(issuerSeq);

//16进制
print(d);
print(d.toRadixString(16).padLeft(8, '0'));
}

class X509Generate {
static const String BEGIN_CERT = '-----BEGIN CERTIFICATE-----';
static const String END_CERT = '-----END CERTIFICATE-----';
Expand All @@ -43,12 +23,23 @@ class X509Generate {
static const String SERIAL_NUMBER = "2.5.4.5";
static const String DN_QUALIFIER = "2.5.4.46";

static int x509NameHashOld(ASN1Object subject) {
// Convert ASN1Object to DER encoded byte array
final derEncoded = subject.encode();

var convert = md5.convert(derEncoded!);
return convert.bytes[0] << 24 | convert.bytes[1] << 16 | convert.bytes[2] << 8 | convert.bytes[3];
///android 系统证书名称
static String getSubjectHashName(Map<String, String?> subject) {
// Add Issuer
var issuerSeq = ASN1Sequence();
for (var k in subject.keys) {
var s = X509Generate._identifier(k, subject[k]!);
issuerSeq.add(s);
}
var derEncoded = issuerSeq.encode();
// Convert the hash to a long value
var hashBytes = md5.convert(derEncoded).bytes;
int hash = (hashBytes[0] & 0xff) |
((hashBytes[1] & 0xff) << 8) |
((hashBytes[2] & 0xff) << 16) |
((hashBytes[3] & 0xff) << 24);
String hexString = hash.toRadixString(16).padLeft(8, '0');
return hexString;
}

///
Expand Down
10 changes: 10 additions & 0 deletions lib/network/util/crts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ class CertificateManager {
return csrPem;
}

/// 获取证书主题hash
static Future<String> subjectHashName() async {
if (_state != StartState.initialized) {
await initCAConfig();
}

var subject = caCert.tbsCertificate!.subject;
return X509Generate.getSubjectHashName(subject);
}

//重新生成根证书
static Future<void> generateNewRootCA() async {
if (_state != StartState.initialized) {
Expand Down
115 changes: 87 additions & 28 deletions lib/ui/component/cert_hash.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,67 +16,126 @@

import 'dart:io';

import 'package:basic_utils/basic_utils.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_toastr/flutter_toastr.dart';
import 'package:network_proxy/network/util/cert/x509.dart';

///证书哈希名称查看
///@author Hongen Wang
class CertHashWidget extends StatefulWidget {
const CertHashWidget({super.key});
class CertHashPage extends StatefulWidget {
const CertHashPage({super.key});

@override
State<StatefulWidget> createState() {
return _CertHashWidgetState();
return _CertHashPageState();
}
}

class _CertHashWidgetState extends State<CertHashWidget> {
class _CertHashPageState extends State<CertHashPage> {
var input = TextEditingController();
TextEditingController decodeData = TextEditingController();

AppLocalizations get localizations => AppLocalizations.of(context)!;

@override
void dispose() {
input.dispose();
decodeData.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("系统证书名称", style: TextStyle(fontSize: 16)), centerTitle: true),
appBar: AppBar(title: Text(localizations.systemCertName, style: TextStyle(fontSize: 16)), centerTitle: true),
resizeToAvoidBottomInset: false,
body: ListView(children: [
Row(mainAxisAlignment: MainAxisAlignment.end, children: [
//选择文件
Wrap(alignment: WrapAlignment.end, children: [
ElevatedButton.icon(
onPressed: () async {
FilePickerResult? result =
await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['js']);
if (result != null) {
// File file = File(result.files.single.path!);
// String content = await file.readAsString();
setState(() {});
}
FilePickerResult? result = await FilePicker.platform
.pickFiles(type: FileType.custom, allowedExtensions: ['crt', 'pem', 'cer']);
if (result == null) return;
File file = File(result.files.single.path!);
String content = await file.readAsString();
input.text = content;
getSubjectName();
},
style: buttonStyle,
icon: const Icon(Icons.folder_open),
label: const Text("File")),
label: Text("File")),
const SizedBox(width: 15),
ElevatedButton.icon(
onPressed: () => input.clear(),
style: buttonStyle,
icon: const Icon(Icons.clear),
label: const Text("Clear")),
const SizedBox(width: 15),
FilledButton.icon(
onPressed: () async {
//失去焦点
onPressed: () {
getSubjectName();
FocusScope.of(context).requestFocus(FocusNode());
},
style: buttonStyle,
icon: const Icon(Icons.play_arrow_rounded),
label: const Text("Run"))
label: const Text("Run")),
const SizedBox(width: 15),
]),
const SizedBox(width: 10),
Container(
padding: const EdgeInsets.all(10),
height: 320,
child: TextField(maxLines: 100, decoration: decoration('证书文件内容'))),
TextButton(onPressed: () {}, child: const Text("Output:", style: TextStyle(fontSize: 16))),
Expanded(
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(10),
height: 150,
child: TextField(maxLines: 50, decoration: decoration('安卓系统证书Hash 名称')))),
height: 350,
child: TextFormField(
maxLines: 50,
controller: input,
onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
keyboardType: TextInputType.text,
decoration: decoration(localizations.inputContent))),
Align(
alignment: Alignment.bottomLeft,
child: TextButton(onPressed: () {}, child: const Text("Output:", style: TextStyle(fontSize: 16)))),
Container(
width: double.infinity,
padding: const EdgeInsets.all(10),
height: 150,
child: TextFormField(
maxLines: 30,
readOnly: true,
controller: decodeData,
decoration: decoration('Android ${localizations.systemCertName}'))),
]));
}

getSubjectName() {
var content = input.text;
if (content.isEmpty) return;
try {
var caCert = X509Utils.x509CertificateFromPem(content);

var subject = caCert.tbsCertificate?.subject;
if (subject == null) return;
var subjectHashName = X509Generate.getSubjectHashName(subject);
decodeData.text = '$subjectHashName.0';
} catch (e) {
FlutterToastr.show(localizations.decodeFail, context, duration: 3, backgroundColor: Colors.red);
}
}

ButtonStyle get buttonStyle =>
ButtonStyle(
padding: WidgetStateProperty.all<EdgeInsets>(EdgeInsets.symmetric(horizontal: 15, vertical: 8)),
textStyle: WidgetStateProperty.all<TextStyle>(TextStyle(fontSize: 14)),
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))));

InputDecoration decoration(String label, {String? hintText}) {
Color color = Theme.of(context).colorScheme.primary;
Color color = Theme
.of(context)
.colorScheme
.primary;
return InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.always,
labelText: label,
Expand Down
11 changes: 8 additions & 3 deletions lib/ui/component/multi_window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:network_proxy/network/components/script_manager.dart';
import 'package:network_proxy/network/http/http.dart';
import 'package:network_proxy/network/util/lists.dart';
import 'package:network_proxy/network/util/logger.dart';
import 'package:network_proxy/ui/component/cert_hash.dart';
import 'package:network_proxy/ui/component/device.dart';
import 'package:network_proxy/ui/component/encoder.dart';
import 'package:network_proxy/ui/component/js_run.dart';
Expand Down Expand Up @@ -66,6 +67,10 @@ Widget multiWindow(int windowId, Map<dynamic, dynamic> argument) {
return QrCodePage(windowId: windowId);
}

if (argument['name'] == 'CertHashPage') {
return CertHashPage();
}

//脚本日志
if (argument['name'] == 'ScriptConsoleWidget') {
return ScriptConsoleWidget(windowId: windowId);
Expand Down Expand Up @@ -204,18 +209,18 @@ void registerMethodHandler() {

if (call.method == 'openFile') {
List<String> extensions =
call.arguments is List ? Lists.convertList<String>(call.arguments) : <String>[call.arguments];
call.arguments is List ? Lists.convertList<String>(call.arguments) : <String>[call.arguments];

XTypeGroup typeGroup =
XTypeGroup(extensions: extensions, uniformTypeIdentifiers: Platform.isMacOS ? const ['public.item'] : null);
XTypeGroup(extensions: extensions, uniformTypeIdentifiers: Platform.isMacOS ? const ['public.item'] : null);
final XFile? file = await openFile(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
if (Platform.isWindows) windowManager.blur();
return file?.path;
}

if (call.method == 'pickFile') {
List<String> extensions =
call.arguments is List ? Lists.convertList<String>(call.arguments) : <String>[call.arguments];
call.arguments is List ? Lists.convertList<String>(call.arguments) : <String>[call.arguments];

var file =
(await FilePicker.platform.pickFiles(allowedExtensions: extensions, type: FileType.custom))?.files.single;
Expand Down
4 changes: 2 additions & 2 deletions lib/ui/component/qr_code_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ class _QrEncodeState extends State<_QrEncode> with AutomaticKeepAliveClientMixin
return ListView(children: [
Container(
padding: const EdgeInsets.all(10),
height: 160,
height: 180,
child: TextField(
controller: inputData,
maxLines: 8,
Expand Down Expand Up @@ -286,7 +286,7 @@ class _QrEncodeState extends State<_QrEncode> with AutomaticKeepAliveClientMixin
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)))),
icon: const Icon(Icons.qr_code, size: 18),
label: Text(localizations.generateQRcode, style: TextStyle(fontSize: 14))),
label: Text(localizations.generateQrCode, style: TextStyle(fontSize: 14))),
const SizedBox(width: 20),
],
),
Expand Down
Loading

0 comments on commit 65c8d24

Please sign in to comment.