All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.as2lib.crypto.BCCryptoHelper Maven / Gradle / Ivy

There is a newer version: 3.1.0
Show newest version
/**
 * The FreeBSD Copyright
 * Copyright 1994-2008 The FreeBSD Project. All rights reserved.
 * Copyright (C) 2013-2016 Philip Helger philip[at]helger[dot]com
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation
 * are those of the authors and should not be interpreted as representing
 * official policies, either expressed or implied, of the FreeBSD Project.
 */
package com.helger.as2lib.crypto;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.DigestOutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;

import javax.activation.CommandMap;
import javax.activation.MailcapCommandMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.mail.MessagingException;
import javax.mail.internet.ContentType;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.RecipientId;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMEEnveloped;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
import org.bouncycastle.mail.smime.SMIMEException;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.mail.smime.SMIMESignedParser;
import org.bouncycastle.mail.smime.SMIMEUtil;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.helger.as2lib.exception.OpenAS2Exception;
import com.helger.as2lib.exception.WrappedOpenAS2Exception;
import com.helger.as2lib.util.CAS2Header;
import com.helger.as2lib.util.IOHelper;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.ReturnsMutableCopy;
import com.helger.commons.base64.Base64;
import com.helger.commons.collection.CollectionHelper;
import com.helger.commons.collection.ext.CommonsArrayList;
import com.helger.commons.collection.ext.ICommonsList;
import com.helger.commons.io.file.FileHelper;
import com.helger.commons.io.stream.NullOutputStream;
import com.helger.commons.io.stream.StreamHelper;
import com.helger.commons.lang.priviledged.AccessControllerHelper;
import com.helger.commons.string.StringHelper;
import com.helger.commons.system.SystemProperties;
import com.helger.mail.cte.EContentTransferEncoding;

/**
 * Implementation of {@link ICryptoHelper} based on BouncyCastle.
 *
 * @author Philip Helger
 */
public final class BCCryptoHelper implements ICryptoHelper
{
  private static final Logger s_aLogger = LoggerFactory.getLogger (BCCryptoHelper.class);
  private static final File s_aDumpDecryptedDirectory;

  static
  {
    final String sDumpDecryptedDirectory = SystemProperties.getPropertyValueOrNull ("AS2.dumpDecryptedDirectory");
    if (StringHelper.hasText (sDumpDecryptedDirectory))
    {
      s_aDumpDecryptedDirectory = new File (sDumpDecryptedDirectory);
      IOHelper.getFileOperationManager ().createDirIfNotExisting (s_aDumpDecryptedDirectory);
      s_aLogger.info ("Using directory " +
                      s_aDumpDecryptedDirectory.getAbsolutePath () +
                      " to dump all decrypted body parts to.");
    }
    else
      s_aDumpDecryptedDirectory = null;
  }

  public BCCryptoHelper ()
  {
    Security.addProvider (new BouncyCastleProvider ());

    final MailcapCommandMap aCommandMap = (MailcapCommandMap) CommandMap.getDefaultCommandMap ();
    aCommandMap.addMailcap ("application/pkcs7-signature;; x-java-content-handler=" +
                            org.bouncycastle.mail.smime.handlers.pkcs7_signature.class.getName ());
    aCommandMap.addMailcap ("application/pkcs7-mime;; x-java-content-handler=" +
                            org.bouncycastle.mail.smime.handlers.pkcs7_mime.class.getName ());
    aCommandMap.addMailcap ("application/x-pkcs7-signature;; x-java-content-handler=" +
                            org.bouncycastle.mail.smime.handlers.x_pkcs7_signature.class.getName ());
    aCommandMap.addMailcap ("application/x-pkcs7-mime;; x-java-content-handler=" +
                            org.bouncycastle.mail.smime.handlers.x_pkcs7_mime.class.getName ());
    aCommandMap.addMailcap ("multipart/signed;; x-java-content-handler=" +
                            org.bouncycastle.mail.smime.handlers.multipart_signed.class.getName ());
    AccessControllerHelper.run ( () -> {
      CommandMap.setDefaultCommandMap (aCommandMap);
      return null;
    });
  }

  @Nonnull
  public KeyStore createNewKeyStore () throws KeyStoreException, NoSuchProviderException
  {
    return KeyStore.getInstance ("PKCS12", BouncyCastleProvider.PROVIDER_NAME);
  }

