OfficeDev / ews-java-api

A java client library to access Exchange web services. The API works against Office 365 Exchange Online as well as on premises Exchange.
MIT License
868 stars 559 forks source link

How to retrieve original attachments from a signed mail using Microsoft Exchange Web services (EWS Managed API) #730

Open MonicaJ94 opened 4 years ago

MonicaJ94 commented 4 years ago

I am using EWS Managed Java API 2.0 for connecting to an Exchange Online (O365) mailbox and read the attachments(txt/xml/pdf) from emails. I am able to establish the connection successfully and read/load attachments from normal emails. But for S/MIME Signed/encrypted emails having "ContentType' as “application/pkcs7-mime; name=smime.p7m”, not able to retrieve original attachments (txt/xml/pdf) instead only smime.p7m is saved as attachment. Tried using item.getMimeContent() method, but still smime.p7m is only shown. Is there any way to retrieve and load the original attachments using this API. Please suggest.

michiwieland commented 3 years ago

This seems to be an general problem with EWS (see https://stackoverflow.com/questions/63504977/ews-not-listing-inline-images-among-attachments)

godtrack commented 3 years ago

Hello, I also met this problem, may I ask whether it has been solved?

michiwieland commented 3 years ago

There is probably a more general solution to this. Nevertheless maybe this solution helps..



protected static final String SMIME_SIGNED_CONTENT_TYPE = "multipart/signed";
protected static final String PKCS7_DETACHED_SIGNATURE_MIME_TYPE = "application/pkcs7-signature";
protected static final String PKCS7_SIGNED_ENVELOPED_FILE_EXTENSION = "p7m";

...

EmailMessage exchangeEmail = getRawEmail(ref, EMAIL_PROPERTY_SET);
FileAttachment signedEmail = getSignedEmailAttachment(exchangeMail);
if (signedEmail != null) {
       signedEmail.load();
      MimeMessage mimeMessage = MailHelper.createMessageFromBytes(signedEmail.getContent());
      List<BinaryResource> envelopedAttachments = MailHelper.getAttachments(mimeMessage);
      for (BinaryResource envelopedAttachment : envelopedAttachments) {
        if (!PKCS7_DETACHED_SIGNATURE_MIME_TYPE.equals(envelopedAttachment.getContentType())) {
          // no referenced attachments, as they are all contained in the *.p7m attachment
          addAttachment(envelopedAttachment);
        }
      }
}

protected EmailMessage getRawEmail(ItemReference ref, PropertySet properties) {
    try {
      EmailMessage exchangeEmail = EmailMessage.bind(
          getService().getExchangeService(),
          ItemId.getItemIdFromString(ref.getItemNo()),
          properties);
      return exchangeEmail;
    }
    catch (Exception e) {
      handleException("Retrieving email", e);
    }
    return null;
  }

/**
   * If an email is signed with S/MIME, the attachments are concealed inside the *.p7m attachment.
   */
  protected FileAttachment getSignedEmailAttachment(EmailMessage exchangeMail) throws ServiceLocalException {
    if (exchangeMail.getHasAttachments() && CollectionUtility.size(exchangeMail.getAttachments().getItems()) == 1) {
      Attachment firstAttachment = CollectionUtility.firstElement(exchangeMail.getAttachments().getItems());
      if (firstAttachment != null && SMIME_SIGNED_CONTENT_TYPE.equals(firstAttachment.getContentType()) && StringUtility.endsWith(firstAttachment.getName(), PKCS7_SIGNED_ENVELOPED_FILE_EXTENSION)
          && firstAttachment instanceof FileAttachment) {
        return (FileAttachment) firstAttachment;
      }
    }
    return null;
  }
godtrack commented 3 years ago

@michiwieland Thank you very much. It's very helpful

godtrack commented 3 years ago

I have solved this problem and our project is already running. Here I provide my solution, which I feel is more perfect.

Maven needs to be introduced

<dependency>
    <groupId>org.eclipse.scout.rt</groupId>
    <artifactId>org.eclipse.scout.rt.mail</artifactId>
    <version>11.0.14</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.52</version>
</dependency>

code

       //获取邮件对象 此处模拟
        EmailMessage emailMsg = new EmailMessage((ExchangeService) null);

        //是否是加密邮件
        if (emailMsg.getAttachments().getItems().size() == 1 && emailMsg.getAttachments().getItems().get(0) instanceof FileAttachment
                && StringUtils.endsWithIgnoreCase(emailMsg.getAttachments().getItems().get(0).getName(), ".p7m")) {
            ArrayList<File> tempFileList = new ArrayList<>();

            FileAttachment attachment = (FileAttachment) emailMsg.getAttachments().getItems().get(0);
            attachment.load();

            byte[] attachmentBytes = attachment.getContent();
            if (StringUtils.containsIgnoreCase(emailMsg.getAttachments().getItems().get(0).getContentType(), "multipart/signed")) {
                // 明文模式签名
                attachmentBytes = attachment.getContent();
            } else if (StringUtils.containsIgnoreCase(emailMsg.getAttachments().getItems().get(0).getContentType(), "application/pkcs")) {
                // 密文模式签名
                CMSSignedData sign = new CMSSignedData(attachment.getContent());
                Object content = sign.getSignedContent().getContent();
                if (content instanceof byte[]) {
                    attachmentBytes = (byte[]) content;
                }
            }

            MailHelper mailHelper = new MailHelper();
            MimeMessage mimeMessage = mailHelper.createMessageFromBytes(attachmentBytes);

            //解析消息正文
            String body = mailHelper.getHtmlBody(mimeMessage);
            System.out.println(body);

            //解析附件  内联附件/邮件附件
            List<Part> attachmentParts = new ArrayList<>();
            List<Part> inlineAttachmentParts = new ArrayList<>();
            mailHelper.collectMailParts(mimeMessage, null, attachmentParts, inlineAttachmentParts);
            //内联附件
            for (Part attachmentPart : inlineAttachmentParts) {
                String[] cheaders = attachmentPart.getHeader("Content-ID");
                if (cheaders == null || cheaders.length < 1) {
                    continue;
                }
                String contentId = cheaders[0];
                if (StringUtils.isBlank(contentId)) {
                    continue;
                }
                //内联附件CID
                contentId = contentId.replaceAll("(?:^<|>$)", "");
                contentId = "cid:" + contentId;
                //文件流
                InputStream stream = attachmentPart.getInputStream();
            }
            //邮件附件
            for (Part attachmentPart : attachmentParts) {
                if (StringUtils.containsIgnoreCase(attachmentPart.getContentType(), "application/pkcs7-signature")) {
                    //签名 smime.p7s 文件,直接过滤
                    continue;
                }

                //获取文件名称
                String fileName = attachmentPart.getFileName();
                if (StringUtils.containsIgnoreCase(attachmentPart.getContentType(), "message/rfc822")) {
                    //项目附件名称
                    fileName = mailHelper.getFilenameFromRefc822Attachment(attachmentPart);
                }
                fileName = fileName == null ? UUID.randomUUID().toString() : MimeUtility.decodeText(fileName);
                //文件流
                InputStream stream = attachmentPart.getInputStream();
            }

        }