التشفير والمفاتيح: كيف تعمل المحافظ الرقمية (Public & Private Keys) برمجياً؟

دقائق القراءة: 6

التشفير والمفاتيح: كيف تعمل المحافظ الرقمية (Public & Private Keys) برمجياً؟

عندما يتعامل المستخدم مع محفظة رقمية في عالم Web3 فهو غالباً يرى واجهة بسيطة: إنشاء محفظة، نسخ عنوان، توقيع معاملة، ثم الإرسال. لكن تحت هذه البساطة توجد طبقة تشفير دقيقة تربط بين الرياضيات، أمن الهوية، وآلية التحقق داخل شبكات Blockchain. فهم هذا المسار برمجياً مهم جداً لكل مطور يريد بناء تطبيقات موثوقة وآمنة.

المحفظة الرقمية ليست مكاناً “يخزن العملات” بالمعنى الحرفي، بل هي أداة لإدارة المفاتيح وتوليد التواقيع التي تثبت ملكية الأصول على السلسلة. الأرصدة نفسها تبقى مسجلة على الشبكة، بينما يمتلك المستخدم القدرة على التحكم بها عبر Private Key سري يقابله Public Key وعنوان قابل للمشاركة.

إذا كنت قد قرأت سابقاً مدخل إلى Web3: ما هو البلوكتشين ولماذا يغير شكل الإنترنت والأنظمة المالية؟ فهذه المقالة تمثل المستوى الأعمق تقنياً: كيف تتحول الهوية المشفرة إلى عنوان، وكيف تُوقَّع الرسائل والمعاملات، وكيف تتحقق العقود الذكية من صحة هذه التواقيع برمجياً.

ما هي المحفظة الرقمية برمجياً؟

برمجياً، المحفظة هي بنية تتعامل مع ثلاثة عناصر رئيسية: Private Key، وPublic Key، وAddress. المفتاح الخاص هو السر الأساسي، والمفتاح العام يُشتق منه رياضياً، أما العنوان فيكون تمثيلاً مختصراً صالحاً للاستخدام على الشبكة.

في شبكات مثل Ethereum يتم استخدام منحنيات بيضوية ضمن خوارزمية ECDSA على المنحنى secp256k1. هذا الاختيار ليس عشوائياً؛ فهو يحقق توازناً ممتازاً بين قوة الأمان، سرعة التوقيع، وإمكانية التحقق داخل بيئة EVM.

سلسلة الاشتقاق: من المفتاح الخاص إلى العنوان

1) توليد المفتاح الخاص

المفتاح الخاص عادة عبارة عن رقم عشوائي بطول 256-بت. هذا الرقم يجب أن يُولَّد من مصدر عشوائية آمن تشفيرياً، لأن أي ضعف في العشوائية يفتح الباب لتوقع المفاتيح واختراق المحافظ. هنا تبدأ الثقة الأمنية للنظام كله.

2) اشتقاق المفتاح العام

بعد إنشاء Private Key يتم ضربه في نقطة مولّدة على المنحنى البيضوي لاستخراج Public Key. هذه العملية سهلة في اتجاه واحد، لكنها شبه مستحيلة عكسياً عملياً، وهذا ما يمنح النظام خاصية الأمان الأساسية.

3) اشتقاق العنوان

في Ethereum يُشتق العنوان بأخذ تجزئة Keccak-256 للمفتاح العام، ثم استخدام آخر 20 بايت. لذلك العنوان ليس المفتاح العام نفسه، بل نسخة مختصرة منه بعد معالجته تجزئياً.

  • توليد رقم سري عشوائي آمن.
  • اشتقاق Public Key باستخدام ECDSA.
  • تجزئة المفتاح العام بخوارزمية Keccak-256.
  • استخراج آخر 20 بايت لتكوين Wallet Address.

كيف يعمل التوقيع الرقمي عند إرسال معاملة؟

عند إرسال معاملة، لا ترسل المحفظة المفتاح الخاص أبداً. بدلاً من ذلك تقوم ببناء رسالة تمثل المعاملة: العنوان المرسل إليه، القيمة، Nonce، وحدود الغاز، ثم تُجزّأ البيانات وتُوقَّع محلياً.

الناتج يكون توقيعاً رقمياً يثبت أن صاحب المفتاح الخاص هو من وافق على هذه البيانات تحديداً. بعد ذلك تتحقق الشبكة من التوقيع باستخدام المفتاح العام المستعاد، دون الحاجة لمعرفة السر نفسه. هذا المبدأ هو قلب الثقة في تطبيقات DApps.

لا يجب مطلقاً إرسال Private Key إلى الخادم أو حفظه في localStorage بشكل خام. أفضل الممارسات تعتمد على التوقيع من جهة العميل باستخدام محفظة موثوقة أو وحدة آمنة، ثم إرسال التوقيع فقط إلى الواجهة الخلفية أو إلى العقد الذكي عند الحاجة.

مثال برمجي باستخدام Ethers.js

في بيئة التطوير، يمكن استخدام مكتبة Ethers.js مع Hardhat لإنشاء محفظة واختبار التوقيع. عملياً، المطور لا ينفذ رياضيات المنحنى يدوياً، بل يعتمد على مكتبات مدققة تنفذ هذه الخطوات بأمان وكفاءة.

// Example concept for wallet interaction in a Solidity-focused article:
// JavaScript libraries like Ethers.js generate wallets off-chain,
// while Solidity verifies signatures on-chain.

pragma solidity ^0.8.20;

contract SignatureNotes {
    string public note = "Wallets sign off-chain; contracts verify on-chain";
}

