/*
* Copyright 2003 Federal Chancellery Austria
* MOA-SPSS has been developed in a cooperation between BRZ, the Federal
* Chancellery Austria - ICT staff unit, and Graz University of Technology.
*
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
* the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
* http://www.osor.eu/eupl/
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*
* This product combines work with different licenses. See the "NOTICE" text
* file for details on the various modules and licenses.
* The "NOTICE" text file is part of the distribution. Any derivative works
* that you distribute must include a readable copy of the "NOTICE" text file.
*/
package at.gv.egovernment.moa.spss.server.invoke;
import iaik.server.modules.AdESFormVerificationResult;
import iaik.server.modules.AdESVerificationResult;
import iaik.server.modules.IAIKException;
import iaik.server.modules.IAIKRuntimeException;
import iaik.server.modules.SignatureVerificationProfile;
import iaik.server.modules.cmsverify.CMSSignatureVerificationModule;
import iaik.server.modules.cmsverify.CMSSignatureVerificationModuleFactory;
import iaik.server.modules.cmsverify.CMSSignatureVerificationProfile;
import iaik.server.modules.cmsverify.CMSSignatureVerificationResult;
import iaik.server.modules.cmsverify.ExtendedCMSSignatureVerificationResult;
import iaik.x509.X509Certificate;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.HexDump;
import org.apache.commons.io.IOUtils;
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.spss.MOAApplicationException;
import at.gv.egovernment.moa.spss.MOAException;
import at.gv.egovernment.moa.spss.api.cmsverify.CMSContent;
import at.gv.egovernment.moa.spss.api.cmsverify.CMSContentExcplicit;
import at.gv.egovernment.moa.spss.api.cmsverify.CMSContentReference;
import at.gv.egovernment.moa.spss.api.cmsverify.CMSDataObject;
import at.gv.egovernment.moa.spss.api.cmsverify.VerifyCMSSignatureRequest;
import at.gv.egovernment.moa.spss.api.cmsverify.VerifyCMSSignatureResponse;
import at.gv.egovernment.moa.spss.api.impl.AdESFormResultsImpl;
import at.gv.egovernment.moa.spss.server.config.TrustProfile;
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.CertificateUtils;
import at.gv.egovernment.moa.spss.util.QCSSCDResult;
/**
* A class providing an interface to the
* CMSSignatureVerificationModule
.
*
* This class performs the invocation of the
* iaik.server.modules.cmsverify.CMSSignatureVerificationModule
* from a VerifyCMSSignatureRequest
. The result of the invocation
* is integrated into a VerifyCMSSignatureResponse
returned.
*
* @author Patrick Peck
* @version $Id$
*/
public class CMSSignatureVerificationInvoker {
/** The single instance of this class. */
private static CMSSignatureVerificationInvoker instance = null;
/**
* Return the only instance of this class.
*
* @return The only instance of this class.
*/
public static synchronized CMSSignatureVerificationInvoker getInstance() {
if (instance == null) {
instance = new CMSSignatureVerificationInvoker();
}
return instance;
}
/**
* Create a new CMSSignatureVerificationInvoker
.
*
* Protected to disallow multiple instances.
*/
protected CMSSignatureVerificationInvoker() {
}
/**
* Verify a CMS signature.
*
* @param request The VerifyCMSSignatureRequest
containing the
* CMS signature, as well as additional data needed for verification.
* @return Element A VerifyCMSSignatureResponse
containing the
* answer to the VerifyCMSSignatureRequest
.
* @throws MOAException An error occurred while processing the request.
*/
public VerifyCMSSignatureResponse verifyCMSSignature(VerifyCMSSignatureRequest request)
throws MOAException {
CMSSignatureVerificationProfileFactory profileFactory =
new CMSSignatureVerificationProfileFactory(request);
VerifyCMSSignatureResponseBuilder responseBuilder =
new VerifyCMSSignatureResponseBuilder();
TransactionContext context =
TransactionContextManager.getInstance().getTransactionContext();
LoggingContext loggingCtx =
LoggingContextManager.getInstance().getLoggingContext();
InputStream signature;
InputStream signedContent = null;
CMSSignatureVerificationProfile profile;
Date signingTime;
List results;
ExtendedCMSSignatureVerificationResult result;
int[] signatories;
InputStream input;
byte[] buf = new byte[2048];
// get the signature
signature = request.getCMSSignature();
// get the actual trustprofile
TrustProfile trustProfile = context.getConfiguration().getTrustProfile(request.getTrustProfileId());
try {
// get the signed content
signedContent = getSignedContent(request);
// build the profile
if(request.isPDF()) {
profile = profileFactory.createPDFProfile();
} else {
profile = profileFactory.createProfile();
}
// get the signing time
signingTime = request.getDateTime();
// verify the signature
CMSSignatureVerificationModule module =
CMSSignatureVerificationModuleFactory.getInstance();
module.setLog(new IaikLog(loggingCtx.getNodeID()));
module.init(
signature,
signedContent,
profile,
new TransactionId(context.getTransactionID()));
input = module.getInputStream();
while (input.read(buf) > 0);
//results = module.verifyCAdESSignature(signingTime);
results = module.verifySignature(signingTime);
} catch (IAIKException e) {
MOAException moaException = IaikExceptionMapper.getInstance().map(e);
throw moaException;
} catch (IAIKRuntimeException e) {
MOAException moaException = IaikExceptionMapper.getInstance().map(e);
throw moaException;
} catch (IOException e) {
throw new MOAApplicationException("2244", null, e);
} catch (MOAException e)
{
throw e;
}
finally
{
try
{
if (signedContent != null) signedContent.close();
}
catch (Throwable t)
{
// Intentionally do nothing here
}
}
QCSSCDResult qcsscdresult = new QCSSCDResult();
// build the response: for each signatory add the result to the response
signatories = request.getSignatories();
if (signatories == VerifyCMSSignatureRequest.ALL_SIGNATORIES) {
Iterator resultIter;
for (resultIter = results.iterator(); resultIter.hasNext();) {
Object resultObject = resultIter.next();
CMSSignatureVerificationResult cmsResult = null;
List adesResults = null;
if(resultObject instanceof ExtendedCMSSignatureVerificationResult) {
result = (ExtendedCMSSignatureVerificationResult) resultObject;
adesResults = getAdESResult(result.getFormVerificationResult());
if (adesResults != null) {
Iterator adesIterator = adesResults.iterator();
while (adesIterator.hasNext()) {
Logger.info("ADES Formresults: " + adesIterator.next().toString());
}
}
} else {
cmsResult = (CMSSignatureVerificationResult)resultObject;
}
String issuerCountryCode = null;
// QC/SSCD check
List list = cmsResult.getCertificateValidationResult().getCertificateChain();
if (list != null) {
X509Certificate[] chain = new X509Certificate[list.size()];
Iterator it = list.iterator();
int i = 0;
while(it.hasNext()) {
chain[i] = (X509Certificate)it.next();
i++;
}
qcsscdresult = CertificateUtils.checkQCSSCD(chain, trustProfile.isTSLEnabled());
// get signer certificate issuer country code
issuerCountryCode = CertificateUtils.getIssuerCountry((X509Certificate)list.get(0));
}
responseBuilder.addResult(cmsResult, trustProfile, qcsscdresult.isQC(), qcsscdresult.isQCSourceTSL(), qcsscdresult.isSSCD(), qcsscdresult.isSSCDSourceTSL(), issuerCountryCode, adesResults);
}
} else {
int i;
for (i = 0; i < signatories.length; i++) {
int sigIndex = signatories[i] - 1;
try {
result =
(ExtendedCMSSignatureVerificationResult) results.get(signatories[i] - 1);
String issuerCountryCode = null;
CMSSignatureVerificationResult cmsResult = result.getCMSSignatureVerificationResult();
List adesResults = getAdESResult(result.getFormVerificationResult());
if (adesResults != null) {
Iterator adesIterator = adesResults.iterator();
while (adesIterator.hasNext()) {
Logger.info("ADES Formresults: " + adesIterator.next().toString());
}
}
// QC/SSCD check
List list = cmsResult.getCertificateValidationResult().getCertificateChain();
if (list != null) {
X509Certificate[] chain = new X509Certificate[list.size()];
Iterator it = list.iterator();
int j = 0;
while(it.hasNext()) {
chain[j] = (X509Certificate)it.next();
j++;
}
qcsscdresult = CertificateUtils.checkQCSSCD(chain, trustProfile.isTSLEnabled());
issuerCountryCode = CertificateUtils.getIssuerCountry((X509Certificate)list.get(0));
}
responseBuilder.addResult(cmsResult, trustProfile, qcsscdresult.isQC(), qcsscdresult.isQCSourceTSL(), qcsscdresult.isSSCD(), qcsscdresult.isSSCDSourceTSL(), issuerCountryCode, adesResults);
} catch (IndexOutOfBoundsException e) {
throw new MOAApplicationException(
"2249",
new Object[] { new Integer(sigIndex)});
}
}
}
return responseBuilder.getResponse();
}
/**
* Get the signed content contained either in the request itself or given as a
* reference to external data.
*
* @param request The VerifyCMSSignatureRequest
containing the
* signed content (or the reference to the signed content).
* @return InputStream A stream providing the signed content data, or
* null
if no signed content was provided with the request.
* @throws MOAApplicationException An error occurred building the stream.
*/
private InputStream getSignedContent(VerifyCMSSignatureRequest request)
throws MOAApplicationException {
InputStream is = null;
CMSDataObject dataObj;
CMSContent content;
// select the Content element
dataObj = request.getDataObject();
if (dataObj == null) {
return null;
}
content = dataObj.getContent();
// build the content data
switch (content.getContentType()) {
case CMSContent.EXPLICIT_CONTENT :
is = ((CMSContentExcplicit) content).getBinaryContent();
is = excludeByteRange(is, request);
return is;
case CMSContent.REFERENCE_CONTENT :
String reference = ((CMSContentReference) content).getReference();
if (!"".equals(reference)) {
ExternalURIResolver resolver = new ExternalURIResolver();
is = resolver.resolve(reference);
is = excludeByteRange(is, request);
return is;
} else {
return null;
}
default :
return null;
}
}
private InputStream excludeByteRange(InputStream contentIs, VerifyCMSSignatureRequest request) throws MOAApplicationException {
int byteRead;
ByteArrayOutputStream contentOs = new ByteArrayOutputStream();
CMSDataObject dataobject = request.getDataObject();
BigDecimal from = dataobject.getExcludeByteRangeFrom();
BigDecimal to = dataobject.getExcludeByteRangeTo();
if ( (from == null) || (to == null))
return contentIs;
BigDecimal counter = new BigDecimal("0");
BigDecimal one = new BigDecimal("1");
try {
while ((byteRead=contentIs.read()) >= 0) {
if (inRange(counter, dataobject)) {
// if byte is in byte range, set byte to 0x00
contentOs.write(0);
}
else
contentOs.write(byteRead);
counter = counter.add(one);
}
InputStream is = new ByteArrayInputStream(contentOs.toByteArray());
return is;
} catch (IOException e) {
throw new MOAApplicationException("2301", null, e);
}
}
private boolean inRange(BigDecimal counter, CMSDataObject dataobject) {
BigDecimal from = dataobject.getExcludeByteRangeFrom();
BigDecimal to = dataobject.getExcludeByteRangeTo();
if ( (from == null) || (to == null))
return false;
int compare = counter.compareTo(from);
if (compare == -1)
return false;
else {
compare = counter.compareTo(to);
if (compare == 1)
return false;
else
return true;
}
}
private List getAdESResult(AdESFormVerificationResult adesFormVerification) {
if (adesFormVerification == null) {
// no form information
return null;
}
List adesList = new ArrayList();
checkSubResult(adesFormVerification.getSubResult(SignatureVerificationProfile.LEVEL_LTA),
SignatureVerificationProfile.LEVEL_LTA, adesList);
checkSubResult(adesFormVerification.getSubResult(SignatureVerificationProfile.LEVEL_LT),
SignatureVerificationProfile.LEVEL_LT, adesList);
checkSubResult(adesFormVerification.getSubResult(SignatureVerificationProfile.LEVEL_T),
SignatureVerificationProfile.LEVEL_T, adesList);
checkSubResult(adesFormVerification.getSubResult(SignatureVerificationProfile.LEVEL_B),
SignatureVerificationProfile.LEVEL_B, adesList);
return adesList;
}
private void checkSubResult(AdESVerificationResult subResult, String level, List adesList) {
if (subResult != null) {
Logger.info("Checking Level: " + level);
try {
AdESFormResultsImpl adESFormResultsImpl = new AdESFormResultsImpl();
adESFormResultsImpl.setCode(subResult.getResultCode());
adESFormResultsImpl.setInfo(subResult.getInfo());
adESFormResultsImpl.setName(subResult.getName());
adesList.add(adESFormResultsImpl);
} catch (NullPointerException e) {
Logger.warn("Catching NullPointer Exception, of invalid? Form Results", e);
}
}
}
}