/*
* 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.IAIKException;
import iaik.server.modules.IAIKRuntimeException;
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.x509.X509Certificate;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
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.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;
CMSSignatureVerificationResult result;
int[] signatories;
InputStream input;
byte[] buf = new byte[256];
// 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
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.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();) {
result = (CMSSignatureVerificationResult) resultIter.next();
String issuerCountryCode = null;
// QC/SSCD check
List list = result.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(result, trustProfile, qcsscdresult.isQC(), qcsscdresult.isQCSourceTSL(), qcsscdresult.isSSCD(), qcsscdresult.isSSCDSourceTSL(), issuerCountryCode);
}
} else {
int i;
for (i = 0; i < signatories.length; i++) {
int sigIndex = signatories[i] - 1;
try {
result =
(CMSSignatureVerificationResult) results.get(signatories[i] - 1);
String issuerCountryCode = null;
// QC/SSCD check
List list = result.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(result, trustProfile, qcsscdresult.isQC(), qcsscdresult.isQCSourceTSL(), qcsscdresult.isSSCD(), qcsscdresult.isSSCDSourceTSL(), issuerCountryCode);
} 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;
}
}
}