View Javadoc

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