  @Nonnull
  public KeyStore loadKeyStore (@Nullable final InputStream aIS, @Nonnull final char [] aPassword) throws Exception
  {
    final KeyStore aKeyStore = createNewKeyStore ();
    if (aIS != null)
      aKeyStore.load (aIS, aPassword);
    return aKeyStore;
  }

  @Nonnull
  @Deprecated
  public KeyStore loadKeyStore (@Nonnull final String sFilename, @Nonnull final char [] aPassword) throws Exception
  {
    final InputStream aIS = FileHelper.getInputStream (sFilename);
    try
    {
      return loadKeyStore (aIS, aPassword);
    }
    finally
    {
      StreamHelper.close (aIS);
    }
  }

  public boolean isEncrypted (@Nonnull final MimeBodyPart aPart) throws MessagingException
  {
    ValueEnforcer.notNull (aPart, "Part");

    // Content-Type is sthg like this if encrypted:
    // application/pkcs7-mime; name=smime.p7m; smime-type=enveloped-data
    final ContentType aContentType = new ContentType (aPart.getContentType ());
    final String sBaseType = aContentType.getBaseType ().toLowerCase (Locale.US);
    if (!sBaseType.equals ("application/pkcs7-mime"))
      return false;

    final String sSmimeType = aContentType.getParameter ("smime-type");
    return sSmimeType != null && sSmimeType.equalsIgnoreCase ("enveloped-data");
  }

  public boolean isSigned (@Nonnull final MimeBodyPart aPart) throws MessagingException
  {
    ValueEnforcer.notNull (aPart, "Part");

    final ContentType aContentType = new ContentType (aPart.getContentType ());
    final String sBaseType = aContentType.getBaseType ();
    return sBaseType.equalsIgnoreCase ("multipart/signed");
  }

  public boolean isCompressed (@Nonnull final String sContentType) throws OpenAS2Exception
  {
    ValueEnforcer.notNull (sContentType, "ContentType");

    try
    {
      // Content-Type is sthg like this if compressed:
      // application/pkcs7-mime; smime-type=compressed-data; name=smime.p7z
      final ContentType aContentType = new ContentType (sContentType);

      final String sSmimeType = aContentType.getParameter ("smime-type");
      return sSmimeType != null && sSmimeType.equalsIgnoreCase ("compressed-data");
    }
    catch (final MessagingException ex)
    {
      throw WrappedOpenAS2Exception.wrap (ex);
    }
  }

  @Nonnull
  @ReturnsMutableCopy
  private static byte [] _getAsciiBytes (@Nonnull final String sString)
  {
    final char [] aChars = sString.toCharArray ();
    final int nLength = aChars.length;
    final byte [] ret = new byte [nLength];
    for (int i = 0; i < nLength; i++)
      ret[i] = (byte) aChars[i];
    return ret;
  }

  @Nonnull
  public String calculateMIC (@Nonnull final MimeBodyPart aPart,
                              @Nonnull final ECryptoAlgorithmSign eDigestAlgorithm,
                              final boolean bIncludeHeaders) throws GeneralSecurityException,
                                                             MessagingException,
                                                             IOException
  {
    ValueEnforcer.notNull (aPart, "MimeBodyPart");
    ValueEnforcer.notNull (eDigestAlgorithm, "DigestAlgorithm");

    if (s_aLogger.isDebugEnabled ())
      s_aLogger.debug ("BCCryptoHelper.calculateMIC (" +
                       eDigestAlgorithm +
                       " [" +
                       eDigestAlgorithm.getOID ().getId () +
                       "], " +
                       bIncludeHeaders +
                       ")");

    final ASN1ObjectIdentifier aMICAlg = eDigestAlgorithm.getOID ();

    final MessageDigest aMessageDigest = MessageDigest.getInstance (aMICAlg.getId (),
                                                                    BouncyCastleProvider.PROVIDER_NAME);

    if (bIncludeHeaders)
    {
      // Start hashing the header
      final byte [] aCRLF = new byte [] { '\r', '\n' };
      final Enumeration  aHeaderLines = aPart.getAllHeaderLines ();
      while (aHeaderLines.hasMoreElements ())
      {
        aMessageDigest.update (_getAsciiBytes ((String) aHeaderLines.nextElement ()));
        aMessageDigest.update (aCRLF);
      }

      // The CRLF separator between header and content
      aMessageDigest.update (aCRLF);
    }

    // No need to canonicalize here - see issue #12
    try (final DigestOutputStream aDOS = new DigestOutputStream (new NullOutputStream (), aMessageDigest);
         final OutputStream aOS = MimeUtility.encode (aDOS, aPart.getEncoding ()))
    {
      aPart.getDataHandler ().writeTo (aOS);
    }

    // Build result digest array
    final byte [] aMIC = aMessageDigest.digest ();

    // Perform Base64 encoding and append algorithm ID
    final String ret = Base64.encodeBytes (aMIC) + ", " + eDigestAlgorithm.getID ();

    if (s_aLogger.isDebugEnabled ())
      s_aLogger.debug ("  MIC = " + ret);

    return ret;
  }

