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.
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;
}
}
}