Kandungan:
pengenalan
Dalam dunia moden, anda tidak boleh hidup tanpa aplikasi web. Dan kita akan mulakan dengan percubaan kecil. Semasa kecil, saya masih ingat bagaimana semua gerai menjual surat khabar seperti "Hujah dan Fakta". Saya ingat mereka kerana mengikut persepsi peribadi saya sejak kecil, akhbar-akhbar ini sentiasa kelihatan pelik. Dan saya memutuskan sama ada kita perlu pergi ke laman web mereka:
Jika kami pergi ke bantuan Google Chrome, kami akan membaca bahawa tapak ini tidak menggunakan sambungan selamat dan maklumat yang anda tukar dengan tapak mungkin boleh diakses oleh pihak ketiga. Mari kita semak beberapa berita lain, contohnya berita St. Petersburg dari Fontanka, sebuah media elektronik:
Seperti yang anda lihat, laman web Fontanka tidak mempunyai masalah keselamatan mengikut data ini. Ternyata sumber web mungkin selamat atau tidak. Kami juga melihat bahawa akses kepada sumber yang tidak dilindungi berlaku melalui protokol HTTP. Dan jika sumber itu dilindungi, maka pertukaran data dijalankan menggunakan protokol HTTPS, di mana S pada akhirnya bermaksud "Secure". Protokol HTTPS diterangkan dalam spesifikasi rfc2818: "
HTTP Over TLS ". Mari cuba buat aplikasi web kita sendiri dan lihat sendiri cara ia berfungsi. Dan sepanjang perjalanan kita akan memahami syarat-syaratnya.
Aplikasi web dalam Java
Jadi, kita perlu mencipta aplikasi web yang sangat mudah di Java. Pertama, kita memerlukan aplikasi Java itu sendiri. Untuk melakukan ini, kami akan menggunakan sistem binaan automatik projek Gradle. Ini akan membolehkan kami tidak membuat struktur direktori yang diperlukan secara manual + Gradle akan mengurus semua perpustakaan yang diperlukan untuk projek untuk kami dan memastikan ia tersedia semasa melaksanakan kod. Anda boleh membaca lebih lanjut tentang Gradle dalam ulasan ringkas: "
Pengenalan Ringkas kepada Gradle ". Mari gunakan
Gradle Init Plugin dan jalankan arahan:
gradle init --type java-application
Selepas ini, mari buka skrip bina
build.gradle
, yang menerangkan pustaka yang terdiri daripada projek kami, yang akan diberikan oleh Gradle kepada kami. Mari tambahkan kebergantungan pada pelayan web yang akan kami uji:
dependencies {
implementation 'io.undertow:undertow-core:2.0.20.Final'
testImplementation 'junit:junit:4.12'
}
Untuk aplikasi web berfungsi, kami pasti memerlukan pelayan web di mana aplikasi kami akan dihoskan. Terdapat pelbagai jenis pelayan web, tetapi yang utama ialah: Tomcat, Jetty, Undertow. Kali ini kita akan memilih Undertow. Untuk memahami cara kami boleh bekerja dengan pelayan web kami ini, mari pergi ke tapak web rasmi
Undertow dan pergi ke bahagian
dokumentasi . Anda dan saya telah menyambungkan pergantungan pada Undertow Core, jadi kami berminat dengan bahagian tentang
Core ini , iaitu teras, asas pelayan web. Cara paling mudah ialah menggunakan API Builder untuk 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();
}
Jika kami melaksanakan kod tersebut, kami boleh menavigasi ke sumber web berikut:
Ia berfungsi dengan mudah. Terima kasih kepada Undertow Builder API, kami menambah pendengar HTTP pada localhost dan port 8080. Pendengar ini menerima permintaan daripada pelayar web dan mengembalikan rentetan "Hello World" sebagai tindak balas. Aplikasi web yang hebat. Tetapi seperti yang kita lihat, kita menggunakan protokol HTTP, i.e. Jenis pertukaran data ini tidak selamat. Mari kita fikirkan cara pertukaran dijalankan menggunakan protokol HTTPS.
Keperluan untuk HTTPS
Untuk memahami cara mendayakan HTTPS, mari kembali kepada spesifikasi HTTPS: "
RFC-2818: HTTP Over TLS ". Mengikut spesifikasi, data dalam protokol HTTPS dihantar melalui protokol kriptografi SSL atau TLS. Orang ramai sering terpedaya dengan konsep SSL dan TLS. Sebenarnya, SSL telah berkembang dan menukar versinya. Kemudian, TLS menjadi langkah seterusnya dalam pembangunan protokol SSL. Maksudnya, TLS hanyalah versi baharu SSL. Spesifikasi mengatakan demikian: "SSL, dan penggantinya TLS". Jadi, kami mengetahui bahawa terdapat protokol kriptografi SSL/TLS.
SSL ialah singkatan untuk Lapisan Soket Selamat dan diterjemahkan sebagai "lapisan soket selamat". Soket yang diterjemahkan daripada bahasa Inggeris ialah penyambung. Peserta dalam penghantaran data melalui rangkaian menggunakan soket sebagai antara muka pengaturcaraan (iaitu, API) untuk berkomunikasi antara satu sama lain melalui rangkaian. Penyemak imbas bertindak sebagai pelanggan dan menggunakan soket klien, dan pelayan yang menerima permintaan dan mengeluarkan respons menggunakan soket pelayan. Dan di antara soket inilah pertukaran data berlaku. Itulah sebabnya protokol itu pada asalnya dipanggil SSL. Tetapi masa berlalu dan protokol berkembang. Dan pada satu ketika, protokol SSL menjadi protokol TLS. TLS ialah singkatan untuk Transport Layer Security. Protokol TLS pula adalah berdasarkan spesifikasi protokol SSL versi 3.0. Protokol TLS ialah topik artikel dan ulasan yang berasingan, jadi saya hanya akan menunjukkan bahan yang saya rasa menarik:
Ringkasnya, asas HTTPS ialah jabat tangan TLS dan semakan "Identiti Pelayan" (iaitu, pengenalan pelayan) menggunakan sijil digitalnya. Ia penting. Mari kita ingat ini, kerana... Kami akan kembali kepada fakta ini kemudian. Jadi, sebelum ini kami menggunakan HttpListener untuk memberitahu pelayan cara untuk beroperasi melalui protokol HTTP. Jika dalam contoh di atas kami menambah HttpListener untuk berfungsi melalui HTTP, kemudian untuk bekerja melalui HTTPS kami perlu menambah HttpsListener:
Tetapi untuk menambahnya, kami memerlukan SSLContext. Menariknya, SSLContext bukan kelas dari Undertow, tetapi
javax.net.ssl.SSLContext
. Kelas SSLContext adalah sebahagian daripada apa yang dipanggil "
Java Secure Socket Extension " (JSSE) - sambungan Java untuk memastikan keselamatan sambungan Internet. Sambungan ini diterangkan dalam "
Panduan Rujukan Sambungan Soket Selamat Java (JSSE) ". Seperti yang anda boleh lihat daripada bahagian pengenalan dokumentasi, JSSE menyediakan rangka kerja dan pelaksanaan Java bagi protokol SSL dan TLS. Bagaimanakah kita mendapatkan SSLContext? Buka JavaDoc SSLContext dan cari kaedah
getInstance . Seperti yang anda lihat, untuk mendapatkan SSLContext kita perlu menentukan nama "Protokol Soket Selamat". Perihalan parameter menunjukkan bahawa nama-nama ini boleh didapati dalam "
Java Cryptography Architecture Standard Algorithm Name Documentation ". Oleh itu, mari ikut arahan dan pergi ke dokumentasi. Dan kami melihat bahawa kami boleh memilih antara SSL dan TLS:
Sekarang kita faham bahawa kita perlu mencipta SSLContext seperti berikut:
public SSLContext getSSLContext() {
SSLContext context = null;
try {
context = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
return context;
}
Setelah mencipta konteks baharu, kami ingat bahawa SSLContext telah diterangkan dalam "
Panduan Rujukan Sambungan Soket Selamat Java (JSSE) ". Kami membaca dan melihat bahawa "Konteks SSL yang baru dibuat harus dimulakan dengan memanggil kaedah init". Iaitu, mencipta konteks tidak mencukupi. Ia perlu dimulakan. Dan ini adalah logik, kerana tentang keselamatan, kami hanya memberitahu anda bahawa kami ingin menggunakan protokol TLS. Untuk memulakan SSLContext kami perlu menyediakan tiga perkara: KeyManager, TrustManager, SecureRandom.
KeyManager
KeyManager ialah pengurus utama. Dia bertanggungjawab ke atas "kelayakan pengesahan" yang perlu diberikan kepada seseorang yang menghubungi kami. Tauliah boleh diterjemahkan sebagai identiti. Identiti diperlukan supaya pelanggan yakin bahawa pelayan adalah yang didakwanya dan boleh dipercayai. Apakah yang akan digunakan sebagai pengenalan? Seperti yang kita ingat, Identiti Pelayan disahkan oleh sijil digital pelayan. Proses ini boleh diwakili seperti berikut:
Selain itu, "
Panduan Rujukan JSSE: Cara SSL Berfungsi " mengatakan bahawa SSL menggunakan "kriptografi asimetri", yang bermaksud bahawa kita memerlukan pasangan kunci: kunci awam dan kunci peribadi. Memandangkan kita bercakap tentang kriptografi, "Java Cryptography Architecture" (JCA) mula dimainkan. Oracle menyediakan dokumen yang sangat baik tentang seni bina ini: "
Panduan Rujukan Seni Bina Kriptografi Java (JCA) ". Selain itu, anda boleh membaca gambaran ringkas JCA di JavaRush: "
Seni Bina Kriptografi Java: Kenalan pertama ." Jadi, untuk memulakan KeyManager, kami memerlukan KeyStore, yang akan menyimpan sijil pelayan kami. Cara paling biasa untuk mencipta kedai kunci dan sijil ialah utiliti alat kunci, yang disertakan dengan JDK. Contoh boleh dilihat dalam dokumentasi JSSE: "
Mencipta Kedai Kunci untuk Digunakan dengan JSSE ". Jadi, kita perlu menggunakan utiliti KeyTool untuk mencipta stor kunci dan menulis sijil di sana. Menariknya, penjanaan kunci sebelum ini ditentukan menggunakan -genkey, tetapi kini disyorkan untuk menggunakan -genkeypair. Kita perlu menentukan perkara berikut:
- alias : Alias atau nama ringkas di mana entri akan disimpan dalam Keystore
keyalg : Algoritma penyulitan kunci. Mari kita pilih algoritma RSA, yang pada asasnya merupakan penyelesaian standard untuk tujuan kita.
- saiz kekunci : Saiz kunci (dalam bit). Saiz minimum yang disyorkan ialah 2048, kerana... saiz yang lebih kecil telah pun retak. Anda boleh membaca lebih lanjut di sini: " sijil ssl dalam 2048 bit ".
- dname : Nama Terbilang, nama terbilang.
Adalah penting untuk memahami bahawa sumber yang diminta (contohnya, https://localhost) akan dibandingkan dengannya. Ini dipanggil "padanan cn subjek".
- kesahan : Tempoh dalam hari di mana sijil yang dihasilkan adalah sah, i.e. sah.
- ext : Sambungan Sijil dinyatakan dalam " Sambungan Dinamakan ".
Untuk Sijil yang ditandatangani sendiri (iaitu untuk sijil yang dibuat secara bebas), anda mesti menentukan sambungan berikut:
- -ext san:critical=dns:localhost,ip:127.0.0.1 > untuk melaksanakan pemadanan subjek mengikut SubjectAlternativeName
- -ext bc=ca:false > untuk menunjukkan bahawa sijil ini tidak digunakan untuk menandatangani sijil lain
Mari jalankan arahan (contoh untuk OS Windows):
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
Kerana fail akan dibuat, pastikan anda mempunyai semua hak untuk mencipta fail. Anda juga mungkin akan melihat nasihat seperti ini:
Di sini kita diberitahu bahawa JKS adalah format proprietari. Proprietari bermaksud ia adalah hak milik persendirian pengarang dan bertujuan untuk digunakan hanya di Jawa. Apabila bekerja dengan utiliti pihak ketiga, konflik mungkin timbul, itulah sebabnya kami diberi amaran. Di samping itu, kami mungkin menerima ralat:
The destination pkcs12 keystore has different storepass and keypass
. Ralat ini berlaku kerana kata laluan untuk entri dalam Keystore dan untuk keystore itu sendiri adalah berbeza. Seperti yang dinyatakan dalam dokumentasi
alat kunci , "Sebagai contoh, kebanyakan alatan pihak ketiga memerlukan laluan simpan dan laluan kekunci dalam stor kunci PKCS #12 supaya sama." Kita boleh menentukan kunci sendiri (contohnya, -destkeypass entrypassw). Tetapi lebih baik tidak melanggar keperluan dan tetapkan kata laluan yang sama. Jadi import mungkin kelihatan seperti ini:
keytool -importkeystore -srckeystore C:/keystore.jks -destkeystore C:/keystore.jks -deststoretype pkcs12
Contoh kejayaan:
Untuk mengeksport sijil ke fail, anda boleh menjalankan:
keytool -export -alias ssl -storepass passw0rd -file C:/server.cer -keystore C:/keystore.jks
Selain itu, kami boleh mendapatkan kandungan Keystore seperti ini:
keytool -list -v -keystore C:/keystore.jks -storepass passw0rd
Bagus, kini kami mempunyai stor kunci yang mengandungi sijil. Kini anda boleh mendapatkannya daripada kod:
public KeyStore getKeyStore() {
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);
}
}
Jika terdapat KeyStore, maka kita boleh memulakan 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);
}
}
Matlamat pertama kami telah tercapai. Ia masih untuk mengetahui apa itu TrustManager. TrustManager diterangkan dalam dokumentasi JSSE dalam bahagian "
Antara Muka TrustManager ". Ia sangat serupa dengan KeyManager, tetapi tujuannya adalah untuk menyemak sama ada orang yang meminta sambungan itu boleh dipercayai. Secara terang-terangan, ini adalah KeyManager secara terbalik =) Kami tidak memerlukan TrustManager, jadi kami akan lulus null. TrustManager lalai kemudiannya akan dibuat yang tidak mengesahkan pengguna akhir membuat permintaan ke pelayan kami. Dokumentasi mengatakan demikian: "pelaksanaan lalai akan digunakan". Sama dengan SecureRandom. Jika kami menentukan nol, pelaksanaan lalai akan digunakan. Mari kita ingat bahawa SecureRandom ialah kelas JCA dan diterangkan dalam dokumentasi JCA dalam bahagian "
The SecureRandom Class ". Secara keseluruhan, penyediaan dengan mengambil kira semua kaedah yang diterangkan di atas mungkin kelihatan seperti ini:
public static void main(String[] args) {
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);
}
Yang tinggal hanyalah untuk memulakan pelayan:
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();
}
Kali ini pelayan kami akan tersedia di alamat
https://localhost:443
Walau bagaimanapun, kami masih akan menerima ralat bahawa sumber ini tidak boleh dipercayai:
Mari kita fikirkan apa yang salah dengan sijil dan apa yang perlu dilakukan mengenainya.
Pengurusan sijil
Jadi, pelayan kami sudah sedia untuk berfungsi melalui HTTPS, tetapi pelanggan tidak mempercayainya. kenapa? Mari lihat:
Sebab sijil ini adalah Sijil yang ditandatangani sendiri. Sijil SSL yang ditandatangani sendiri merujuk kepada sijil kunci awam yang dikeluarkan dan ditandatangani oleh orang yang sama yang dikenal pastinya. Iaitu, ia tidak dikeluarkan oleh mana-mana pihak berkuasa pensijilan yang dihormati (CA, juga dikenali sebagai Pihak Berkuasa Sijil). Pihak Berkuasa Sijil bertindak sebagai pemegang amanah dan serupa dengan notari dalam kehidupan seharian. Dia memberi jaminan bahawa sijil yang dikeluarkannya boleh dipercayai. Perkhidmatan mengeluarkan sijil oleh CA tersebut dibayar, jadi tiada siapa yang memerlukan kehilangan kepercayaan dan risiko reputasi. Secara lalai, terdapat beberapa pihak berkuasa sijil yang dipercayai. Senarai ini boleh diedit. Dan setiap sistem pengendalian mempunyai pengurusan sendiri senarai pihak berkuasa pensijilan. Sebagai contoh, menguruskan senarai ini dalam Windows boleh dibaca di sini: "
Urus Sijil Root Dipercayai dalam Windows ". Mari tambah sijil kepada yang dipercayai seperti yang ditunjukkan dalam mesej ralat. Untuk melakukan ini, mula-mula, muat turun sijil:
Dalam OS Windows, tekan Win+R dan laksanakan
mmc
untuk memanggil konsol kawalan. Seterusnya, tekan Ctrl+M untuk menambah bahagian "Sijil" pada konsol semasa. Seterusnya, dalam subseksyen "Pihak Berkuasa Pensijilan Root Dipercayai" kami akan melaksanakan
Действия / Все задачи / Импорт
. Mari import fail yang telah dimuat turun tadi ke dalam fail. Penyemak imbas mungkin telah mengingati keadaan amanah masa lalu bagi sijil. Oleh itu, sebelum membuka halaman anda perlu memulakan semula penyemak imbas. Sebagai contoh, dalam Google Chrome dalam bar alamat yang anda perlukan untuk menjalankan
chrome://restart
. Dalam OS Windows, anda juga boleh menggunakan utiliti untuk melihat sijil
certmgr.msc
:
Jika kami melakukan semuanya dengan betul, kami akan melihat panggilan yang berjaya ke pelayan kami melalui HTTPS:
Seperti yang anda lihat, sijil kini dianggap sah, sumber tersedia dan tiada ralat.
Pokoknya
Oleh itu, kami telah mengetahui rupa skema untuk membolehkan protokol HTTPS pada pelayan web dan apa yang diperlukan untuk ini. Saya harap pada ketika ini adalah jelas bahawa sokongan disediakan oleh interaksi Java Cryptography Architecture (JCA), yang bertanggungjawab untuk kriptografi, dan Java Secure Socket Extension (JSSE), yang menyediakan pelaksanaan TLS di sebelah Java. Kami melihat bagaimana utiliti alat kunci yang disertakan dalam JDK digunakan untuk berfungsi dengan kunci KeyStore dan kedai sijil. Selain itu, kami menyedari bahawa HTTPS menggunakan protokol SSL/TLS untuk keselamatan. Untuk mengukuhkan ini, saya menasihati anda untuk membaca artikel yang sangat baik mengenai topik ini:
Mudah-mudahan, selepas semakan kecil ini, HTTPS akan menjadi lebih telus. Dan jika anda perlu mendayakan HTTPS, anda boleh memahami terma dengan mudah daripada dokumentasi pelayan aplikasi dan rangka kerja anda. #Viacheslav
GO TO FULL VERSION