  private static void _dumpDecrypted (@Nonnull final byte [] aPayload)
  {
    // Ensure a unique filename
    File aDestinationFile;
    int nIndex = 0;
    do
    {
      aDestinationFile = new File (s_aDumpDecryptedDirectory,
                                   "as2-decrypted-" + Long.toString (new Date ().getTime ()) + "-" + nIndex + ".part");
      nIndex++;
    } while (aDestinationFile.exists ());

    s_aLogger.info ("Dumping decrypted MIME part to file " + aDestinationFile.getAbsolutePath ());
    final OutputStream aOS = FileHelper.getOutputStream (aDestinationFile);
    try
    {
      // Add payload
      aOS.write (aPayload);
    }
    catch (final IOException ex)
    {
      s_aLogger.error ("Failed to dump decrypted MIME part to file " + aDestinationFile.getAbsolutePath (), ex);
    }
    finally
    {
      StreamHelper.close (aOS);
    }
  }

  @Nonnull
  public MimeBodyPart decrypt (@Nonnull final MimeBodyPart aPart,
                               @Nonnull final X509Certificate aX509Cert,
                               @Nonnull final PrivateKey aPrivateKey,
                               final boolean bForceDecrypt) throws GeneralSecurityException,
                                                            MessagingException,
                                                            CMSException,
                                                            SMIMEException
  {
    ValueEnforcer.notNull (aPart, "MimeBodyPart");
    ValueEnforcer.notNull (aX509Cert, "X509Cert");
    ValueEnforcer.notNull (aPrivateKey, "PrivateKey");

    if (s_aLogger.isDebugEnabled ())
      s_aLogger.debug ("BCCryptoHelper.decrypt; X509 subject=" +
                       aX509Cert.getSubjectX500Principal ().getName () +
                       "; forceDecrypt=" +
                       bForceDecrypt);

    // Make sure the data is encrypted
    if (!bForceDecrypt && !isEncrypted (aPart))
      throw new GeneralSecurityException ("Content-Type indicates data isn't encrypted: " + aPart.getContentType ());

    // Parse the MIME body into an SMIME envelope object
    final SMIMEEnveloped aEnvelope = new SMIMEEnveloped (aPart);

    // Get the recipient object for decryption
    final RecipientId aRecipientID = new JceKeyTransRecipientId (aX509Cert);

    final RecipientInformation aRecipient = aEnvelope.getRecipientInfos ().get (aRecipientID);
    if (aRecipient == null)
      throw new GeneralSecurityException ("Certificate does not match part signature");

    // try to decrypt the data
    final byte [] aDecryptedData = aRecipient.getContent (new JceKeyTransEnvelopedRecipient (aPrivateKey).setProvider (BouncyCastleProvider.PROVIDER_NAME));

    if (s_aDumpDecryptedDirectory != null)
      _dumpDecrypted (aDecryptedData);

    return SMIMEUtil.toMimeBodyPart (aDecryptedData);
  }

  @Nonnull
  public MimeBodyPart encrypt (@Nonnull final MimeBodyPart aPart,
                               @Nonnull final X509Certificate aX509Cert,
                               @Nonnull final ECryptoAlgorithmCrypt eAlgorithm) throws GeneralSecurityException,
                                                                                SMIMEException,
                                                                                CMSException
  {
    ValueEnforcer.notNull (aPart, "MimeBodyPart");
    ValueEnforcer.notNull (aX509Cert, "X509Cert");
    ValueEnforcer.notNull (eAlgorithm, "Algorithm");

    if (s_aLogger.isDebugEnabled ())
      s_aLogger.debug ("BCCryptoHelper.encrypt; X509 subject=" +
                       aX509Cert.getSubjectX500Principal ().getName () +
                       "; algorithm=" +
                       eAlgorithm);

    // Check if the certificate is expired or active.
    aX509Cert.checkValidity ();

    final ASN1ObjectIdentifier aEncAlg = eAlgorithm.getOID ();

    final SMIMEEnvelopedGenerator aGen = new SMIMEEnvelopedGenerator ();
    aGen.addRecipientInfoGenerator (new JceKeyTransRecipientInfoGenerator (aX509Cert).setProvider (BouncyCastleProvider.PROVIDER_NAME));

    final OutputEncryptor aEncryptor = new JceCMSContentEncryptorBuilder (aEncAlg).setProvider (BouncyCastleProvider.PROVIDER_NAME)
                                                                                  .build ();
    final MimeBodyPart aEncData = aGen.generate (aPart, aEncryptor);
    return aEncData;
  }

