JavaRush /وبلاگ جاوا /Random-FA /از HTTP تا HTTPS
Viacheslav
مرحله

از HTTP تا HTTPS

در گروه منتشر شد
از HTTP تا HTTPS - 1
محتوا:

معرفی

در دنیای مدرن، شما نمی توانید بدون برنامه های کاربردی وب زندگی کنید. و ما با یک آزمایش کوچک شروع می کنیم. در کودکی، به یاد می‌آورم که چگونه همه غرفه‌ها چنین روزنامه‌ای را به عنوان «برهان و حقایق» می‌فروشند. آنها را به خاطر آوردم زیرا طبق تصور شخصی من از کودکی، این روزنامه ها همیشه عجیب به نظر می رسیدند. و من تصمیم گرفتم که آیا باید به وب سایت آنها برویم:
از HTTP به HTTPS - 2
اگر به راهنمای Google Chrome برویم، خواهیم خواند که این سایت از اتصال ایمن استفاده نمی کند و اطلاعاتی که با سایت تبادل می کنید ممکن است برای اشخاص ثالث قابل دسترسی باشد. بیایید برخی اخبار دیگر را بررسی کنیم، برای مثال اخبار سنت پترزبورگ از فونتانکا، یک رسانه الکترونیکی:
از HTTP به HTTPS - 3
همانطور که می بینید، وب سایت فونتانکا بر اساس این داده ها هیچ مشکل امنیتی ندارد. به نظر می رسد که منابع وب ممکن است ایمن باشند یا نباشند. همچنین می بینیم که دسترسی به منابع محافظت نشده از طریق پروتکل HTTP اتفاق می افتد. و اگر منبع محافظت شود، تبادل داده ها با استفاده از پروتکل HTTPS انجام می شود، جایی که S در انتها به معنای "امن" است. پروتکل HTTPS در مشخصات rfc2818 توضیح داده شده است: " HTTP Over TLS ". بیایید سعی کنیم برنامه وب خود را ایجاد کنیم و خودمان ببینیم که چگونه کار می کند. و در طول راه ما شرایط را درک خواهیم کرد.
از HTTP تا HTTPS - 4

برنامه وب در جاوا

بنابراین، ما باید یک برنامه وب بسیار ساده در جاوا ایجاد کنیم. ابتدا به خود برنامه جاوا نیاز داریم. برای این کار از سیستم ساخت خودکار پروژه Gradle استفاده می کنیم. این به ما این امکان را می دهد که به صورت دستی ساختار دایرکتوری لازم را ایجاد نکنیم + Gradle تمام کتابخانه های لازم برای پروژه را برای ما مدیریت می کند و اطمینان حاصل می کند که هنگام اجرای کد در دسترس هستند. می‌توانید در یک بررسی کوتاه درباره Gradle بیشتر بخوانید: " معرفی مختصر بر Gradle ". بیایید از پلاگین Gradle Init استفاده کنیم و دستور را اجرا کنیم:
gradle init --type java-application
پس از این، بیایید اسکریپت ساخت را باز کنیم build.gradle، که توضیح می‌دهد پروژه ما از چه کتابخانه‌هایی تشکیل شده است که Gradle در اختیار ما قرار می‌دهد. بیایید یک وابستگی به سرور وب اضافه کنیم که روی آن آزمایش خواهیم کرد:
dependencies {
    // Web server
    implementation 'io.undertow:undertow-core:2.0.20.Final'
     // Use JUnit test framework
     testImplementation 'junit:junit:4.12'
}
برای اینکه یک برنامه وب کار کند، ما قطعا به یک وب سرور نیاز داریم که در آن برنامه ما میزبانی شود. انواع بسیار زیادی از سرورهای وب وجود دارد، اما اصلی ترین آنها عبارتند از: Tomcat، Jetty، Undertow. این بار Undertow را انتخاب می کنیم. برای درک اینکه چگونه می توانیم با این وب سرور خود کار کنیم، به وب سایت رسمی Undertow برویم و به بخش مستندات برویم . من و شما یک وابستگی به Undertow Core وصل کرده‌ایم، بنابراین ما به بخش مربوط به همین Core ، یعنی هسته، اساس وب سرور علاقه مندیم. ساده ترین راه استفاده از Builder API برای Undertow است:
public static void main(String[] args) {
	Undertow server = Undertow.builder()
            .addHttpListener(8080, "localhost")
            .setHandler(new HttpHandler() {
                @Override
                public void handleRequest(final HttpServerExchange exchange) throws Exception {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                    exchange.getResponseSender().send("Hello World");
                }
            }).build();
    server.start();
}
اگر کد را اجرا کنیم، می توانیم به منبع وب زیر بروید:
از HTTP تا HTTPS - 5
به سادگی کار می کند. به لطف Undertow Builder API، ما یک شنونده HTTP به لوکال هاست و پورت 8080 اضافه می کنیم. این شنونده درخواست هایی را از مرورگر وب دریافت می کند و رشته "Hello World" را در پاسخ برمی گرداند. برنامه وب عالی اما همانطور که می بینیم، ما از پروتکل HTTP استفاده می کنیم. این نوع تبادل داده ایمن نیست. بیایید بفهمیم که مبادلات چگونه با استفاده از پروتکل HTTPS انجام می شود.
از HTTP تا HTTPS - 6

