/*
* Copyright 2011 Federal Chancellery Austria and
* Graz University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package at.gv.util.client.moaspss;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.soap.SOAPBinding;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.xpath.XPathAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import at.gv.util.DOMUtils;
import at.gv.util.LaxHostNameVerifier;
import at.gv.util.LoggingHandler;
import at.gv.util.MiscUtil;
import at.gv.util.config.EgovUtilConfiguration;
import at.gv.util.ex.EgovUtilException;
import at.gv.util.wsdl.SignatureCreationService;
import at.gv.util.wsdl.SignatureVerificationService;
import at.gv.util.xsd.moaspss.CreateXMLSignatureRequest;
import at.gv.util.xsd.moaspss.CreateXMLSignatureResponseType;
import at.gv.util.xsd.moaspss.ErrorResponseType;
import at.gv.util.xsd.moaspss.VerifyCMSSignatureResponseType;
import at.gv.util.xsd.moaspss.VerifyXMLSignatureRequestType;
import at.gv.util.xsd.moaspss.VerifyXMLSignatureResponseType;
/**
* MOA-SS client.
*
* @author Arne Tauber
*
*/
public class MOASPSSClient {
private static Logger log = LoggerFactory.getLogger(MOASPSSClient.class);
private EgovUtilConfiguration config = null;
public MOASPSSClient(EgovUtilConfiguration config) {
if (config == null) {
throw new NullPointerException("Argument 'config' must not be null.");
}
this.config = config;
}
public MOASPSSClient() {
}
@SuppressWarnings("unchecked")
public Element sendSignatureCreationRequest(String serviceURL,
InputStream signatureCreationRequest) throws MOASPSSClientException {
log.debug("Creating XML signature using raw CreateXMLSignatureRequest.");
// check for arguments
if (serviceURL == null) {
throw new NullPointerException("Argument 'serviceURL' must not be null.");
}
if (signatureCreationRequest == null) {
throw new NullPointerException(
"Argument 'signatureRequest' must not be null.");
}
try {
log.trace("MOA-SS signature service URL: " + serviceURL);
URL url = MOASPSSClient.class.getResource("/wsdl/MOA-SPSS-1.3.wsdl");
SignatureCreationService service = new SignatureCreationService(
url,
new QName(
"http://reference.e-government.gv.at/namespace/moa/20020822#moa.wsdl",
"SignatureCreationService"));
QName qname = new QName(
"http://localhost:8080/moa-spss/services/SignatureCreation",
"CreateXMLSignatureRequest");
service.addPort(qname, SOAPBinding.SOAP11HTTP_BINDING, serviceURL);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
MiscUtil.copyStream(signatureCreationRequest, bos);
Source source = new StreamSource(new ByteArrayInputStream(
bos.toByteArray()));
Dispatch dispatch = service.createDispatch(qname, Source.class,
Service.Mode.PAYLOAD);
BindingProvider bindingProvider = (BindingProvider) dispatch;
log.trace("Adding JAX-WS request/response trace handler.");
List handlerList = bindingProvider.getBinding()
.getHandlerChain();
if (handlerList == null) {
handlerList = new ArrayList();
}
LoggingHandler loggingHandler = new LoggingHandler();
handlerList.add(loggingHandler);
bindingProvider.getBinding().setHandlerChain(handlerList);
// initialize ssl
Map requestContext = bindingProvider.getRequestContext();
if (serviceURL.toLowerCase().startsWith("https")) {
log.trace("Using ssl for MOA-SP request.");
if (this.config == null) {
throw new MOASPSSClientException(
"SSL requires client to be configured.");
}
SSLContext sslContext = this.config.getMOASPSSsslConfiguration()
.getSSLContext(false);
if (sslContext == null) {
throw new MOASPSSClientException(
"SSL context from configuration is empty. Please configure an SSL context in the configuration first.");
}
Client client = ClientProxy.getClient(dispatch);
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(36000);
httpClientPolicy.setAllowChunking(false);
httpClientPolicy.setReceiveTimeout(32000);
http.setClient(httpClientPolicy);
TLSClientParameters tlsParams = new TLSClientParameters();
tlsParams.setSSLSocketFactory(sslContext.getSocketFactory());
// check for lax hostname
if (this.config.getMOASPSSsslConfiguration().useLaxHostNameVerifier()) {
log.trace("LaxHostnameVerifier enabled. This setting is not recommended to use.");
tlsParams.setHostnameVerifier(new LaxHostNameVerifier());
}
http.setTlsClientParameters(tlsParams );
}
log.trace("Invoking MOA-SS signature creation service.");
Source response = dispatch.invoke(source);
log.trace("Parsing MOA-SS response.");
byte[] moaResponse = MiscUtil.sourceToByteArray(response);
JAXBContext ctx = JAXBContext.newInstance(CreateXMLSignatureRequest.class
.getPackage().getName());
JAXBElement jaxbElement = (JAXBElement) ctx
.createUnmarshaller()
.unmarshal(new ByteArrayInputStream(moaResponse));
CreateXMLSignatureResponseType createXMLResponse = jaxbElement.getValue();
for (Object obj : createXMLResponse
.getSignatureEnvironmentOrErrorResponse()) {
if (obj instanceof ErrorResponseType) {
ErrorResponseType errorResponse = (ErrorResponseType) obj;
log.trace("Could not create signature: "
+ errorResponse.getErrorCode() + "/" + errorResponse.getInfo());
throw new MOASPSSClientException("MOA-SS signature error: "
+ errorResponse.getErrorCode() + "/" + errorResponse.getInfo());
}
}
log.trace(new String(moaResponse));
log.trace("Signature successfully created. Extracting from MOA-SS container.");
// ok, we have success
Document doc = MiscUtil.parseDocument(new ByteArrayInputStream(
moaResponse));
String xpathExpression = "/moa:CreateXMLSignatureResponse/moa:SignatureEnvironment/child::*";
Element nsNode = doc.createElement("NsNode");
nsNode.setAttribute("xmlns:moa", doc.getDocumentElement()
.getNamespaceURI());
log.trace("Selecting signed doc " + xpathExpression);
Element documentNode = (Element) XPathAPI.selectSingleNode(doc,
xpathExpression, nsNode);
log.trace("Signed document: " + DOMUtils.serializeNode(documentNode));
return documentNode;
} catch (TransformerException e) {
throw new MOASPSSClientException(e);
} catch (IOException e) {
throw new MOASPSSClientException(e);
} catch (JAXBException e) {
throw new MOASPSSClientException(e);
} catch (EgovUtilException e) {
throw new MOASPSSClientException(e);
}
}
public VerifyCMSSignatureResponseType sendCMSSignatureVerificationRequest(
String serviceURL, InputStream signatureVerificationRequest)
throws MOASPSSClientException {
log.debug("Verifying CMS signature using raw VerifyXMLSignatureRequest.");
try {
byte[] moaResponse = sendBasicSignatureVerificationRequest(serviceURL, signatureVerificationRequest);
JAXBContext ctx = JAXBContext
.newInstance(VerifyCMSSignatureResponseType.class.getPackage()
.getName());
JAXBElement jaxbElement = (JAXBElement) ctx
.createUnmarshaller()
.unmarshal(new ByteArrayInputStream(moaResponse));
VerifyCMSSignatureResponseType verifyXMLResponse = jaxbElement.getValue();
log.trace(new String(moaResponse));
return verifyXMLResponse;
} catch (JAXBException e) {
throw new MOASPSSClientException(e);
}
}
@SuppressWarnings("unchecked")
public VerifyXMLSignatureResponseType sendSignatureVerificationRequest(
String serviceURL, InputStream signatureVerificationRequest)
throws MOASPSSClientException {
log.debug("Verifying XML signature using raw VerifyXMLSignatureRequest.");
try {
byte[] moaResponse = sendBasicSignatureVerificationRequest(serviceURL, signatureVerificationRequest);
JAXBContext ctx = JAXBContext
.newInstance(VerifyXMLSignatureRequestType.class.getPackage()
.getName());
JAXBElement jaxbElement = (JAXBElement) ctx
.createUnmarshaller()
.unmarshal(new ByteArrayInputStream(moaResponse));
VerifyXMLSignatureResponseType verifyXMLResponse = jaxbElement.getValue();
log.trace(new String(moaResponse));
return verifyXMLResponse;
} catch (JAXBException e) {
throw new MOASPSSClientException(e);
}
}
private byte[] sendBasicSignatureVerificationRequest(String serviceURL, InputStream signatureVerificationRequest)
throws MOASPSSClientException {
// check for arguments
if (serviceURL == null) {
throw new NullPointerException("Argument 'serviceURL' must not be null.");
}
if (signatureVerificationRequest == null) {
throw new NullPointerException(
"Argument 'signatureRequest' must not be null.");
}
try {
log.trace("MOA-SP verification service URL: " + serviceURL);
URL url = MOASPSSClient.class.getResource("/wsdl/MOA-SPSS-1.3.wsdl");
SignatureVerificationService service = new SignatureVerificationService(
url,
new QName(
"http://reference.e-government.gv.at/namespace/moa/20020822#moa.wsdl",
"SignatureCreationService"));
QName qname = new QName(
"http://localhost:8080/moa-spss/services/SignatureVerification",
"VerifyXMLSignatureRequest");
service.addPort(qname, SOAPBinding.SOAP11HTTP_BINDING, serviceURL);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
MiscUtil.copyStream(signatureVerificationRequest, bos);
Source source = new StreamSource(new ByteArrayInputStream(
bos.toByteArray()));
Dispatch dispatch = service.createDispatch(qname, Source.class,
Service.Mode.PAYLOAD);
BindingProvider bindingProvider = (BindingProvider) dispatch;
log.trace("Adding JAX-WS request/response trace handler.");
List handlerList = bindingProvider.getBinding()
.getHandlerChain();
if (handlerList == null) {
handlerList = new ArrayList();
}
LoggingHandler loggingHandler = new LoggingHandler();
handlerList.add(loggingHandler);
bindingProvider.getBinding().setHandlerChain(handlerList);
// initialize ssl
Map requestContext = bindingProvider.getRequestContext();
if (serviceURL.toLowerCase().startsWith("https")) {
log.trace("Using ssl for MOA-SP request.");
if (this.config == null) {
throw new MOASPSSClientException(
"SSL requires client to be configured.");
}
SSLContext sslContext = this.config.getMOASPSSsslConfiguration()
.getSSLContext(false);
if (sslContext == null) {
throw new MOASPSSClientException(
"SSL context from configuration is empty. Please configure an SSL context in the configuration first.");
}
Client client = ClientProxy.getClient(dispatch);
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(36000);
httpClientPolicy.setAllowChunking(false);
httpClientPolicy.setReceiveTimeout(32000);
http.setClient(httpClientPolicy);
TLSClientParameters tlsParams = new TLSClientParameters();
tlsParams.setSSLSocketFactory(sslContext.getSocketFactory());
// check for lax hostname
if (this.config.getMOASPSSsslConfiguration().useLaxHostNameVerifier()) {
log.trace("LaxHostnameVerifier enabled. This setting is not recommended to use.");
tlsParams.setHostnameVerifier(new LaxHostNameVerifier());
}
http.setTlsClientParameters(tlsParams );
}
log.trace("Invoking MOA-SP signature verification service.");
Source response = dispatch.invoke(source);
log.trace("Parsing MOA-SP response.");
byte[] moaResponse = MiscUtil.sourceToByteArray(response);
return moaResponse;
} catch (TransformerException e) {
throw new MOASPSSClientException(e);
} catch (IOException e) {
throw new MOASPSSClientException(e);
} catch (EgovUtilException e) {
throw new MOASPSSClientException(e);
}
}
public static boolean isSuccess(VerifyXMLSignatureResponseType verifyResult) {
if (verifyResult == null) {
throw new NullPointerException(
"Argument 'verifyResult' must not be null.");
}
log.trace("Checking for signature verification result.");
int signatureCheckCode = verifyResult.getSignatureCheck().getCode()
.intValue();
int signtaureManifestCheckCode = verifyResult.getSignatureManifestCheck()
.getCode().intValue();
int certificateCheckCode = verifyResult.getCertificateCheck().getCode()
.intValue();
log.trace("Signature check code: " + signatureCheckCode);
log.trace("Signature manifest check code: " + signtaureManifestCheckCode);
log.trace("Certificate check code: " + certificateCheckCode);
return signatureCheckCode == 0 && signtaureManifestCheckCode == 0
&& certificateCheckCode == 0;
}
}