  @Nonnull
  public MimeBodyPart sign (@Nonnull final MimeBodyPart aPart,
                            @Nonnull final X509Certificate aX509Cert,
                            @Nonnull final PrivateKey aPrivateKey,
                            @Nonnull final ECryptoAlgorithmSign eAlgorithm,
                            final boolean bIncludeCertificateInSignedContent,
                            final boolean bUseOldRFC3851MicAlgs) throws GeneralSecurityException,
                                                                 SMIMEException,
                                                                 MessagingException,
                                                                 OperatorCreationException
  {
    ValueEnforcer.notNull (aPart, "MimeBodyPart");
    ValueEnforcer.notNull (aX509Cert, "X509Cert");
    ValueEnforcer.notNull (aPrivateKey, "PrivateKey");
    ValueEnforcer.notNull (eAlgorithm, "Algorithm");

    if (s_aLogger.isDebugEnabled ())
      s_aLogger.debug ("BCCryptoHelper.sign; X509 subject=" +
                       aX509Cert.getSubjectX500Principal ().getName () +
                       "; algorithm=" +
                       eAlgorithm +
                       "; includeCertificateInSignedContent=" +
                       bIncludeCertificateInSignedContent);

    // Check if the certificate is expired or active.
    aX509Cert.checkValidity ();

    // create a CertStore containing the certificates we want carried
    // in the signature
    final ICommonsList  aCertList = new CommonsArrayList<> (aX509Cert);
    final JcaCertStore aCertStore = new JcaCertStore (aCertList);

    // create some smime capabilities in case someone wants to respond
    final ASN1EncodableVector aSignedAttrs = new ASN1EncodableVector ();
    final SMIMECapabilityVector aCapabilities = new SMIMECapabilityVector ();
    aCapabilities.addCapability (eAlgorithm.getOID ());
    aSignedAttrs.add (new SMIMECapabilitiesAttribute (aCapabilities));

    // add an encryption key preference for encrypted responses -
    // normally this would be different from the signing certificate...
    // final IssuerAndSerialNumber issAndSer = new IssuerAndSerialNumber (new
    // X500Name (signDN),
    // aX509Cert.getSerialNumber ());
    // aSignedAttrs.add (new SMIMEEncryptionKeyPreferenceAttribute (issAndSer));

    // create the generator for creating an smime/signed message
    final SMIMESignedGenerator aSGen = new SMIMESignedGenerator (bUseOldRFC3851MicAlgs ? SMIMESignedGenerator.RFC3851_MICALGS
                                                                                       : SMIMESignedGenerator.RFC5751_MICALGS);

    // aSGen.addSigner (aPrivKey, aX509Cert, aSignDigest.getId ());

    // add a signer to the generator - this specifies we are using SHA1 and
    // adding the smime attributes above to the signed attributes that
    // will be generated as part of the signature. The encryption algorithm
    // used is taken from the key - in this RSA with PKCS1Padding
    aSGen.addSignerInfoGenerator (new JcaSimpleSignerInfoGeneratorBuilder ().setProvider (BouncyCastleProvider.PROVIDER_NAME)
                                                                            .setSignedAttributeGenerator (new AttributeTable (aSignedAttrs))
                                                                            .build (eAlgorithm.getSignAlgorithmName (),
                                                                                    aPrivateKey,
                                                                                    aX509Cert));

    if (bIncludeCertificateInSignedContent)
    {
      // add our pool of certs and cerls (if any) to go with the signature
      aSGen.addCertificates (aCertStore);
    }

    final MimeMultipart aSignedData = aSGen.generate (aPart);

    final MimeBodyPart aTmpBody = new MimeBodyPart ();
    aTmpBody.setContent (aSignedData);
    aTmpBody.setHeader (CAS2Header.HEADER_CONTENT_TYPE, aSignedData.getContentType ());
    return aTmpBody;
  }