الزامات HTTPS

برای درک نحوه فعال کردن HTTPS، اجازه دهید به مشخصات HTTPS برگردیم: " RFC-2818: HTTP Over TLS ". با توجه به مشخصات، داده ها در پروتکل HTTPS از طریق پروتکل های رمزنگاری SSL یا TLS منتقل می شوند. مردم اغلب با مفهوم SSL و TLS گمراه می شوند. در واقع SSL تکامل یافته و نسخه های خود را تغییر داده است. بعدها، TLS به مرحله بعدی در توسعه پروتکل SSL تبدیل شد. یعنی TLS به سادگی یک نسخه جدید از SSL است. مشخصات چنین می گوید: "SSL و جانشین آن TLS". بنابراین، ما فهمیدیم که پروتکل های رمزنگاری SSL/TLS وجود دارد. SSL مخفف Secure Sockets Layer است و به عنوان "Secure Socket Layer" ترجمه می شود. سوکت ترجمه شده از انگلیسی یک رابط است. شرکت کنندگان در انتقال داده از طریق شبکه از سوکت ها به عنوان یک رابط برنامه نویسی (یعنی یک API) برای برقراری ارتباط با یکدیگر از طریق شبکه استفاده می کنند. مرورگر به عنوان یک کلاینت عمل می کند و از سوکت مشتری استفاده می کند و سروری که درخواستی را دریافت می کند و پاسخی را صادر می کند از سوکت سرور استفاده می کند. و بین این سوکت ها است که تبادل داده ها اتفاق می افتد. به همین دلیل این پروتکل در ابتدا SSL نام داشت. اما زمان گذشت و پروتکل تکامل یافت. و در یک نقطه، پروتکل SSL به پروتکل TLS تبدیل شد. TLS مخفف عبارت Transport Layer Security است. پروتکل TLS به نوبه خود بر اساس مشخصات پروتکل SSL نسخه 3.0 است. پروتکل TLS موضوع مقالات و بررسی های جداگانه است، بنابراین من به سادگی مطالبی را که به نظرم جالب است اشاره می کنم: به طور خلاصه، اساس HTTPS، دست دادن TLS و بررسی «هویت سرور» (یعنی شناسایی سرور) با استفاده از گواهی دیجیتال آن است. مهم است. این را به خاطر بسپاریم، زیرا ... بعداً به این واقعیت باز خواهیم گشت. بنابراین، قبلاً از HttpListener استفاده کردیم تا به سرور بگوییم که چگونه از طریق پروتکل HTTP کار کند. اگر در مثال بالا یک HttpListener اضافه کردیم تا روی HTTP کار کند، برای کار روی HTTPS باید یک HttpsListener اضافه کنیم:
از HTTP تا HTTPS - 7
اما برای اضافه کردن آن به SSLContext نیاز داریم. جالب اینجاست که SSLContext کلاسی از Undertow نیست، بلکه javax.net.ssl.SSLContext. کلاس SSLContext بخشی از به اصطلاح " Java Secure Socket Extension " (JSSE) است - یک برنامه افزودنی جاوا برای اطمینان از امنیت اتصال به اینترنت. این برنامه افزودنی در « راهنمای مرجع برنامه افزودنی سوکت امن جاوا (JSSE) » توضیح داده شده است. همانطور که از قسمت مقدماتی مستندات می بینید، JSSE یک چارچوب و پیاده سازی جاوا از پروتکل های SSL و TLS را ارائه می دهد. چگونه متن SSLC را بدست آوریم؟ JavaDoc SSLContext را باز کنید و متد getInstance را پیدا کنید . همانطور که می بینید، برای دریافت متن SSLC باید نام "پروتکل سوکت امن" را مشخص کنیم. شرح پارامترها نشان می دهد که این نام ها را می توان در " مستندات نام الگوریتم استاندارد معماری رمزنگاری جاوا " یافت. بنابراین، بیایید دستورالعمل ها را دنبال کنیم و به سراغ مستندات برویم. و می بینیم که می توانیم بین SSL و TLS یکی را انتخاب کنیم:
از HTTP تا HTTPS - 8
اکنون متوجه شدیم که باید متن SSLC را به صورت زیر ایجاد کنیم:
public SSLContext getSSLContext() {
	// 1. Получаем контекст, в рамках которого будем работать по TLS протоколу
	SSLContext context = null;
	try {
		context = SSLContext.getInstance("TLS");
	} catch (NoSuchAlgorithmException e) {
		throw new IllegalStateException(e);
	}
	return context;
}
پس از ایجاد یک زمینه جدید، به یاد می آوریم که SSLContext در " راهنمای مرجع برنامه افزودنی سوکت امن جاوا (JSSE) توضیح داده شده است . می خوانیم و می بینیم که "یک متن جدید SSLC باید با فراخوانی متد init مقداردهی اولیه شود". یعنی ایجاد زمینه کافی نیست. باید مقداردهی اولیه شود. و این منطقی است، زیرا در مورد امنیت، ما فقط به شما گفتیم که می خواهیم از پروتکل TLS استفاده کنیم. برای مقداردهی اولیه SSLContext باید سه چیز را ارائه کنیم: KeyManager، TrustManager، SecureRandom.
از HTTP به HTTPS - 9

