diff options
Diffstat (limited to 'spss.server/src/at/gv/egovernment/moa/spss/server/invoke/XMLSignatureVerificationInvoker.java')
-rw-r--r-- | spss.server/src/at/gv/egovernment/moa/spss/server/invoke/XMLSignatureVerificationInvoker.java | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/spss.server/src/at/gv/egovernment/moa/spss/server/invoke/XMLSignatureVerificationInvoker.java b/spss.server/src/at/gv/egovernment/moa/spss/server/invoke/XMLSignatureVerificationInvoker.java new file mode 100644 index 000000000..b8af3c02a --- /dev/null +++ b/spss.server/src/at/gv/egovernment/moa/spss/server/invoke/XMLSignatureVerificationInvoker.java @@ -0,0 +1,580 @@ +package at.gv.egovernment.moa.spss.server.invoke; + +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 iaik.IAIKException; +import iaik.IAIKRuntimeException; +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 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.util.CollectionUtils; +import at.gv.egovernment.moa.util.Constants; + +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.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.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; + +/** + * A class providing a DOM based interface to the + * <code>XMLSignatureVerificationModule</code>. + * + * This class performs the invocation of the + * <code>iaik.server.modules.xmlverify.XMLSignatureVerificationModule</code> + * from a <code>VerifyXMLSignatureRequest</code> given as a DOM element. The + * result of the invocation is integrated into a + * <code>VerifyXMLSignatureResponse</code> 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 <code>XMLSignatureCreationInvoker</code>. + * + * Protected to disallow multiple instances. + */ + protected XMLSignatureVerificationInvoker() { + } + + /** + * Process the <code>VerifyXMLSignatureRequest<code> message and invoke the + * <code>XMLSignatureVerificationModule</code>. + * + * @param request A <code>VerifyXMLSignatureRequest<code> API object + * containing the data for verifying an XML signature. + * @return A <code>VerifyXMLSignatureResponse</code> containing the + * answert to the <code>VerifyXMLSignatureRequest</code>. + * 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); + + // build the response + responseBuilder.setResult(result, profile, signatureManifestCheck); + + return responseBuilder.getResponse(); + } + + /** + * Select the <code>dsig:Signature</code> DOM element within the signature + * environment. + * + * @param signatureEnvironment The signature environment containing the + * <code>dsig:Signature</code>. + * @param request The <code>VerifyXMLSignatureRequest</code> containing the + * signature environment. + * @return The <code>dsig:Signature</code> element wrapped in a + * <code>XMLSignature</code> object. + * @throws MOAApplicationException An error occurred locating the + * <code>dsig:Signature</code>. + */ + 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 + * <code>VerifyXMLSignatureRequest</code>. + * + * @param supplements A <code>List</code> of + * <code>XMLDataObjectAssociation</code>s containing the supplement data. + * @return A <code>List</code> of <code>DataObject</code>s 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; + + 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 + * <code>VerifyXMLSignatureRequest</code>. + * + * @param request The <code>VerifyXMLSignatureRequest</code> containing the + * supplemental data. + * @return A <code>List</code> of <code>XMLDataObjectAssociation</code> + * 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(); + + if (supplementProfiles != null) { + List supplements = new ArrayList(); + 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; + } + + return null; + } + + /** + * Perform additional validations of the + * <code>XMLSignatureVerificationResult</code>. + * + * <p> In particular, it is verified that: + * <ul> + * <li>Each <code>ReferenceData</code> object contains transformation + * chain that matches one of the <code>Transforms</code> given in the + * corresponding <code>SignatureManifestCheckParams/ReferenceInfo</code></li> + * <li>The hash values of the <code>TransformParameter</code>s are valid. + * </li> + * </ul> + * </p> + * + * @param request The <code>VerifyXMLSignatureRequest</code> containing the + * signature to verify. + * @param result The result produced by + * <code>XMLSignatureVerificationModule</code>. + * @param profile The profile used for validating the <code>request</code>. + * @return The result of additional validations of the signature manifest. + * @throws MOAApplicationException Post-validation of the + * <code>XMLSignatureVerificaitonResult</code> 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 (profile.checkSecurityLayerManifest()) { + 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 (profile.checkSecurityLayerManifest() + && result.containsSecurityLayerManifest()) { + Map hashValues = buildTransformParameterHashValues(request); + Set transformParameterURIs = + buildTransformParameterURIs(profile.getTransformationSupplements()); + List referenceInfoList = + result.getSecurityLayerManifest().getReferenceInfoList(); + 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 <code>Transform</code>s contained in all the + * <code>VerifyTransformsInfoProfile</code>s of the given + * <code>ReferenceInfo</code>. + * + * @param refInfo The <code>ReferenceInfo</code> object containing + * the transformations. + * @return A <code>List</code> of <code>List</code>s. Each of the + * <code>List</code>s contains <code>Transformation</code> objects. + * @throws MOAApplicationException An error occurred building one of the + * <code>Transformation</code>s. + */ + 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 <code>Set</code> of all <code>TransformParameter</code> URIs. + * + * @param transformParameters The <code>List</code> of + * <code>TransformParameter</code>s, as provided to the verification. + * @return The <code>Set</code> of all <code>TransformParameter</code> 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 <code>TransformParameter</code> URIs (a + * <code>String</code> and <code>dsig:HashValue</code> (a + * <code>byte[]</code>). + * + * @param request The <code>VerifyXMLSignatureRequest</code>. + * @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 <code>ReferenceInfo</code>s returned by the + * <code>VerifyXMLSignatureResult</code> for comparison with the + * <code>ReferenceInfo</code> elements in the request. + * + * @param referenceInfos The <code>ReferenceInfo</code>s from the + * <code>VerifyXMLSignatureResult</code>. + * @return A <code>List</code> of all <code>ReferenceInfo</code>s 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; + } + +} |