/*
* 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 java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
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.common.ExtendedCertificateCheckResult;
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.AdESResultUtils;
import at.gv.egovernment.moa.spss.util.CertificateUtils;
import at.gv.egovernment.moa.spss.util.QCSSCDResult;
import at.gv.egovernment.moaspss.logging.Logger;
import at.gv.egovernment.moaspss.logging.LoggingContext;
import at.gv.egovernment.moaspss.logging.LoggingContextManager;
import iaik.server.ConfigurationException;
import iaik.server.modules.AdESConstants;
import iaik.server.modules.AdESFormVerificationResult;
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.server.modules.pdfverify.ExtendedPDFSignatureVerificationResult;
import iaik.server.modules.pdfverify.PDFSignatureVerificationModule;
import iaik.server.modules.pdfverify.PDFSignatureVerificationProfile;
import iaik.server.modules.pdfverify.PDFSignatureVerificationResult;
import iaik.x509.X509Certificate;
/**
* 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;
Date signingTime;
List results;
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 signing time
signingTime = request.getDateTime();
// build the profile
if (request.isPDF()) {
PDFSignatureVerificationProfile profile = profileFactory.createPDFProfile();
Logger.info("Sending PDFSignatureVerificationProfile to IAIK-MOA");
PDFSignatureVerificationModule module = iaik.server.modules.pdfverify.PDFSignatureVerificationModuleFactory
.getInstance();
module.setLog(new IaikLog(loggingCtx.getNodeID()));
//Logger.info(" Available: " + signature.available());
module.init(signature, profile, new TransactionId(context.getTransactionID()));
// input = module.getInputStream();
// while (input.read(buf) > 0);
if(request.isExtended()) {
Logger.info("Running extended validation");
results = module.verifyPAdESSignature(signingTime);
} else {
Logger.info("Running not extended validation");
results = module.verifySignature(signingTime);
}
} else {
// get the signed content
signedContent = getSignedContent(request);
CMSSignatureVerificationProfile profile = profileFactory.createProfile();
Logger.info("Sending CMSSignatureVerificationProfile to IAIK-MOA");
// 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)
;
if(request.isExtended()) {
Logger.info("Running extended validation");
results = module.verifyCAdESSignature(signingTime);
} else {
Logger.info("Running not extended validation");
results = module.verifySignature(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();
if (!request.isPDF()) {
handleCMSResult(resultObject, responseBuilder, trustProfile);
} else {
handlePDFResult(resultObject, responseBuilder, trustProfile);
}
}
} else {
int i;
for (i = 0; i < signatories.length; i++) {
int sigIndex = signatories[i] - 1;
try {
Object resultObject = results.get(signatories[i] - 1);
if (!request.isPDF()) {
handleCMSResult(resultObject, responseBuilder, trustProfile);
} else {
handlePDFResult(resultObject, responseBuilder, trustProfile);
}
} catch (IndexOutOfBoundsException e) {
throw new MOAApplicationException("2249", new Object[] { new Integer(sigIndex) });
}
}
}
return responseBuilder.getResponse();
}
private void handleCMSResult(Object resultObject, VerifyCMSSignatureResponseBuilder responseBuilder,
TrustProfile trustProfile) throws MOAException {
QCSSCDResult qcsscdresult = new QCSSCDResult();
if(resultObject == null) {
Logger.warn("Result Object is null!");
return;
}
CMSSignatureVerificationResult cmsResult = null;
List adesResults = null;
ExtendedCertificateCheckResult extCheckResult = null;
if (resultObject instanceof ExtendedCMSSignatureVerificationResult) {
Logger.info("Got ExtendedCMSSignatureVerificationResult");
ExtendedCMSSignatureVerificationResult result = (ExtendedCMSSignatureVerificationResult) resultObject;
cmsResult = result.getCMSSignatureVerificationResult();
adesResults = AdESResultUtils.getAdESResult(result.getFormVerificationResult());
if (adesResults != null) {
Iterator adesIterator = adesResults.iterator();
while (adesIterator.hasNext()) {
Logger.info("ADES Formresults: " + adesIterator.next().toString());
}
}
try {
//Logger.info("Extended Validation Report: " + result.getName());
Logger.info("Extended Validation Code: " + result.getResultCode().toString());
Logger.info("Extended Validation Info: " + result.getInfo());
extCheckResult = AdESResultUtils.getExtendedResult(result.getResultCode());
} catch (ConfigurationException e) {
Logger.warn("Cannot generate Extendend Result. Check SVA Configuration!", e);
} catch (NullPointerException e) {
Logger.info("No extendend validation result available.");
}
} else {
Logger.info("Got CMSSignatureVerificationResult");
cmsResult = (CMSSignatureVerificationResult) resultObject;
}
String issuerCountryCode = null;
// QC/SSCD check
if(cmsResult.getCertificateValidationResult() != null) {
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, extCheckResult);
}
private void handlePDFResult(Object resultObject, VerifyCMSSignatureResponseBuilder responseBuilder,
TrustProfile trustProfile) throws MOAException {
QCSSCDResult qcsscdresult = new QCSSCDResult();
if(resultObject == null) {
Logger.warn("Result Object is null!");
return;
}
PDFSignatureVerificationResult cmsResult = null;
List adesResults = null;
ExtendedCertificateCheckResult extCheckResult = null;
if (resultObject instanceof ExtendedPDFSignatureVerificationResult) {
Logger.info("Got ExtendedPDFSignatureVerificationResult");
ExtendedPDFSignatureVerificationResult result = (ExtendedPDFSignatureVerificationResult) resultObject;
cmsResult = result.getPDFSignatureVerificationResult();
adesResults = AdESResultUtils.getAdESResult(result.getFormVerificationResult());
if (adesResults != null) {
Iterator adesIterator = adesResults.iterator();
while (adesIterator.hasNext()) {
Logger.info("ADES Formresults: " + adesIterator.next().toString());
}
}
cmsResult = result.getPDFSignatureVerificationResult();
try {
//Logger.info("Extended Validation Report: " + result.getName());
Logger.info("Extended Validation Code: " + result.getResultCode().toString());
Logger.info("Extended Validation Info: " + result.getInfo());
extCheckResult = AdESResultUtils.getExtendedResult(result.getResultCode());
} catch (ConfigurationException e) {
Logger.warn("Cannot generate Extendend Result. Check SVA Configuration!", e);
} catch (NullPointerException e) {
Logger.info("No extendend validation result available.");
}
} else {
Logger.info("Got PDFSignatureVerificationResult");
cmsResult = (PDFSignatureVerificationResult) 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,
extCheckResult);
}
/**
* 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(ExtendedCMSSignatureVerificationResult adesFormVerification) throws ConfigurationException {
if (adesFormVerification == null) {
// no form information
return null;
}
List adesList = new ArrayList();
Logger.info("Checking AdES Results:");
//AdESResultUtils.buildResult(adesFormVerification.getDetailedExtendedReport(), adesList);
//AdESResultUtils.checkSubResult(adesFormVerification.getSubResult(AdESConstants.LONG_TERM_VALIDATION),
// SignatureVerificationProfile.LEVEL_LTA, adesList);
AdESResultUtils.checkSubResult(adesFormVerification.getSubResult(AdESConstants.LONG_TERM_VALIDATION),
SignatureVerificationProfile.LEVEL_LT, adesList);
AdESResultUtils.checkSubResult(adesFormVerification.getSubResult(AdESConstants.ADES_T_VALIDATION),
SignatureVerificationProfile.LEVEL_T, adesList);
AdESResultUtils.checkSubResult(adesFormVerification.getSubResult("basic report"),
SignatureVerificationProfile.LEVEL_B, adesList);
return adesList;
}
private List getAdESResult(ExtendedPDFSignatureVerificationResult adesFormVerification) throws ConfigurationException {
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);
*/
AdESResultUtils.checkSubResult(adesFormVerification.getSubResult(AdESConstants.LONG_TERM_VALIDATION),
SignatureVerificationProfile.LEVEL_LT, adesList);
AdESResultUtils.checkSubResult(adesFormVerification.getSubResult(AdESConstants.ADES_T_VALIDATION),
SignatureVerificationProfile.LEVEL_T, adesList);
AdESResultUtils.checkSubResult(adesFormVerification.getSubResult("basic report"),
SignatureVerificationProfile.LEVEL_B, adesList);
return adesList;
}
}