مدیر کلیدی

KeyManager یک مدیر کلیدی است. او مسئول است که چه "معتبر احراز هویت" را به کسی که با ما تماس می گیرد ارائه دهد. اعتبار را می توان به عنوان هویت ترجمه کرد. هویت مورد نیاز است تا مشتری مطمئن شود که سرور همان چیزی است که ادعا می کند و می توان به او اعتماد کرد. چه چیزی به عنوان شناسایی استفاده خواهد شد؟ همانطور که به یاد داریم، هویت سرور توسط گواهی دیجیتال سرور تأیید می شود. این فرآیند را می توان به صورت زیر نشان داد:
از HTTP تا HTTPS - 10
علاوه بر این، " راهنمای مرجع JSSE: چگونه SSL کار می کند " می گوید که SSL از "رمزگذاری نامتقارن" استفاده می کند، به این معنی که ما به یک جفت کلید نیاز داریم: یک کلید عمومی و یک کلید خصوصی. از آنجایی که ما در مورد رمزنگاری صحبت می کنیم، "معماری رمزنگاری جاوا" (JCA) وارد عمل می شود. Oracle یک سند عالی در مورد این معماری ارائه می دهد: " راهنمای مرجع معماری رمزنگاری جاوا (JCA) ". علاوه بر این، می توانید مروری کوتاه بر JCA در JavaRush بخوانید: " معماری رمزنگاری جاوا: اولین آشنایی ." بنابراین، برای مقداردهی اولیه KeyManager، به یک KeyStore نیاز داریم که گواهی سرور ما را ذخیره کند. متداول ترین راه برای ایجاد یک فروشگاه کلید و گواهی، ابزار keytool است که در JDK گنجانده شده است. یک مثال را می توان در مستندات JSSE مشاهده کرد: " ایجاد یک فروشگاه کلید برای استفاده با JSSE ". بنابراین، ما باید از ابزار KeyTool برای ایجاد یک فروشگاه کلید استفاده کنیم و گواهی را در آنجا بنویسیم. جالب اینجاست که تولید کلید قبلا با استفاده از -genkey مشخص شده بود، اما اکنون توصیه می شود از -genkeypair استفاده کنید. باید موارد زیر را تعریف کنیم:
  • نام مستعار : نام مستعار یا به سادگی نامی که ورودی تحت آن در Keystore ذخیره می شود
  • keyalg : الگوریتم رمزگذاری کلید. بیایید الگوریتم RSA را انتخاب کنیم، که اساساً یک راه حل استاندارد برای هدف ما است.
  • اندازه کلید : اندازه کلید (به بیت). حداقل اندازه توصیه شده 2048 است، زیرا ... اندازه کوچکتر قبلاً کرک شده است. شما می توانید در اینجا بیشتر بخوانید: " یک گواهی ssl در 2048 بیت ".
  • dname : نام ممتاز، نام ممتاز.
