View Javadoc

1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  
21  
22  package org.apache.james.mailet.crypto.mailet;
23  
24  import java.io.IOException;
25  import java.security.GeneralSecurityException;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.Iterator;
29  
30  import javax.mail.MessagingException;
31  import javax.mail.Multipart;
32  import javax.mail.Part;
33  import javax.mail.internet.MimeBodyPart;
34  import javax.mail.internet.MimeMessage;
35  
36  import org.apache.james.mailet.crypto.SMIMEKeyHolder;
37  import org.apache.mailet.base.GenericMailet;
38  import org.apache.mailet.Mail;
39  import org.apache.mailet.MailetConfig;
40  import org.bouncycastle.cms.CMSException;
41  import org.bouncycastle.cms.RecipientId;
42  import org.bouncycastle.cms.RecipientInformation;
43  import org.bouncycastle.mail.smime.SMIMEEnveloped;
44  import org.bouncycastle.mail.smime.SMIMEUtil;
45  
46  /**
47   * This mailet decrypts a s/mime encrypted message. It takes as input an
48   * encrypted message and it tries to dechiper it using the key specified in its
49   * configuration. If the decryption is successful the mail will be changed and
50   * it will contain the decrypted message. The mail attribute
51   * <code>org.apache.james.SMIMEDecrypt</code> will contain the public
52   * certificate of the key used in the process. 
53   * 
54   * The configuration parameters of this mailet are summarized below. The firsts
55   * define the keystore where the key that will be used to decrypt messages is
56   * saved.
57   * <ul>
58   * <li>keyStoreType (default: system dependent): defines the type of the store.
59   * Usually jks, pkcs12 or pkcs7</li>
60   * <li>keyStoreFileName (mandatory): private key store path.</li>
61   * <li>keyStorePassword (default: ""): private key store password</li>
62   * </ul>
63   * The other parameters define which private key have to be used. (if the store
64   * contains more than one key).
65   * <ul>
66   * <li>keyAlias: private key alias.</li>
67   * <li>keyPass: private key password</li>
68   * </ul>
69   * 
70   */
71  public class SMIMEDecrypt extends GenericMailet {
72  
73      private SMIMEKeyHolder keyHolder;
74      protected String mailAttribute = "org.apache.james.SMIMEDecrypt";
75      
76      public void init() throws MessagingException {
77          super.init();
78          
79          MailetConfig config = getMailetConfig();
80          
81          String privateStoreType = config.getInitParameter("keyStoreType");
82          
83          String privateStoreFile = config.getInitParameter("keyStoreFileName");
84          if (privateStoreFile == null) throw new MessagingException("No keyStoreFileName specified");
85          
86          String privateStorePass = config.getInitParameter("keyStorePassword");
87          
88          String keyAlias= config.getInitParameter("keyAlias");
89          String keyPass = config.getInitParameter("keyAliasPassword");
90          
91          String mailAttributeConf = config.getInitParameter("mailAttribute");
92          if (mailAttributeConf != null) mailAttribute = mailAttributeConf;
93          
94          try {
95              keyHolder = new SMIMEKeyHolder(privateStoreFile, privateStorePass, keyAlias, keyPass, privateStoreType);
96          } catch (IOException e) {
97              throw new MessagingException("Error loading keystore", e);
98          } catch (GeneralSecurityException e) {
99              throw new MessagingException("Error loading keystore", e);
100         }
101 
102         
103     }
104     
105     /**
106      * @see org.apache.mailet.Mailet#service(org.apache.mailet.Mail)
107      */
108     public void service(Mail mail) throws MessagingException {
109         MimeMessage message = mail.getMessage();
110         Part strippedMessage = null;
111         log("Starting message decryption..");
112         if (message.isMimeType("application/x-pkcs7-mime") || message.isMimeType("application/pkcs7-mime")) {
113             try {
114                 SMIMEEnveloped env = new SMIMEEnveloped(message);
115                 Collection recipients = env.getRecipientInfos().getRecipients();
116                 for (Iterator iter = recipients.iterator();iter.hasNext();) {
117                     RecipientInformation info = (RecipientInformation) iter.next();
118                     RecipientId id = info.getRID();
119                     if (id.match(keyHolder.getCertificate())) {
120                         try {
121                             MimeBodyPart part = SMIMEUtil.toMimeBodyPart(info.getContent(keyHolder.getPrivateKey(), "BC"));
122                             // strippedMessage contains the decrypted message.
123                             strippedMessage = part;
124                             log("Encrypted message decrypted");
125                         } catch (Exception e) { 
126                             throw new MessagingException("Error during the decryption of the message", e); }
127                     } else {
128                         log("Found an encrypted message but it isn't encrypted for the supplied key");
129                     }
130                 }
131             } catch (CMSException e) { throw new MessagingException("Error during the decryption of the message",e); }
132         }
133         
134         // if the decryption has been successful..
135         if (strippedMessage != null) {
136             // I put the private key's public certificate as a mailattribute.
137             // I create a list of certificate because I want to minic the
138             // behavior of the SMIMEVerifySignature mailet. In that way
139             // it is possible to reuse the same matchers to analyze
140             // the result of the operation.
141             ArrayList list = new ArrayList(1);
142             list.add(keyHolder.getCertificate());
143             mail.setAttribute(mailAttribute, list);
144 
145             // I start the message stripping.
146             try {
147                 MimeMessage newmex = new MimeMessage(message);
148                 Object obj = strippedMessage.getContent();
149                 if (obj instanceof Multipart) {
150                     log("The message is multipart, content type "+((Multipart)obj).getContentType());
151                     newmex.setContent((Multipart)obj);
152                 } else {
153                     newmex.setContent(obj, strippedMessage.getContentType());
154                     newmex.setDisposition(null);
155                 }
156                 newmex.saveChanges();
157                 mail.setMessage(newmex);
158             } catch (IOException e) { 
159                 log("Error during the strip of the encrypted message");
160                 throw new MessagingException("Error during the stripping of the encrypted message",e);
161             }
162         }
163     }
164 }