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

org.simplejavamail.internal.smimesupport.SMIMESupport Maven / Gradle / Ivy

There is a newer version: 8.11.3
Show newest version
/*
 * Copyright © 2009 Benny Bottema ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.simplejavamail.internal.smimesupport;

import jakarta.mail.MessagingException;
import jakarta.mail.Multipart;
import jakarta.mail.Session;
import jakarta.mail.internet.ContentType;
import jakarta.mail.internet.InternetHeaders;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
import jakarta.mail.internet.MimePart;
import jakarta.mail.util.ByteArrayDataSource;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMEException;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.util.Store;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simplejavamail.api.email.AttachmentResource;
import org.simplejavamail.api.email.Email;
import org.simplejavamail.api.email.OriginalSmimeDetails;
import org.simplejavamail.api.email.OriginalSmimeDetails.SmimeMode;
import org.simplejavamail.api.email.config.SmimeEncryptionConfig;
import org.simplejavamail.api.email.config.SmimeSigningConfig;
import org.simplejavamail.api.internal.general.MessageHeader;
import org.simplejavamail.api.internal.outlooksupport.model.OutlookMessage;
import org.simplejavamail.api.internal.outlooksupport.model.OutlookSmime.OutlookSmimeApplicationSmime;
import org.simplejavamail.api.internal.outlooksupport.model.OutlookSmime.OutlookSmimeMultipartSigned;
import org.simplejavamail.api.internal.smimesupport.model.AttachmentDecryptionResult;
import org.simplejavamail.api.internal.smimesupport.model.SmimeDetails;
import org.simplejavamail.api.mailer.config.Pkcs12Config;
import org.simplejavamail.internal.modules.SMIMEModule;
import org.simplejavamail.internal.smimesupport.builder.SmimeParseResultBuilder;
import org.simplejavamail.internal.smimesupport.model.OriginalSmimeDetailsImpl;
import org.simplejavamail.internal.smimesupport.model.SmimeDetailsImpl;
import org.simplejavamail.utils.mail.smime.KeyEncapsulationAlgorithm;
import org.simplejavamail.utils.mail.smime.SmimeKey;
import org.simplejavamail.utils.mail.smime.SmimeKeyStore;
import org.simplejavamail.utils.mail.smime.SmimeMessageIdFixingMimeMessage;
import org.simplejavamail.utils.mail.smime.SmimeMessageIdFixingSMTPMessage;
import org.simplejavamail.utils.mail.smime.SmimeState;
import org.simplejavamail.utils.mail.smime.SmimeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Optional.ofNullable;
import static org.simplejavamail.internal.smimesupport.SmimeException.ERROR_DECRYPTING_SMIME_SIGNED_ATTACHMENT;
import static org.simplejavamail.internal.smimesupport.SmimeException.ERROR_DETERMINING_SMIME_SIGNER;
import static org.simplejavamail.internal.smimesupport.SmimeException.ERROR_EXTRACTING_SIGNEDBY_FROM_SMIME_SIGNED_ATTACHMENT;
import static org.simplejavamail.internal.smimesupport.SmimeException.ERROR_EXTRACTING_SUBJECT_FROM_CERTIFICATE;
import static org.simplejavamail.internal.smimesupport.SmimeException.ERROR_OBTAINING_SMIME_KEY;
import static org.simplejavamail.internal.smimesupport.SmimeException.ERROR_READING_SMIME_CONTENT_TYPE;
import static org.simplejavamail.internal.smimesupport.SmimeException.MIMEPART_ASSUMED_SIGNED_ACTUALLY_NOT_SIGNED;
import static org.simplejavamail.internal.util.MiscUtil.defaultTo;


/**
 * This class only serves to hide the S/MIME implementation behind an easy-to-load-with-reflection class.
 */
@SuppressWarnings("unused") // it is used through reflection
public class SMIMESupport implements SMIMEModule {

	private static final Logger LOGGER = LoggerFactory.getLogger(SMIMESupport.class);
	private static final List SMIME_MIMETYPES = asList("application/pkcs7-mime", "application/x-pkcs7-mime", "multipart/signed");
	private static final Map SIMPLE_SMIMESTORE_CACHE = new HashMap<>();

	static {
		Security.addProvider(new BouncyCastleProvider());
	}