  @Nonnull
  private X509Certificate _verifyFindCertificate (@Nullable final X509Certificate aX509Cert,
                                                  final boolean bUseCertificateInBodyPart,
                                                  @Nonnull final SMIMESignedParser aSignedParser) throws CMSException,
                                                                                                  CertificateException,
                                                                                                  GeneralSecurityException
  {
    X509Certificate aRealX509Cert = aX509Cert;
    if (bUseCertificateInBodyPart)
    {
      // get all certificates contained in the body part
      final Collection  aContainedCerts = aSignedParser.getCertificates ().getMatches (null);
      if (!aContainedCerts.isEmpty ())
      {
        // For PEPPOL the certificate is passed in
        if (aContainedCerts.size () > 1)
          s_aLogger.warn ("Signed part contains " + aContainedCerts.size () + " certificates - using the first one!");

        final X509CertificateHolder aCertHolder = ((X509CertificateHolder) CollectionHelper.getFirstElement (aContainedCerts));
        final X509Certificate aCert = new JcaX509CertificateConverter ().setProvider (BouncyCastleProvider.PROVIDER_NAME)
                                                                        .getCertificate (aCertHolder);
        if (aX509Cert != null && !aX509Cert.equals (aCert))
          s_aLogger.warn ("Certificate mismatch! Provided certificate\n" +
                          aX509Cert +
                          " differs from certficate contained in message\n" +
                          aCert);

        aRealX509Cert = aCert;
      }
    }
    if (aRealX509Cert == null)
      throw new GeneralSecurityException ("No certificate provided" +
                                          (bUseCertificateInBodyPart ? " and none found in the message" : "") +
                                          "!");
    return aRealX509Cert;
  }

  @Nonnull
  public MimeBodyPart verify (@Nonnull final MimeBodyPart aPart,
                              @Nullable final X509Certificate aX509Cert,
                              final boolean bUseCertificateInBodyPart,
                              final boolean bForceVerify) throws GeneralSecurityException,
                                                          IOException,
                                                          MessagingException,
                                                          CMSException,
                                                          OperatorCreationException
  {
    if (s_aLogger.isDebugEnabled ())
      s_aLogger.debug ("BCCryptoHelper.verify; X509 subject=" +
                       (aX509Cert == null ? "null" : aX509Cert.getSubjectX500Principal ().getName ()) +
                       "; useCertificateInBodyPart=" +
                       bUseCertificateInBodyPart +
                       "; forceVerify=" +
                       bForceVerify);

    // Make sure the data is signed
    if (!bForceVerify && !isSigned (aPart))
      throw new GeneralSecurityException ("Content-Type indicates data isn't signed: " + aPart.getContentType ());

    final MimeMultipart aMainPart = (MimeMultipart) aPart.getContent ();
    // SMIMESignedParser uses "7bit" as the default - AS2 wants "binary"
    final SMIMESignedParser aSignedParser = new SMIMESignedParser (new JcaDigestCalculatorProviderBuilder ().setProvider (BouncyCastleProvider.PROVIDER_NAME)
                                                                                                            .build (),
                                                                   aMainPart,
                                                                   EContentTransferEncoding.AS2_DEFAULT.getID ());

    final X509Certificate aRealX509Cert = _verifyFindCertificate (aX509Cert, bUseCertificateInBodyPart, aSignedParser);

    if (s_aLogger.isDebugEnabled ())
      s_aLogger.debug (aRealX509Cert == aX509Cert ? "Verifying signature using the provided certificate (partnership)"
                                                  : "Verifying signature using the certificate contained in the MIME body part");

    // Check if the certificate is expired or active.
    aRealX509Cert.checkValidity ();

    // Verify certificate
    final SignerInformationVerifier aSIV = new JcaSimpleSignerInfoVerifierBuilder ().setProvider (BouncyCastleProvider.PROVIDER_NAME)
                                                                                    .build (aRealX509Cert.getPublicKey ());

    for (final Object aSigner : aSignedParser.getSignerInfos ().getSigners ())
    {
      final SignerInformation aSignerInfo = (SignerInformation) aSigner;
      if (!aSignerInfo.verify (aSIV))
        throw new SignatureException ("Verification failed");
    }

    return aSignedParser.getContent ();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy