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