	public SmimeParseResultBuilder decryptAttachments(@NotNull final List attachments, @NotNull final OutlookMessage outlookMessage,
			@Nullable final Pkcs12Config pkcs12Config) {
		final SmimeParseResultBuilder smimeBuilder = new SmimeParseResultBuilder();

		if (outlookMessage.getSmimeMime() instanceof OutlookSmimeApplicationSmime) {
			final OutlookSmimeApplicationSmime s = (OutlookSmimeApplicationSmime) outlookMessage.getSmimeMime();
			smimeBuilder.getOriginalSmimeDetails().completeWith(OriginalSmimeDetailsImpl.builder()
					.smimeMime(s.getSmimeMime())
					.smimeType(s.getSmimeType())
					.smimeName(s.getSmimeName())
					.build());
		} else if (outlookMessage.getSmimeMime() instanceof OutlookSmimeMultipartSigned) {
			final OutlookSmimeMultipartSigned s = (OutlookSmimeMultipartSigned) outlookMessage.getSmimeMime();
			smimeBuilder.getOriginalSmimeDetails().completeWith(OriginalSmimeDetailsImpl.builder()
					.smimeMime(s.getSmimeMime())
					.smimeProtocol(s.getSmimeProtocol())
					.smimeMicalg(s.getSmimeMicalg())
					.build());
		}

		decryptAttachments(smimeBuilder, attachments, pkcs12Config);

		if (smimeBuilder.getOriginalSmimeDetails().getSmimeMode() == SmimeMode.SIGNED) {
			// this is the only way for Outlook messages to know a valid signature was included
			smimeBuilder.getOriginalSmimeDetails().completeWithSmimeSignatureValid(smimeBuilder.getSmimeSignedOrEncryptedEmail() != null);
		}

		return smimeBuilder;
	}

	public SmimeParseResultBuilder decryptAttachments(@NotNull final List attachments, @NotNull final MimeMessage mimeMessage, @Nullable final Pkcs12Config pkcs12Config) {
		final SmimeParseResultBuilder smimeBuilder = new SmimeParseResultBuilder();

		initSmimeMetadata(smimeBuilder, mimeMessage);
		decryptAttachments(smimeBuilder, attachments, pkcs12Config);
		finalizeSmimeMetadata(smimeBuilder, mimeMessage);

		return smimeBuilder;
	}

	private void initSmimeMetadata(final SmimeParseResultBuilder smimeBuilder, @NotNull final MimeMessage mimeMessage) {
		try {
			if (mimeMessage.getHeader(MessageHeader.CONTENT_TYPE.getName(), null) != null) {
				ContentType ct = new ContentType(mimeMessage.getHeader(MessageHeader.CONTENT_TYPE.getName(), null));
				if (SmimeRecognitionUtil.isSmimeContentType(ct)) {
					smimeBuilder.getOriginalSmimeDetails()
							.completeWith(OriginalSmimeDetailsImpl.builder()
									.smimeMime(ct.getBaseType())
									.smimeType(ct.getParameter("smime-type"))
									.smimeName(ct.getParameter("name"))
									.smimeProtocol(ct.getParameter("protocol"))
									.smimeMicalg(ct.getParameter("micalg"))
									.build());
				}
			}
		} catch (MessagingException e) {
			throw new SmimeException(ERROR_READING_SMIME_CONTENT_TYPE, e);
		}
	}

	private void finalizeSmimeMetadata(final SmimeParseResultBuilder smimeBuilder, @NotNull final MimeMessage mimeMessage) {
		final OriginalSmimeDetailsImpl originalSmimeDetails = smimeBuilder.getOriginalSmimeDetails();

		if (originalSmimeDetails.getSmimeMode() != SmimeMode.PLAIN) {
			LOGGER.debug("checking who signed this message...");
			originalSmimeDetails.completeWithSmimeSignedBy(getSignedByAddress(mimeMessage));
			if (originalSmimeDetails.getSmimeMode() == SmimeMode.SIGNED) {
				originalSmimeDetails.completeWithSmimeSignatureValid(checkSignature(mimeMessage, originalSmimeDetails));
			}
		}
	}

	private boolean checkSignature(@NotNull final MimeMessage mimeMessage, @Nullable final OriginalSmimeDetails messageSmimeDetails) {
		if (messageSmimeDetails != null) {
			LOGGER.debug("verifying signed mimemessage...");
			final boolean validSignature = verifyValidSignature(mimeMessage, messageSmimeDetails);
			if (!validSignature) {
				LOGGER.warn("Message contains invalid S/MIME signature! Assume this emal has been tampered with.");
			}
			return validSignature;
		}
		return false;
	}

