در ارزهای رمزنگاری شده، یک کلید خصوصی به کاربر اجازه می دهد تا به کیف پول خود دسترسی پیدا کند. شخصی که کلید خصوصی را در دست دارد به ارز های موجود در آن کیف پول دسترسی کامل دارد. به همین دلیل، باید آن را جای امن نگه دارد. و اگر واقعاً میخواهید کلید خصوصی را خودتان ایجاد کنید، منطقی است که آن را به روشی امن بسازید.
در اینجا، من مقدمه ای برای کلیدهای خصوصی ارائه می کنم و به شما نشان می دهم که چگونه می توانید کلید خود را با استفاده از توابع مختلف رمزنگاری ایجاد کنید.
آیا واقعا نیازه که خودم کلید خصوصی را تولید کنم ؟
بیشتر اوقات شما این کار را نمی کنید. به عنوان مثال، اگر از کیف پول های گرم مانند metamask یا trust wallet استفاده می کنید، آنها کلید خصوصی را برای شما ایجاد و مدیریت می کنند. برای مبادلات هم همینطوره
کیف پول های موبایل و وب و دسکتاپ معمولا یک کلید خصوصی برای شما ایجاد می کنند، اگرچه ممکن است از کلید خصوصی خودشان برای شما کلیدی بسازند .
دلایلی برای ساخت کلید خصوصی:
- شما می خواهید مطمئن شوید که هیچ کس کلید را نمی داند
- شما فقط می خواهید در مورد رمزنگاری و تولید اعداد تصادفی (RNG) بیشتر بدانید
کلید خصوصی دقیقا چیست؟
به طور رسمی، یک کلید خصوصی برای بیت کوین (و بسیاری از ارزهای دیجیتال دیگر) یک سری 32 بایتی است. اکنون راه های زیادی برای ایجاد این بایت ها وجود دارد. این می تواند یک رشته از 256 یک و صفر (32 * 8 = 256) باشد. می تواند یک رشته باینری، رشته Base64، یک کلید WIF، عبارت 12 کلمه (mnemonic phrase – که مربوط به bip 39 می) یا در نهایت، یک رشته hex باشد.
چرا 32 بایت؟ سوال عالی! ببینید، برای ایجاد یک کلید عمومی از یک کلید خصوصی، بیت کوین از الگوریتم امضای دیجیتالی ECDSA یا منحنی بیضی استفاده می کند. به طور خاص، از یک منحنی خاص به نام secp256k1 استفاده می کند.
اکنون، این منحنی دارای ترتیب 256 بیت است، 256 بیت را به عنوان ورودی می گیرد و اعداد صحیح 256 بیتی را خروجی می دهد. و 256 بیت دقیقا 32 بایت است. بنابراین، به بیان دیگر، ما به 32 بایت داده برای این الگوریتم منحنی نیاز داریم.
یک نیاز اضافی برای کلید خصوصی وجود دارد. از آنجایی که ما از ECDSA استفاده می کنیم، کلید باید مثبت باشد و از ترتیب منحنی کمتر باشد. ترتیب secp256k1 [symple_highlight color=”yellow”]FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141[/symple_highlight] است، که بسیار بزرگ است: تقریباً هر عدد 32 بایتی کوچکتر از آن خواهد بود.
روش ساده
بنابراین، چگونه یک عدد صحیح 32 بایتی تولید کنیم؟ اولین چیزی که به ذهن می رسد این است که فقط از یک کتابخانه RNG به زبان انتخابی خود استفاده کنید. من اینجا از پایتون استفاده می کنم:
import random bits = random.getrandbits(256) # 30848827712021293731208415302456569301499384654877289245795786476741155372082 bits_hex = hex(bits) # 0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32 private_key = bits_hex[2:] # 4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
به نظر خوبه، اما در واقع، اینطور نیست. ببینید، کتابخانههای RNG معمولی برای رمزنگاری در نظر گرفته نشدهاند، زیرا خیلی امن نیستند. آنها اعداد را بر اساس یک seed تولید می کنند و به طور پیش فرض، زمان حال است. به این ترتیب، اگر میدانید تقریباً چه زمانی بیتهای بالا را تولید کردم، تنها کاری که باید انجام دهید این است که چند نوع از آنها را brute-force کنید.
هنگامی که یک کلید خصوصی تولید می کنید، می خواهید بسیار امن باشید. به یاد داشته باشید، اگر کسی کلید خصوصی را پیدا کند، به راحتی می تواند تمام کوین ها را از کیف پول مربوطه بدزدد و شما هیچ شانسی برای پس گرفتن آنها ندارید.
بنابراین بیایید سعی کنیم آن را با امنیت بیشتری انجام دهیم.
RNG با رمزنگاری قوی
در کنار یک روش استاندارد RNG، زبان های برنامه نویسی معمولاً RNG را ارائه می دهند که به طور خاص برای عملیات رمزنگاری طراحی شده است. این روش معمولاً بسیار امن تر است، زیرا آنتروپی را مستقیماً از سیستم عامل می گیرد. بازتولید نتیجه چنین RNG بسیار دشوارتر است. شما نمی توانید با دانستن زمان تولید یا داشتن seed این کار را انجام دهید، زیرا seed ای وجود ندارد. خوب، حداقل کاربر یک Seed را وارد نمی کند – بلکه توسط برنامه ایجاد شده است.
در پایتون، RNG قوی از نظر رمزنگاری در ماژول Secrets پیاده سازی شده است. بیایید کد بالا را تغییر دهیم تا تولید کلید خصوصی ایمن شود!
import secrets bits = secrets.randbits(256) # 46518555179467323509970270980993648640987722172281263586388328188640792550961 bits_hex = hex(bits) # 0x66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31 private_key = bits_hex[2:] # 66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
جالبه. شرط می بندم که حتی با دسترسی به سیستم من نمی توانید این را دوباره تولید کنید. اما آیا می توانیم تخصصی تر پیش بریم؟
سایت های مخصوص اعداد رندوم و تولید کلید خصوصی بیت کوین
سایت هایی وجود دارند که اعداد تصادفی را برای شما تولید می کنند. ما در اینجا فقط دو مورد را در نظر خواهیم گرفت. یکی از آنها random.org است که یک تولید کننده اعداد تصادفی با هدف عمومی شناخته شده است. یکی دیگر bitaddress.org است که به طور خاص برای تولید کلید خصوصی بیت کوین طراحی شده است.
آیا random.org می تواند به ما در تولید یک کلید کمک کند؟ قطعاً، زیرا آنها سرویسی برای تولید بایت های تصادفی دارند. اما در اینجا دو مشکل پیش می آید. Random.org ادعا می کند که یک تولید کننده واقعا تصادفی است، اما آیا می توانید به آن اعتماد کنید؟ آیا می توانید مطمئن باشید که واقعاً تصادفی است؟ آیا میتوانید مطمئن باشید که مالک همه نتایج را ضبط نمیکند، بهویژه آنهایی که شبیه کلیدهای خصوصی هستند؟ پاسخ به شما بستگی دارد. شما نمی توانید آن را روی کامپیوتر خود بدون اینترنت اجرا کنید، که یک مشکل بزرگ است. این روش 100% امن نیست.
اکنون، bitaddress.org یک داستان کاملاً متفاوت دارد. منبع باز است، بنابراین می توانید آنچه را که در پشت آن وجود دارد، ببینید. این سایت در سمت کلاینت اجرا می شود، بنابراین می توانید آن را دانلود کرده و به صورت لوکال اجرا کنید، حتی بدون اتصال به اینترنت.
پس چگونه کار می کند؟ از شما به عنوان منبع آنتروپی استفاده می کند. از شما می خواهد که ماوس خود را حرکت دهید یا کلیدهای تصادفی را فشار دهید. شما آنقدر این کار را انجام می دهید تا بازتولید نتایج غیرممکن باشد.
آیا علاقه مندید که ببینید bitaddress.org چگونه کار می کند؟ برای اهداف آموزشی، ما به کد آن نگاه می کنیم و سعی می کنیم آن را در پایتون بازتولید کنیم.
Bitaddress
Bitaddress آنتروپی را به دو شکل ایجاد می کند: با حرکت ماوس و فشار کلید. ما در مورد هر دو صحبت خواهیم کرد، اما روی فشار دادن کلیدها تمرکز خواهیم کرد، زیرا اجرای ردیابی ماوس در زبان Python دشوار است. ما از کاربر انتظار داریم تا کلیک کند تا زمانی که آنتروپی کافی داشته باشیم، و سپس یک کلید ایجاد میکنیم.
Bitaddress سه کار را انجام می دهد. آرایه بایت را مقداردهی اولیه می کند، سعی می کند تا حد امکان آنتروپی را از رایانه شما دریافت کند، آرایه را با ورودی کاربر پر می کند و سپس یک کلید خصوصی تولید می کند.
Bitaddress از آرایه 256 بایتی برای ذخیره آنتروپی استفاده می کند. این آرایه به صورت چرخه ای بازنویسی می شود، بنابراین هنگامی که آرایه برای اولین بار پر می شود، نشانگر به صفر می رسد و فرآیند پر شدن دوباره شروع می شود.
window.crypto
این برنامه یک آرایه با 256 بایت از window.crypto راه اندازی می کند. سپس، با یک timestamp چهار بایت آنتروپی را به دست می آورد. در نهایت، داده هایی مانند اندازه صفحه، منطقه زمانی شما، اطلاعات مربوط به افزونه های مرورگر، منطقه شما و موارد دیگر را دریافت می کند. این به آن 6 بایت دیگر می دهد.
پس از مقداردهی اولیه، برنامه به طور مداوم منتظر ورودی کاربر برای بازنویسی بایت های اولیه است. هنگامی که کاربر موس را حرکت می دهد، برنامه موقعیت مکان موس را می نویسد. وقتی کاربر کلیک را فشار می دهد، برنامه کد کاراکتر دکمه فشرده شده را می نویسد.
در نهایت، bitaddress از آنتروپی پر شده برای تولید یک کلید خصوصی استفاده می کند. باید 32 بایت تولید کند. برای این کار، bitaddress از یک الگوریتم RNG به نام ARC4 استفاده می کند. برنامه ARC4 را با زمان فعلی و آنتروپی جمع آوری شده مقداردهی اولیه می کند، سپس 32 بار بایت ها را یک به یک دریافت می کند.
امیدوارم که ایده را دریافت کرده باشید. می توانید الگوریتم را با جزئیات کامل در Github بررسی کنید.
بیایید برنامه ای مثل bitaddress بسازیم
برای اهداف خود، نسخه ساده تری از bitaddress را خواهیم ساخت. اولا، ما اطلاعاتی در مورد ماشین و مکان کاربر جمع آوری نمی کنیم. دوم، آنتروپی را فقط از طریق متن وارد می کنیم، زیرا دریافت مداوم موقعیت ماوس با اسکریپت پایتون بسیار چالش برانگیز است (اگر می خواهید این کار را انجام دهید PyAutoGUI را بررسی کنید).
اول کتابخانه را ایجاد میکنیم . ابتدا یک آرایه بایت را با RNG رمزنگاری مقداردهی می کند، سپس مهر timestamp را پر می کند و در نهایت رشته ایجاد شده توسط کاربر را پر می کند. پس از پر شدن seed pool، کتابخانه به توسعه دهنده اجازه می دهد یک کلید ایجاد کند. در واقع، آنها میتوانند هر تعداد که میخواهند کلید خصوصی ایجاد کنند که همگی توسط آنتروپی جمعآوریشده ایمن شدهاند.
راه اندازی pool
در اینجا تعدادی بایت از RNG رمزنگاری و یک timestamp قرار می دهیم. __seed_int و __seed_byte دو روش کمکی هستند که آنتروپی را در آرایه pool ما وارد می کنند. توجه داشته باشید که ما از ماژول secret استفاده می کنیم
def __init_pool(self): for i in range(self.POOL_SIZE): random_byte = secrets.randbits(8) self.__seed_byte(random_byte) time_int = int(time.time()) self.__seed_int(time_int) def __seed_int(self, n): self.__seed_byte(n) self.__seed_byte(n >> 8) self.__seed_byte(n >> 16) self.__seed_byte(n >> 24) def __seed_byte(self, n): self.pool[self.pool_pointer] ^= n & 255 self.pool_pointer += 1 if self.pool_pointer >= self.POOL_SIZE: self.pool_pointer = 0
seed
در اینجا ابتدا یک timestamp و سپس رشته ورودی را کاراکتر به کاراکتر قرار می دهیم.
def seed_input(self, str_input): time_int = int(time.time()) self.__seed_int(time_int) for char in str_input: char_code = ord(char) self.__seed_byte(char_code)
تولید کلید خصوصی
این بخش ممکن است سخت به نظر برسد، اما در واقع بسیار ساده است.
ابتدا باید عدد 32 بایتی را با استفاده از pool تولید کنیم. متأسفانه، ما نمی توانیم فقط یک آبجکت تصادفی خودمان را ایجاد کنیم و از آن فقط برای ایجاد کلید استفاده کنیم. در عوض، یک آبجکت به اشتراک گذاشته شده وجود دارد که توسط هر کدی که در یک اسکریپت اجرا می شود استفاده می شود.
به چه معنا است برای ما؟ این بدان معناست که در هر لحظه، در هر نقطه از کد، یک random.seed(0) ساده می تواند تمام آنتروپی جمع آوری شده ما را از بین ببرد. ما آن را نمی خواهیم. خوشبختانه پایتون متدهای getstate و setstate را ارائه می کند. بنابراین، برای ذخیره آنتروپی خود هر بار که کلیدی را تولید می کنیم، حالتی را که در آن توقف کرده ایم به خاطر می آوریم و دفعه بعد که می خواهیم کلید بسازیم، آن را تنظیم می کنیم.
دوم، ما فقط مطمئن می شویم که کلید ما در محدوده (1، CURVE_ORDER) قرار دارد. این یک الزام برای همه کلیدهای خصوصی ECDSA است. CURVE_ORDER مرتبه منحنی secp256k1 است که[symple_highlight color=”blue”]FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141[/symple_highlight] است.
در نهایت، برای راحتی، ما به هگز تبدیل میکنیم و قسمت «0x» را از بین میبریم.
def generate_key(self): big_int = self.__generate_big_int() big_int = big_int % (self.CURVE_ORDER — 1) # key < curve order big_int = big_int + 1 # key > 0 key = hex(big_int)[2:] return key def __generate_big_int(self): if self.prng_state is None: seed = int.from_bytes(self.pool, byteorder=’big’, signed=False) random.seed(seed) self.prng_state = random.getstate() random.setstate(self.prng_state) big_int = random.getrandbits(self.KEY_BYTES * 8) self.prng_state = random.getstate() return big_int
در عمل
بیایید سعی کنیم از کتابخانه استفاده کنیم. در واقع، بسیار ساده است: می توانید یک کلید خصوصی را در سه خط کد ایجاد کنید!
kg = KeyGenerator() kg.seed_input(‘Truly random string. I rolled a dice and got 4.’) kg.generate_key() # 60cf347dbc59d31c1358c8e5cf5e45b822ab85b79cb32a9f3d98184779a9efc2
خودت میتونی ببینیش کلید تصادفی و کاملا معتبر است. علاوه بر این، هر بار که این کد را اجرا می کنید، نتایج متفاوتی دریافت می کنید.
نتیجه
همانطور که می بینید، راه های زیادی برای تولید کلیدهای خصوصی وجود دارد. آنها در سادگی و امنیت متفاوت هستند.
تولید کلید خصوصی تنها اولین قدم است. مرحله بعدی استخراج یک کلید عمومی و یک آدرس کیف پول است که می توانید از آنها برای دریافت پرداخت استفاده کنید.