درک این نکته مهم است که منبع درخواستی (به عنوان مثال https://localhost) با آن مقایسه خواهد شد. به این "تطابق موضوع cn" می گویند. برای گواهینامه های خودامضا (به عنوان مثال، برای گواهی هایی که به طور مستقل ایجاد شده اند)، باید پسوندهای زیر را مشخص کنید:
  • -ext san:critical=dns:localhost,ip:127.0.0.1 > برای انجام تطبیق موضوع توسط SubjectAlternativeName
  • -ext bc=ca:false > نشان می دهد که این گواهی برای امضای گواهی های دیگر استفاده نمی شود
بیایید دستور را اجرا کنیم (مثال برای سیستم عامل ویندوز):
keytool -genkeypair -alias ssl -keyalg RSA -keysize 2048 -dname "CN=localhost,OU=IT,O=Javarush,L=SaintPetersburg,C=RU,email=contact@email.com" -validity 90 -keystore C:/keystore.jks -storepass passw0rd -keypass passw0rd -ext san:critical=dns:localhost,ip:127.0.0.1 -ext bc=ca:false
زیرا فایل ایجاد خواهد شد، مطمئن شوید که تمام حقوق ایجاد فایل را دارید. همچنین احتمالاً توصیه هایی مانند این را خواهید دید:
از HTTP تا HTTPS - 11
در اینجا به ما گفته می شود که JKS یک فرمت اختصاصی است. اختصاصی به این معنی است که مالکیت خصوصی نویسندگان است و فقط برای استفاده در جاوا در نظر گرفته شده است. هنگام کار با ابزارهای شخص ثالث، ممکن است درگیری ایجاد شود، به همین دلیل به ما هشدار داده شده است. علاوه بر این، ممکن است این خطا را دریافت کنیم: The destination pkcs12 keystore has different storepass and keypass. این خطا به این دلیل رخ می دهد که رمزهای عبور ورودی در Keystore و برای خود keystore متفاوت است. همانطور که مستندات ابزار کلید می گوید ، "به عنوان مثال، اکثر ابزارهای شخص ثالث نیاز به یک گذرگاه و کلید در یک فروشگاه کلید PKCS #12 دارند." ما می توانیم کلید را خودمان مشخص کنیم (مثلا -destkeypass enterpassw). اما بهتر است الزامات را نقض نکنید و همان رمز عبور را تنظیم کنید. بنابراین واردات ممکن است به شکل زیر باشد:
keytool -importkeystore -srckeystore C:/keystore.jks -destkeystore C:/keystore.jks -deststoretype pkcs12
مثال موفقیت:
از HTTP تا HTTPS - 12
برای صادر کردن گواهی به یک فایل، می توانید اجرا کنید:
keytool -export -alias ssl -storepass passw0rd -file C:/server.cer -keystore C:/keystore.jks
علاوه بر این، می‌توانیم محتویات Keystore را مانند این دریافت کنیم:
keytool -list -v -keystore C:/keystore.jks -storepass passw0rd
عالی است، اکنون یک فروشگاه کلید داریم که حاوی گواهی است. اکنون می توانید آن را از کد زیر دریافت کنید:
public KeyStore getKeyStore() {
	// Согласно https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore
	try(FileInputStream fis = new FileInputStream("C:/keystore.jks")){
		KeyStore keyStore = KeyStore.getInstance("pkcs12");
		keyStore.load(fis, "passw0rd".toCharArray());
		return keyStore;
	} catch (IOException ioe) {
		throw new IllegalStateException(ioe);
	} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
		throw new IllegalStateException(e);
	}
}
اگر KeyStore وجود داشته باشد، می توانیم KeyManager را مقداردهی اولیه کنیم:
public KeyManager[] getKeyManagers(KeyStore keyStore) {
	String keyManagerAlgo = KeyManagerFactory.getDefaultAlgorithm();
	KeyManagerFactory keyManagerFactory = null;
	try {
		keyManagerFactory = KeyManagerFactory.getInstance(keyManagerAlgo);
		keyManagerFactory.init(keyStore, "passw0rd".toCharArray());
		return keyManagerFactory.getKeyManagers();
	} catch (NoSuchAlgorithmException e) {
		throw new IllegalStateException(e);
	} catch (UnrecoverableKeyException | KeyStoreException e) {
		throw new IllegalStateException(e);
	}
}
هدف اول ما محقق شده است. باقی مانده است که بفهمیم TrustManager چیست. TrustManager در مستندات JSSE در بخش " رابط TrustManager " توضیح داده شده است. این بسیار شبیه به KeyManager است، اما هدف آن بررسی این است که آیا می توان به فردی که درخواست اتصال را درخواست می کند اعتماد کرد یا خیر. به بیان صریح، این KeyManager به صورت معکوس است =) ما به TrustManager نیاز نداریم، بنابراین ما null را پاس می کنیم. سپس یک TrustManager پیش‌فرض ایجاد می‌شود که درخواست‌های کاربر نهایی را به سرور ما تأیید نمی‌کند. مستندات چنین می گوید: "پیاده سازی پیش فرض استفاده خواهد شد". با SecureRandom هم همینطور. اگر null را مشخص کنیم، پیاده سازی پیش فرض استفاده می شود. فقط به یاد داشته باشیم که SecureRandom یک کلاس JCA است و در مستندات JCA در بخش " کلاس SecureRandom " توضیح داده شده است. در مجموع، آماده سازی با در نظر گرفتن تمام روش های شرح داده شده در بالا ممکن است به شکل زیر باشد:
public static void main(String[] args) {
	// 1. Подготавливаем приложение к работе по HTTPS
	App app = new App();
	SSLContext sslContext = app.getSSLContext();
	KeyStore keyStore = app.getKeyStore();
	KeyManager[] keyManagers = app.getKeyManagers(keyStore);
	try {
		sslContext.init(keyManagers, null, null);
	} catch (KeyManagementException e) {
		throw new IllegalStateException(e);
	}
تنها چیزی که باقی می ماند راه اندازی سرور است:
// 2. Поднимаем server
 	int httpsPort = 443;
	Undertow server = Undertow.builder()
            .addHttpsListener(httpsPort, "localhost", sslContext)
            .setHandler(new HttpHandler() {
                @Override
                public void handleRequest(final HttpServerExchange exchange) throws Exception {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                    exchange.getResponseSender().send("Hello World");
                }
            }).build();
	server.start();
}
این بار سرور ما در آدرس زیر در دسترس خواهد بود. https://localhost:443 با این حال، همچنان خطایی دریافت می کنیم که این منبع قابل اعتماد نیست:
از HTTP به HTTPS - 13
بیایید بفهمیم که گواهینامه چه مشکلی دارد و در مورد آن چه باید کرد.
از HTTP تا HTTPS - 14

