/* * Copyright 2011 by Graz University of Technology, Austria * MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint * initiative of the Federal Chancellery Austria and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egiz.bku.slcommands.impl; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Scanner; import javax.xml.crypto.MarshalException; import javax.xml.crypto.URIReferenceException; import javax.xml.crypto.dsig.XMLSignatureException; import org.apache.commons.configuration.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSSerializer; import at.buergerkarte.namespaces.securitylayer._1_2_3.CreateXMLSignatureRequestType; import at.buergerkarte.namespaces.securitylayer._1_2_3.DataObjectInfoType; import at.gv.egiz.bku.binding.HTTPBindingProcessorImpl; import at.gv.egiz.bku.conf.MoccaConfigurationFacade; import at.gv.egiz.bku.slcommands.CreateXMLSignatureCommand; import at.gv.egiz.bku.slcommands.SLCommandContext; import at.gv.egiz.bku.slcommands.SLResult; import at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory; import at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactoryImpl; import at.gv.egiz.bku.slcommands.impl.xsect.IdValueFactory; import at.gv.egiz.bku.slcommands.impl.xsect.IdValueFactoryImpl; import at.gv.egiz.bku.slcommands.impl.xsect.Signature; import at.gv.egiz.bku.slexceptions.SLCommandException; import at.gv.egiz.bku.slexceptions.SLException; import at.gv.egiz.bku.slexceptions.SLRequestException; import at.gv.egiz.bku.slexceptions.SLViewerException; import at.gv.egiz.dom.DOMUtils; import at.gv.egiz.stal.InfoboxReadRequest; import at.gv.egiz.stal.STALRequest; /** * This class implements the security layer command * CreateXMLSignatureRequest. * * @author mcentner */ public class CreateXMLSignatureCommandImpl extends SLCommandImpl implements CreateXMLSignatureCommand { /** * Logging facility. */ private final static Logger log = LoggerFactory.getLogger(CreateXMLSignatureCommandImpl.class); /** * The signing certificate. */ protected X509Certificate signingCertificate; /** * The keybox identifier of the key used for signing. */ protected String keyboxIdentifier; /** * The to-be signed signature. */ protected Signature signature; /** * The configuration facade used to access the MOCCA configuration. */ private ConfigurationFacade configurationFacade = new ConfigurationFacade(); private class ConfigurationFacade implements MoccaConfigurationFacade { private Configuration configuration; public static final String USE_STRONG_HASH = "UseStrongHash"; public static final String USE_XADES_1_4 = HTTPBindingProcessorImpl.ConfigurationFacade.USE_XADES_1_4; public static final String USE_XADES_1_4_BLACKLIST = HTTPBindingProcessorImpl.ConfigurationFacade.USE_XADES_1_4_BLACKLIST; public static final String XADES_1_4_BLACKLIST_URL = HTTPBindingProcessorImpl.ConfigurationFacade.XADES_1_4_BLACKLIST_URL; public static final int XADES_1_4_BLACKLIST_EXPIRY = HTTPBindingProcessorImpl.ConfigurationFacade.XADES_1_4_BLACKLIST_EXPIRY; public void setConfiguration(Configuration configuration) { this.configuration = configuration; } public boolean getUseStrongHash() { return configuration.getBoolean(USE_STRONG_HASH, true); } public boolean getUseXAdES14() { return configuration.getBoolean(USE_XADES_1_4, true); } public boolean getUseXAdES14Blacklist() { return configuration.getBoolean(USE_XADES_1_4_BLACKLIST, true); } } private static long XADES_1_4_BLACKLIST_TS; private static final List XADES_1_4_BLACKLIST; static { XADES_1_4_BLACKLIST = new ArrayList(); loadXAdES14Blacklist(); } private static void loadXAdES14Blacklist() { XADES_1_4_BLACKLIST_TS = System.currentTimeMillis(); XADES_1_4_BLACKLIST.clear(); try { URLConnection blc = new URL(ConfigurationFacade.XADES_1_4_BLACKLIST_URL).openConnection(); blc.setUseCaches(false); InputStream in = blc.getInputStream(); Scanner s = new Scanner(in); while (s.hasNext()){ XADES_1_4_BLACKLIST.add(s.next()); } s.close(); } catch (Exception e) { log.error("Blacklist load error", e); } } private static boolean matchesXAdES14Blacklist(String url) { log.debug("Checking DataURL against XAdES14 blacklist: {}", url); if ((System.currentTimeMillis() - XADES_1_4_BLACKLIST_TS) > (ConfigurationFacade.XADES_1_4_BLACKLIST_EXPIRY * 1000)) { log.debug("Updating XAdES14 blacklist"); loadXAdES14Blacklist(); } if (url != null) { for (String bl_entry : XADES_1_4_BLACKLIST) { if (url.matches(bl_entry)) { log.debug("XAdES14 blacklist match"); return true; } } } return false; } public void setConfiguration(Configuration configuration) { configurationFacade.setConfiguration(configuration); } @Override public void prepareXMLSignature(SLCommandContext commandContext) throws SLCommandException, SLRequestException { CreateXMLSignatureRequestType request = getRequestValue(); // TODO: make configurable? IdValueFactory idValueFactory = new IdValueFactoryImpl(); // TODO: make configurable? AlgorithmMethodFactory algorithmMethodFactory; try { algorithmMethodFactory = new AlgorithmMethodFactoryImpl( signingCertificate, configurationFacade.getUseStrongHash()); } catch (NoSuchAlgorithmException e) { log.error("Failed to get DigestMethod.", e); throw new SLCommandException(4006); } boolean useXAdES14 = configurationFacade.getUseXAdES14(); if (useXAdES14 && configurationFacade.getUseXAdES14Blacklist()) { String dataURL = commandContext.getDataURL(); if (matchesXAdES14Blacklist(dataURL)) useXAdES14 = false; } signature = new Signature(commandContext.getURLDereferencer(), idValueFactory, algorithmMethodFactory, useXAdES14); // SigningTime signature.setSigningTime(new Date()); // SigningCertificate signature.setSignerCertificate(signingCertificate); // SignatureInfo if (request.getSignatureInfo() != null) { signature.setSignatureInfo(request.getSignatureInfo()); } // DataObjects for (DataObjectInfoType dataObjectInfo : request.getDataObjectInfo()) { signature.addDataObject(dataObjectInfo); } signature.buildXMLSignature(); } /** * Gets the signing certificate from STAL. * @param commandContext TODO * * @throws SLCommandException * if getting the singing certificate fails */ private void getSigningCertificate(SLCommandContext commandContext) throws SLCommandException { CreateXMLSignatureRequestType request = getRequestValue(); keyboxIdentifier = request.getKeyboxIdentifier(); InfoboxReadRequest stalRequest = new InfoboxReadRequest(); stalRequest.setInfoboxIdentifier(keyboxIdentifier); STALHelper stalHelper = new STALHelper(commandContext.getSTAL()); stalHelper.transmitSTALRequest(Collections.singletonList((STALRequest) stalRequest)); List certificates = stalHelper.getCertificatesFromResponses(); if (certificates == null || certificates.size() != 1) { log.info("Got an unexpected number of certificates from STAL."); throw new SLCommandException(4000); } signingCertificate = certificates.get(0); } /** * Signs the signature. * @param commandContext TODO * * @throws SLCommandException * if signing the signature fails * @throws SLViewerException */ private void signXMLSignature(SLCommandContext commandContext) throws SLCommandException, SLViewerException { try { signature.sign(commandContext.getSTAL(), keyboxIdentifier); } catch (MarshalException e) { log.error("Failed to marshall XMLSignature.", e); throw new SLCommandException(4000); } catch (XMLSignatureException e) { if (e.getCause() instanceof URIReferenceException) { URIReferenceException uriReferenceException = (URIReferenceException) e .getCause(); if (uriReferenceException.getCause() instanceof SLCommandException) { throw (SLCommandException) uriReferenceException.getCause(); } } log.error("Failed to sign XMLSignature.", e); throw new SLCommandException(4000); } } @Override public SLResult execute(SLCommandContext commandContext) { try { // get certificate in order to select appropriate algorithms for hashing // and signing log.info("Requesting signing certificate."); getSigningCertificate(commandContext); if (log.isDebugEnabled()) { log.debug("Got signing certificate. {}", signingCertificate); } else { log.info("Got signing certificate."); } // prepare the XMLSignature for signing log.info("Preparing XML signature."); prepareXMLSignature(commandContext); // sign the XMLSignature log.info("Signing XML signature."); signXMLSignature(commandContext); if (log.isDebugEnabled()) { DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS(); LSSerializer serializer = domImplLS.createLSSerializer(); String debugString = serializer.writeToString(signature.getDocument()); log.debug(debugString); } else { log.info("XML signature signed."); } return new CreateXMLSignatureResultImpl(signature.getDocument()); } catch (SLException e) { return new ErrorResultImpl(e, commandContext.getLocale()); } } @Override public String getName() { return "CreateXMLSignatureRequest"; } }