5.4. 웹 서비스 클라이언트 보안
5.4.1. 개요
기본 Camel CXF 프록시 데모에서 Web 서비스 클라이언트는 실제로 src/test
디렉터리에 JUnit 테스트로 구현됩니다. 즉, mvn test
를 사용하여 클라이언트를 쉽게 실행할 수 있습니다. 클라이언트에서 SSL/TLS 보안을 활성화하기 위해 테스트 클라이언트의 Java 구현은 완전히 교체되고 SSL/TLS 구성이 포함된 Spring 파일이 src/test/resources/META-INF/spring
디렉터리에 추가됩니다. 클라이언트를 설정하기 위해 수행해야 하는 단계를 설명하기 전에 이 섹션에서는 클라이언트의 Java 코드 및 Spring 구성에 대한 몇 가지 세부 사항을 설명합니다.
5.4.2. 암시적 구성
엔드포인트 주소의 URL 스키마를 https
로 변경하는 것 외에도 클라이언트 프록시에서 SSL/TLS 보안을 활성화하는 대부분의 구성은 Spring 구성의 http:conduit
요소에 포함됩니다. 그러나 이 구성이 클라이언트 프록시에 적용되는 방식은 다음과 같은 이유로 혼란스러울 수 있습니다. http:conduit
요소는 클라이언트 프록시를 명시적으로 참조하지 않으며 클라이언트 프록시는 http:conduit
요소를 명시적으로 참조하지 않습니다. http:conduit
요소와 클라이언트 프록시 간의 연결은 모두 클라이언트 프록시 Implicitly configured by http:conduit 에 설명된 대로 동일한 WSDL 포트를 참조하도록 암시적으로 설정됩니다.
클라이언트 프록시 Implicitly configured by http:conduit
Element
클라이언트 프록시와 http:conduit
요소 간의 연결은 다음과 같이 설정됩니다.
-
클라이언트는
http:conduit
요소가 포함된 Spring 구성 파일을 로드하고 구문 분석합니다. -
http:conduit
8080이 생성되면 레지스트리에 해당 항목이 생성되고, 해당 항목은 지정된 WSDL 포트 이름 아래에 빈에 대한 참조를 저장합니다(이름이 QName 형식으로 저장되어 있음). -
Cryostat-WS 클라이언트 프록시가 생성되면 레지스트리를 검사하여 프록시의 WSDL 포트 이름과 연결된
http:conduit
entries를 찾을 수 있는지 확인합니다. 이러한 빈을 찾으면 구성 세부 정보를 프록시에 자동으로 삽입합니다.
5.4.3. 클라이언트 측에 필요한 인증서
클라이언트는 src/main/resources/certs
디렉터리의 clientKeystore.jks
키 저장소 파일로 구성됩니다. 이 키 저장소에는 다음과 같이 두 개의 항목이 포함되어 있습니다.
- 신뢰할 수 있는 인증서 항목
- 서버 인증서와 클라이언트 인증서 모두를 발행하고 서명한 CA 인증서가 포함된 신뢰할 수 있는 인증서 항목입니다.
- 개인 키 항목
- 클라이언트 자체 X.509 인증서 및 개인 키가 포함된 개인 키 항목입니다. 실제로 이 인증서는 현재 예제를 실행하는 데 반드시 필요한 것은 아닙니다. 서버에 TLS 핸드셰이크 중에 인증서를 전송할 필요가 없기 때문입니다( 예 5.2. “httpj:engine-factory Element with SSL/TLS enabled”참조).
5.4.4. 클라이언트에 Spring 정의 로드
예제 클라이언트는 Spring 컨테이너에 직접 배포되지 않지만 보안 HTTP conduit를 정의하려면 일부 Spring 정의가 필요합니다. 그렇다면 Spring 컨테이너 없이 Spring 정의를 생성하려면 어떻게 해야 합니까? org.apache.cxf.bus.spring.SpringBusFactory
클래스를 사용하여 Java 기반 클라이언트에 Spring 정의를 쉽게 읽을 수 있습니다.
다음 코드는 META-INF/spring/cxf-client.xml
파일에서 Spring 정의를 읽고 해당 정의를 통합하는 Apache CXF Bus 오브젝트를 생성하는 방법을 보여줍니다.
// Java import org.apache.cxf.bus.spring.SpringBusFactory; ... protected void startCxfBus() throws Exception { bf = new SpringBusFactory(); Bus bus = bf.createBus("META-INF/spring/cxf-client.xml"); bf.setDefaultBus(bus); }
5.4.5. 클라이언트 프록시 생성
기본적으로 WSDL 프록시를 생성하는 방법에는 여러 가지가 있습니다. WSDL 파일의 내용을 기반으로 프록시를 생성하기 위해 Cryostat-WS API를 사용하여 WSDL 파일 없이 프록시를 생성할 수 있습니다. 또는 Apache CXF 관련 클래스인 JaxWsProxyFactoryBean
을 사용하여 프록시를 생성할 수 있습니다.
이 SSL/TLS 클라이언트의 경우 가장 편리한 방법은 다음 Java 샘플과 같이 WSDL 파일을 사용하지 않고 프록시를 생성하도록 하는 것입니다.
// Java import javax.xml.ws.Service; import org.apache.camel.example.reportincident.ReportIncidentEndpoint; ... // create the webservice client and send the request Service s = Service.create(SERVICE_NAME); s.addPort( PORT_NAME, "http://schemas.xmlsoap.org/soap/", ADDRESS_URL ); ReportIncidentEndpoint client = s.getPort(PORT_NAME, ReportIncidentEndpoint.class);
이 예제에서는 JaxWsProxyFactoryBean
접근 방식을 사용하여 프록시를 생성할 수 없습니다. 이러한 방식으로 생성된 프록시는 Spring 구성 파일에 지정된 HTTP conduit 설정을 찾지 못하기 때문입니다.
SERVICE_NAME
및 PORT_NAME
상수는 각각 예 5.1. “ReportIncidentEndpointService WSDL Service” 에 정의된 WSDL 서비스 및 WSDL 포트의 QNames입니다. ADDRESS_URL
문자열에는 프록시 웹 서비스 주소와 동일한 값이 있으며 다음과 같이 정의됩니다.
private static final String ADDRESS_URL = "https://localhost:9080/camel-example-cxf-proxy/webservices/incident";
특히 주소는 SSL/TLS를 통해 HTTP를 선택하는 URL 체계 https
를 사용하여 정의해야 합니다.
5.4.6. 클라이언트에 SSL/TLS 보안을 추가하는 단계
SSL/TLS 보안이 활성화된 Cryostat-WS 클라이언트를 정의하려면 다음 단계를 수행합니다.
5.4.7. Java 클라이언트를 테스트 케이스로 생성
예 5.3. “ReportIncidentRoutesTest Java 클라이언트” JUnit 테스트 케이스로 구현된 Java 클라이언트의 전체 코드를 표시합니다. 이 클라이언트는 예제/camel-example-cxf-proxy
데모의 src/test/java/org/apache/camel/example/reportincident
하위 디렉터리에 있는 ReportIncidentRoutesTest.java
.java를 대체합니다.
CamelInstallDir/examples/camel-example-cxf-proxy
데모에 클라이언트를 추가하려면 src/test/java/org/apache/camel/example/reportincident
sub-directory로 이동하여 기존 ReportIncidentRoutesTest.java
파일을 백업 위치로 이동한 다음 새 ReportIncidentRoutesTest.java
파일을 생성합니다. 예 5.3. “ReportIncidentRoutesTest Java 클라이언트”
예 5.3. ReportIncidentRoutesTest Java 클라이언트
// Java package org.apache.camel.example.reportincident; import org.apache.camel.spring.Main; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.junit.Test; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; import org.apache.cxf.Bus; import org.apache.cxf.bus.spring.SpringBusFactory; import org.apache.camel.example.reportincident.ReportIncidentEndpoint; import org.apache.camel.example.reportincident.ReportIncidentEndpointService; import static org.junit.Assert.assertEquals; /** * Unit test of our routes */ public class ReportIncidentRoutesTest { private static final QName SERVICE_NAME = new QName("http://reportincident.example.camel.apache.org", "ReportIncidentEndpointService"); private static final QName PORT_NAME = new QName("http://reportincident.example.camel.apache.org", "ReportIncidentEndpoint"); private static final String WSDL_URL = "file:src/main/resources/etc/report_incident.wsdl"; // should be the same address as we have in our route private static final String ADDRESS_URL = "https://localhost:9080/camel-example-cxf-proxy/webservices/incident"; protected SpringBusFactory bf; protected void startCxfBus() throws Exception { bf = new SpringBusFactory(); Bus bus = bf.createBus("META-INF/spring/cxf-client.xml"); bf.setDefaultBus(bus); } @Test public void testRendportIncident() throws Exception { startCxfBus(); runTest(); } protected void runTest() throws Exception { // create input parameter InputReportIncident input = new InputReportIncident(); input.setIncidentId("123"); input.setIncidentDate("2008-08-18"); input.setGivenName("Claus"); input.setFamilyName("Ibsen"); input.setSummary("Bla"); input.setDetails("Bla bla"); input.setEmail("davsclaus@apache.org"); input.setPhone("0045 2962 7576"); // create the webservice client and send the request Service s = Service.create(SERVICE_NAME); s.addPort(PORT_NAME, "http://schemas.xmlsoap.org/soap/", ADDRESS_URL); ReportIncidentEndpoint client = s.getPort(PORT_NAME, ReportIncidentEndpoint.class); OutputReportIncident out = client.reportIncident(input); // assert we got a OK back assertEquals("OK;456", out.getCode()); } }
5.4.8. Spring 구성에 http:conduit 요소 추가
예 5.4. “HTTP:conduit Element with SSL/TLS enabled” ReportIncidentEndpoint
WSDL 포트에 대한 http:conduit
요소를 정의하는 Spring 구성을 표시합니다. http:conduit
요소는 지정된 WSDL 포트를 사용하는 모든 클라이언트 프록시에 대해 SSL/TLS 보안을 사용하도록 구성되어 있습니다.
클라이언트 테스트 케이스에 Spring 구성을 추가하려면 src/test/resources/META-INF/spring
하위 디렉토리를 만들고 즐겨 찾는 텍스트 편집기를 사용하여 cxf-client.xml
을 생성한 다음 예 5.4. “HTTP:conduit Element with SSL/TLS enabled” 내용을 파일에 붙여넣습니다.
예 5.4. HTTP:conduit Element with SSL/TLS enabled
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://camel.apache.org/schema/cxf" xmlns:sec="http://cxf.apache.org/configuration/security" xmlns:http="http://cxf.apache.org/transports/http/configuration" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd "> <http:conduit name="{http://reportincident.example.camel.apache.org}ReportIncidentEndpoint.http-conduit"> <http:tlsClientParameters disableCNCheck="true" secureSocketProtocol="TLSv1"> <sec:keyManagers keyPassword="ckpass"> <sec:keyStore password="cspass" type="JKS" resource="certs/clientKeystore.jks" /> </sec:keyManagers> <sec:trustManagers> <sec:keyStore password="cspass" type="JKS" resource="certs/clientKeystore.jks" /> </sec:trustManagers> <sec:cipherSuitesFilter> <sec:include>.*_WITH_3DES_.*</sec:include> <sec:include>.*_WITH_DES_.*</sec:include> <sec:exclude>.*WITH_NULL.</sec:exclude>* <sec:exclude>.*DH_anon.</sec:exclude>* </sec:cipherSuitesFilter> </http:tlsClientParameters> </http:conduit> </beans>
이전 구성에 대해서는 다음 사항에 유의하십시오.
-
http:
및sec:
namespace 접두사는http:conduit
요소를 정의하는 데 필요합니다.xsi:schemaLocation
요소에서는 해당http://cxf.apache.org/configuration/security
및http://cxf.apache.org/transports/http/configuration
네임스페이스의 위치를 지정해야 합니다. http:tlsClientParameters
요소의disableCNCheck
속성은true
로 설정됩니다. 즉, 클라이언트는 서버의 X.509 인증서의 일반 이름이 서버 호스트 이름과 일치하는지 확인하지 않습니다. 자세한 내용은 부록 A. 인증서 관리 에서 참조하십시오.중요프로덕션 배포에서는 CN 검사를 비활성화하는 것이 권장되지 않습니다.
sec:keystore
요소에서 인증서 위치는 classpath에서 인증서를 찾는resource
특성을 사용하여 지정됩니다. Maven이 테스트를 실행하면 classpath에서src/main/resources
의 콘텐츠를 자동으로 사용할 수 있으므로src/main/resources/certs
디렉터리에서 인증서를 읽을 수 있습니다.참고파일 시스템에서 보는
file
속성을 사용하여 인증서 위치를 지정하는 옵션도 있습니다. 그러나resource
속성은 번들에 패키지된 애플리케이션에서 사용하기에 더 적합합니다.sec:cipherSuitesFilter
요소는 일치하는 암호화 제품군을 제외하도록 구성됩니다.** 및 .*
. 이러한 암호화 제품군은 효과적으로 불완전하며 일반 용도로는 사용되지 않습니다.DH_ anon.\*
.* .*중요일치하는 암호를 항상 제외 하는 것이 좋습니다.* 및
.*
.DH_ anon.\*
.* .*-
서버 프로토콜과 일치하고 SSLv3 프로토콜을 사용하지 않도록
secureSocketProtocol
속성을 TLSv1로 설정하고POODLE 보안 취약점 (CVE-2014-3566)을 사용해야 합니다.
5.4.9. 클라이언트 실행
클라이언트는 테스트 사례로 정의되므로 표준 Maven 테스트 목표를 사용하여 클라이언트를 실행할 수 있습니다. 클라이언트를 실행하려면 새 명령 창을 열고 CamelInstallDir/examples/camel-example-cxf-proxy
로 디렉토리를 변경하고 다음 Maven 명령을 입력합니다.
mvn test
테스트가 성공적으로 실행되면 OSGi 콘솔 창에 다음 출력이 표시됩니다.
Incident was 123, changed to 456 Invoked real web service: id=456 by Claus Ibsen