مدیریت گواهی

بنابراین، سرور ما از قبل آماده کار از طریق HTTPS است، اما مشتری به آن اعتماد ندارد. چرا؟ بیایید نگاهی بیندازیم:
از HTTP تا HTTPS - 15
دلیل آن این است که این گواهی یک گواهی امضا شده است. گواهی SSL با امضای خود به یک گواهی کلید عمومی اشاره دارد که توسط همان شخصی که شناسایی می کند صادر و امضا شده است. یعنی توسط هیچ مرجع معتبر صدور گواهینامه (CA، همچنین به عنوان مرجع صدور گواهینامه) صادر نشده است. مرجع صدور گواهی به عنوان امین عمل می کند و در زندگی روزمره شبیه یک سردفتر است. او اطمینان می دهد که گواهی هایی که صادر می کند قابل اعتماد هستند. خدمات صدور گواهی توسط چنین CA ها پولی است، بنابراین هیچ کس نیازی به از دست دادن اعتماد و خطرات اعتباری ندارد. به طور پیش فرض، چندین مرجع گواهی وجود دارد که مورد اعتماد هستند. این لیست قابل ویرایش است. و هر سیستم عامل مدیریت خاص خود را برای لیست مقامات صدور گواهینامه دارد. برای مثال، مدیریت این لیست در ویندوز را می‌توانید در اینجا بخوانید: « مدیریت گواهی‌های ریشه مورد اعتماد در ویندوز ». بیایید گواهی را همانطور که در پیام خطا نشان داده شده است به موارد قابل اعتماد اضافه کنیم. برای انجام این کار ابتدا گواهی را دانلود کنید:
از HTTP تا HTTPS - 16
در سیستم عامل ویندوز، Win+R را فشار داده و اجرا کنید mmcتا کنسول کنترل را فراخوانی کنید. سپس، Ctrl+M را فشار دهید تا بخش Certificates را به کنسول فعلی اضافه کنید. در مرحله بعد، در زیر بخش "مقامات صدور گواهی ریشه مورد اعتماد" را اجرا می کنیم Действия / Все задачи / Импорт. بیایید فایلی را که قبلا دانلود شده بود وارد فایل کنیم. ممکن است مرورگر وضعیت اعتماد گذشته گواهی را به خاطر داشته باشد. بنابراین، قبل از باز کردن صفحه، باید مرورگر را مجددا راه اندازی کنید. به عنوان مثال، در Google Chrome در نوار آدرس باید اجرا کنید chrome://restart. در سیستم عامل ویندوز، همچنین می توانید از ابزار برای مشاهده گواهی ها استفاده کنید certmgr.msc:
از HTTP تا HTTPS - 17
اگر همه چیز را به درستی انجام دهیم، یک تماس موفقیت آمیز با سرور خود از طریق HTTPS مشاهده خواهیم کرد:
از HTTP تا HTTPS - 18
همانطور که می بینید، گواهی اکنون معتبر در نظر گرفته می شود، منبع در دسترس است و هیچ خطایی وجود ندارد.
از HTTP تا HTTPS - 19

خط پایین

بنابراین ما متوجه شده ایم که طرح فعال کردن پروتکل HTTPS در یک وب سرور چگونه است و برای این کار چه چیزی لازم است. امیدوارم در این مرحله واضح باشد که پشتیبانی از طریق تعامل معماری رمزنگاری جاوا (JCA) که مسئول رمزنگاری است و جاوا Secure Socket Extension (JSSE) که اجرای TLS را در سمت جاوا ارائه می‌کند، ارائه می‌شود. ما دیدیم که چگونه ابزار keytool موجود در JDK برای کار با کلید KeyStore و فروشگاه گواهی استفاده می شود. علاوه بر این، متوجه شدیم که HTTPS از پروتکل های SSL/TLS برای امنیت استفاده می کند. برای تقویت این موضوع، به شما توصیه می کنم مقالات عالی در این زمینه را بخوانید: امیدواریم پس از این بررسی کوچک، HTTPS کمی شفاف‌تر شود. و اگر نیاز به فعال کردن HTTPS دارید، می توانید به راحتی شرایط را از مستندات سرورها و فریم ورک های برنامه خود درک کنید. #ویاچسلاو
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION