1. 배경
HTTP는 컨텐츠 전송의 가독성을 허용하는 공개 프로토콜이며 클라이언트 및 서버의 데이터는 일반 텍스트를 통해 완전히 전송됩니다. 이 배경에서 HTTP 프로토콜에 의존하는 전체 인터넷 데이터는 투명하여 데이터 보안 위험이 뛰어납니다. 이 문제를 해결하기위한 두 가지 아이디어가 있습니다.
첫 번째 유형은 상상보다 실제로 더 넓은 애플리케이션을 가지고 있습니다. 두 당사자는 오프라인에서 키를 교환하고 클라이언트는 투명한 HTTP 프로토콜을 통해 인터넷에서 전송되는 데이터를 보낼 때 암호 텍스트를 사용합니다. 요청을받은 후 서버는 합의 된 방식으로 일반 텍스트를 해독하고 얻습니다. 이런 종류의 콘텐츠가 납치 되더라도 제 3자는 암호화 및 암호 해독 방법을 모르기 때문에 중요하지 않습니다. 그러나이 접근 방식은 너무 특별하며 클라이언트와 서버는이 특별한 암호화 및 암호 해독 논리에 대해 관리해야합니다.
두 번째 유형의 C/S 측은 위의 특수 논리에 신경 쓰지 않을 수 있습니다. 그들은 암호화 및 암호 해독 부품이 프로토콜 자체에 의해 처리 되었기 때문에 전송 및 수신은 일반 텍스트라고 생각합니다.
결과로부터 판단하면 두 솔루션 사이에는 차이가없는 것으로 보이지만 소프트웨어 엔지니어의 관점에서 차이는 매우 큽니다. 첫 번째 유형은 비즈니스 시스템이 응답에 대한 암호화 및 암호 해독 함수를 개발해야하므로 오프라인 대화식 키가 필요하므로 두 번째 유형에는 개발 볼륨이 없습니다.
HTTPS는 HTTP의 가장 인기있는 보안 형태로 NetScape에서 처음 만들었습니다. https에서 URL은 http : // 대신 https : //부터 시작합니다. HTTPS를 사용하면 모든 HTTP 요청 및 응답은 SSL 계층에서 구현되는 네트워크로 전송되기 전에 암호화됩니다.
2. 암호화 방법
일반 텍스트 데이터는 SSL 계층을 통해 암호화 된 다음 인터넷으로 전송하여 전송하여 HTTP 프로토콜의 원래 데이터 보안 문제를 해결합니다. 일반적으로, 데이터 암호화 방법은 대칭 암호화 및 비대칭 암호화로 나뉩니다.
2.1 대칭 암호화
대칭 암호화는 암호화 및 암호 해독과 동일한 키를 사용하는 것을 말합니다. 일반적인 알고리즘에는 DES 및 AE 등이 포함되며 알고리즘 시간은 키 길이와 관련이 있습니다.
대칭 키의 가장 큰 단점은 많은 수의 대칭 키를 유지하고 오프라인 교환이 필요하다는 것입니다. N 엔티티와 네트워크에 가입하려면 N (N-1) 키가 필요합니다.
2.2 비대칭 암호화
비대칭 암호화는 공개/개인 키를 기반으로 암호화 방법을 말합니다. 일반적인 알고리즘에는 대칭 암호화보다 일반적으로 암호화가 느린 RSA가 포함됩니다.
대칭 암호화는 비대칭 암호화, 즉 각각에 의해 유지되는 키보다는 서버 공개 키를 얻는 것보다 한 단계 더 단계가 있습니다.
전체 암호화 알고리즘은 특정 숫자 이론을 기반으로하며 그 효과는 암호화 결과가 돌이킬 수 없다는 것입니다. 즉, 개인 키를 통해서만 공개 키에 의해 암호화 된 암호 텍스트를 해독 할 수 있습니다.
이 알고리즘 하에서 전체 네트워크의 키 수는 크게 줄어들고 각 사람은 한 쌍의 회사 키를 유지하면됩니다. 즉, N 엔터티 네트워크에서 키 수는 2n입니다.
단점은 천천히 실행된다는 것입니다.
2.3 하이브리드 암호화
Stephen Chow의 영화 "Cookery"의 장면이 있는데, 지하 세계가 핫스팟에있는 장면이 있습니다. 음식의 신은 말했다.
대칭 암호화의 장점은 빠른 속도이며, 단점은 키 교환이 필요하다는 것입니다. 비대칭 암호화의 장점은 대화식 키가 필요하지 않으며 단점은 속도가 느린다는 것입니다. 함께 섞어서 잘 사용하십시오.
하이브리드 암호화는 HTTPS 프로토콜에서 사용하는 암호화 방법입니다. 대칭 키는 먼저 비대칭 암호화를 통해 교환 한 다음 대칭 키를 통해 데이터가 전송됩니다.
데이터 전송의 양은 연결을 설정하는 초기 단계에서 키를 교환하는 데 사용되는 데이터의 양보다 훨씬 크기 때문에 비대칭 암호화의 성능 영향은 기본적으로 무시할 수 있으며 동시에 효율성을 향상시킵니다.
3. https 핸드 셰이크
HTTPS가 원래 HTTP 프로토콜을 기반으로 HTTPS가 보안 계층 처리를 추가했다는 것을 알 수 있습니다.
4. httpclient의 HTTPS 프로토콜 지원
4.1 SSL 연결 공장 및 도메인 이름 확인 장치 획득
소프트웨어 엔지니어로서 우리가 관심있는 것은 "HTTPS 프로토콜"이 코드에서 어떻게 구현 되는가? httpclient 소스 코드의 미스터리를 탐색하기 위해 모든 것이 httpclientBuilder로 시작합니다.
public closeblehttpclient build () {// 일부 코드를 생략 httpclientConnectionManager connagercopy = this.connmanager; // 연결 풀 관리자가 지정된 경우 지정된 것을 사용하십시오. 그렇지 않으면 새 기본값을 작성하십시오. if (connmanagercopy == null) {layerEdConnectionSocketFactory sslSocketFactoryCopy = this.sslsocketFactory; if (sslsocketfactoryCopy == null) {// 사용 환경 변수가 활성화되면 HTTPS 버전 및 암호 제어를 읽으십시오. split (system.getProperty ( "https.protocols")) : null; 최종 문자열 [] supportedCipherSuites = SystemProperties? split (system.getProperty ( "https.cipherSuites")) : null; // 지정되지 않은 경우 기본 도메인 이름 유효성 검사기를 사용하면 도메인 이름이 SSL 세션 호스트에서 서버에서 반환 한 인증서와 일치하는지 여부를 확인합니다. if (hostnameverifiercopy == null) {hostnameverifiercopy = new defaulthostnameverifier (publicsuffixmatchercopy); } // SSLContext가 공식화되면 사용자 정의 된 SSL 연결 공장이 생성됩니다. 그렇지 않으면 (SSLContext! = NULL) {SSLSLSOCKETFACTORYCOPY = NEW SSLCONNECTIONSOCKESTOCKS, SUPPUREDEDPROTOCOLS, SUPPORDEDVERIFERIFIERCOPY) 인 경우 기본 연결 공장이 사용됩니다. } else {if (SystemProperties) {SSLSOcketFactoryCopy = New SSLConnectionSocketFactory ((SSLSOcketFactory) SSLSocketFactory.GetDefault (), SupportedProtocols, SupporedCipherSuites, HostNameVerifierCopy); } else {sslSocketFactoryCopy = new sslConnectionSocketSocketFactory (sslcontexts.createdEfault (), hostNameVerifierCopy); }}} // 연결 풀 관리자에 SSL 연결 공장을 등록합니다. HTTPS 연결이 필요할 때, SSL 연결은 위의 SSL Connection Factory @SuppressWarnings ( "Resource") 최종 PoolingHttpClientConnectionManager PoolingMGR = New PoolinghttPclientConnectionManager (registryBuilder. <ConnectionSocketFactory> (). Register ( "Httpp", Regrister ( "HTTP",)를 기반으로 생성됩니다. playconnectionsocketfactory.getSocketFactory ()) .register ( "https", sslsocketfactorycopy) .build (), null, null, dnsresolver, conntimetolive, conntimetolevetimeUnit! = null? milliseconds); // 일부 코드 생략}}위의 코드는 SSLConnectionSocketFactory SSLConnectionSocketFactory를 생성하고 나중에 SSL 연결의 생산을 위해 Connection Pool Manager에 등록합니다. 연결 풀링 참조 : http://www.vevb.com/article/141015.htm
여기서는 SSLConnectionSocketFactory, 도메인 이름 유효성 검사기 HostNameVerifier 및 Context SSLContext를 구성 할 때 몇 가지 주요 구성 요소가 사용됩니다.
그 중에서도 HostNameVerifier는 서버 인증서가 도메인 이름과 일치하는지 확인하는 데 사용됩니다. 많은 구현이 있습니다. defaulthostnameverifier는 기본 확인 규칙을 사용하여 이전 버전의 BrowserCompathOthnameverifier 및 StricthostNameverifier를 대체합니다. NoophostNameverifier 도메인 이름을 확인하지 않는 전략을 사용하여 allowallhostnameverifier를 대체합니다.
여기에는 몇 가지 차이점 이 있습니다. 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 호스트, 최종 inetSocketAddress LocalAddress, 최종 INT ConnectTimeout, Final SocketConfig SocketConfig, 최종 HTTPContext Context) IOException {// HTTPS의 연결 풀 구현은 HTTPClientBuilder에 등록되었습니다. 여기서 조회는 HTTPS의 구현, 즉 SSLConnectionSocketFactory 최종 조회 <ConnessectionSocketFactory> 레지스트리 = getSocketFactoryRegistry (Context)를 얻습니다. 최종 ConnectionSocketFactory sf = retuistry.Lookup (host.getSchemename ()); if (sf == null) {새로 지원되지 않은 새 nullschemeexception (host.getSchemename () + "프로토콜이 지원되지 않음"); } // 주소가 IP 형식 인 경우 직접 사용할 수 있습니다. 그렇지 않으면 DNS 파서를 사용하여 도메인 이름에 해당하는 IP를 구문 분석하여 최종 inetAddress [] address = host.getAddress ()! = null? new inetAddress [] {host.getAddress ()} : this.dnsresolver.resolve (host.gethostName ()); 최종 int port = this.schemeportresolver.resolve (호스트); // 도메인 이름은 다중 IP에 해당 할 수 있으며 (int i = 0; i <address.length; i ++) {Final inetAddress address = addresses [i]; 최종 부울 마지막 = i == 주소 -length -1; // 이것은 단지 소켓이 생성되었으며 소켓 SOCK = SF.CreatesOcket (Context)과 연결되지 않습니다. // 일부 tcp 레이어 매개 변수를 설정했습니다. 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 ()); } 최종 int linger = socketconfig.getSolinger (); if (linger> = 0) {sock.setsolinger (true, linger); } conn.bind (양말); 최종 inetSocketAddress RemoteadDress = 새로운 inetSocketAddress (주소, 포트); if (this.log.isdebugenabled ()) {this.log.debug ( " + remoteaddress에 연결); } try {// sslConnectionSocketFactory를 통해 연결을 설정하고 Conn Sock = sf.connectSocket에 바인딩합니다 (ConnectTimeout, Sock, Host, RemoteadDress, LocalAddress, Context); conn.bind (양말); if (this.log.isdebugenabled ()) {this.log.debug ( "연결 설정" + conn); } 반품; } // 코드를 생략}}위의 코드에서는 SSL 연결을 설정하기 전에 준비 작업이 있음을 알 수 있습니다. 이것은 일반적인 프로세스이며 일반적인 HTTP 연결에 대해서도 마찬가지입니다. SSL 연결의 특수 프로세스는 어디에 반영됩니까?
sslConnectionSocketFactory의 소스 코드는 다음과 같습니다.
@override public Socket ConnectSocket (최종 INT ConnectTimeout, 최종 소켓 소켓, 최종 HTTPHOST 호스트, 최종 inetSocketAddress RemoteadDress, Final InetSocketAddress LocalAddress, 최종 HTTPContext Context) IoException {args.notnull (http Host "); args.notnull (RemoteadDress, "원격 주소"); 최종 소켓 양말 = 소켓! = 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 ( "시간 초과가있는" + Remoteaddress + "에 소켓 연결" + ConnectTimeout); } // Conection sock.connect (RemoteadDress, ConnectTimeout); } catch (Final IoException ex) {try {sock.close (); } catch (Final IoException 무시) {} throw ex; } // 현재 SSLSocket 인 경우 SSL 핸드 셰이크 및 도메인 이름 확인 (SSLSocket의 SOCE 인스턴스) {Final SSLSocket SSLSock = (SSLSocket) SOOD; this.log.debug ( "핸드 셰이크 시작"); sslsock.starthandshake (); VerifyHostName (sslsock, host.gethostName ()); 귀환 양말; } else {// sslsocket이 아닌 경우 sslsocket으로 랩핑하면 CreatelayeredSocket (sock, host.gethostname (), remoteaddress.getport (), 컨텍스트); }} @Override Public Socket CreatelayerEdSocket (최종 소켓 소켓, 최종 문자열 대상, 최종 int 포트, 최종 HTTPContext Context)은 ioexception {// 정상 소켓을 SSLSocket으로 랩핑하고 SocketFactory는 HTTPClientBuilder에서 SSLContext를 기반으로 생성됩니다. (sslsocket) this.socketfactory.createsocket (소켓, 대상, 포트, 참); // SSL 레이어 프로토콜 버전 및 암호화 알고리즘이 공식화되면 지정된 하나를 사용하십시오. 그렇지 않으면 기본값 if (supportedProtocols! = null) {sslsock.setenabledProtocols (supportedProtocols); } else {// 지원되는 프로토콜이 명시 적으로 설정되지 않은 경우 모든 SSL 프로토콜 버전을 제거하십시오. 최종 문자열 [] allProtocols = sslsock.getenabledProtocols (); 최종 목록 <string> enabledProtocols = new ArrayList <string> (AllProtocols.length); for (최종 문자열 프로토콜 : allProtocols) {if (! prococol.startSwith ( "ssl")) {enabledProtocols.add (프로토콜); }} if (! enabledProtocols.isempty ()) {sslsock.setenabledProtocols (enabledProtocols.toArray (new String [enabledProtocols.size ()]); }} if (supportEdCipherSuites! = null) {sslsock.setenabledCipherSuites (supporedCipherSuites); } if (this.log.isdebugenabled ()) {this.log.debug ( "활성화 프로토콜 :" + arrays.aslist (sslsock.getenabledProtocols ())); this.log.debug ( "활성화 된 암호 스위트 :" + arrays.aslist (sslsock.getenabledciphersuites ())); } repaysocket (sslsock); this.log.debug ( "핸드 셰이크 시작"); // SSL Connection 핸드 셰이크 sslsock.starthandshake (); // 핸드 셰이크가 성공한 후에는 반환 된 인증서가 도메인 이름 verifyhostName (sslsock, target)과 일치하는지 확인하십시오. SSLSOCK을 반환합니다. }알 수 있듯이 SSL 커뮤니케이션을 위해. 먼저 정상적인 소켓 연결을 설정 한 다음 SSL 핸드 셰이크를 수행 한 다음 인증서와 도메인 이름의 일관성을 확인하십시오. 후속 작업은 sslsocketimpl을 통해 통신하는 것입니다. 프로토콜 세부 사항은 SSLSocketImpl 클래스에 반영되지만 코드 JDK 의이 부분은 오픈 소스가 아닙니다. 관심있는 사람들은 해당 OpenJDK 소스 코드를 다운로드하여 분석을 계속할 수 있습니다.
5.이 기사의 요약
좋아, 위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.