package at.gv.egovernment.moa.spss.server.invoke; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.w3c.dom.DocumentFragment; import org.w3c.dom.NodeList; import iaik.ixsil.algorithms.CanonicalizationAlgorithm; import iaik.ixsil.algorithms.CanonicalizationAlgorithmImplExclusiveCanonicalXMLWithComments; import iaik.server.modules.xml.BinaryDataObject; import iaik.server.modules.xml.DataObject; import iaik.server.modules.xml.XMLDataObject; import iaik.server.modules.xml.XMLNodeListDataObject; import iaik.server.modules.xmlverify.CertificateValidationResult; import iaik.server.modules.xmlverify.DsigManifest; import iaik.server.modules.xmlverify.HashUnavailableException; import iaik.server.modules.xmlverify.ReferenceData; import iaik.server.modules.xmlverify.ReferenceInfo; import iaik.server.modules.xmlverify.SecurityLayerManifest; import iaik.server.modules.xmlverify.XMLSignatureVerificationProfile; import iaik.server.modules.xmlverify.XMLSignatureVerificationResult; import iaik.x509.X509Certificate; import at.gv.egovernment.moa.util.CollectionUtils; import at.gv.egovernment.moa.util.DOMUtils; import at.gv.egovernment.moa.util.NodeListAdapter; import at.gv.egovernment.moa.spss.MOAApplicationException; 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.Content; import at.gv.egovernment.moa.spss.api.common.SignerInfo; import at.gv.egovernment.moa.spss.api.xmlverify.ManifestRefsCheckResultInfo; 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.VerifyXMLSignatureResponse; /** * A class to build a VerifyXMLSignatureResponse object. * *

Via a call to addResult() the only result of the * signature verification must be added.

* *

The getResponseElement() method then returns the * VerifyXMLSignatureResponse built so far.

* * @author Patrick Peck * @version $Id$ */ public class VerifyXMLSignatureResponseBuilder { /** The SPSSFactory for creating API objects. */ private SPSSFactory factory = SPSSFactory.getInstance(); /** Information about the signer certificate. */ private SignerInfo signerInfo; /** The hash input data. */ private List hashInputDatas; /** The reference input data. */ private List referenceInputDatas; /** The result of the signature check. */ private ReferencesCheckResult signatureCheck; /** The result of the signature manifest check. */ private ReferencesCheckResult signatureManifestCheck; /** The result of the XMLDsig manifest check. */ private List xmlDsigManifestChecks; /** The result of the certificate check. */ private CheckResult certificateCheck; /** * Get the VerifyMLSignatureResponse built so far. * * @return The VerifyXMLSignatureResponse built so far. */ public VerifyXMLSignatureResponse getResponse() { return factory.createVerifyXMLSignatureResponse( signerInfo, hashInputDatas, referenceInputDatas, signatureCheck, signatureManifestCheck, xmlDsigManifestChecks, certificateCheck); } /** * Sets the verification result to the response. * * This method must be called exactly once to ensure a valid * VerifyXMLSignatureResponse. * * @param result The result to set for the response. * @param profile The profile used for verifying the signature. * @throws MOAApplicationException An error occurred adding the result. */ public void setResult( XMLSignatureVerificationResult result, XMLSignatureVerificationProfile profile, ReferencesCheckResult transformsSignatureManifestCheck) throws MOAApplicationException { CertificateValidationResult certResult = result.getCertificateValidationResult(); List referenceDataList; ReferenceData referenceData; List dsigManifestList; ReferencesCheckResultInfo checkResultInfo; int[] failedReferences; Iterator iter; // create the SignerInfo; signerInfo = factory.createSignerInfo( (X509Certificate) certResult.getCertificateChain().get(0), certResult.isQualifiedCertificate(), certResult.isPublicAuthorityCertificate(), certResult.getPublicAuthorityID()); // add HashInputData Content objects referenceDataList = result.getReferenceDataList(); if (profile.includeHashInputData()) { hashInputDatas = new ArrayList(); for (iter = referenceDataList.iterator(); iter.hasNext();) { referenceData = (ReferenceData) iter.next(); hashInputDatas.add(buildContent(referenceData.getHashInputData())); } } // create the ReferenceInputData Content objects if (profile.includeReferenceInputData()) { referenceInputDatas = new ArrayList(); for (iter = referenceDataList.iterator(); iter.hasNext();) { referenceData = (ReferenceData) iter.next(); referenceInputDatas.add( buildContent(referenceData.getReferenceInputData())); } } // create the signature check failedReferences = buildFailedReferences(result.getReferenceDataList()); checkResultInfo = failedReferences != null ? factory.createReferencesCheckResultInfo(null, failedReferences) : null; signatureCheck = factory.createReferencesCheckResult( result.getSignatureValueVerificationCode().intValue(), checkResultInfo); // create the signature manifest check if (profile.checkSecurityLayerManifest()) { if (transformsSignatureManifestCheck.getCode() == 1) { // checking the transforms failed signatureManifestCheck = transformsSignatureManifestCheck; } else if (result.isSecurityLayerManifestRequired()) { if (!result.containsSecurityLayerManifest()) { // required security layer manifest is missing in signature signatureManifestCheck = factory.createReferencesCheckResult(2, null); } else { // security layer manifest exists, so we have to check its validity SecurityLayerManifest slManifest = result.getSecurityLayerManifest(); int verificationResult = slManifest.getManifestVerificationResult().intValue(); if (SecurityLayerManifest.CODE_MANIFEST_VALID.intValue() == verificationResult) { // security layer manifest exists and is free of errors signatureManifestCheck = factory.createReferencesCheckResult(0, null); } else { // security layer manifest exists, but has errors failedReferences = buildFailedReferences(slManifest.getReferenceInfoList()); checkResultInfo = (failedReferences != null) ? factory.createReferencesCheckResultInfo(null, failedReferences) : null; if (SecurityLayerManifest.CODE_MANIFEST_INCOMPLETE.intValue() == verificationResult) { signatureManifestCheck = factory.createReferencesCheckResult(3, checkResultInfo); } else if (SecurityLayerManifest.CODE_REFERENCE_HASH_INVALID.intValue() == verificationResult) { signatureManifestCheck = factory.createReferencesCheckResult(4, checkResultInfo); } else { // Should not happen throw new RuntimeException("Unexpected result from security layer manifest verification."); } } } } else { // no security layer manifest is required, so the signature manifest check is ok signatureManifestCheck = factory.createReferencesCheckResult(0, null); } } // create the xmlDsigManifestCheck if (profile.checkXMLDsigManifests()) { xmlDsigManifestChecks = new ArrayList(); dsigManifestList = result.getDsigManifestList(); for (iter = dsigManifestList.iterator(); iter.hasNext();) { DsigManifest dsigManifest = (DsigManifest) iter.next(); int refIndex = dsigManifest.getReferringReferenceInfo().getReferenceIndex(); ManifestRefsCheckResultInfo manifestCheckResultInfo; failedReferences = buildFailedReferences(dsigManifest.getReferenceInfoList()); manifestCheckResultInfo = factory.createManifestRefsCheckResultInfo( null, failedReferences, refIndex); xmlDsigManifestChecks.add( factory.createManifestRefsCheckResult( dsigManifest.getManifestVerificationResult().intValue(), manifestCheckResultInfo)); } } // create the certificate check certificateCheck = factory.createCheckResult( certResult.getValidationResultCode().intValue(), null); } /** * Build a Content object from the given DataObject. * * @param dataObject The DataObject from which to build the * Content. Based on the type of this parameter, the type of * Content will either be XML_CONTENT or * BINARY_CONTENT. * @return The Content object containing the data. * @throws MOAApplicationException An error occurred adding the content. */ private Content buildContent(DataObject dataObject) throws MOAApplicationException { if (dataObject instanceof BinaryDataObject) { BinaryDataObject binaryData = (BinaryDataObject) dataObject; return factory.createContent(binaryData.getInputStream(), null); } else if (dataObject instanceof XMLDataObject) { XMLDataObject xmlData = (XMLDataObject) dataObject; List nodes = new ArrayList(); nodes.add(xmlData.getElement()); return factory.createContent(new NodeListAdapter(nodes), null); } else { // dataObject instanceof XMLNodeListDataObject // if the data in the NodeList can be converted back to valid XML, // write it as XMLContent; otherwise, write it as Base64Content XMLNodeListDataObject nodeData = (XMLNodeListDataObject) dataObject; NodeList nodes = nodeData.getNodeList(); if (DOMUtils.checkAttributeParentsInNodeList(nodes)) { // insert as XMLContent try { DocumentFragment fragment = DOMUtils.nodeList2DocumentFragment(nodes); return factory.createContent(fragment.getChildNodes(), null); } catch (Exception e) { // not successful -> fall through to the Base64Content } } // insert canonicalized NodeList as binary content try { CanonicalizationAlgorithm c14n = new CanonicalizationAlgorithmImplExclusiveCanonicalXMLWithComments(); InputStream is; c14n.setInput(nodes); is = c14n.canonicalize(); return factory.createContent(is, null); } catch (Exception e) { throw new MOAApplicationException("2200", null); } } } /** * Build the failed references. * * Failed references are references for which the isHashValid() * method returns false. * * @param refInfos A List containing the * ReferenceInfo objects to be checked. * @return The indexes of the failed references. */ private int[] buildFailedReferences(List refInfos) { List failedReferencesList = new ArrayList(); int i; // find out the failed references for (i = 0; i < refInfos.size(); i++) { ReferenceInfo refInfo = (ReferenceInfo) refInfos.get(i); try { if (refInfo.isHashCalculated() && !refInfo.isHashValid()) { failedReferencesList.add(new Integer(i + 1)); } } catch (HashUnavailableException e) { // nothing to do here because we called refInfo.isHashCalculated first } } // convert to an int array if (failedReferencesList.isEmpty()) { return null; } else { int[] failedReferences = CollectionUtils.toIntArray(failedReferencesList); return failedReferences; } } }