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.
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 SignatureManifest references
if (result.containsSecurityLayerManifest())
{
List sigMFReferenceDataList = result.getSecurityLayerManifest().getReferenceDataList();
addHashInputDatas(
hashInputDatas,
sigMFReferenceDataList,
InputData.CONTAINER_SIGNATUREMANIFEST_,
result.getSecurityLayerManifest().getReferringReferenceInfo().getReferenceIndex());
}
// 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 SignatureManifest references
if (result.containsSecurityLayerManifest())
{
List sigMFReferenceDataList = result.getSecurityLayerManifest().getReferenceDataList();
addReferenceInputDatas(
referenceInputDatas,
sigMFReferenceDataList,
InputData.CONTAINER_SIGNATUREMANIFEST_,
result.getSecurityLayerManifest().getReferringReferenceInfo().getReferenceIndex());
}
// 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;
}
}
}