c_shield_sdk 1.0.0
c_shield_sdk: ^1.0.0 copied to clipboard
CMC Cyber Security's C-Shield SDK for Flutter is a powerful security solution designed to protect your Flutter applications from various threats and vulnerabilities. It provides a comprehensive set of [...]
C-Shield Flutter SDK #
C-Shield Flutter SDK là plugin bảo mật di động cung cấp hai lớp bảo vệ cho ứng dụng Flutter:
- RASP (Runtime Application Self-Protection): Phát hiện và phản ứng với các mối đe dọa bảo mật trong suốt vòng đời ứng dụng — debugger, root/jailbreak, giả mạo, emulator, trạng thái thiết bị không an toàn, và chứng chỉ CA giả.
- AIP (API Integrity Protection): Bảo vệ giao tiếp giữa app và server thông qua certificate pinning và ký số request/response.
SDK bọc native AAR (Android) và XCFramework (iOS), đảm bảo khả năng phát hiện mối đe dọa mạnh mẽ ở tầng native.
Mục lục #
- Tích hợp SDK
- Khởi tạo SDK
- Threat phát hiện lúc load app
- RASP — Runtime Application Self-Protection
- AIP — API Integrity Protection
- SSL — Certificate Pinning
- 6.1 Lấy giá trị pin
- 6.2 Cấu hình CShieldSSL
- 6.3 Tích hợp với http package
- 6.4 Tích hợp với Dio
- 6.5 Xác minh thủ công
- Exceptions
1. Tích hợp SDK #
1.1 Thêm dependency vào pubspec.yaml #
Thêm c_shield_sdk vào pubspec.yaml:
dependencies:
flutter:
sdk: flutter
c_shield_sdk: ^1.0.0
Sau đó chạy:
flutter pub get
1.2 Cấu hình Android #
Bước 1 — Nhận các file AAR từ CMC CShield
C-Shield SDK Android được build riêng cho từng khách hàng với certificate hash của app đã ký. Liên hệ CMC CShield để nhận file c-shield-sdk.aar tương ứng với signing certificate của bạn.
Bước 2 — Đặt các file AAR vào project
Tạo thư mục libs/ trong android/app/ và đặt file AAR vào đó:
your_app_flutter/
└── android/
└── app/
└── libs/ ← đặt file AAR vào đây
└── c-shield-sdk-release.aar
└── c-shield-sdk-debug.aar
Bước 3 — Khai báo dependency trong build.gradle
Mở android/app/build.gradle.kts (hoặc build.gradle) và thêm:
android {
defaultConfig {
minSdk = 24 // yêu cầu tối thiểu của C-Shield SDK
}
compileSdk = 34 // yêu cầu tối thiểu của C-Shield SDK
}
dependencies {
// C-Shield Android SDK — file AAR do CMC CShield cung cấp
debugImplementation(files("libs/c-shield-sdk-debug.aar"))
releaseImplementation(files("libs/c-shield-sdk-release.aar"))
// Các dependency bắt buộc của C-Shield SDK (Compose UI nội bộ + networking)
implementation(platform("androidx.compose:compose-bom:2024.09.00"))
implementation("androidx.compose.runtime:runtime")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.material3:material3")
implementation("androidx.activity:activity-compose:1.11.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
}
Tại sao phải khai báo thủ công? File AAR được cung cấp trực tiếp (không qua Maven), nên Gradle không tự resolve được các transitive dependency. Các thư viện trên là những gì C-Shield SDK sử dụng nội bộ (Compose UI cho màn hình cảnh báo, Retrofit/Gson cho networking).
Bước 4 — Build AAB khi release
C-Shield SDK chứa native code cho 4 ABI (arm64-v8a, armeabi-v7a, x86, x86_64) để hỗ trợ cả thiết bị thật và emulator. Khi phát hành lên Play Store, bắt buộc dùng Android App Bundle (AAB) để Play Store tự động chỉ giao đúng ABI cho từng thiết bị — giúp giảm dung lượng download ~50-70%:
flutter build appbundle --release
1.3 Cấu hình iOS #
Vui lòng đọc và làm theo các bước tại iOS Integration Guide.
2. Khởi tạo SDK #
Gọi CShieldSdk.initialize() trước runApp() trong hàm main():
import 'package:c_shield_sdk/c_shield_sdk.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await CShieldSdk.initialize();
runApp(const MyApp());
}
Lưu ý theo nền tảng:
| Nền tảng | Hành vi khi gọi initialize() |
|---|---|
| Android | Gọi CShieldSDK.initialize(context) native; idempotent — gọi nhiều lần vẫn an toàn |
| iOS | Khởi động watchdog thread chạy nền mỗi 20-30 giây; bắt buộc phải gọi |
Nếu không gọi initialize() trước khi dùng các API khác, SDK sẽ throw CShieldException với code notInitialized.
3. Threat phát hiện lúc load app #
Khi app khởi động, native SDK tự động quét một số mối đe dọa nguy hiểm nhất (Frida, Rooting/Jailbreaking, Hooking, tampering). Nếu phát hiện, app sẽ bị kill ngay lập tức mà không cần bất kỳ can thiệp nào từ phía Flutter.
await CShieldSdk.initialize();
| Giá trị | Mô tả | Nền tảng |
|---|---|---|
Frida |
Phát hiện Frida framework | Android & IOS |
Rooting/Jailbreaking |
Phát hiện Zygisk module (Magisk module rooting) hoặc thiết bị jailbreak | Android & IOS |
Hooking |
Phát hiện framework hooking | Android & IOS |
tampering |
App bị can thiệp, chữ ký không hợp lệ | Android & iOS |
4. RASP — Runtime Application Self-Protection #
RASP kiểm tra môi trường runtime trong suốt quá trình app chạy. Khác với load-time threat, RASP có thể được cấu hình để chỉ thông báo (notifyApp) thay vì kill app ngay lập tức, cho phép bạn hiển thị UI tuỳ chỉnh.
4.1 Xây dựng RASPChecker #
Dùng RASPChecker.builder() để chọn các loại kiểm tra cần bật:
final checker = RASPChecker.builder(
checkDebugger: true, // phát hiện debugger đang attach
rootDetector: true, // phát hiện root (Android) / jailbreak
tampering: true, // phát hiện app bị giả mạo (Android only)
emulator: true, // phát hiện emulator (Android) / simulator (iOS)
deviceSecurityState: true, // phát hiện trạng thái thiết bị không an toàn
userCA: true, // phát hiện user-installed CA / proxy CA
);
Tất cả tham số đều mặc định là true. Tắt những check không cần thiết để giảm overhead.
Lưu ý theo nền tảng:
| Tham số | Android | iOS |
|---|---|---|
checkDebugger |
Debuggable flag + debugger connected | ptrace / exception port |
rootDetector |
Root detection (SuperSU, Magisk, KernelSU...) | Không áp dụng |
tampering |
Certificate integrity + untrusted store | Không áp dụng |
emulator |
Nhiều phương pháp phát hiện emulator | Phát hiện iOS Simulator |
deviceSecurityState |
PIN, ADB, developer mode, VPN... | PIN, Face ID, developer mode, VPN... |
userCA |
User CA, injected CA, proxy CA | User CA qua profile/MDM, proxy CA |
4.2 Cấu hình RASPConfig #
Sau khi tạo checker, cấu hình hành động cho từng loại threat:
await checker.setRASPConfig(
RASPConfig(
trustedStores: [
'com.android.vending', // Google Play Store
'com.samsung.android.galaxyapps', // Samsung Galaxy Store
],
threatActionConfig: ThreatActionConfig(
debuggerDetectedAction: ThreatDetectedAction.killApp,
rootDetectedAction: ThreatDetectedAction.killApp,
tamperingDetectedAction: ThreatDetectedAction.killApp,
emulatorDetectedAction: ThreatDetectedAction.notifyApp,
deviceSecurityStateUnsafeDetectedAction: ThreatDetectedAction.notifyApp,
userCADetectedAction: ThreatDetectedAction.notifyApp,
),
),
);
RASPConfig:
| Trường | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
trustedStores |
List<String>? |
null |
Package name của các store tin cậy (Android only). null = dùng danh sách mặc định (Google Play, Samsung, Xiaomi, Huawei, OPPO, Vivo, Amazon, OnePlus) |
threatActionConfig |
ThreatActionConfig? |
null |
Hành động cho từng loại threat. null = tất cả mặc định notifyApp |
ThreatDetectedAction:
| Giá trị | Hành vi |
|---|---|
ThreatDetectedAction.killApp |
Hiển thị dialog cảnh báo -> tự động kill app sau ~3 giây |
ThreatDetectedAction.notifyApp |
Phát sự kiện để app tự xử lý (hiển thị cảnh báo tuỳ chỉnh, ghi log...) |
4.3 quickCheck — kiểm tra nhanh #
quickCheck() chạy một lần và trả về mối đe dọa đầu tiên tìm thấy. Phù hợp để chặn lối vào app (màn hình splash, màn hình đăng nhập).
final result = await checker.quickCheck();
switch (result) {
case RASPResult.secure:
// thiết bị an toàn, tiếp tục
break;
case RASPResult.debuggerFound:
showDialog(context, 'Phát hiện debugger');
break;
case RASPResult.deviceRooted:
showDialog(context, 'Thiết bị đã root');
break;
case RASPResult.deviceTampered:
showDialog(context, 'App bị giả mạo');
break;
case RASPResult.emulatorFound:
case RASPResult.simulatorFound:
showDialog(context, 'Đang chạy trên máy ảo');
break;
case RASPResult.deviceSecurityStateUnsafe:
showDialog(context, 'Thiết bị không an toàn');
break;
case RASPResult.userCADetected:
showDialog(context, 'Phát hiện chứng chỉ CA giả');
break;
}
Toàn bộ giá trị RASPResult:
| Giá trị | Mô tả | Nền tảng |
|---|---|---|
secure |
Không phát hiện mối đe dọa | Android & iOS |
debuggerFound |
Debugger đang attach | Android & iOS |
deviceRooted |
Thiết bị đã root | Android only |
deviceTampered |
App bị giả mạo hoặc cài từ nguồn không tin cậy | Android only |
emulatorFound |
Đang chạy trên Android emulator | Android only |
simulatorFound |
Đang chạy trên iOS Simulator | iOS only |
deviceSecurityStateUnsafe |
Trạng thái bảo mật thiết bị không đạt yêu cầu | Android & iOS |
userCADetected |
Phát hiện user-installed CA hoặc proxy CA | Android & iOS |
quickCheck()chỉ trả về mối đe dọa đầu tiên theo thứ tự ưu tiên. Dùngsubscribe()để nhận toàn bộ kết quả.
4.4 subscribe — kiểm tra liên tục #
subscribe() chạy kiểm tra định kỳ và trả về Stream<RASPExtendedResult>. Phù hợp để giám sát liên tục trong suốt phiên sử dụng app.
final subscription = checker.subscribe(
detail: false, // true = chi tiết từng sub-check, false = tổng quan theo nhóm
automaticallyShowPopup: true, // true = SDK tự hiển thị dialog khi phát hiện threat
).listen((RASPExtendedResult result) {
if (!result.vulnerable) return;
switch (result.threatAction) {
case ThreatDetectedAction.killApp:
// SDK đã tự hiển thị dialog và sẽ kill app — không cần làm gì thêm
break;
case ThreatDetectedAction.notifyApp:
// Tự xử lý UI
_handleThreat(result.checkType);
break;
}
});
// Huỷ khi không cần nữa (ví dụ: dispose widget)
await subscription.cancel();
Tham số subscribe():
| Tham số | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
detail |
bool |
false |
false = một sự kiện tổng quan mỗi nhóm; true = sự kiện cho từng sub-check |
automaticallyShowPopup |
bool |
true |
Tự động hiển thị dialog khi phát hiện threat |
RASPExtendedResult:
class RASPExtendedResult {
final RASPCheckType checkType; // loại kiểm tra cụ thể
final bool vulnerable; // true = phát hiện mối đe dọa
final ThreatDetectedAction threatAction; // hành động được cấu hình
}
Ví dụ với detail: true — xử lý từng sub-check:
checker.subscribe(detail: true, automaticallyShowPopup: false).listen((result) {
if (!result.vulnerable) return;
if (result.checkType is Magisk) {
_logThreat('Magisk detected');
} else if (result.checkType is FingerprintFromEmulator) {
_logThreat('Emulator fingerprint detected');
}
});
4.5 Bảng RASPCheckType chi tiết #
Khi detail: true, RASPExtendedResult.checkType là một trong các lớp sau:
Debugger (Android & iOS):
| Lớp | key |
Mô tả |
|---|---|---|
DebuggerOverview |
DebuggerOverviewCheck |
Tổng quan nhóm debugger (dùng khi detail: false) |
Debuggable |
Debuggable |
App có flag FLAG_DEBUGGABLE (Android) |
DebuggerConnected |
DebuggerConnected |
Debugger đang kết nối |
Root (Android only):
| Lớp | key |
Mô tả |
|---|---|---|
RootOverview |
RootCheckOverview |
Tổng quan nhóm root |
SuperSu |
SuperSu |
Binary su tại các đường dẫn phổ biến |
Magisk |
Magisk |
Magisk qua /proc/self/mounts |
SysWritable |
SysWritable |
Phân vùng /system có thể ghi |
HasProperties |
HasProperties |
System properties nguy hiểm (ro.debuggable=1, ro.secure=0) |
KernelSUorAPatch |
KernelSUorAPatch |
KernelSU hoặc APatch |
Emulator / Simulator (Android & iOS):
| Lớp | key |
Mô tả |
|---|---|---|
EmulatorOverview |
EmulatorOverviewCheck |
Tổng quan nhóm emulator |
AvdDevice |
AvdDevice |
Android Virtual Device |
AvdHardware |
AvdHardware |
AVD hardware fingerprint |
Genymotion |
Genymotion |
Genymotion emulator |
Nox |
Nox |
Nox emulator |
Memu |
Memu |
MEmu emulator |
Bluestacks |
Bluestacks |
BlueStacks emulator |
GoogleEmulator |
GoogleEmulator |
Google AVD chính thức |
FingerprintFromEmulator |
FingerprintFromEmulator |
Build fingerprint có dấu hiệu emulator |
SensorsFromEmulator |
SensorsFromEmulator |
Cảm biến giả (goldfish/qemu) |
SuspiciousFiles |
SuspiciousFiles |
File đặc trưng của emulator |
SuspiciousPackages |
SuspiciousPackages |
Package của emulator |
SuspiciousQemuProperties |
SuspiciousQemuProperties |
System properties QEMU |
SuspiciousMounts |
SuspiciousMounts |
Mount point bất thường |
SuspiciousCpu |
SuspiciousCpu |
CPU model bất thường |
SuspiciousModules |
SuspiciousModules |
Kernel module bất thường |
SuspiciousRadioVersion |
SuspiciousRadioVersion |
Radio version giả |
SimulatorCheck |
SimulatorCheck |
iOS Simulator |
Tampering (Android only):
| Lớp | key |
Mô tả |
|---|---|---|
TamperingOverview |
TamperingCheckOverview |
Tổng quan nhóm tampering |
InvalidCertificateIntegrity |
InvalidCertificateIntegrity |
Chứng chỉ ký APK không khớp |
UntrustedStore |
UntrustedStore |
App cài từ store không nằm trong trustedStores |
Device Security State (Android & iOS):
| Lớp | key |
Mô tả |
|---|---|---|
DeviceSecurityStateOverview |
DeviceSecurityStateCheckOverview |
Tổng quan nhóm trạng thái bảo mật |
DeviceUnlocked |
DeviceUnlocked |
Không có PIN/pattern/biometric |
HardwareBackedKeystoreUnavailable |
HardwareBackedKeystoreUnavailable |
Hardware-backed keystore không khả dụng |
DeveloperModeOn |
DeveloperModeOn |
Developer mode đang bật |
AdbEnabled |
AdbEnabled |
ADB đang bật (Android only) |
SystemVpnEnabled |
SystemVpnEnabled |
Đang kết nối VPN |
AccessibilityServiceOn |
AccessibilityServiceOn |
Có accessibility service đang chạy (Android only) |
User CA (Android & iOS):
| Lớp | key |
Mô tả |
|---|---|---|
UserCAOverview |
UserCACheckOverview |
Tổng quan nhóm user CA |
UserInstalledCA |
UserInstalledCA |
CA do người dùng tự cài |
InjectedSystemCA |
InjectedSystemCA |
CA giả được inject vào hệ thống |
ProxyCA |
ProxyCA |
CA của proxy phổ biến (Burp, Charles...) |
4.6 Giải phóng tài nguyên #
Khi không còn dùng checker, gọi dispose() để giải phóng tài nguyên native:
@override
void dispose() {
_checker.dispose();
super.dispose();
}
Sau khi dispose(), mọi lời gọi method khác trên checker sẽ throw CShieldException(raspCheckerDisposed).
5. AIP — API Integrity Protection #
AIP ký số mỗi request gửi lên server và xác thực chữ ký của mỗi response nhận về, ngăn chặn MITM và replay attack.
SDK cung cấp hai cách tích hợp:
- Chế độ tự động (khuyến nghị): Dùng
CShieldInterceptor(chohttppackage) hoặcCShieldDioInterceptor(cho Dio). Sign/verify diễn ra hoàn toàn tự động. - Chế độ thủ công: Dùng
CShieldAIPtrực tiếp để kiểm soát hoàn toàn payload và timing.
5.1 Chế độ tự động — CShieldInterceptor (http) #
import 'package:c_shield_sdk/c_shield_sdk.dart';
import 'package:http/http.dart' as http;
// Khởi tạo một lần, tái sử dụng cho toàn bộ app
final client = CShieldInterceptor();
// Hoặc kết hợp với SSL pinning:
final client = CShieldInterceptor(
inner: CShieldSSL.createIOClient(),
);
// Sử dụng như http.Client thông thường:
final response = await client.post(
Uri.parse('https://api.example.com/users'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'name': 'Alice'}),
);
// cs-timestamp / cs-signature được đính kèm tự động vào request.
// Response signature được xác thực tự động trước khi trả về.
Tham số CShieldInterceptor:
| Tham số | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
inner |
http.Client? |
http.Client() |
HTTP client bên trong (truyền client có SSL pinning để kết hợp) |
verifyResponses |
bool |
true |
Xác thực chữ ký response; đặt false nếu server chưa tích hợp ký response |
5.2 Chế độ tự động — CShieldDioInterceptor (Dio) #
import 'package:c_shield_sdk/c_shield_sdk.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
final dio = Dio(BaseOptions(baseUrl: 'https://api.example.com'));
// Thêm AIP interceptor
dio.interceptors.add(const CShieldDioInterceptor());
// Kết hợp với SSL pinning:
dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: CShieldSSL.createHttpClient,
);
dio.interceptors.add(const CShieldDioInterceptor());
// Sử dụng bình thường
final response = await dio.post('/api/v1/login', data: {'user': 'alice'});
Tham số CShieldDioInterceptor:
| Tham số | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
verifyResponses |
bool |
true |
Xác thực chữ ký response; đặt false nếu server chưa ký response |
Khi
verifyResponses: true, interceptor tạm thời forceResponseType.bytesđể đọc raw bytes xác thực, sau đó decode lại sang kiểu gốc trước khi trả về caller.
5.3 Chế độ thủ công — CShieldAIP #
Dùng khi cần kiểm soát hoàn toàn — WebSocket, custom HTTP client, hoặc khi cần log chi tiết payload.
import 'package:c_shield_sdk/c_shield_sdk.dart';
// 1. Ký request thủ công
final aipHeaders = await CShieldAIP.signRequest(
method: 'POST',
path: '/api/v1/login', // chỉ path, không có query string
body: Uint8List.fromList(utf8.encode(jsonEncode({'user': 'alice'}))),
contentType: 'application/json',
);
// aipHeaders = {'cs-timestamp': '...', 'cs-signature': '...'}
// Đính kèm vào request trước khi gửi
// 2. Xác thực response thủ công
await CShieldAIP.verifyResponse(
statusCode: 200,
path: '/api/v1/login',
headers: response.headers,
body: responseBytes,
);
// Không throw = hợp lệ; throw CShieldException nếu thất bại
// 3. Ký payload thô (nâng cao)
final ts = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final norm = await CShieldAIP.normalizeBody(
body: bodyBytes,
contentType: 'application/json',
);
final payload = 'POST./api/v1/login.$ts.${norm['hash']}';
final signature = await CShieldAIP.sign(payload);
// 4. Xác thực chữ ký thô
await CShieldAIP.verify(payload: payload, signature: signature);
API CShieldAIP:
| Phương thức | Mô tả |
|---|---|
signRequest(method, path, body, contentType) |
Ký request và trả về map {'cs-timestamp', 'cs-signature'} |
verifyResponse(statusCode, path, headers, body) |
Xác thực chữ ký response; throw CShieldException nếu thất bại |
sign(payload) |
Ký payload thô; caller tự xây dựng payload string |
verify(payload, signature) |
Xác thực chữ ký payload thô |
normalizeBody(body, contentType) |
Chuẩn hoá body và tính hash; trả về {'normalizedString', 'sizeInBytes', 'hash'} |
5.4 Giao thức ký số #
Request — client gửi lên server:
Headers đính kèm:
cs-timestamp: <unix_seconds>
cs-signature: <RSA_signature>
Payload được ký:
{METHOD}.{path}.{timestamp}.{SHA256(body)}
Ví dụ:
POST./api/v1/login.1746700000.e3b0c44298fc1c149afbf4c8996fb924...
Response — server trả về:
Headers mà server phải đính kèm:
cs-timestamp: <unix_seconds>
cs-signature: <RSA_signature>
Payload server ký:
{statusCode}.{path}.{timestamp}.{SHA256(responseBody)}
Quy tắc:
- Timestamp phải nằm trong cửa sổ +-30 giây so với giờ thiết bị.
pathlà URL path không bao gồm query string (/api/v1/login, không phải/api/v1/login?token=abc).- SHA-256 của body là lowercase hex.
Body normalization:
| Content-Type | Cách xử lý |
|---|---|
application/json / text |
Body bytes dùng nguyên văn |
multipart/form-data |
Chỉ lấy text fields (bỏ qua file parts), sắp xếp theo tên field, serialize JSON |
6. SSL — Certificate Pinning #
Certificate pinning đảm bảo app chỉ chấp nhận đúng chứng chỉ của server đã biết, ngăn chặn MITM kể cả khi thiết bị tin tưởng CA giả.
6.1 Lấy giá trị pin #
Pin là SHA-256 của SPKI (Subject Public Key Info) của certificate, encode base64:
# Lấy pin từ server trực tiếp
openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \
| openssl x509 -pubkey -noout \
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256 -binary \
| openssl base64
# Thêm prefix "sha256/" vào kết quả
# Ví dụ: sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
Khuyến nghị: luôn cung cấp tối thiểu 2 pin (primary + backup) để tránh lockout khi rotate certificate.
6.2 Cấu hình CShieldSSL #
Gọi configure() sau initialize(), trước khi thực hiện bất kỳ network request nào:
await CShieldSSL.configure(
pins: [
'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', // primary
'sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=', // backup
],
hostname: 'api.example.com',
);
API CShieldSSL:
| Phương thức | Mô tả |
|---|---|
configure(pins, hostname) |
Cấu hình pinning; throw ArgumentError nếu pin rỗng, hostname trống, hoặc pin không có prefix sha256/ |
updatePins(pins, hostname) |
Cập nhật pin mới sau khi server rotate certificate (alias của configure) |
isConfigured() |
Trả về true nếu đã cấu hình |
createDioAdapter() |
(Khuyến nghị) Tạo HttpClientAdapter cho Dio với SPKI pinning post-handshake — verify pin cho cả CA-signed cert |
createHttpClient() |
Tạo HttpClient (dart:io) với SPKI pinning qua badCertificateCallback |
createIOClient() |
Tạo IOClient (http package) — drop-in cho http.Client() |
verifyPin(certDerBase64, host) |
Xác minh thủ công một certificate DER base64; trả về true/false |
6.3 Tích hợp với http package #
import 'package:c_shield_sdk/c_shield_sdk.dart';
// Setup (một lần trong main() hoặc khởi tạo app)
await CShieldSSL.configure(
pins: ['sha256/...'],
hostname: 'api.example.com',
);
// Tạo client (tái sử dụng, không tạo lại mỗi request)
final client = CShieldSSL.createIOClient();
// Sử dụng giống http.Client thông thường
final response = await client.get(
Uri.parse('https://api.example.com/data'),
);
Kết hợp SSL pinning + AIP:
final client = CShieldInterceptor(
inner: CShieldSSL.createIOClient(), // SSL pinning ở lớp trong
);
// client vừa có certificate pinning vừa ký/xác thực AIP tự động
6.4 Tích hợp với Dio #
import 'package:c_shield_sdk/c_shield_sdk.dart';
import 'package:dio/dio.dart';
await CShieldSSL.configure(
pins: ['sha256/...'],
hostname: 'api.example.com',
);
final dio = Dio(BaseOptions(baseUrl: 'https://api.example.com'));
// Gắn SSL pinning vào Dio — createDioAdapter() kiểm tra SPKI pin
// post-handshake qua HttpClientResponse.certificate, bao gồm cả
// CA-signed cert (Let's Encrypt...). Khác với badCertificateCallback
// chỉ fire khi cert không được hệ thống tin cậy.
dio.httpClientAdapter = CShieldSSL.createDioAdapter();
// Kết hợp với AIP
dio.interceptors.add(const CShieldDioInterceptor());
6.5 Xác minh thủ công #
Dùng verifyPin() trong interceptor tuỳ chỉnh hoặc WebSocket:
// Lấy DER bytes của leaf certificate từ kết nối TLS
final certDerBase64 = base64.encode(peerCertificateDerBytes);
final trusted = await CShieldSSL.verifyPin(
certDerBase64: certDerBase64,
host: 'api.example.com',
);
if (!trusted) {
throw CShieldException(
CShieldErrorCode.sslPinMismatch,
'Certificate pin mismatch for api.example.com',
);
}
7. Exceptions #
Tất cả lỗi từ SDK đều được throw dưới dạng CShieldException:
class CShieldException implements Exception {
final CShieldErrorCode code; // enum mã lỗi
final String message; // mô tả lỗi
final Object? nativeCause; // lỗi gốc từ native (nếu có)
}
CShieldErrorCode:
| Code | Nguyên nhân |
|---|---|
aipMissingHeader |
Response thiếu header cs-timestamp hoặc cs-signature |
aipTimestampExpired |
Timestamp nằm ngoài cửa sổ +-30 giây |
aipInvalidSignature |
Chữ ký response không hợp lệ (response bị tampering) |
aipSigningFailed |
Lỗi ký request (private key chưa sẵn sàng) |
aipDetectProxyCA |
Phát hiện proxy CA — AIP từ chối xử lý |
sslNotConfigured |
Gọi createHttpClient()/createIOClient() trước khi gọi CShieldSSL.configure() |
sslPinMismatch |
Certificate của server không khớp với pin đã cấu hình |
raspCheckerDisposed |
Gọi method trên RASPChecker đã bị dispose() |
notInitialized |
Gọi API trước khi gọi CShieldSdk.initialize() |
invalidArgument |
Tham số không hợp lệ |
nativeError |
Lỗi không xác định từ native SDK |
Cách bắt lỗi:
try {
final response = await client.post(Uri.parse('https://api.example.com/login'), ...);
} on CShieldException catch (e) {
switch (e.code) {
case CShieldErrorCode.aipDetectProxyCA:
// Có proxy CA đang active — không cho phép tiếp tục
_logSecurityEvent('Proxy CA detected');
break;
case CShieldErrorCode.aipTimestampExpired:
// Đồng hồ lệch hoặc bị replay attack
_showError('Lỗi xác thực thời gian');
break;
case CShieldErrorCode.aipInvalidSignature:
// Response bị can thiệp
_logSecurityEvent('Response tampered');
break;
case CShieldErrorCode.sslPinMismatch:
// Certificate không khớp — MITM hoặc cần rotate pin
_logSecurityEvent('SSL pin mismatch');
break;
default:
_showError('Lỗi bảo mật: ${e.message}');
}
}
Luồng tích hợp điển hình #
main()
+-- WidgetsFlutterBinding.ensureInitialized()
+-- CShieldSdk.initialize() // bắt buộc — trước runApp()
+-- CShieldSdk.setThreatListener(...) // tuỳ chọn — nhận load-time threats
+-- CShieldSSL.configure(pins, host) // nếu dùng certificate pinning
+-- runApp()
Khởi tạo HTTP client (singleton)
+-- CShieldInterceptor(inner: CShieldSSL.createIOClient()) // http package
// hoặc Dio:
+-- dio.httpClientAdapter = CShieldSSL.createDioAdapter()
+-- dio.interceptors.add(CShieldDioInterceptor())
Tại màn hình splash / đăng nhập
+-- RASPChecker.builder(...).quickCheck() // kiểm tra nhanh một lần
Trong suốt phiên sử dụng
+-- checker.subscribe(...).listen(...) // giám sát liên tục