	private void decryptAttachments(@NotNull final SmimeParseResultBuilder smimeBuilder, @NotNull final List attachments,
			@Nullable final Pkcs12Config pkcs12Config) {
		LOGGER.debug("checking for S/MIME signed / encrypted attachments...");
		List decryptedAttachments = decryptAttachments(attachments, pkcs12Config, smimeBuilder.getOriginalSmimeDetails());
		smimeBuilder.addDecryptedAttachments(decryptedAttachments);

		if (attachments.size() == 1) {
			final AttachmentResource onlyAttachment = attachments.get(0);
			final AttachmentDecryptionResult onlyAttachmentDecrypted = smimeBuilder.getDecryptedAttachmentResults().get(0);
			if (isSmimeAttachment(onlyAttachment) && isMimeMessageAttachment(onlyAttachmentDecrypted.getAttachmentResource())) {
				smimeBuilder.getOriginalSmimeDetails().completeWith(determineSmimeDetails(onlyAttachment));
				smimeBuilder.setSmimeSignedOrEncryptedEmail(onlyAttachmentDecrypted.getAttachmentResource());
			}
		}
	}

	private boolean isMimeMessageAttachment(final AttachmentResource attachment) {
		return attachment.getDataSource().getContentType().equals("message/rfc822");
	}

	@NotNull
	private OriginalSmimeDetailsImpl determineSmimeDetails(final AttachmentResource attachment) {
		LOGGER.debug("Single S/MIME signed / encrypted attachment found; assuming the attachment is the message "
				+ "body, a record of the original S/MIME details will be stored on the Email root...");
		SmimeDetails smimeDetails = getSmimeDetails(attachment);
		return OriginalSmimeDetailsImpl.builder()
				.smimeMime(smimeDetails.getSmimeMime())
				.smimeSignedBy(smimeDetails.getSignedBy())
				.build();
	}

	/**
	 * @see SMIMEModule#decryptAttachments(List, Pkcs12Config, OriginalSmimeDetails)
	 */
	@NotNull
	@Override
	public List decryptAttachments(
			@NotNull final List attachments,
			@Nullable final Pkcs12Config pkcs12Config,
			@NotNull final OriginalSmimeDetails messageSmimeDetails) {
		final List decryptedAttachments = new ArrayList<>();
		for (final AttachmentResource attachment : attachments) {
			if (isSmimeAttachment(attachment)) {
				try {
					LOGGER.debug("decrypting S/MIME signed attachment '{}'...", attachment.getName());
					decryptedAttachments.add(decryptAndUnsignAttachment(attachment, pkcs12Config, messageSmimeDetails));
				} catch (Exception e) {
					throw new SmimeException(format(ERROR_DECRYPTING_SMIME_SIGNED_ATTACHMENT, attachment), e);
				}
			} else {
				decryptedAttachments.add(new AttachmentDecryptionResultImpl(SmimeMode.PLAIN, attachment));
			}
		}

		return decryptedAttachments;
	}

	/**
	 * @see SMIMEModule#isSmimeAttachment(AttachmentResource)
	 */
	@Override
	public boolean isSmimeAttachment(@NotNull final AttachmentResource attachment) {
		return SMIME_MIMETYPES.contains(attachment.getDataSource().getContentType());
	}

	private AttachmentDecryptionResult decryptAndUnsignAttachment(
			@NotNull final AttachmentResource attachment,
			@Nullable final Pkcs12Config pkcs12Config,
			@NotNull final OriginalSmimeDetails messageSmimeDetails) {
		try {
			final MimeBodyPart mimeBodyPart = new MimeBodyPart(new InternetHeaders(), attachment.readAllBytes());
			mimeBodyPart.addHeader("Content-Type", restoreSmimeContentType(attachment, messageSmimeDetails));

			AttachmentDecryptionResult liberatedContent = null;

			final SmimeState smimeState = determineStatus(mimeBodyPart, messageSmimeDetails);
			if (smimeState == SmimeState.ENCRYPTED) {
				liberatedContent = getEncryptedContent(pkcs12Config, mimeBodyPart);
			} else if (smimeState == SmimeState.SIGNED) {
				liberatedContent = getSignedContent(mimeBodyPart);
			} else if (smimeState == SmimeState.PROBABLY_SIGNED) {
				liberatedContent = tryGetSignedContent(mimeBodyPart);
			}

			return liberatedContent != null ? liberatedContent : new AttachmentDecryptionResultImpl(SmimeMode.PLAIN, attachment);
		} catch (MessagingException | IOException e) {
			throw new SmimeException(format(ERROR_DECRYPTING_SMIME_SIGNED_ATTACHMENT, attachment), e);
		}
	}

