/* * 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); } } } }