في التطبيق الفعلي خارج السلسلة، تمر العملية البرمجية غالباً بالمراحل التالية:

  1. إنشاء كائن محفظة من عبارة استرداد أو مفتاح خاص.
  2. بناء الرسالة أو بيانات المعاملة.
  3. توليد التوقيع باستخدام دالة مثل signMessage.
  4. إرسال الرسالة والتوقيع إلى العقد أو الخادم للتحقق.

التحقق من التوقيع داخل العقد الذكي

داخل عقود Solidity يتم استخدام الدالة ecrecover لاستعادة العنوان الذي أنشأ التوقيع. هذه الدالة لا تعطيك المفتاح الخاص أو العام مباشرة، لكنها تسمح لك بمقارنة العنوان المستعاد مع عنوان موثوق متوقع.

pragma solidity ^0.8.20;

contract SignatureVerifier {
    function getMessageHash(
        address user,
        uint256 amount,
        uint256 nonce
    ) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(user, amount, nonce));
    }

    function getEthSignedMessageHash(bytes32 messageHash)
        public
        pure
        returns (bytes32)
    {
        return keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash)
        );
    }

    function recoverSigner(bytes32 ethSignedMessageHash, bytes memory signature)
        public
        pure
        returns (address)
    {
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
        return ecrecover(ethSignedMessageHash, v, r, s);
    }

    function verify(
        address expectedSigner,
        address user,
        uint256 amount,
        uint256 nonce,
        bytes memory signature
    ) public pure returns (bool) {
        bytes32 messageHash = getMessageHash(user, amount, nonce);
        bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
        return recoverSigner(ethSignedMessageHash, signature) == expectedSigner;
    }

    function splitSignature(bytes memory sig)
        internal
        pure
        returns (bytes32 r, bytes32 s, uint8 v)
    {
        require(sig.length == 65, "invalid signature length");

        assembly {
            r := mload(add(sig, 32))
            s := mload(add(sig, 64))
            v := byte(0, mload(add(sig, 96)))
        }
    }
}

هذا النمط شائع في أنظمة Meta Transactions، وأنظمة السماح المسبق، والتوثيق اللامركزي. بدل أن يدفع المستخدم معاملة كاملة لكل خطوة، يمكنه توقيع رسالة فقط، ثم يقوم طرف آخر بتمريرها على السلسلة.

أين تدخل عبارة الاسترداد Seed Phrase؟

العديد من المحافظ الحديثة لا تنشئ مفتاحاً واحداً فقط، بل تولد عبارة استرداد وفق معايير مثل BIP-39 وHD Wallets. من هذه العبارة يمكن اشتقاق عدد كبير من الحسابات بطريقة هرمية ومنظمة.

هذا يعني أن المستخدم لا يحتاج إلى حفظ كل مفتاح بشكل منفصل. لكن الخطورة هنا أكبر: إذا سُرّبت عبارة الاسترداد، فإن جميع الحسابات المشتقة منها تصبح معرضة للخطر، وليس حساباً واحداً فقط.

اعتبارات أمنية مهمة للمطورين

التعامل مع المفاتيح في التطبيقات اللامركزية ليس تفصيلاً ثانوياً، بل هو الجزء الأكثر حساسية. الخطأ هنا لا يؤدي فقط إلى Bug تقني، بل قد يسبب خسارة أموال نهائية وغير قابلة للاسترجاع.

أمن التوقيع لا يكتمل إلا بإضافة Nonce أو معرف فريد للرسالة. من دون ذلك قد يتعرض التطبيق لهجمات Replay Attack حيث يُعاد استخدام توقيع قديم لتنفيذ نفس الإجراء مرة أخرى.

  • اعتمد على مكتبات معروفة ومدققة بدل تنفيذ التشفير يدوياً.
  • فرّق بين توقيع الرسائل وتوقيع المعاملات وتوقيع البيانات المهيكلة.
  • تحقق من النطاق والسلسلة والمهلة الزمنية عند بناء الرسائل الموقعة.
  • اختبر حالات الفشل والبيانات المعدّلة باستخدام Hardhat وبيئات اختبار معزولة.

تحسينات الغاز عند التحقق من التواقيع

التحقق على السلسلة مكلف نسبياً، خصوصاً عند تكرار عمليات التوقيع داخل حلقات أو عند تصميم آليات مصادقة معقدة. لذلك من الأفضل تنفيذ أكبر قدر ممكن من المعالجة خارج السلسلة، ثم تمرير أقل قدر ضروري من البيانات إلى العقد.

لتقليل Gas Fees، اجعل الرسائل الموقعة مضغوطة وبنيتها ثابتة، وتجنب تخزين التواقيع كاملة على السلسلة إلا عند الضرورة. كما أن استخدام التحقق المؤقت ثم حفظ نتيجة منطقية أبسط قد يكون أوفر من إعادة التحقق مراراً.

الخلاصة

المحفظة الرقمية ليست مجرد أداة دخول إلى عالم Web3، بل نظام تشفيري متكامل يبدأ من العشوائية الآمنة، ويمر باشتقاق المفاتيح والعناوين، ثم ينتهي بإثبات الملكية عبر التوقيع والتحقق. كل خطوة في هذه السلسلة تؤثر مباشرة في أمان التطبيق وموثوقيته.

لذلك، فإن المطور المحترف لا يكتفي باستخدام المحفظة كواجهة جاهزة، بل يفهم كيف تُبنى الرسائل، وكيف تعمل ECDSA، وكيف يتحقق العقد من التوقيع داخل Solidity. هذا الفهم هو ما يصنع الفارق بين تطبيق لامركزي يعمل ظاهرياً، ومنتج قوي يمكن الوثوق به فعلاً في البيئات المالية المفتوحة.

16 comments

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *