package at.gv.egovernment.moa.spss.server.invoke; import iaik.IAIKException; import iaik.IAIKRuntimeException; import iaik.ixsil.exceptions.URIException; import iaik.ixsil.util.URI; import iaik.server.modules.xml.DataObject; import iaik.server.modules.xml.XMLDataObject; import iaik.server.modules.xml.XMLSignature; import iaik.server.modules.xmlsign.XMLConstants; import iaik.server.modules.xmlverify.DsigManifest; import iaik.server.modules.xmlverify.ReferenceData; import iaik.server.modules.xmlverify.SecurityLayerManifest; import iaik.server.modules.xmlverify.XMLSignatureVerificationModule; import iaik.server.modules.xmlverify.XMLSignatureVerificationModuleFactory; import iaik.server.modules.xmlverify.XMLSignatureVerificationProfile; import iaik.server.modules.xmlverify.XMLSignatureVerificationResult; import iaik.x509.X509Certificate; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.w3c.dom.Element; import org.w3c.dom.Node; import at.gv.egovernment.moa.logging.LogMsg; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.logging.LoggingContext; import at.gv.egovernment.moa.logging.LoggingContextManager; import at.gv.egovernment.moa.spss.MOAApplicationException; import at.gv.egovernment.moa.spss.MOAException; import at.gv.egovernment.moa.spss.MOASystemException; import at.gv.egovernment.moa.spss.api.SPSSFactory; import at.gv.egovernment.moa.spss.api.common.CheckResult; import at.gv.egovernment.moa.spss.api.common.XMLDataObjectAssociation; import at.gv.egovernment.moa.spss.api.xmlverify.ReferenceInfo; import at.gv.egovernment.moa.spss.api.xmlverify.ReferencesCheckResult; import at.gv.egovernment.moa.spss.api.xmlverify.ReferencesCheckResultInfo; import at.gv.egovernment.moa.spss.api.xmlverify.SupplementProfileExplicit; import at.gv.egovernment.moa.spss.api.xmlverify.TransformParameter; import at.gv.egovernment.moa.spss.api.xmlverify.TransformParameterHash; import at.gv.egovernment.moa.spss.api.xmlverify.VerifySignatureLocation; import at.gv.egovernment.moa.spss.api.xmlverify.VerifyTransformsInfoProfileExplicit; import at.gv.egovernment.moa.spss.api.xmlverify.VerifyXMLSignatureRequest; import at.gv.egovernment.moa.spss.api.xmlverify.VerifyXMLSignatureResponse; import at.gv.egovernment.moa.spss.server.config.ConfigurationProvider; import at.gv.egovernment.moa.spss.server.config.TrustProfile; import at.gv.egovernment.moa.spss.server.iaik.xml.XMLSignatureImpl; import at.gv.egovernment.moa.spss.server.logging.IaikLog; import at.gv.egovernment.moa.spss.server.logging.TransactionId; import at.gv.egovernment.moa.spss.server.transaction.TransactionContext; import at.gv.egovernment.moa.spss.server.transaction.TransactionContextManager; import at.gv.egovernment.moa.spss.util.MessageProvider; import at.gv.egovernment.moa.util.CollectionUtils; import at.gv.egovernment.moa.util.Constants; /** * A class providing a DOM based interface to the * XMLSignatureVerificationModule. * * This class performs the invocation of the * iaik.server.modules.xmlverify.XMLSignatureVerificationModule * from a VerifyXMLSignatureRequest given as a DOM element. The * result of the invocation is integrated into a * VerifyXMLSignatureResponse and returned. * * @author Patrick Peck * @version $Id$ */ public class XMLSignatureVerificationInvoker { /** The single instance of this class. */ private static XMLSignatureVerificationInvoker instance = null; private static Set FILTERED_REF_TYPES; static { FILTERED_REF_TYPES = new HashSet(); FILTERED_REF_TYPES.add(DsigManifest.XML_DSIG_MANIFEST_TYPE); FILTERED_REF_TYPES.add(SecurityLayerManifest.SECURITY_LAYER_MANIFEST_TYPE); FILTERED_REF_TYPES.add( SecurityLayerManifest.SECURITY_LAYER_MANIFEST_TYPE_OLD); FILTERED_REF_TYPES.add( XMLConstants.NAMESPACE_ETSI_STRING + "SignedProperties"); } /** * Get the single instance of this class. * * @return The single instance of this class. */ public static synchronized XMLSignatureVerificationInvoker getInstance() { if (instance == null) { instance = new XMLSignatureVerificationInvoker(); } return instance; } /** * Create a new XMLSignatureCreationInvoker. * * Protected to disallow multiple instances. */ protected XMLSignatureVerificationInvoker() { } /** * Process the VerifyXMLSignatureRequest message and invoke the * XMLSignatureVerificationModule. * * @param request A VerifyXMLSignatureRequest API object * containing the data for verifying an XML signature. * @return A VerifyXMLSignatureResponse containing the * answert to the VerifyXMLSignatureRequest. * MOA schema definition. * @throws MOAException An error occurred during signature verification. */ public VerifyXMLSignatureResponse verifyXMLSignature(VerifyXMLSignatureRequest request) throws MOAException { TransactionContext context = TransactionContextManager.getInstance().getTransactionContext(); LoggingContext loggingCtx = LoggingContextManager.getInstance().getLoggingContext(); XMLSignatureVerificationProfileFactory profileFactory = new XMLSignatureVerificationProfileFactory(request); VerifyXMLSignatureResponseBuilder responseBuilder = new VerifyXMLSignatureResponseBuilder(); XMLSignatureVerificationResult result; XMLSignatureVerificationProfile profile; ReferencesCheckResult signatureManifestCheck; DataObjectFactory dataObjFactory; XMLDataObject signatureEnvironment; Node signatureEnvironmentParent = null; Element requestElement = null; XMLSignature xmlSignature; Date signingTime; List supplements; List dataObjectList; // get the supplements supplements = getSupplements(request); // build XMLSignature dataObjFactory = DataObjectFactory.getInstance(); signatureEnvironment = dataObjFactory.createSignatureEnvironment( request.getSignatureInfo().getVerifySignatureEnvironment(), supplements); xmlSignature = buildXMLSignature(signatureEnvironment, request); // build the list of DataObjects dataObjectList = buildDataObjectList(supplements); // build profile profile = profileFactory.createProfile(); // get the signingTime signingTime = request.getDateTime(); // make the signature environment the root of the document, if it is not a // separate document anyway; this is done to assure that canonicalization // of the signature environment contains the correct namespace declarations requestElement = signatureEnvironment.getElement().getOwnerDocument().getDocumentElement(); if (requestElement != signatureEnvironment.getElement()) { signatureEnvironmentParent = signatureEnvironment.getElement().getParentNode(); requestElement.getOwnerDocument().replaceChild( signatureEnvironment.getElement(), requestElement); } // verify the signature try { XMLSignatureVerificationModule module = XMLSignatureVerificationModuleFactory.getInstance(); module.setLog(new IaikLog(loggingCtx.getNodeID())); result = module.verifySignature( xmlSignature, dataObjectList, profile, signingTime, new TransactionId(context.getTransactionID())); } catch (IAIKException e) { MOAException moaException = IaikExceptionMapper.getInstance().map(e); throw moaException; } catch (IAIKRuntimeException e) { MOAException moaException = IaikExceptionMapper.getInstance().map(e); throw moaException; } // swap back in the request as root document if (requestElement != signatureEnvironment.getElement()) { requestElement.getOwnerDocument().replaceChild( requestElement, signatureEnvironment.getElement()); signatureEnvironmentParent.appendChild(signatureEnvironment.getElement()); } // check the result signatureManifestCheck = validateSignatureManifest(request, result, profile); // Check if signer certificate is in trust profile's allowed signer certificates pool TrustProfile trustProfile = context.getConfiguration().getTrustProfile(request.getTrustProfileId()); CheckResult certificateCheck = validateSignerCertificate(result, trustProfile); // build the response responseBuilder.setResult(result, profile, signatureManifestCheck, certificateCheck); return responseBuilder.getResponse(); } /** * Checks if the signer certificate matches one of the allowed signer certificates specified * in the provided trustProfile. * * @param result The result produced by the XMLSignatureVerificationModule. * * @param trustProfile The trust profile the signer certificate is validated against. * * @return The overal result of the certificate validation for the signer certificate. * * @throws MOAException if one of the signer certificates specified in the trustProfile * cannot be read from the file system. */ private CheckResult validateSignerCertificate(XMLSignatureVerificationResult result, TrustProfile trustProfile) throws MOAException { MessageProvider msg = MessageProvider.getInstance(); int resultCode = result.getCertificateValidationResult().getValidationResultCode().intValue(); if (resultCode == 0 && trustProfile.getSignerCertsUri() != null) { X509Certificate signerCertificate = (X509Certificate) result.getCertificateValidationResult().getCertificateChain().get(0); File signerCertsDir = null; try { signerCertsDir = new File(new URI(trustProfile.getSignerCertsUri()).getPath()); } catch (URIException e) { throw new MOASystemException("2900", null, e); // Should not happen, already checked at loading the MOA configuration } File[] files = signerCertsDir.listFiles(); if (files == null) resultCode = 1; int i; for (i = 0; i < files.length; i++) { if (!files[i].isDirectory()) { FileInputStream currentFIS = null; try { currentFIS = new FileInputStream(files[i]); } catch (FileNotFoundException e) { throw new MOASystemException("2900", null, e); } try { X509Certificate currentCert = new X509Certificate(currentFIS); currentFIS.close(); if (currentCert.equals(signerCertificate)) break; } catch (Exception e) { // Simply ignore file if it cannot be interpreted as certificate String logMsg = msg.getMessage("invoker.03", new Object[]{trustProfile.getId(), files[i].getName()}); Logger.warn(logMsg); try { currentFIS.close(); } catch (IOException e1) { // If clean-up fails, do nothing } } } } if (i >= files.length) { resultCode = 1; // No signer certificate from the trustprofile pool matches the actual signer certificate } } SPSSFactory factory = SPSSFactory.getInstance(); return factory.createCheckResult(resultCode, null); } /** * Select the dsig:Signature DOM element within the signature * environment. * * @param signatureEnvironment The signature environment containing the * dsig:Signature. * @param request The VerifyXMLSignatureRequest containing the * signature environment. * @return The dsig:Signature element wrapped in a * XMLSignature object. * @throws MOAApplicationException An error occurred locating the * dsig:Signature. */ private XMLSignature buildXMLSignature( XMLDataObject signatureEnvironment, VerifyXMLSignatureRequest request) throws MOAApplicationException { VerifySignatureLocation signatureLocation = request.getSignatureInfo().getVerifySignatureLocation(); Element signatureParent; // evaluate the VerifySignatureLocation to get the signature parent signatureParent = InvokerUtils.evaluateSignatureLocation( signatureEnvironment.getElement(), signatureLocation); // check for signatureParent to be a dsig:Signature element if (!"Signature".equals(signatureParent.getLocalName()) || !Constants.DSIG_NS_URI.equals(signatureParent.getNamespaceURI())) { throw new MOAApplicationException("2266", null); } return new XMLSignatureImpl(signatureParent); } /** * Build the supplemental data objects contained in the * VerifyXMLSignatureRequest. * * @param supplements A List of * XMLDataObjectAssociations containing the supplement data. * @return A List of DataObjects representing the * supplemental data objects. * @throws MOASystemException A system error occurred building one of the data * objects. * @throws MOAApplicationException An error occurred building one of the data * objects. */ private List buildDataObjectList(List supplements) throws MOASystemException, MOAApplicationException { List dataObjectList = new ArrayList(); DataObjectFactory factory = DataObjectFactory.getInstance(); DataObject dataObject; Iterator iter; if (supplements != null) { for (iter = supplements.iterator(); iter.hasNext();) { XMLDataObjectAssociation supplement = (XMLDataObjectAssociation) iter.next(); dataObject = factory.createFromXmlDataObjectAssociation(supplement, true, false); dataObjectList.add(dataObject); } } return dataObjectList; } /** * Get the supplemental data contained in the * VerifyXMLSignatureRequest. * * @param request The VerifyXMLSignatureRequest containing the * supplemental data. * @return A List of XMLDataObjectAssociation * objects containing the supplemental data. * @throws MOAApplicationException An error occurred resolving one of the * supplement profiles. */ private List getSupplements(VerifyXMLSignatureRequest request) throws MOAApplicationException { TransactionContext context = TransactionContextManager.getInstance().getTransactionContext(); ConfigurationProvider config = context.getConfiguration(); List supplementProfiles = request.getSupplementProfiles(); List supplements = new ArrayList(); if (supplementProfiles != null) { List mappedProfiles = ProfileMapper.mapSupplementProfiles(supplementProfiles, config); Iterator iter; for (iter = mappedProfiles.iterator(); iter.hasNext();) { SupplementProfileExplicit profile = (SupplementProfileExplicit) iter.next(); supplements.add(profile.getSupplementProfile()); } } return supplements; } /** * Perform additional validations of the * XMLSignatureVerificationResult. * *

In particular, it is verified that: *

    *
  • Each ReferenceData object contains transformation * chain that matches one of the Transforms given in the * corresponding SignatureManifestCheckParams/ReferenceInfo
  • *
  • The hash values of the TransformParameters are valid. *
  • *
*

* * @param request The VerifyXMLSignatureRequest containing the * signature to verify. * @param result The result produced by * XMLSignatureVerificationModule. * @param profile The profile used for validating the request. * @return The result of additional validations of the signature manifest. * @throws MOAApplicationException Post-validation of the * XMLSignatureVerificaitonResult failed. */ private ReferencesCheckResult validateSignatureManifest( VerifyXMLSignatureRequest request, XMLSignatureVerificationResult result, XMLSignatureVerificationProfile profile) throws MOAApplicationException { SPSSFactory factory = SPSSFactory.getInstance(); MessageProvider msg = MessageProvider.getInstance(); // validate that each ReferenceData object contains transforms specified // in the corresponding SignatureManifestCheckParams/ReferenceInfo if (request.getSignatureManifestCheckParams() != null) { List refInfos = request.getSignatureManifestCheckParams().getReferenceInfos(); List refDatas = filterReferenceInfos(result.getReferenceDataList()); List failedReferencesList = new ArrayList(); Iterator refInfoIter; Iterator refDataIter; if (refInfos.size() != refDatas.size()) { return factory.createReferencesCheckResult(1, null); } refInfoIter = refInfos.iterator(); refDataIter = filterReferenceInfos(result.getReferenceDataList()).iterator(); while (refInfoIter.hasNext()) { ReferenceInfo refInfo = (ReferenceInfo) refInfoIter.next(); ReferenceData refData = (ReferenceData) refDataIter.next(); List transforms = buildTransformsList(refInfo); boolean found = false; Iterator trIter; for (trIter = transforms.iterator(); trIter.hasNext() && !found;) { found = trIter.next().equals(refData.getTransformationList()); } if (!found) { Integer refIndex = new Integer(refData.getReferenceIndex()); String logMsg = msg.getMessage("invoker.01", new Object[] { refIndex }); failedReferencesList.add(refIndex); Logger.debug(new LogMsg(logMsg)); } } if (!failedReferencesList.isEmpty()) { // at least one reference failed - return their indexes and check code 1 int[] failedReferences = CollectionUtils.toIntArray(failedReferencesList); ReferencesCheckResultInfo checkInfo = factory.createReferencesCheckResultInfo(null, failedReferences); return factory.createReferencesCheckResult(1, checkInfo); } } // validate the hashes contained in all the ReferenceInfo objects of the // security layer manifest if (request.getSignatureManifestCheckParams() != null && result.containsSecurityLayerManifest()) { Map hashValues = buildTransformParameterHashValues(request); Set transformParameterURIs = buildTransformParameterURIs(profile.getTransformationSupplements()); List referenceInfoList = result.getSecurityLayerManifest().getReferenceDataList(); Iterator refIter; for (refIter = referenceInfoList.iterator(); refIter.hasNext();) { iaik.server.modules.xmlverify.ReferenceInfo ref = (iaik.server.modules.xmlverify.ReferenceInfo) refIter.next(); byte[] hash = (byte[]) hashValues.get(ref.getURI()); if (!transformParameterURIs.contains(ref.getURI()) || (hash != null && !Arrays.equals(hash, ref.getHashValue()))) { // the transform parameter doesn't exist or the hashs do not match // return the index of the failed reference and check code 1 int[] failedReferences = new int[] { ref.getReferenceIndex()}; ReferencesCheckResultInfo checkInfo = factory.createReferencesCheckResultInfo(null, failedReferences); String logMsg = msg.getMessage( "invoker.02", new Object[] { new Integer(ref.getReferenceIndex())}); Logger.debug(new LogMsg(logMsg)); return factory.createReferencesCheckResult(1, checkInfo); } } } return factory.createReferencesCheckResult(0, null); } /** * Get all Transforms contained in all the * VerifyTransformsInfoProfiles of the given * ReferenceInfo. * * @param refInfo The ReferenceInfo object containing * the transformations. * @return A List of Lists. Each of the * Lists contains Transformation objects. * @throws MOAApplicationException An error occurred building one of the * Transformations. */ private List buildTransformsList(ReferenceInfo refInfo) throws MOAApplicationException { TransactionContext context = TransactionContextManager.getInstance().getTransactionContext(); ConfigurationProvider config = context.getConfiguration(); List profiles = refInfo.getVerifyTransformsInfoProfiles(); List mappedProfiles = ProfileMapper.mapVerifyTransformsInfoProfiles(profiles, config); List transformsList = new ArrayList(); TransformationFactory factory = TransformationFactory.getInstance(); Iterator iter; for (iter = mappedProfiles.iterator(); iter.hasNext();) { VerifyTransformsInfoProfileExplicit profile = (VerifyTransformsInfoProfileExplicit) iter.next(); List transforms = profile.getTransforms(); if (transforms != null) { transformsList.add(factory.createTransformationList(transforms)); } } return transformsList; } /** * Build the Set of all TransformParameter URIs. * * @param transformParameters The List of * TransformParameters, as provided to the verification. * @return The Set of all TransformParameter URIs. */ private Set buildTransformParameterURIs(List transformParameters) { Set uris = new HashSet(); Iterator iter; for (iter = transformParameters.iterator(); iter.hasNext();) { DataObject transformParameter = (DataObject) iter.next(); uris.add(transformParameter.getURI()); } return uris; } /** * Build a mapping between TransformParameter URIs (a * String and dsig:HashValue (a * byte[]). * * @param request The VerifyXMLSignatureRequest. * @return Map The resulting mapping. * @throws MOAApplicationException An error occurred accessing one of * the profiles. */ private Map buildTransformParameterHashValues(VerifyXMLSignatureRequest request) throws MOAApplicationException { TransactionContext context = TransactionContextManager.getInstance().getTransactionContext(); ConfigurationProvider config = context.getConfiguration(); Map hashValues = new HashMap(); List refInfos = request.getSignatureManifestCheckParams().getReferenceInfos(); Iterator refIter; for (refIter = refInfos.iterator(); refIter.hasNext();) { ReferenceInfo refInfo = (ReferenceInfo) refIter.next(); List profiles = refInfo.getVerifyTransformsInfoProfiles(); List mappedProfiles = ProfileMapper.mapVerifyTransformsInfoProfiles(profiles, config); Iterator prIter; for (prIter = mappedProfiles.iterator(); prIter.hasNext();) { VerifyTransformsInfoProfileExplicit profile = (VerifyTransformsInfoProfileExplicit) prIter.next(); List trParameters = profile.getTransformParameters(); Iterator trIter; for (trIter = trParameters.iterator(); trIter.hasNext();) { TransformParameter transformParameter = (TransformParameter) trIter.next(); String uri = transformParameter.getURI(); if (transformParameter.getTransformParameterType() == TransformParameter.HASH_TRANSFORMPARAMETER) { hashValues.put( uri, ((TransformParameterHash) transformParameter).getDigestValue()); } } } } return hashValues; } /** * Filter the ReferenceInfos returned by the * VerifyXMLSignatureResult for comparison with the * ReferenceInfo elements in the request. * * @param referenceInfos The ReferenceInfos from the * VerifyXMLSignatureResult. * @return A List of all ReferenceInfos whose type * is not a XMLDsig manifest, Security Layer manifest, or ETSI signed * property. */ private List filterReferenceInfos(List referenceInfos) { List filtered = new ArrayList(); Iterator iter; for (iter = referenceInfos.iterator(); iter.hasNext();) { iaik.server.modules.xmlverify.ReferenceInfo refInfo = (iaik.server.modules.xmlverify.ReferenceInfo) iter.next(); String refType = refInfo.getReferenceType(); if (refType == null || !FILTERED_REF_TYPES.contains(refType)) { filtered.add(refInfo); } } return filtered; } }