Skip to content
This repository has been archived by the owner on Jan 6, 2022. It is now read-only.

Added support for SMTPMessage #9

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
.project
.classpath
.checkstyle
/.idea
/*.iml
76 changes: 70 additions & 6 deletions src/main/java/net/markenwerk/utils/mail/smime/SmimeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,18 @@
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimePart;

import com.sun.mail.smtp.SMTPMessage;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
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.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
Expand Down Expand Up @@ -390,23 +394,28 @@ private static JcaCertStore getCertificateStore(SmimeKey smimeKey) throws Certif
}
return new JcaCertStore(certificateList);
}

/**
* Signs a MIME message and yields a new S/MIME signed MIME message.
*
*
* @param session
* The {@link Session} that is used in conjunction with the
* original {@link MimeMessage}.
* @param mimeMessage
* The original {@link MimeMessage} to be signed.
* The original {@link MimeMessage} or {@link SMTPMessage} to be signed.
* @param smimeKey
* The {@link SmimeKey} used to obtain the {@link PrivateKey} to
* sign the original message with.
* @return The new S/MIME signed {@link MimeMessage}.
* @return The new S/MIME signed {@link MimeMessage} or {@link SMTPMessage}.
*/
public static MimeMessage sign(Session session, MimeMessage mimeMessage, SmimeKey smimeKey) {
public static <T extends MimeMessage> T sign(Session session, T mimeMessage, SmimeKey smimeKey) {
return (mimeMessage instanceof SMTPMessage)
? sign(mimeMessage, (T) new SMTPMessage(session), smimeKey)
: sign(mimeMessage, (T) new MimeMessage(session), smimeKey);
}

private static <T extends MimeMessage> T sign(T mimeMessage, T signedMessage, SmimeKey smimeKey) {
try {
MimeMessage signedMessage = new MimeMessage(session);
copyHeaderLines(mimeMessage, signedMessage);
copyContent(sign(extractMimeBodyPart(mimeMessage), smimeKey), signedMessage);
return signedMessage;
Expand Down Expand Up @@ -491,6 +500,61 @@ private static boolean checkSignature(SMIMESigned smimeSigned) throws MessagingE
throw handledException(e);
}
}

/**
* @param mimeMultipart
* The {@link MimeMultipart} to be checked.
* @return The subject / address to which the certificate was issued to. Email clients may use this to show
* {@code "Signed by: <subject / address>"}
*/
public static String getSignedByAddress(MimeMultipart mimeMultipart) {
try {
return getSignedByAddress(new SMIMESigned(mimeMultipart));
} catch (Exception e) {
throw handledException(e);
}
}

/**
* @param mimePart
* The {@link MimePart} to be checked.
* @return The subject / address to which the certificate was issued to. Email clients may use this to show
* {@code "Signed by: <subject / address>"}
*/
public static String getSignedByAddress(MimePart mimePart) {
try {
if (mimePart.isMimeType("multipart/signed")) {
return getSignedByAddress(new SMIMESigned((MimeMultipart) mimePart.getContent()));
} else if (mimePart.isMimeType("application/pkcs7-mime") || mimePart.isMimeType("application/x-pkcs7-mime")) {
return getSignedByAddress(new SMIMESigned(mimePart));
} else {
throw new SmimeException("Message not signed");
}
} catch (Exception e) {
throw handledException(e);
}
}

/**
* Returns the subject / address to which the certificate was issued to. Email clients may use this to show
* {@code "Signed by: <subject / address>"}
*/
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 handledException(e);
}
}

private static X509Certificate getCertificate(@SuppressWarnings("rawtypes") Store certificates, SignerId signerId)
throws CertificateException {
Expand Down