	@Nullable
	private AttachmentDecryptionResult getEncryptedContent(final @Nullable Pkcs12Config pkcs12Config, final MimeBodyPart mimeBodyPart)
			throws MessagingException, IOException {
		if (pkcs12Config != null) {
			MimeBodyPart liberatedBodyPart = SmimeUtil.decrypt(mimeBodyPart, retrieveSmimeKeyFromPkcs12Keystore(pkcs12Config));
			if (SmimeUtil.getStatus(liberatedBodyPart) == SmimeState.SIGNED_ENVELOPED) {
				final AttachmentDecryptionResult signedContent = getSignedContent(liberatedBodyPart);
				if (signedContent != null) {
					return new AttachmentDecryptionResultImpl(SmimeMode.SIGNED_ENCRYPTED, signedContent.getAttachmentResource());
				}
				// apparently the sign was invalid, so ignore and continue with the decrypted attachment instead
			}
			return new AttachmentDecryptionResultImpl(SmimeMode.ENCRYPTED, handleLiberatedContent(liberatedBodyPart.getContent()));
		}
		LOGGER.warn("Message was encrypted, but no Pkcs12Config was given to decrypt it with, skipping attachment...");
		return null;
	}

	@Nullable
	private AttachmentDecryptionResult getSignedContent(final MimeBodyPart mimeBodyPart)
			throws MessagingException, IOException {
		if (SmimeUtil.checkSignature(mimeBodyPart)) {
			MimeBodyPart liberatedBodyPart = SmimeUtil.getSignedContent(mimeBodyPart);
			return new AttachmentDecryptionResultImpl(SmimeMode.SIGNED, handleLiberatedContent(liberatedBodyPart.getContent()));
		}
		LOGGER.warn("Content is S/MIME signed, but signature is not valid; skipping S/MIME interpeter...");
		return null;
	}

	@Nullable
	private AttachmentDecryptionResult tryGetSignedContent(final MimeBodyPart mimeBodyPart)
			throws MessagingException, IOException {
		try {
			if (SmimeUtil.checkSignature(mimeBodyPart)) {
				MimeBodyPart liberatedBodyPart = SmimeUtil.getSignedContent(mimeBodyPart);
				return new AttachmentDecryptionResultImpl(SmimeMode.SIGNED, handleLiberatedContent(liberatedBodyPart.getContent()));
			}
		} catch (MessagingException | IOException e) {
			// ignore, apparently not S/SMIME after all
		}
        LOGGER.warn("Content classified as signed, but apparently not using S/MIME (or it was and the signature was not valid); skipping S/MIME interpeter...");
		return null;
	}

	private String restoreSmimeContentType(@NotNull final AttachmentResource attachment, final OriginalSmimeDetails originalSmimeDetails) {
		String contentType = attachment.getDataSource().getContentType();
		if (contentType.contains("multipart/signed") && !contentType.contains("protocol") && originalSmimeDetails.getSmimeProtocol() != null) {
			// this step is needed, because converted messages from Outlook don't come out correctly
			contentType = format("multipart/signed;protocol=\"%s\";micalg=%s",
					originalSmimeDetails.getSmimeProtocol(), originalSmimeDetails.getSmimeMicalg());
		}
		return contentType;
	}

	@Nullable
	private AttachmentResource handleLiberatedContent(final Object content)
			throws MessagingException, IOException {

		if (content instanceof MimeMultipart) {
			final ByteArrayOutputStream os = new ByteArrayOutputStream();
			final MimeMessage decryptedMessage = new MimeMessage((Session) null) {
				@Override
				protected void updateMessageID() throws MessagingException {
					setHeader("Message-ID", SmimeRecognitionUtil.SMIME_ATTACHMENT_MESSAGE_ID);
				}
			};
			decryptedMessage.setContent((Multipart) content);
			decryptedMessage.writeTo(os);
			return new AttachmentResource("signed-email.eml", new ByteArrayDataSource(os.toByteArray(), "message/rfc822"));
		}
        LOGGER.warn("S/MIME signed content type not recognized, please raise an issue for {}", content.getClass());
		return null;
	}

