/* * 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.sig.tsl.utils.MiscUtil; 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.ConfigurationProvider; 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.debug("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); } //PAdES module had to be closed manually module.closeModule(); } else { // get the signed content signedContent = getSignedContent(request); CMSSignatureVerificationProfile profile = profileFactory.createProfile(); Logger.debug("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(); if (signature != null) signature.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 (Logger.isDebugEnabled() && adesResults != null) { Iterator adesIterator = adesResults.iterator(); while (adesIterator.hasNext()) Logger.debug("ADES Formresults: " + adesIterator.next().toString()); } try { //Logger.info("Extended Validation Report: " + result.getName()); Logger.debug("Extended Validation Code: " + result.getResultCode().toString()); Logger.debug("Extended Validation Info: " + result.getInfo()); extCheckResult = AdESResultUtils.getExtendedResult(result.getResultCode()); } catch (NullPointerException e) { Logger.info("No extendend validation result available."); } } else { Logger.debug("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, cmsResult.getSigningTime(), trustProfile.isTSLEnabled(), ConfigurationProvider.getInstance()); // 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, qcsscdresult.getTslInfos()); } 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 (Logger.isDebugEnabled() && adesResults != null) { Iterator adesIterator = adesResults.iterator(); while (adesIterator.hasNext()) Logger.debug("ADES Formresults: " + adesIterator.next().toString()); } cmsResult = result.getPDFSignatureVerificationResult(); try { Logger.debug("Extended Validation Code: " + result.getResultCode().toString()); if (result.getDetailedExtendedReport() != null) Logger.debug("Extended Validation Info: " + result.getDetailedExtendedReport().getMessage()); else Logger.debug("Extended Validation Info: " + result.getInfo()); Logger.debug("Full extended Validation Infos: " + result.getInfo()); extCheckResult = AdESResultUtils.getExtendedResult(result.getResultCode()); } catch (NullPointerException e) { Logger.info("No extendend validation result available."); } } else { Logger.debug("Got PDFSignatureVerificationResult"); cmsResult = (PDFSignatureVerificationResult) resultObject; } if (MiscUtil.isNotEmpty(cmsResult.getError())) Logger.info("Signature validation stopped with an error: " + cmsResult.getError()); 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, cmsResult.getSigningTime(), trustProfile.isTSLEnabled(), ConfigurationProvider.getInstance()); // 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, qcsscdresult.getTslInfos()); } /** * 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; } } }