1。背景
HTTPは、コンテンツを転送する可能性を可能にするパブリックプロトコルであり、クライアントとサーバーからのデータはプレーンテキストを介して完全に送信されます。この背景では、HTTPプロトコルに依存するインターネットデータ全体が透明であり、優れたデータセキュリティリスクをもたらします。この問題を解決するための2つのアイデアがあります。
最初のタイプには、想像よりも幅広いアプリケーションが現実にあります。 2つの関係者はキーをオフラインで交換し、クライアントはデータを送信するときに暗号文を使用します。これは、透明なHTTPプロトコルを介してインターネット上で送信されます。リクエストを受信した後、サーバーは合意された方法でプレーンテキストを復号化して取得します。この種のコンテンツがハイジャックされたとしても、第三者は暗号化と復号化方法を知らないため、それは問題ではありません。ただし、このアプローチは特別すぎるため、クライアントとサーバーの両方がこの特別な暗号化と復号化ロジックに注意する必要があります。
C/S側の2番目のタイプは、上記の特別なロジックを気にしない場合があります。彼らは、暗号化と復号化の部分がプロトコル自体によって処理されているため、送信と受信はプレーンテキストであると考えています。
結果から判断すると、2つのソリューションの間に違いはないようですが、ソフトウェアエンジニアの観点から見ると、違いは非常に大きいです。最初のタイプでは、ビジネスシステムが応答の暗号化と復号化関数を開発する必要があり、オフラインインタラクティブキーが必要であるため、2番目のタイプには開発ボリュームがありません。
HTTPSは、Netscapeによって最初に作成されたHTTPのセキュリティの最も人気のある形式です。 httpsでは、urlsはhttp://の代わりにhttps://で始まります。 HTTPSを使用すると、すべてのHTTP要求と応答が暗号化され、SSLレイヤーで実装されるネットワークに送信されます。
2。暗号化方法
プレーンテキストデータは、SSLレイヤーを介して暗号化され、インターネットに送信されて送信されます。これにより、HTTPプロトコルの元のデータセキュリティ問題が解決されます。一般的に、データを暗号化する方法は、対称暗号化と非対称暗号化に分割されます。
2.1対称暗号化
対称暗号化とは、暗号化と復号化と同じキーの使用を指します。一般的なアルゴリズムにはDESやAEなどが含まれ、アルゴリズム時間はキーの長さに関連しています。
対称キーの最大の欠点は、多数の対称キーを維持し、オフライン交換が必要であることです。 nエンティティでネットワークに参加するには、n(n-1)キーが必要です。
2.2非対称暗号化
非対称暗号化とは、パブリック/プライベートキーに基づく暗号化方法を指します。一般的なアルゴリズムには、対称暗号化よりも一般的に暗号化が遅いRSAが含まれます。
対称暗号化には、非対称暗号化よりも1つのステップ、つまり、それぞれが維持するキーではなく、サーバーの公開キーを取得するためのステップが1つあります。
暗号化アルゴリズム全体は特定の数値理論に基づいており、その効果は暗号化の結果が不可逆的であることです。つまり、秘密鍵を介してのみ、公開キーによって暗号化された暗号文を復号化できます。
このアルゴリズムでは、ネットワーク全体のキーの数が大幅に削減され、各人は会社のキーを維持する必要があります。つまり、nエンティティのネットワークでは、キーの数は2nです。
欠点は、ゆっくりと実行されることです。
2.3ハイブリッド暗号化
スティーブンチョウの映画「神の料理」には、アンダーワールドがホットスポットにあるシーンがあり、ピービルボールとビーフボールの間のシャーシ師団の問題について議論しています。食べ物の神は、「それは本当に厄介です。おしっこビーフボールを作るために一緒に混ざり合っています!」
対称暗号化の利点は、その高速速度であり、不利な点は、キー交換が必要であることです。非対称暗号化の利点は、インタラクティブなキーを必要としないことであり、不利な点は、速度が遅いことです。一緒に混ぜて、よく使用してください。
ハイブリッド暗号化は、HTTPSプロトコルで使用される暗号化方法です。対称キーは、最初に非対称暗号化を介して交換され、次にデータは対称キーを介して送信されます。
データ送信の量は、接続の確立の初期段階でキーを交換するために使用されるデータの量よりもはるかに大きいため、非対称暗号化のパフォーマンスへの影響は基本的に無視でき、同時に効率が向上します。
3。HTTPSハンドシェイク
元のHTTPプロトコルに基づいて、HTTPSがセキュリティレイヤー処理を追加していることがわかります。
4。HTTPSプロトコルに対するHTTPCLIENTのサポート
4.1 SSL接続工場とドメイン名確認装置を取得する
ソフトウェアエンジニアとして、私たちが気にしているのは、「HTTPSプロトコル」がコードにどのように実装されているかということです。 httpclientソースコードの謎を探るために、すべてがhttpclientBuilderから始まります。
public closeablehttpclient build(){//いくつかのコードhttpclientconnectionmanager connmanagercopy = this.connmanager; //接続プールマネージャーが指定されている場合、指定されたマネージャーを使用します。そうでない場合は、新しいデフォルトを作成します(connmanagercopy == null){leayeredConnectionSocketFactory SSLSocketFactoryCopy = this.sslsocketFactory; if(sslsocketfactorycopy == null){//使用環境変数が有効になっている場合、httpsバージョンとパスワード制御が最終文字列[] supportprotocols = systempropertiesを読み取りますか? split(system.getProperty( "https.protocols")):null; final string [] supportedCipherSuites = SystemProperties? split(system.getProperty( "https.ciphersuites")):null; //指定されていない場合は、デフォルトのドメイン名VALDICATORを使用すると、ドメイン名がSSLセッションのサーバーが返した証明書と一致するかどうかが確認されます。 if(hostnameverifiercopy == null){hostnameverifiercopy = new defulthostnameverifier(publicsuffixmatchercopy); } // SSLContextが策定されている場合、カスタマイズされたSSL接続ファクトリが生成されます。そうしないと、デフォルトの接続ファクトリが使用されます。 } else {if(systemProperties){sslsocketFactoryCopy = new SSLConnectionSocketFactory((SSLSocketFactory)SSLSocketFactory.getDefault()、SupportedCipherSuites、hostNameverifiercopy); } else {sslsocketFactoryCopy = new SSLConnectionSocketFactory(sslcontexts.createdefault()、hostnameverifiercopy); }}} //接続プールマネージャーにSSL接続ファクトリを登録します。 HTTPS接続が必要な場合、SSL接続は上記のSSL接続ファクトリー@SuppressWarnings(「リソース」)に基づいて生成されます。 PlainConnectionSocketFactory.getSocketFactory()).register( "https"、sslsocketfactorycopy)、null、null、dnsresolver、conntimetolive、conntimetolivetimeunit!= null:null //いくつかのコードを省略します}}上記のコードは、SSLConnectionSocketFactory SSLConnectionSocketFactoryを作成し、SSL接続の後の生産のために接続プールマネージャーに登録します。接続プーリングのリファレンス:http://www.vevb.com/article/141015.htm
ここでは、SSLConnectionSocketFactory、Domain Name Balidator hostNameVerifier、およびContext SSLContextを構成するときに、いくつかの重要なコンポーネントが使用されます。
その中で、HostNameVerifierを使用して、サーバー証明書がドメイン名と一致するかどうかを確認します。多くの実装があります。 Defaulthostnameverifierは、以前のバージョンでBrowsercompathostnameverifierとstricthostnameverifierを置き換えて、デフォルトの検証ルールを使用します。 noophostnameverifierは、ドメイン名を検証しない戦略を使用して、Alloallhostnameverifierを置き換えます。
ここにはいくつかの違いがあることに注意してください。Browsercompathostnameverifierは、マルチレベルのサブドメイン名と一致し、「*.foo.com」が「abfoo.com」と一致する可能性があります。 stricthostnameverifierは、「a.foo.com」とのみマルチレベルのサブドメイン名と一致することはできません。
4.4以降、HTTPClientは新しいDefulthostNameverifierを使用して、上記の2つの戦略を置き換え、1つの厳格な戦略とStricthostNameverifierのみを保持しました。厳格な戦略はIE6およびJDK自体の戦略であるため、非厳格な戦略はCurlとFirefoxの戦略です。つまり、デフォルトのHTTPCLIENT実装は、マルチレベルのサブドメイン名マッチング戦略をサポートしていません。
SSLContextは、キーに関連するキー情報を保存します。これは、ビジネスに直接関連しており、非常に重要です。これは、後で個別に分析することです。
4.2 SSL接続を取得する方法
接続プールから接続を取得する方法は?このプロセスは、以前の記事で分析されています。ここでは分析しません。接続を参照してください://www.vevb.com/article/141015.htm。
接続プールから接続を取得した後、接続が確立された状態にない場合は、最初に接続を確立する必要があります。
defaulthttpclientConnectionOperator部品のコードは次のとおりです。
public void connect(最終的なmanagedhttpclientconnection conn、final httphostホスト、最終的なinetsocketAddress localAddress、final connecttimeout、final socketconfig socketconfig、final httpcontextコンテキスト)ioexception {//以前は、httpsの接続プールの実装が登録されました。ここで、LookupはHTTPSの実装を取得します。つまり、SSLConnectionSocketFactory Final Lookup <ConnectionSocketFactory> registry = GetSocketFactoryRegistry(Context); final ConnectionSocketFactory SF = registry.lookup(host.getSchemename()); if(sf == null){新しいunsupportedschemeexception(host.getschemename() + "プロトコルがサポートされていない"); } //アドレスがIPの形式である場合、それは直接使用できます。そうでなければDNSパーサーを使用してドメイン名に対応するIPを解析して、最終的なinetAddress []アドレス= host.getAddress()!= null? new inetAddress [] {host.getAddress()}:this.dnsresolver.resolve(host.gethostname()); final int port = this.schemeportresolver.resolve(host); //ドメイン名は複数のIPに対応し、(int i = 0; i <address.length; i ++){final inetAddressアドレス=アドレス[i];最終的なブーリアンlast = i == address.length -1; //これは単に生成されたソケットであり、ソケットソック= sf.createsocket(context)への接続はありません。 //いくつかのTCPレイヤーパラメーターSOCK.SETSOTIMEOUT(socketconfig.getSotimeout()); sock.setreuseaddress(socketconfig.issoreuseaddress()); sock.settcpnodelay(socketconfig.istcpnodelay()); sock.setkeepalive(socketconfig.issokeepalive()); if(socketconfig.getrcvbufsize()> 0){sock.setReceiveBufferize(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); final inetsocketAddress remoteaddress = new inetsocketAddress(アドレス、ポート); if(this.log.isdebugenabled()){this.log.debug( " + remoteaddress); } try {// sslconnectionSocketFactoryを介して接続を確立し、connock = sf.connectsocket(connecttimeout、sock、host、remoteaddress、localAddress、context)にバインドします。 conn.bind(sock); if(this.log.isdebugenabled()){this.log.debug( "connection setativent" + conn); } 戻る; } //いくつかのコードを省略}}上記のコードでは、準備作業がSSL接続を確立する前にあることがわかります。これは一般的なプロセスであり、同じことが通常のHTTP接続にも当てはまります。 SSL接続の特別なプロセスはどこに反映されますか?
SSLConnectionSocketFactoryのソースコードは次のとおりです。
@OverrideパブリックソケットConnectSocket(Final Int ConnectTimeout、Final Socket Socket、Final HttPhost Host、最終InetsocketAddress Remoteaddress、最終的なInetsocketAddress localAddress、最終httpcontextコンテキスト)IoException {args.notnull(host、 "http host"); args.notnull(remoteaddress、 "remoteアドレス");最終的なソケットソック=ソケット!= null?ソケット:createSocket(context); if(localaddress!= null){sock.bind(localaddress); } try {if(connecttimeout> 0 && sock.getSotimeout()== 0){sock.setsotimeout(connecttimeout); } if(this.log.isdebugenabled()){this.log.debug( "socketing socketing to" + remoteaddress + "with timeout" + connecttimeout); } //接続sock.connect(remoteaddress、connecttimeout); } catch(final ioException ex){try {sock.close(); } catch(final ioException Ingrore){} shrow ex; } //現在SSLSocketの場合、SSLハンドシェイクとドメイン名の確認を実行するif(sslsocketのソックインスタンス){final sslsocket sslsock =(sslsocket)sock; this.log.debug( "握手の開始"); sslsock.starthandshake(); verifyhostname(sslsock、host.gethostname());靴下を返します。 } else {// sslsocketでない場合は、sslsocket return createLayeredsocket(sock、host.gethostname()、remoteaddress.getport()、context)としてrapします。 }} @OverrideパブリックソケットCreateLayeredSocket(最終ソケットソケット、最終文字列ターゲット、最終的なINTポート、最終HTTPContextコンテキスト)IOEXCEPTION {// SSLSocketとして通常のソケットをスローします。 this.SocketFactory.Createsocket(Socket、Target、Port、True); // SSLレイヤープロトコルバージョンと暗号化アルゴリズムが策定されている場合、指定されたものを使用します。 } else {//サポートされているプロトコルが明示的に設定されていない場合、すべてのSSLプロトコルバージョンfinal string [] allprotocols = sslsock.getEnabledProtocols();最終リスト<String> enabledProtocols = new ArrayList <String>(allProtoCols.Length); for(final string protocol:allprotocols){if(!protocol.startswith( "ssl")){enabledProtocols.add(protocol); }} if(!enabledProtocols.isempty()){sslsock.setEnabledProtocols(enabledProtocols.ToArray(new String [enabledProtocols.size())); }} if(supportedCipherSuites!= null){sslsock.setEnabledCipherSuites(supportedCipherSuites); } if(this.log.isdebugenabled()){this.log.debug( "enabled protocols:" + arrays.aslist(sslsock.getEnabledProtocols()); this.log.debug( "enabled cipher suites:" + arrays.aslist(sslsock.getEnabledCipherSuites())); } preperesocket(sslsock); this.log.debug( "握手の開始"); // SSL接続ハンドシェイクSSLSOCK.STARTHANDSHAKE(); //ハンドシェイクが成功したら、返された証明書がドメイン名VerifyHostname(SSLSOCK、ターゲット)と一致しているかどうかを確認します。 sslsockを返します。 }ご覧のとおり、SSL通信の場合。まず、通常のソケット接続を確立し、SSLハンドシェイクを実行し、証明書とドメイン名の一貫性を確認します。その後の操作は、SSLSocketImplを介して通信することです。プロトコルの詳細はSSLSocketImplクラスに反映されていますが、コードJDKのこの部分はオープンソースではありません。興味のある人は、対応するOpenJDKソースコードをダウンロードして、分析を継続することができます。
5。この記事の概要
さて、上記はこの記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。