	private SmimeState determineStatus(@NotNull final MimePart mimeBodyPart, @NotNull final OriginalSmimeDetails messageSmimeDetails) {
		SmimeState status = SmimeUtil.getStatus(mimeBodyPart);
		boolean trustStatus = status != SmimeState.ENCRYPTED || messageSmimeDetails.getSmimeMode() == SmimeMode.PLAIN;
		if (trustStatus) {
			return status;
		}
		return "signed-data".equals(messageSmimeDetails.getSmimeType()) ? SmimeState.SIGNED : SmimeState.ENCRYPTED;
	}

	/**
	 * @see SMIMEModule#getSmimeDetails(AttachmentResource)
	 */
	@NotNull
	@Override
	public SmimeDetails getSmimeDetails(@NotNull final AttachmentResource attachment) {
		final String contentType = attachment.getDataSource().getContentType();
		final String signedByAddress = getSignedByAddress(attachment);
		return new SmimeDetailsImpl(contentType, signedByAddress);
	}

	/**
	 * @see SMIMEModule#getSignedByAddress(AttachmentResource)
	 */
	@Override
	@Nullable
	public String getSignedByAddress(@NotNull AttachmentResource smimeAttachment) {
		try {
			final InternetHeaders internetHeaders = new InternetHeaders();
			internetHeaders.addHeader("Content-Type", smimeAttachment.getDataSource().getContentType());
			return getSignedByAddress(new MimeBodyPart(internetHeaders, smimeAttachment.readAllBytes()));
		} catch (MessagingException | IOException e) {
			throw new SmimeException(format(ERROR_EXTRACTING_SIGNEDBY_FROM_SMIME_SIGNED_ATTACHMENT, smimeAttachment), e);
		}
	}

	/**
	 * Delegates to {@link #determineSMIMESigned(MimePart)} and {@link #getSignedByAddress(SMIMESigned)}.
	 *
	 * @see SMIMEModule#getSignedByAddress(MimePart)
	 */
	@Nullable
	public String getSignedByAddress(@NotNull MimePart mimePart) {
		try {
			return getSignedByAddress(determineSMIMESigned(mimePart));
		} catch (SmimeException e) {
			// not the right scenario to find signed-by, skip attempt
			return null;
		}
	}

	public boolean verifyValidSignature(@NotNull MimeMessage mimeMessage, @NotNull OriginalSmimeDetails messageSmimeDetails) {
		return determineStatus(mimeMessage, messageSmimeDetails) != SmimeState.SIGNED || SmimeUtil.checkSignature(mimeMessage);
	}

	@NotNull
	private static SMIMESigned determineSMIMESigned(MimePart mimePart) {
		try {
			if (mimePart.isMimeType("multipart/signed")) {
				return new SMIMESigned((MimeMultipart) mimePart.getContent());
			} else if (mimePart.isMimeType("application/pkcs7-mime") || mimePart.isMimeType("application/x-pkcs7-mime")) {
				return new SMIMESigned(mimePart);
			} else {
				throw new SmimeException(format(MIMEPART_ASSUMED_SIGNED_ACTUALLY_NOT_SIGNED, mimePart));
			}
		} catch (MessagingException | CMSException | SMIMEException | IOException e) {
			throw new SmimeException(ERROR_DETERMINING_SMIME_SIGNER, e);
		}
	}

	/**
	 * @deprecated Should be removed once the pull-request has been merged and released
	 * @see "https://github.com/markenwerk/java-utils-mail-smime/issues/5"
	 */
	@SuppressWarnings("DeprecatedIsStillUsed")
	private static String getSignedByAddress(SMIMESigned smimeSigned) {
		try {
			@SuppressWarnings("rawtypes")
			Store certificates = smimeSigned.getCertificates();

			SignerInformation signerInformation = smimeSigned.getSignerInfos().getSigners().iterator().next();
			X509Certificate certificate = getCertificate(certificates, signerInformation.getSID());
			SignerInformationVerifier verifier = getVerifier(certificate);
			X500Name x500name = verifier.getAssociatedCertificate().getSubject();
			RDN cn = x500name.getRDNs(BCStyle.CN)[0];
			return IETFUtils.valueToString(cn.getFirst().getValue());
		} catch (Exception e) {
			throw new SmimeException(ERROR_EXTRACTING_SUBJECT_FROM_CERTIFICATE, e);
		}
	}

