تصميم التشفير الهجين في Flutter: تغليف مفتاح جلسة بـRSA-OAEP وتوثيق الحمولة بـAES-GCM مع إدارة nonce ومنع إعادة الإرسال.
مثال AES ليس بروتوكول RSA مكتملًا
العينة أدناه تختبر تشفير رسالة محلية وفكها فقط؛ لا تنفذ تبادل المفاتيح أو توثيق الخادم أو دورة حياة جلسة إنتاجية. يجب الاتفاق على بنية مظروف الرسالة وإصداره ومعرف المفتاح ومكوناته قبل التكامل. المفتاح العشوائي لا يساوي كلمة مرور بشرية، وBase64 ترميز للنقل وليس حماية إضافية. لا تسجل المفتاح أو النص الحساس في سجلات التطبيق.
اختبار الفشل أهم من تطابق النص
اكتب اختبارًا يغير بايتًا من الحمولة ثم يتوقع رفضها، وآخر يبدل AAD وثالثًا يجرب مفتاحًا مختلفًا. اختبر الحقول المفقودة والأطوال والترميز قبل استدعاء المكتبة، وأعد خطأ عامًا للمستخدم مع تشخيص لا يحتوي أسرارًا. لا تُخرج نصًا جزئيًا عند فشل التوثيق، ولا تعاود استخدام nonce مع المفتاح نفسه للرد على الطلب. سلامة round trip وحدها لا تثبت سلامة تصميم التبادل.
الاختبارات المشتركة
احتفظ بمتجهات اختبار متفق عليها بين العميل والخادم، واختبر رفض الحمولة المعدلة قبل اعتماد أي تبادل في الإنتاج.
خطوات التطبيق والشرح
التشفير الهجين في Flutter يجمع حماية مفتاح الجلسة بالتشفير غير المتماثل مع تشفير البيانات باستخدام AES-GCM. تعرّف على التصميم وحدود الحماية قبل كتابة الكود.
كيف تتوزع مسؤولية RSA وAES؟
ينشئ الخادم زوج مفاتيح RSA ويحتفظ بالمفتاح الخاص داخله. ينشئ العميل مفتاح جلسة عشوائيًا، ويغلّفه بالمفتاح العام الموثوق للخادم باستخدام مخطط متفق عليه مثل RSA-OAEP. تُشفّر الحمولة بمفتاح AES عبر GCM، وتُرسل معها قيمة nonce ووسم التحقق والبيانات المرتبطة اللازمة. RSA هنا لحماية سر صغير، وليس لتقسيم ملفات كبيرة وتشفيرها كتلة بعد كتلة.
ما الذي يجب الاتفاق عليه مع الخادم؟
لا يكفي أن يذكر الطرفان كلمة OAEP. يجب مطابقة دالة التجزئة وMGF1 والـ label وترميز المفتاح، ثم اختبار فك التشفير بين اللغتين. اتفق كذلك على طول مفتاح AES وحجم nonce وطول الوسم وترميز Base64 وترتيب الحقول. لا تضع مفتاح RSA الخاص داخل APK أو مستودع Git، ولا تنشئ بروتوكولًا جديدًا دون مراجعة أمنية.
nonce مختلف لكل عملية تشفير
يجب ألا تتكرر قيمة nonce مع المفتاح نفسه. تستخدم مكتبات التشفير الموثوقة مولدًا آمنًا؛ لا تستبدله بالوقت أو Random العادي أو قيمة ثابتة. الطلب والرد عمليتان مستقلتان ولا يعيدان استخدام الزوج نفسه. عند فشل وسم GCM ارفض الرسالة بدل عرض نص جزئي. يمكن ربط معرّف الجلسة ونوع الرسالة كـ AAD لمنع خلط السياقات.
لماذا يظل HTTPS ضروريًا؟
التشفير الإضافي لا يثبت وحده هوية الخادم ولا يحمي بيانات الاتصال الوصفية، ولا يمنع إعادة إرسال رسالة صحيحة قديمة. أبقِ TLS والتحقق من الشهادة، وصمّم صلاحية للجلسة ومعرّفات طلبات وسياسة لمنع الإعادة. لا تسجل المفاتيح أو النصوص الحساسة في Logcat. امسح مراجع الجلسة عند انتهائها، مع إدراك أن الذاكرة المُدارة لا تضمن مسحًا فوريًا لكل نسخة.
اختبار التصميم قبل الإنتاج
ابدأ ببيانات وهمية واختبارات معروفة النتائج: رسالة عربية، حمولة فارغة، تعديل بايت في النص المشفر، مفتاح خاطئ، AAD مختلف، وإعادة الطلب نفسه. يجب أن يفشل العبث بصورة واضحة. المثال التالي يختبر AES-GCM فقط؛ لا ينفذ تبادل RSA أو مصادقة الخادم، ولذلك ليس بروتوكول دفع جاهزًا ولا دليل امتثال.
مثال للتطبيق
أضف حزمة cryptography إلى مشروع الاختبار باستخدام dart pub add cryptography، أو flutter pub add cryptography لمشروع Flutter. اختُبر المثال على cryptography 2.9.0. يعرض newSecretKey وnonce المولد داخل encrypt نموذجًا محليًا؛ لا تحفظ المفتاح الناتج داخل الشيفرة.
import 'dart:convert';
import 'package:cryptography/cryptography.dart';
Future<void> main() async {
final algorithm = AesGcm.with256bits();
final key = await algorithm.newSecretKey();
final aad = utf8.encode('demo:v1');
final box = await algorithm.encrypt(utf8.encode('مرحبا'), secretKey: key, aad: aad);
final clear = await algorithm.decrypt(box, secretKey: key, aad: aad);
print(utf8.decode(clear));
}المرجع الرسمي
للتفاصيل الخاصة بإصدار الأداة وبيئة التشغيل، راجع توثيق التشفير الهجين في Flutter. قد تختلف أسماء القوائم بين الإصدارات؛ طابق الإعداد مع مشروعك قبل تطبيقه.
مرجع للتنفيذ
راجع التوثيق الخاص بهذا الموضوع وطابق إعداداته مع إصدار أدوات مشروعك. اختبر السلوك المستهدف وحالة الفشل قبل إدماج المثال في تطبيق منشور.






