package at.gv.egovernment.moa.spss.server.invoke; 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 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 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.InputData; import at.gv.egovernment.moa.spss.api.common.SignerInfo; import at.gv.egovernment.moa.spss.api.impl.InputDataBinaryImpl; import at.gv.egovernment.moa.spss.api.impl.InputDataXMLImpl; 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; import at.gv.egovernment.moa.util.CollectionUtils; import at.gv.egovernment.moa.util.DOMUtils; import at.gv.egovernment.moa.util.NodeListAdapter; /** * 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. * @param transformsSignatureManifestCheck The overall result for the signature * manifest check. * @param certificateCheck The overall result for the certificate check. * @throws MOAApplicationException An error occurred adding the result. */ public void setResult( XMLSignatureVerificationResult result, XMLSignatureVerificationProfile profile, ReferencesCheckResult transformsSignatureManifestCheck, CheckResult certificateCheck) 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()); // Create HashInputData Content objects referenceDataList = result.getReferenceDataList(); if (profile.includeHashInputData()) { hashInputDatas = new ArrayList(); // Include SignedInfo references addHashInputDatas( hashInputDatas, referenceDataList, InputData.CONTAINER_SIGNEDINFO_, InputData.REFERER_NONE_); // Include XMLDSIGManifest references List xMLDSIGManifests = result.getDsigManifestList(); for (iter = xMLDSIGManifests.iterator(); iter.hasNext();) { DsigManifest currentMF = (DsigManifest) iter.next(); List xMLDSIGMFReferenceDataList = currentMF.getReferenceDataList(); addHashInputDatas( hashInputDatas, xMLDSIGMFReferenceDataList, InputData.CONTAINER_XMLDSIGMANIFEST_, currentMF.getReferringReferenceInfo().getReferenceIndex()); } } // Create the ReferenceInputData Content objects if (profile.includeReferenceInputData()) { referenceInputDatas = new ArrayList(); // Include SignedInfo references addReferenceInputDatas( referenceInputDatas, referenceDataList, InputData.CONTAINER_SIGNEDINFO_, InputData.REFERER_NONE_); // Include XMLDSIGManifest references List xMLDSIGManifests = result.getDsigManifestList(); for (iter = xMLDSIGManifests.iterator(); iter.hasNext();) { DsigManifest currentMF = (DsigManifest) iter.next(); List xMLDSIGMFReferenceDataList = currentMF.getReferenceDataList(); addReferenceInputDatas( referenceInputDatas, xMLDSIGMFReferenceDataList, InputData.CONTAINER_XMLDSIGMANIFEST_, currentMF.getReferringReferenceInfo().getReferenceIndex()); } } // 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.getReferenceDataList()); 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.getReferenceDataList()); manifestCheckResultInfo = factory.createManifestRefsCheckResultInfo( null, failedReferences, refIndex); xmlDsigManifestChecks.add( factory.createManifestRefsCheckResult( dsigManifest.getManifestVerificationResult().intValue(), manifestCheckResultInfo)); } } // create the certificate check this.certificateCheck = certificateCheck; } /** * Adds {@link InputData} entries to the specified inputDatas list. The content of the entry will * be created from {@link ReferenceData#getHashInputData()}. * * @param inputDatas The list to be amended. * * @param referenceDataList The list of {@link ReferenceData} objects to be investigated. * * @param containerType The type of container of the {@link InputData} objects to be created. * * @param refererNumber The number of the referring reference for the {@link InputData} objects to be created. * * @throws MOAApplicationException if creating an {@link InputData} fails. */ private void addHashInputDatas(List inputDatas, List referenceDataList, String containerType, int refererNumber) throws MOAApplicationException { for (Iterator iter = referenceDataList.iterator(); iter.hasNext();) { ReferenceData referenceData = (ReferenceData) iter.next(); inputDatas.add(buildInputData( referenceData.getHashInputData(), containerType, refererNumber)); } } /** * Adds {@link InputData} entries to the specified inputDatas list. The content of the entry will * be created from {@link ReferenceData#getReferenceInputData()}. * * @param inputDatas The list to be amended. * * @param referenceDataList The list of {@link ReferenceData} objects to be investigated. * * @param containerType The type of container of the {@link InputData} objects to be created. * * @param refererNumber The number of the referring reference for the {@link InputData} objects to be created. * * @throws MOAApplicationException if creating an {@link InputData} fails. */ private void addReferenceInputDatas(List inputDatas, List referenceDataList, String containerType, int refererNumber) throws MOAApplicationException { for (Iterator iter = referenceDataList.iterator(); iter.hasNext();) { ReferenceData referenceData = (ReferenceData) iter.next(); inputDatas.add(buildInputData( referenceData.getReferenceInputData(), containerType, refererNumber)); } } /** * Build a InputDataBinaryImpl or an InputDataXMLImpl * object from the given DataObject and the given attributes. * * @param dataObject The DataObject from which to build the result. * Based on the type of this parameter, the type of the result will either be * InputDataBinaryImpl or InputDataXMLImpl. * * @param partof see {@link InputData} * * @param referringReferenceNumber see {@link InputData} * * @return The corresponinding input data implementation. * * @throws MOAApplicationException An error occurred creating the result. */ private Content buildInputData(DataObject dataObject, String partOf, int referringReferenceNumber) throws MOAApplicationException { if (dataObject instanceof BinaryDataObject) { BinaryDataObject binaryData = (BinaryDataObject) dataObject; return new InputDataBinaryImpl( factory.createContent(binaryData.getInputStream(), null), partOf, referringReferenceNumber); } else if (dataObject instanceof XMLDataObject) { XMLDataObject xmlData = (XMLDataObject) dataObject; List nodes = new ArrayList(); nodes.add(xmlData.getElement()); return new InputDataXMLImpl( factory.createContent(new NodeListAdapter(nodes), null), partOf, referringReferenceNumber); } 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 new InputDataXMLImpl( factory.createContent(fragment.getChildNodes(), null), partOf, referringReferenceNumber); } 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 new InputDataBinaryImpl( factory.createContent(is, null), partOf, referringReferenceNumber); } 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; } } }