	/**
	 * @deprecated This is duplicate code from SmimeUtil and should be removed once the pull-request has been merged and released
	 * @see "https://github.com/markenwerk/java-utils-mail-smime/issues/5"
	 */
	@Deprecated
	private static X509Certificate getCertificate(@SuppressWarnings("rawtypes") Store certificates, SignerId signerId)
			throws CertificateException {
		@SuppressWarnings({ "unchecked" })
		X509CertificateHolder certificateHolder = (X509CertificateHolder) certificates.getMatches(signerId).iterator()
				.next();
		JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
		certificateConverter.setProvider(BouncyCastleProvider.PROVIDER_NAME);
		return certificateConverter.getCertificate(certificateHolder);
	}

	/**
	 * @deprecated This is duplicate code from SmimeUtil and should be removed once the pull-request has been merged and released
	 * @see "https://github.com/markenwerk/java-utils-mail-smime/issues/5"
	 */
	@Deprecated
	private static SignerInformationVerifier getVerifier(X509Certificate certificate) throws OperatorCreationException {
		JcaSimpleSignerInfoVerifierBuilder builder = new JcaSimpleSignerInfoVerifierBuilder();
		builder.setProvider(BouncyCastleProvider.PROVIDER_NAME);
		return builder.build(certificate);
	}

	@NotNull
	@Override
	public MimeMessage signMessageWithSmime(@Nullable final Session session, @NotNull final Email email, @NotNull final MimeMessage messageToProtect, @NotNull final SmimeSigningConfig smimeSigningConfig) {
		return SmimeUtil.sign(session, email.getId(), messageToProtect, retrieveSmimeKeyFromPkcs12Keystore(smimeSigningConfig.getPkcs12Config()),
				defaultTo(smimeSigningConfig.getSignatureAlgorithm(), SmimeUtil.DEFAULT_SIGNATURE_ALGORITHM_NAME));
	}

	@NotNull
	@Override
	public MimeMessage encryptMessageWithSmime(@Nullable final Session session, @NotNull final Email email, @NotNull final MimeMessage messageToProtect, @NotNull final SmimeEncryptionConfig smimeEncryptionConfige) {
        return SmimeUtil.encrypt(session, messageToProtect, email.getId(), smimeEncryptionConfige.getX509Certificate(),
                ofNullable(smimeEncryptionConfige.getKeyEncapsulationAlgorithm())
                        .map(KeyEncapsulationAlgorithm::valueOf)
                        .orElse(SmimeUtil.DEFAULT_KEY_ENCAPSULATION_ALGORITHM),
                ofNullable(smimeEncryptionConfige.getCipherAlgorithm())
                        .map(CMSAlgorithmResolver::resolve)
                        .orElse(SmimeUtil.DEFAULT_CIPHER));
	}

	@Override
	public boolean isMessageIdFixingMessage(MimeMessage message) {
		return message instanceof SmimeMessageIdFixingMimeMessage || message instanceof SmimeMessageIdFixingSMTPMessage;
	}

	@NotNull
	private SmimeKey retrieveSmimeKeyFromPkcs12Keystore(@NotNull Pkcs12Config pkcs12Config) {
		if (!SIMPLE_SMIMESTORE_CACHE.containsKey(pkcs12Config)) {
			SmimeKey smimeKey = produceSmimeKey(pkcs12Config);
			if (smimeKey == null) {
				throw new SmimeException(ERROR_OBTAINING_SMIME_KEY);
			}
			SIMPLE_SMIMESTORE_CACHE.put(pkcs12Config, smimeKey);
		}
        return SIMPLE_SMIMESTORE_CACHE.get(pkcs12Config);
	}

	@Nullable
	private SmimeKey produceSmimeKey(final @NotNull Pkcs12Config pkcs12) {
		return new SmimeKeyStore(new ByteArrayInputStream(pkcs12.getPkcs12StoreData()), pkcs12.getStorePassword())
				.getPrivateKey(pkcs12.getKeyAlias(), pkcs12.getKeyPassword());
	}

	@Override
	public  boolean isGeneratedSmimeMessageId(String key, T headerValue) {
		return SmimeRecognitionUtil.isGeneratedSmimeMessageId(key, headerValue);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy