1. الخلفية
HTTP هو بروتوكول عام يتيح قابلية نقل المحتوى ، ويتم نقل البيانات من العميل والخادم بالكامل من خلال النص العادي. بموجب هذه الخلفية ، فإن بيانات الإنترنت بأكملها التي تعتمد على بروتوكول HTTP شفاف ، مما يجلب مخاطر أمان بيانات رائعة. هناك فكرتان لحل هذه المشكلة:
يحتوي النوع الأول على مجموعة واسعة من التطبيقات في الواقع مما يتخيل. يقوم الطرفان بتبادل مفاتيح الإنترنت دون اتصال ، ويستخدم العميل نصًا مشفرًا عند إرسال البيانات ، والتي يتم إرسالها على الإنترنت من خلال بروتوكول HTTP الشفاف. بعد تلقي الطلب ، يقوم الخادم بإلغاء فك التشفير ويحصل على النص العادي بالطريقة المتفق عليها. حتى إذا تم اختطاف هذا النوع من المحتوى ، فهذا لا يهم ، لأن الأطراف الثالثة لا تعرف أساليب تشفيرها وفك التشفير. ومع ذلك ، فإن هذا النهج مميز للغاية ، ويحتاج كل من العميل والخادم إلى الاهتمام بهذا التشفير الخاص وفك التشفير.
قد لا يهتم النوع الثاني من جانب C/S بالمنطق الخاص أعلاه. وهم يعتقدون أن الإرسال والاستقبال هو نص عادي لأن جزء التشفير والتشفير قد تمت معالجته بواسطة البروتوكول نفسه.
انطلاقًا من النتائج ، يبدو أنه لا يوجد فرق بين الحلين ، ولكن من منظور مهندسي البرمجيات ، يكون الفرق ضخمًا للغاية. نظرًا لأن النوع الأول يتطلب من نظام الأعمال تطوير وظيفة التشفير وفك التشفير للاستجابة ، والمفتاح التفاعلي غير المتصلة بالإنترنت مطلوب ، فإن النوع الثاني لا يحتوي على حجم التطوير.
HTTPS هو الشكل الأكثر شعبية للأمان لـ HTTP ، الذي تم إنشاؤه لأول مرة بواسطة NetScape. في https ، تبدأ عناوين URL بـ https: // بدلاً من http: //. عند استخدام HTTPS ، يتم تشفير جميع طلبات HTTP والاستجابات قبل إرسالها إلى الشبكة ، والتي يتم تنفيذها في طبقة SSL.
2. طريقة التشفير
يتم تشفير بيانات النص العادي من خلال طبقة SSL ثم يتم إرسالها إلى الإنترنت لإرسالها ، والتي تحل مشكلة أمان البيانات الأصلية لبروتوكول HTTP. بشكل عام ، تنقسم طرق تشفير البيانات إلى تشفير متماثل وتشفير غير متماثل.
2.1 تشفير متماثل
يشير التشفير المتماثل إلى استخدام نفس مفتاح التشفير والتشفير. تشمل الخوارزميات الشائعة DES و AES ، وما إلى ذلك ، ويرتبط وقت الخوارزمية بطول المفتاح.
أكبر عيب للمفاتيح المتماثلة هو أنها بحاجة إلى الحفاظ على عدد كبير من المفاتيح المتماثلة ويتطلبون تبادلًا في وضع عدم الاتصال. للانضمام إلى شبكة مع كيانات N ، مطلوب مفاتيح N (N-1).
2.2 التشفير غير المتماثل
يشير التشفير غير المتماثل إلى طرق التشفير على أساس المفاتيح العامة/الخاصة. تشمل الخوارزميات الشائعة RSA ، والتي تبطئ عمومًا التشفير من التشفير المتماثل.
يحتوي التشفير المتماثل على خطوة واحدة أكثر من التشفير غير المتماثل ، أي الحصول على مفتاح الخادم العام ، بدلاً من المفتاح الذي يحتفظ به كل منهما.
تعتمد خوارزمية التشفير بأكملها على نظرية عدد معينة ، والتأثير هو أن نتيجة التشفير لا رجعة فيها. وهذا هو ، فقط من خلال المفتاح الخاص يمكن فك تشفير النص المشفر بواسطة المفتاح العمومي.
تحت هذه الخوارزمية ، يتم تقليل عدد المفاتيح في الشبكة بأكملها إلى حد كبير ، ويحتاج كل شخص فقط إلى الحفاظ على زوج من مفاتيح الشركة. وهذا هو ، في شبكة الكيانات n ، عدد المفاتيح هو 2n.
العيب هو أنه يعمل ببطء.
2.3 التشفير الهجين
يوجد مشهد في فيلم Stephen Chow "God of Cookery" حيث يكون العالم السفلي في مكان ساخن ، يتجادل حول قضية تقسيم الهيكل بين الروبيان البغي وكرات اللحم البقري. قال إله الطعام ، "إنه أمر مزعج حقًا. إنه مختلط معًا لصنع كرة لحم بقر غبي!"
ميزة التشفير المتماثل هي سرعته السريعة ، والعيوب هي أنه يتطلب تبادل المفاتيح. تتمثل ميزة التشفير غير المتماثل في أنه لا يتطلب مفتاحًا تفاعليًا ، والعيوب هو أنها سرعة بطيئة. فقط امزجه معًا واستخدمه جيدًا.
التشفير المختلط هو بالضبط طريقة التشفير المستخدمة بواسطة بروتوكول HTTPS. يتم تبادل المفتاح المتماثل أولاً من خلال التشفير غير المتماثل ، ثم يتم نقل البيانات من خلال المفتاح المتماثل.
نظرًا لأن كمية نقل البيانات أكبر بكثير من مقدار البيانات المستخدمة لتبادل المفاتيح في المرحلة المبكرة من إنشاء اتصال ، فإن تأثير أداء التشفير غير المتماثل يمكن أن يكون ضئيلًا بشكل أساسي ، وفي الوقت نفسه يحسن الكفاءة.
3. HTTPS المصافحة
يمكن ملاحظة أنه بناءً على بروتوكول HTTP الأصلي ، أضافت HTTPS معالجة طبقة الأمان:
4. دعم HTTPCLIENT لبروتوكول HTTPS
4.1 الحصول على جهاز التحقق من مصنع اتصال SSL وجهاز التحقق من اسم المجال
كمهندس برمجيات ، ما نهتم به هو كيف يتم تنفيذ "بروتوكول HTTPS" في الكود؟ لاستكشاف لغز رمز مصدر httpclient ، يبدأ كل شيء بـ httpclientbuilder.
public clovablehttpclient build () {// حذف بعض الكود httpclientConnectionManager connmanagerCopy = this.connmanager ؛ // إذا تم تحديد مدير تجمع الاتصال ، فاستخدم واحد محدد ، وإلا قم بإنشاء افتراضي جديد إذا (connmanagercopy == null) {layeredConnectionSocketFactory sslsocketfactorycopy = this.sslsocketfactory ؛ إذا (SSLSOCKETFACTORYCOPY == NULL) {// إذا تم تمكين متغير البيئة الاستخدام ، فإن إصدار HTTPS والتحكم في كلمة المرور يقرأون السلسلة النهائية [] SupportProtocols = SystemProperties؟ split (system.getProperty ("https.protocols")): null ؛ السلسلة النهائية [] SupportedCiphersuites = SystemProperties؟ split (system.getProperty ("https.ciphersuites")): null ؛ // إذا لم يتم تحديدها ، فاستخدم مدقق اسم المجال الافتراضي ، وسيتحقق مما إذا كان اسم المجال يتطابق مع الشهادة التي تم إرجاعها بواسطة الخادم في SSL Session HostNameverifier HostNameverifierCopy = this.hostNameverifier ؛ if (hostNameverifierCopy == NULL) {hostNameverifierCopy = new DefaulThoStNameverifier (publicsuffixMatcherCopy) ؛ } // إذا تم صياغة SSLContext ، يتم إنشاء مصنع اتصال SSL مخصص ، وإلا يتم استخدام مصنع الاتصال الافتراضي إذا (sslContext! = null) {sslsocketfactorycopy = new SSLConnectionSocketFactory (sslcontext ، supportedprotoCols ، superedcipyverikeveriveifififiarifier. } آخر {if (systemproperties) {sslsocketfactorycopy = sslconnections -SocketFactory جديد ((sslsocketfactory) sslsocketfactory.getDefault () ، دعم protocols ، aduredciphersuites ، hostnameverfiercopy) ؛ } آخر {sslsocketfactorycopy = sslconnectionsocketfactory جديد (sslContexts.createdefault () ، hostnameverifiercopy) ؛ }}} // قم بتسجيل مصنع اتصال SSL في مدير مجموعة الاتصال. عند الحاجة إلى اتصال HTTPS ، سيتم إنتاج اتصال SSL استنادًا إلى مصنع اتصال SSL أعلاه suppressWarnings ("Resource") Final PoolingHttpClientConnectionManager poolingmgr = new poolinghttpclientConnectionManageR (registryBuilder. PlainConnectionsocketFactory.GetSocketFactory () // حذف بعض الرموز}}يقوم الرمز أعلاه بإنشاء SSLConnectionsOctionFactory SSLConnectionSocketFactory ويسجله في مدير مجموعة الاتصال لإنتاج متصلات SSL لاحقًا. مرجع لتجميع الاتصال: http://www.vevb.com/article/141015.htm
هنا ، يتم استخدام العديد من مكونات المفاتيح عند تكوين SSLConnectionsOcketFactory ، وموافقة اسم المجال HostNameverifier و Context SSLContext.
من بينها ، يتم استخدام HostNameverifier للتحقق مما إذا كانت شهادة الخادم تتطابق مع اسم المجال. هناك العديد من التطبيقات. يستخدم DefaulThoStNameverifier قواعد التحقق الافتراضية ، واستبدال BrowserCompathostNameverifier و StricThostNameverifier في الإصدار السابق. يستبدل NoophostNameverifier المسموح به ، باستخدام استراتيجية لعدم التحقق من اسم المجال.
لاحظ أن هناك بعض الاختلافات هنا ، يمكن لـ BrowserCompathoStNameverifier مطابقة أسماء النطاق الفرعي متعدد المستويات ، ويمكن "*.foo.com" مطابقة "abfoo.com". لا يمكن لـ StricThoStNameverifier مطابقة أسماء النطاق الفرعي متعدد المستويات ، فقط إلى "A.FOO.com".
بعد 4.4 ، استخدم HttpClient DefaulthoStNameverifier الجديد لاستبدال الاستراتيجيتين أعلاه ، واحتفظت فقط باستراتيجية صارمة واحدة و stricthostnameverifier. نظرًا لأن الاستراتيجيات الصارمة هي استراتيجيات IE6 و JDK نفسها ، فإن الاستراتيجيات غير الشريطية هي استراتيجيات Curl و Firefox. أي أن تطبيق HTTPCLIENT الافتراضي لا يدعم استراتيجية مطابقة اسم المجال الفرعي متعدد المستويات.
يقوم SSLContext بتخزين المعلومات الرئيسية المتعلقة بالمفتاح ، والذي يرتبط مباشرة بالعمل وهو مهم للغاية. هذا ليتم تحليله بشكل منفصل في وقت لاحق.
4.2 كيفية الحصول على اتصال SSL
كيف تحصل على اتصال من تجمع اتصال؟ تم تحليل هذه العملية في المقالات السابقة. لن أقوم بتحليله هنا. يرجى الرجوع إلى الاتصال: //www.vevb.com/article/141015.htm.
بعد الحصول على اتصال من تجمع الاتصال ، إذا لم يكن الاتصال في الحالة المحددة ، فأنت بحاجة إلى إنشاء الاتصال أولاً.
رمز جزء defaulthttpclientConnectionOperator هو:
Public Void Connect (Final ManagedHttPclientConnection Conn ، Httphost Host النهائي ، Final InetSocketaddress localaddress ، Final int ConnectTimeout ، تم تسجيل تطبيق SocketConfig Final SocketConfig ، HTTPContext النهائي) IOException {// سابقًا ، تنفيذ مجموعة HTTPS في HTTPClientBuilder. هنا ، يحصل Lookup على تنفيذ HTTPS ، أي SSLConnectionsocketFactory Final Lookup <ConnectionSocketFactory> registry = getSocketFactoryRegistry (السياق) ؛ ConnectionSocketFactory SF = registry.lookup (host.getSchemename ()) ؛ if (sf == null) {رمي جديد UnsupportedSchemeexception (host.getschemename () + "البروتوكول غير مدعوم") ؛ } // إذا كان العنوان في نموذج IP ، فيمكن استخدامه مباشرة ، وإلا استخدم محلل DNS لتحليل IP المقابل لاسم المجال للحصول على العناوين النهائية []] new inetaddress [] {host.getaddress ()}: this.dnsresolver.resolve (host.gethostname ()) ؛ المنفذ int النهائي = this.schemeportresolver.resolve (host) ؛ // قد يتوافق اسم المجال مع IP متعددة ، ويحاول الاتصال من أجل (int i = 0 ؛ i <addresses.length ؛ i ++) {Final inetaddress address = address [i] ؛ boolean النهائي الماضي = i == addresses.length - 1 ؛ // هذا مجرد مأخذ توصيل تم إنشاؤه ، ولا يوجد اتصال بجورب المقبس = sf.createsocket (السياق) ؛ // قم بتعيين بعض معلمات طبقة TCP Sock.SetSotimeout (socketConfig.getSotimeout ()) ؛ Sock.setReuseadDress (SocketConfig.issoreuseaddress ()) ؛ sock.settcpnodelay (socketConfig.istcpnodelay ()) ؛ Sock.setKeepalive (socketConfig.issokeePalive ()) ؛ if (socketConfig.getRcvBufSize ()> 0) {sock.setReceiveBuffersize (socketConfig.getRcvBufSize ()) ؛ } if (socketConfig.getSndBufSize ()> 0) {Sock.SetSendBuffersize (SocketConfig.getSndBufSize ()) ؛ } Final Int Linger = SocketConfig.getSolinger () ؛ if (linger> = 0) {sock.setsolinger (true ، linger) ؛ } conn.bind (sock) ؛ inetsocketaddress النهائي remoteaddress = new inetsocketaddress (العنوان ، المنفذ) ؛ if (this.log.isdebugenabled ()) {this.log.debug ("connecting to" + remoteaddress) ؛ } جرب {// إنشاء اتصال من خلال sslconnectionsocketfactory وربط conn sock = sf.connectsocket (connecttimeout ، sock ، host ، remoteaddress ، localaddress ، context) ؛ conn.bind (جورب) ؛ if (this.log.isdebugenabled ()) {this.log.debug ("connection late" + conn) ؛ } يعود؛ } // حذف بعض الكود}}في الكود أعلاه ، نرى أن عمل التحضير هو قبل إنشاء اتصال SSL. هذه عملية عامة ، وينطبق الشيء نفسه على اتصالات HTTP العادية. أين تعكس العملية الخاصة لاتصال SSL؟
الكود المصدري لـ SSLConnections SocketFactory كما يلي:
Override Public Socket ConnectSocket (Final int ConnectTimeout ، مقبس المقبس النهائي ، مضيف HttPhost النهائي ، Final Inetsocketaddress Remoteaddress ، Final Inetsocketaddress localaddress ، HTTPContext النهائي) يلقي IoException {args.notnull (HTTP Host ") ؛ args.notnull (RemoTeadDress ، "العنوان البعيد") ؛ جورب المقبس النهائي = المقبس! = فارغ؟ المقبس: CreateSocket (سياق) ؛ if (localaddress! = null) {sock.bind (localaddress) ؛ } جرب {if (connectTimeOut> 0 && sock.getSotimeout () == 0) {sock.setsotimeout (connectTimeOut) ؛ } if (this.log.isdebugenabled ()) {this.log.debug ("توصيل المقبس بـ" + remoteaddress + "مع timeout" + connecttimeout) ؛ } // إنشاء اتصال sock.connect (remoteaddress ، connecttimeout) ؛ } catch (Final ioException ex) {try {sock.close () ؛ } catch (Final ioException تجاهل) {} رمي السابقين ؛ } // إذا كان SSLSocket حاليًا ، فقم بإجراء مصافحة SSL والتحقق من اسم المجال إذا (Sock extorlyof sslsocket) {Final sslsocket sslsock = (sslsocket) sock ؛ this.log.debug ("بداية المصافحة") ؛ sslsock.starthandshake () ؛ VerifyHostName (sslsock ، host.gethostname ()) ؛ إرجاع جورب } آخر {// إذا لم يكن sslsocket ، لفه على أنه sslsocket return createLayeredSocket (sock ، host.gethostname () ، remoteaddress.getport () ، context) ؛ }}} Override Public Socket CreateLayeredSocket (مقبس المقبس النهائي ، هدف السلسلة النهائية ، منفذ int النهائي ، سياق httpcontext النهائي) يلقي ioException {// لف مقبس عادي كما sslsocket ، يتم إنشاء socketfactory على أساس sslcontext في httpclientbuilder ، والتي تتوافق (sslsocket) this.socketfactory.createsocket (المقبس ، الهدف ، المنفذ ، صحيح) ؛ // إذا تم صياغة إصدار بروتوكول طبقة SSL وخوارزمية التشفير ، فاستخدم الإصدار المحدد ، وإلا الافتراضي إذا كان (SupportedProtocols! = null) {sslsock.setEnabledProtocols (الدعم protocols) ؛ } آخر {// إذا لم يتم تعيين البروتوكولات المدعومة بشكل صريح ، قم بإزالة جميع إصدارات بروتوكول SSL النهائية [] allprotocols = sslsock.getEnabledProtocols () ؛ القائمة النهائية <String> EnupplyProtocols = new ArrayList <string> (allprotocols.length) ؛ لـ (بروتوكول السلسلة النهائية: allprotocols) {if (! protocol.startswith ("ssl")) {enupperprotocols.add (protocol) ؛ }} if (! enupperprotocols.isempty ()) {sslsock.setEnableTProtocols (EnupperProtocols.Toarray (New String [EnupperProtocols.size ()])) ؛ }} if (supportedCiphersuites! = null) {sslsock.setEnabledCiphersuites (supportedCiphersuites) ؛ } if (this.log.isdebugenabled ()) {this.log.debug ("بروتوكولات تمكين:" + arrays.aslist (sslsock.getEnabledProtocols ()) this.log.debug ("Suites Cipher الممكّن:" + arrays.aslist (sslsock.getEnabledCiphersuites ())) ؛ } إعداد (SSLsock) ؛ this.log.debug ("بداية المصافحة") ؛ // SSL Connection Handshake sslsock.starthandshake () ؛ // بعد نجاح المصافحة ، تحقق مما إذا كانت الشهادة التي تم إرجاعها متسقة مع اسم المجال VerifyHostName (SSLSock ، Target) ؛ إرجاع sslsock. }كما يمكن أن نرى ، لتواصل SSL. أولاً ، قم بإنشاء اتصال مقبس عادي ، ثم قم بإجراء مصافحة SSL ، ثم تحقق من اتساق الشهادة واسم المجال. العملية اللاحقة هي التواصل من خلال SSLSocketImpl. تنعكس تفاصيل البروتوكول في فئة SSLSocketImpl ، ولكن هذا الجزء من الكود JDK ليس مفتوحًا. يمكن للمهتمين تنزيل رمز مصدر OpenJDK المقابل لمواصلة التحليل.
5. ملخص هذا المقال
حسنًا ، ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.