You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ad...@apache.org on 2016/11/17 09:38:34 UTC

[4/9] james-project git commit: JAMES-1856 Rename crypto mailet package to mailets for name resolution

http://git-wip-us.apache.org/repos/asf/james-project/blob/a72d89b4/mailet/crypto/src/main/java/org/apache/james/transport/mailets/AbstractSign.java
----------------------------------------------------------------------
diff --git a/mailet/crypto/src/main/java/org/apache/james/transport/mailets/AbstractSign.java b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/AbstractSign.java
new file mode 100644
index 0000000..8f310d1
--- /dev/null
+++ b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/AbstractSign.java
@@ -0,0 +1,714 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+package org.apache.james.transport.mailets;
+
+import org.apache.james.transport.KeyHolder;
+import org.apache.james.transport.SMIMEAttributeNames;
+import org.apache.mailet.base.GenericMailet;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.base.RFC2822Headers;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.internet.ParseException;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.lang.reflect.Constructor;
+
+/**
+ * <P>Abstract mailet providing common SMIME signature services.
+ * It can be subclassed to make authoring signing mailets simple.
+ * By extending it and overriding one or more of the following methods a new behaviour can
+ * be quickly created without the author having to address any issue other than
+ * the relevant one:</P>
+ * <ul>
+ * <li>{@link #initDebug}, {@link #setDebug} and {@link #isDebug} manage the debugging mode.</li>
+ * <li>{@link #initExplanationText}, {@link #setExplanationText} and {@link #getExplanationText} manage the text of
+ * an attachment that will be added to explain the meaning of this server-side signature.</li>
+ * <li>{@link #initKeyHolder}, {@link #setKeyHolder} and {@link #getKeyHolder} manage the {@link KeyHolder} object that will
+ * contain the keys and certificates and will do the crypto work.</li>
+ * <li>{@link #initPostmasterSigns}, {@link #setPostmasterSigns} and {@link #isPostmasterSigns}
+ * determines whether messages originated by the Postmaster will be signed or not.</li>
+ * <li>{@link #initRebuildFrom}, {@link #setRebuildFrom} and {@link #isRebuildFrom}
+ * determines whether the "From:" header will be rebuilt to neutralize the wrong behaviour of
+ * some MUAs like Microsoft Outlook Express.</li>
+ * <li>{@link #initSignerName}, {@link #setSignerName} and {@link #getSignerName} manage the name
+ * of the signer to be shown in the explanation text.</li>
+ * <li>{@link #isOkToSign} controls whether the mail can be signed or not.</li>
+ * <li>The abstract method {@link #getWrapperBodyPart} returns the massaged {@link javax.mail.internet.MimeBodyPart}
+ * that will be signed, or null if the message has to be signed "as is".</li>
+ * </ul>
+ *
+ * <P>Handles the following init parameters:</P>
+ * <ul>
+ * <li>&lt;keyHolderClass&gt;: Sets the class of the KeyHolder object that will handle the cryptography functions,
+ * for example org.apache.james.security.SMIMEKeyHolder for SMIME.</li>
+ * <li>&lt;debug&gt;: if <CODE>true</CODE> some useful information is logged.
+ * The default is <CODE>false</CODE>.</li>
+ * <li>&lt;keyStoreFileName&gt;: the {@link java.security.KeyStore} full file name.</li>
+ * <li>&lt;keyStorePassword&gt;: the <CODE>KeyStore</CODE> password.
+ *      If given, it is used to check the integrity of the keystore data,
+ *      otherwise, if null, the integrity of the keystore is not checked.</li>
+ * <li>&lt;keyAlias&gt;: the alias name to use to search the Key using {@link java.security.KeyStore#getKey}.
+ * The default is to look for the first and only alias in the keystore;
+ * if zero or more than one is found a {@link java.security.KeyStoreException} is thrown.</li>
+ * <li>&lt;keyAliasPassword&gt;: the alias password. The default is to use the <CODE>KeyStore</CODE> password.
+ *      At least one of the passwords must be provided.</li>
+ * <li>&lt;keyStoreType&gt;: the type of the keystore. The default will use {@link java.security.KeyStore#getDefaultType}.</li>
+ * <li>&lt;postmasterSigns&gt;: if <CODE>true</CODE> the message will be signed even if the sender is the Postmaster.
+ * The default is <CODE>false</CODE>.</li></li>
+ * <li>&lt;rebuildFrom&gt;: If <CODE>true</CODE> will modify the "From:" header.
+ * For more info see {@link #isRebuildFrom}.
+ * The default is <CODE>false</CODE>.</li>
+ * <li>&lt;signerName&gt;: the name of the signer to be shown in the explanation text.
+ * The default is to use the "CN=" property of the signing certificate.</li>
+ * <li>&lt;explanationText&gt;: the text of an explanation of the meaning of this server-side signature.
+ * May contain the following substitution patterns (see also {@link #getReplacedExplanationText}):
+ * <CODE>[signerName]</CODE>, <CODE>[signerAddress]</CODE>, <CODE>[reversePath]</CODE>, <CODE>[headers]</CODE>.
+ * It should be included in the signature.
+ * The actual presentation of the text depends on the specific concrete mailet subclass:
+ * see for example {@link SMIMESign}.
+ * The default is to not have any explanation text.</li>
+ * </ul>
+ * @version CVS $Revision$ $Date$
+ * @since 2.2.1
+ */
+public abstract class AbstractSign extends GenericMailet {
+    
+    private static final String HEADERS_PATTERN = "[headers]";
+    
+    private static final String SIGNER_NAME_PATTERN = "[signerName]";
+    
+    private static final String SIGNER_ADDRESS_PATTERN = "[signerAddress]";
+    
+    private static final String REVERSE_PATH_PATTERN = "[reversePath]";
+    
+    /**
+     * Holds value of property debug.
+     */
+    private boolean debug;
+    
+    /**
+     * Holds value of property keyHolderClass.
+     */
+    private Class<?> keyHolderClass;
+    
+    /**
+     * Holds value of property explanationText.
+     */
+    private String explanationText;
+    
+    /**
+     * Holds value of property keyHolder.
+     */
+    private KeyHolder keyHolder;
+    
+    /**
+     * Holds value of property postmasterSigns.
+     */
+    private boolean postmasterSigns;
+    
+    /**
+     * Holds value of property rebuildFrom.
+     */
+    private boolean rebuildFrom;
+    
+    /**
+     * Holds value of property signerName.
+     */
+    private String signerName;
+    
+    /**
+     * Gets the expected init parameters.
+     * @return An array containing the parameter names allowed for this mailet.
+     */
+    protected abstract String[] getAllowedInitParameters();
+    
+    /* ******************************************************************** */
+    /* ****************** Begin of setters and getters ******************** */
+    /* ******************************************************************** */
+    
+    /**
+     * Initializer for property debug.
+     */
+    protected void initDebug() {
+        setDebug((getInitParameter("debug") == null) ? false : Boolean.valueOf(getInitParameter("debug")));
+    }
+    
+    /**
+     * Getter for property debug.
+     * @return Value of property debug.
+     */
+    public boolean isDebug() {
+        return this.debug;
+    }
+    
+    /**
+     * Setter for property debug.
+     * @param debug New value of property debug.
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+    
+    /**
+     * Initializer for property keyHolderClass.
+     */
+    protected void initKeyHolderClass() throws MessagingException {
+        String keyHolderClassName = getInitParameter("keyHolderClass");
+        if (keyHolderClassName == null) {
+            throw new MessagingException("<keyHolderClass> parameter missing.");
+        }
+        try {
+            setKeyHolderClass(Class.forName(keyHolderClassName));
+        } catch (ClassNotFoundException cnfe) {
+            throw new MessagingException("The specified <keyHolderClass> does not exist: " + keyHolderClassName);
+        }
+        if (isDebug()) {
+            log("keyHolderClass: " + getKeyHolderClass());
+        }
+    }
+    
+    /**
+     * Getter for property keyHolderClass.
+     * @return Value of property keyHolderClass.
+     */
+    public Class<?> getKeyHolderClass() {
+        return this.keyHolderClass;
+    }
+    
+    /**
+     * Setter for property keyHolderClass.
+     * @param keyHolderClass New value of property keyHolderClass.
+     */
+    public void setKeyHolderClass(Class<?> keyHolderClass) {
+        this.keyHolderClass = keyHolderClass;
+    }
+    
+    /**
+     * Initializer for property explanationText.
+     */
+    protected void initExplanationText() {
+        setExplanationText(getInitParameter("explanationText"));
+        if (isDebug()) {
+            log("Explanation text:\r\n" + getExplanationText());
+        }
+    }
+    
+    /**
+     * Getter for property explanationText.
+     * Text to be used in the SignatureExplanation.txt file.
+     * @return Value of property explanationText.
+     */
+    public String getExplanationText() {
+        return this.explanationText;
+    }
+    
+    /**
+     * Setter for property explanationText.
+     * @param explanationText New value of property explanationText.
+     */
+    public void setExplanationText(String explanationText) {
+        this.explanationText = explanationText;
+    }
+    
+    /**
+     * Initializer for property keyHolder.
+     */
+    protected void initKeyHolder() throws Exception {
+        Constructor<?> keyHolderConstructor;
+        try {
+            keyHolderConstructor = keyHolderClass.getConstructor(new Class[] {String.class, String.class, String.class, String.class, String.class});
+        } catch (NoSuchMethodException nsme) {
+            throw new MessagingException("The needed constructor does not exist: "
+                    + keyHolderClass + "(String, String, String, String, String)");
+        }
+        
+        
+        String keyStoreFileName = getInitParameter("keyStoreFileName");
+        if (keyStoreFileName == null) {
+            throw new MessagingException("<keyStoreFileName> parameter missing.");
+        }
+        
+        String keyStorePassword = getInitParameter("keyStorePassword");
+        if (keyStorePassword == null) {
+            throw new MessagingException("<keyStorePassword> parameter missing.");
+        }
+        String keyAliasPassword = getInitParameter("keyAliasPassword");
+        if (keyAliasPassword == null) {
+            keyAliasPassword = keyStorePassword;
+            if (isDebug()) {
+                log("<keyAliasPassword> parameter not specified: will default to the <keyStorePassword> parameter.");
+            }
+        }
+        
+        String keyStoreType = getInitParameter("keyStoreType");
+        if (keyStoreType == null) {
+            if (isDebug()) {
+                log("<keyStoreType> parameter not specified: the default will be as appropriate to the keyStore requested.");
+            }
+        }
+        
+        String keyAlias = getInitParameter("keyAlias");
+        if (keyAlias == null) {
+            if (isDebug()) {
+                log("<keyAlias> parameter not specified: will look for the first one in the keystore.");
+            }
+        }
+        
+        if (isDebug()) {
+            StringBuilder logBuffer =
+            new StringBuilder(1024)
+            .append("KeyStore related parameters:")
+            .append("  keyStoreFileName=").append(keyStoreFileName)
+            .append(", keyStoreType=").append(keyStoreType)
+            .append(", keyAlias=").append(keyAlias)
+            .append(" ");
+            log(logBuffer.toString());
+        }
+            
+        // Certificate preparation
+        Object[] parameters = {keyStoreFileName, keyStorePassword, keyAlias, keyAliasPassword, keyStoreType};
+        setKeyHolder((KeyHolder)keyHolderConstructor.newInstance(parameters));
+        
+        if (isDebug()) {
+            log("Subject Distinguished Name: " + getKeyHolder().getSignerDistinguishedName());
+        }
+        
+        if (getKeyHolder().getSignerAddress() == null) {
+            throw new MessagingException("Signer address missing in the certificate.");
+        }
+    }
+    
+    /**
+     * Getter for property keyHolder.
+     * It is <CODE>protected</CODE> instead of <CODE>public</CODE> for security reasons.
+     * @return Value of property keyHolder.
+     */
+    protected KeyHolder getKeyHolder() {
+        return this.keyHolder;
+    }
+    
+    /**
+     * Setter for property keyHolder.
+     * It is <CODE>protected</CODE> instead of <CODE>public</CODE> for security reasons.
+     * @param keyHolder New value of property keyHolder.
+     */
+    protected void setKeyHolder(KeyHolder keyHolder) {
+        this.keyHolder = keyHolder;
+    }
+    
+    /**
+     * Initializer for property postmasterSigns.
+     */
+    protected void initPostmasterSigns() {
+        setPostmasterSigns((getInitParameter("postmasterSigns") == null) ? false : Boolean.valueOf(getInitParameter("postmasterSigns")));
+    }
+    
+    /**
+     * Getter for property postmasterSigns.
+     * If true will sign messages signed by the postmaster.
+     * @return Value of property postmasterSigns.
+     */
+    public boolean isPostmasterSigns() {
+        return this.postmasterSigns;
+    }
+    
+    /**
+     * Setter for property postmasterSigns.
+     * @param postmasterSigns New value of property postmasterSigns.
+     */
+    public void setPostmasterSigns(boolean postmasterSigns) {
+        this.postmasterSigns = postmasterSigns;
+    }
+    
+    /**
+     * Initializer for property rebuildFrom.
+     */
+    protected void initRebuildFrom() throws MessagingException {
+        setRebuildFrom((getInitParameter("rebuildFrom") == null) ? false : Boolean.valueOf(getInitParameter("rebuildFrom")));
+        if (isDebug()) {
+            if (isRebuildFrom()) {
+                log("Will modify the \"From:\" header.");
+            } else {
+                log("Will leave the \"From:\" header unchanged.");
+            }
+        }
+    }
+    
+    /**
+     * Getter for property rebuildFrom.
+     * If true will modify the "From:" header.
+     * <P>The modification is as follows:
+     * assuming that the signer mail address in the signer certificate is <I>trusted-server@xxx.com&gt;</I>
+     * and that <I>From: "John Smith" <jo...@xxx.com></I>
+     * we will get <I>From: "John Smith" <jo...@xxx.com>" &lt;trusted-server@xxx.com&gt;</I>.</P>
+     * <P>If the "ReplyTo:" header is missing or empty it will be set to the original "From:" header.</P>
+     * <P>Such modification is necessary to achieve a correct behaviour
+     * with some mail clients (e.g. Microsoft Outlook Express).</P>
+     * @return Value of property rebuildFrom.
+     */
+    public boolean isRebuildFrom() {
+        return this.rebuildFrom;
+    }
+    
+    /**
+     * Setter for property rebuildFrom.
+     * @param rebuildFrom New value of property rebuildFrom.
+     */
+    public void setRebuildFrom(boolean rebuildFrom) {
+        this.rebuildFrom = rebuildFrom;
+    }
+    
+    /**
+     * Initializer for property signerName.
+     */
+    protected void initSignerName() {
+        setSignerName(getInitParameter("signerName"));
+        if (getSignerName() == null) {
+            if (getKeyHolder() == null) {
+                throw new RuntimeException("initKeyHolder() must be invoked before initSignerName()");
+            }
+            setSignerName(getKeyHolder().getSignerCN());
+            if (isDebug()) {
+                log("<signerName> parameter not specified: will use the certificate signer \"CN=\" attribute.");
+            }
+        }
+    }
+    
+    /**
+     * Getter for property signerName.
+     * @return Value of property signerName.
+     */
+    public String getSignerName() {
+        return this.signerName;
+    }
+    
+    /**
+     * Setter for property signerName.
+     * @param signerName New value of property signerName.
+     */
+    public void setSignerName(String signerName) {
+        this.signerName = signerName;
+    }
+    
+    /* ******************************************************************** */
+    /* ****************** End of setters and getters ********************** */
+    /* ******************************************************************** */    
+    
+    /**
+     * Mailet initialization routine.
+     */
+    public void init() throws MessagingException {
+        
+        // check that all init parameters have been declared in allowedInitParameters
+        checkInitParameters(getAllowedInitParameters());
+        
+        try {
+            initDebug();
+            if (isDebug()) {
+                log("Initializing");
+            }
+            
+            initKeyHolderClass();
+            initKeyHolder();
+            initSignerName();
+            initPostmasterSigns();
+            initRebuildFrom();
+            initExplanationText();
+            
+            
+        } catch (MessagingException me) {
+            throw me;
+        } catch (Exception e) {
+            log("Exception thrown", e);
+            throw new MessagingException("Exception thrown", e);
+        } finally {
+            if (isDebug()) {
+                StringBuilder logBuffer =
+                new StringBuilder(1024)
+                .append("Other parameters:")
+                .append(", signerName=").append(getSignerName())
+                .append(", postmasterSigns=").append(postmasterSigns)
+                .append(", rebuildFrom=").append(rebuildFrom)
+                .append(" ");
+                log(logBuffer.toString());
+            }
+        }
+        
+    }
+    
+    /**
+     * Service does the hard work, and signs
+     *
+     * @param mail the mail to sign
+     * @throws MessagingException if a problem arises signing the mail
+     */
+    public void service(Mail mail) throws MessagingException {
+        
+        try {
+            if (!isOkToSign(mail)) {
+                return;
+            }
+            
+            MimeBodyPart wrapperBodyPart = getWrapperBodyPart(mail);
+            
+            MimeMessage originalMessage = mail.getMessage();
+            
+            // do it
+            MimeMultipart signedMimeMultipart;
+            if (wrapperBodyPart != null) {
+                signedMimeMultipart = getKeyHolder().generate(wrapperBodyPart);
+            } else {
+                signedMimeMultipart = getKeyHolder().generate(originalMessage);
+            }
+            
+            MimeMessage newMessage = new MimeMessage(Session.getDefaultInstance(System.getProperties(),
+            null));
+            @SuppressWarnings("unchecked")
+            Enumeration<String> headerEnum = originalMessage.getAllHeaderLines();
+            while (headerEnum.hasMoreElements()) {
+                newMessage.addHeaderLine((String) headerEnum.nextElement());
+            }
+            
+            newMessage.setSender(new InternetAddress(getKeyHolder().getSignerAddress(), getSignerName()));
+  
+            if (isRebuildFrom()) {
+                // builds a new "mixed" "From:" header
+                InternetAddress modifiedFromIA = new InternetAddress(getKeyHolder().getSignerAddress(), mail.getSender().toString());
+                newMessage.setFrom(modifiedFromIA);
+                
+                // if the original "ReplyTo:" header is missing sets it to the original "From:" header
+                newMessage.setReplyTo(originalMessage.getReplyTo());
+            }
+            
+            newMessage.setContent(signedMimeMultipart, signedMimeMultipart.getContentType());
+            String messageId = originalMessage.getMessageID();
+            newMessage.saveChanges();
+            if (messageId != null) {
+                newMessage.setHeader(RFC2822Headers.MESSAGE_ID, messageId);
+            }
+            
+            mail.setMessage(newMessage);
+            
+            // marks this mail as server-signed
+            mail.setAttribute(SMIMEAttributeNames.SMIME_SIGNING_MAILET, this.getClass().getName());
+            // it is valid for us by definition (signed here by us)
+            mail.setAttribute(SMIMEAttributeNames.SMIME_SIGNATURE_VALIDITY, "valid");
+            
+            // saves the trusted server signer address
+            // warning: should be same as the mail address in the certificate, but it is not guaranteed
+            mail.setAttribute(SMIMEAttributeNames.SMIME_SIGNER_ADDRESS, getKeyHolder().getSignerAddress());
+            
+            if (isDebug()) {
+                log("Message signed, reverse-path: " + mail.getSender() + ", Id: " + messageId);
+            }
+            
+        } catch (MessagingException me) {
+            log("MessagingException found - could not sign!", me);
+            throw me;
+        } catch (Exception e) {
+            log("Exception found", e);
+            throw new MessagingException("Exception thrown - could not sign!", e);
+        }
+        
+    }
+    
+    
+    /**
+     * <P>Checks if the mail can be signed.</P>
+     * <P>Rules:</P>
+     * <OL>
+     * <LI>The reverse-path != null (it is not a bounce).</LI>
+     * <LI>The sender user must have been SMTP authenticated.</LI>
+     * <LI>Either:</LI>
+     * <UL>
+     * <LI>The reverse-path is the postmaster address and {@link #isPostmasterSigns} returns <I>true</I></LI>
+     * <LI>or the reverse-path == the authenticated user
+     * and there is at least one "From:" address == reverse-path.</LI>.
+     * </UL>
+     * <LI>The message has not already been signed (mimeType != <I>multipart/signed</I>
+     * and != <I>application/pkcs7-mime</I>).</LI>
+     * </OL>
+     * @param mail The mail object to check.
+     * @return True if can be signed.
+     */
+    protected boolean isOkToSign(Mail mail) throws MessagingException {
+
+        MailAddress reversePath = mail.getSender();
+        
+        // Is it a bounce?
+        if (reversePath == null) {
+            return false;
+        }
+        
+        String authUser = (String) mail.getAttribute("org.apache.james.SMTPAuthUser");
+        // was the sender user SMTP authorized?
+        if (authUser == null) {
+            return false;
+        }
+        
+        // The sender is the postmaster?
+        if (getMailetContext().getPostmaster().equals(reversePath)) {
+            // should not sign postmaster sent messages?
+            if (!isPostmasterSigns()) {
+                return false;
+            }
+        } else {
+            // is the reverse-path user different from the SMTP authorized user?
+            if (!reversePath.getLocalPart().equals(authUser)) {
+                return false;
+            }
+            // is there no "From:" address same as the reverse-path?
+            if (!fromAddressSameAsReverse(mail)) {
+                return false;
+            }
+        }
+        
+        
+        // if already signed return false
+        MimeMessage mimeMessage = mail.getMessage();
+        return !(mimeMessage.isMimeType("multipart/signed")
+                || mimeMessage.isMimeType("application/pkcs7-mime"));
+
+    }
+    
+    /**
+     * Creates the {@link javax.mail.internet.MimeBodyPart} that will be signed.
+     * For example, may attach a text file explaining the meaning of the signature,
+     * or an XML file containing information that can be checked by other MTAs.
+     * @param mail The mail to massage.
+     * @return The massaged MimeBodyPart to sign, or null to have the whole message signed "as is".
+     */    
+    protected abstract MimeBodyPart getWrapperBodyPart(Mail mail) throws MessagingException, IOException;
+    
+    /**
+     * Utility method that checks if there is at least one address in the "From:" header
+     * same as the <i>reverse-path</i>.
+     * @param mail The mail to check.
+     * @return True if an address is found, false otherwise.
+     */    
+    protected final boolean fromAddressSameAsReverse(Mail mail) {
+        
+        MailAddress reversePath = mail.getSender();
+        
+        if (reversePath == null) {
+            return false;
+        }
+        
+        try {
+            InternetAddress[] fromArray = (InternetAddress[]) mail.getMessage().getFrom();
+            if (fromArray != null) {
+                for (InternetAddress aFromArray : fromArray) {
+                    MailAddress mailAddress;
+                    try {
+                        mailAddress = new MailAddress(aFromArray);
+                    } catch (ParseException pe) {
+                        log("Unable to parse a \"FROM\" header address: " + aFromArray.toString() + "; ignoring.");
+                        continue;
+                    }
+                    if (mailAddress.equals(reversePath)) {
+                        return true;
+                    }
+                }
+            }
+        } catch (MessagingException me) {
+            log("Unable to parse the \"FROM\" header; ignoring.");
+        }
+        
+        return false;
+        
+    }
+    
+    /**
+     * Utility method for obtaining a string representation of the Message's headers
+     * @param message The message to extract the headers from.
+     * @return The string containing the headers.
+     */
+    protected final String getMessageHeaders(MimeMessage message) throws MessagingException {
+        @SuppressWarnings("unchecked")
+        Enumeration<String> heads = message.getAllHeaderLines();
+        StringBuilder headBuffer = new StringBuilder(1024);
+        while(heads.hasMoreElements()) {
+            headBuffer.append(heads.nextElement().toString()).append("\r\n");
+        }
+        return headBuffer.toString();
+    }
+    
+    /**
+     * Prepares the explanation text making substitutions in the <I>explanationText</I> template string.
+     * Utility method that searches for all occurrences of some pattern strings
+     * and substitute them with the appropriate params.
+     * @param explanationText The template string for the explanation text.
+     * @param signerName The string that will replace the <CODE>[signerName]</CODE> pattern.
+     * @param signerAddress The string that will replace the <CODE>[signerAddress]</CODE> pattern.
+     * @param reversePath The string that will replace the <CODE>[reversePath]</CODE> pattern.
+     * @param headers The string that will replace the <CODE>[headers]</CODE> pattern.
+     * @return The actual explanation text string with all replacements done.
+     */    
+    protected final String getReplacedExplanationText(String explanationText, String signerName,
+    String signerAddress, String reversePath, String headers) {
+        
+        String replacedExplanationText = explanationText;
+        
+        replacedExplanationText = getReplacedString(replacedExplanationText, SIGNER_NAME_PATTERN, signerName);
+        replacedExplanationText = getReplacedString(replacedExplanationText, SIGNER_ADDRESS_PATTERN, signerAddress);
+        replacedExplanationText = getReplacedString(replacedExplanationText, REVERSE_PATH_PATTERN, reversePath);
+        replacedExplanationText = getReplacedString(replacedExplanationText, HEADERS_PATTERN, headers);
+        
+        return replacedExplanationText;
+    }
+    
+    /**
+     * Searches the <I>template</I> String for all occurrences of the <I>pattern</I> string
+     * and creates a new String substituting them with the <I>actual</I> String.
+     * @param template The template String to work on.
+     * @param pattern The string to search for the replacement.
+     * @param actual The actual string to use for the replacement.
+     */    
+    private String getReplacedString(String template, String pattern, String actual) {
+         if (actual != null) {
+             StringBuilder sb = new StringBuilder(template.length());
+            int fromIndex = 0;
+            int index;
+            while ((index = template.indexOf(pattern, fromIndex)) >= 0) {
+                sb.append(template.substring(fromIndex, index));
+                sb.append(actual);
+                fromIndex = index + pattern.length();
+            }
+            if (fromIndex < template.length()){
+                sb.append(template.substring(fromIndex));
+            }
+            return sb.toString();
+        } else {
+            return template;
+        }
+    }
+    
+}
+

http://git-wip-us.apache.org/repos/asf/james-project/blob/a72d89b4/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMECheckSignature.java
----------------------------------------------------------------------
diff --git a/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMECheckSignature.java b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMECheckSignature.java
new file mode 100644
index 0000000..e016a60
--- /dev/null
+++ b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMECheckSignature.java
@@ -0,0 +1,228 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+package org.apache.james.transport.mailets;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.james.transport.KeyStoreHolder;
+import org.apache.james.transport.SMIMESignerInfo;
+import org.apache.mailet.base.GenericMailet;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetConfig;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.mail.smime.SMIMEException;
+import org.bouncycastle.mail.smime.SMIMESigned;
+
+/**
+ * <p>
+ * Verifies the s/mime signature of a message. The s/mime signing ensure that
+ * the private key owner is the real sender of the message. To be checked by
+ * this mailet the s/mime signature must contain the actual signature, the
+ * signer's certificate and optionally a set of certificate that can be used to
+ * create a chain of trust that starts from the signer's certificate and leads
+ * to a known trusted certificate.
+ * </p>
+ * <p>
+ * This check is composed by two steps: firstly it's ensured that the signature
+ * is valid, then it's checked if a chain of trust starting from the signer
+ * certificate and that leads to a trusted certificate can be created. The first
+ * check verifies that the the message has not been modified after the signature
+ * was put and that the signer's certificate was valid at the time of the
+ * signing. The latter should ensure that the signer is who he declare to be.
+ * </p>
+ * <p>
+ * The results of the checks perfomed by this mailet are wrote as a mail
+ * attribute which default name is org.apache.james.SMIMECheckSignature (it can
+ * be changed using the mailet parameter <code>mailAttribute</code>). After
+ * the check this attribute will contain a list of SMIMESignerInfo object, one
+ * for each message's signer. These objects contain the signer's certificate and
+ * the trust path.
+ * </p>
+ * <p>
+ * Optionally, specifying the parameter <code>strip</code>, the signature of
+ * the message can be stripped after the check. The message will become a
+ * standard message without an attached s/mime signature.
+ * </p>
+ * <p>
+ * The configuration parameter of this mailet are summerized below. The firsts
+ * defines the location, the format and the password of the keystore containing
+ * the certificates that are considered trusted. Note: only the trusted certificate
+ * entries are read, the key ones are not.
+ * <ul>
+ * <li>keyStoreType (default: jks): Certificate store format . "jks" is the
+ * standard java certificate store format, but pkcs12 is also quite common and
+ * compatible with standard email clients like Outlook Express and Thunderbird.
+ * <li>keyStoreFileName (default: JAVA_HOME/jre/lib/security/cacert): Certificate
+ * store path.
+ * <li>keyStorePassword (default: ""): Certificate store password.
+ * </ul>
+ * Other parameters configure the behavior of the mailet:
+ * <ul>
+ * <li>strip (default: false): Defines if the s/mime signature of the message
+ * have to be stripped after the check or not. Possible values are true and
+ * false.
+ * <li>mailAttribute (default: org.apache.james.SMIMECheckSignature):
+ * specifies in which attribute the check results will be written.
+ * <li>onlyTrusted (default: true): Usually a message signature to be
+ * considered by this mailet as authentic must be valid and trusted. Setting
+ * this mailet parameter to "false" the last condition is relaxed and also
+ * "untrusted" signature are considered will be considered as authentic.
+ * </ul>
+ * </p>
+ * 
+ */
+public class SMIMECheckSignature extends GenericMailet {
+    
+    protected KeyStoreHolder trustedCertificateStore;
+    
+    protected boolean stripSignature = false;
+    protected boolean onlyTrusted = true;
+    
+    protected String mailAttribute = "org.apache.james.SMIMECheckSignature";
+    
+    public SMIMECheckSignature() {
+        super();
+
+    }
+
+    public void init() throws MessagingException {
+        MailetConfig config = getMailetConfig();
+
+        String stripSignatureConf = config.getInitParameter("strip");
+        if (stripSignatureConf != null) stripSignature = Boolean.valueOf(stripSignatureConf);
+        
+        String onlyTrustedConf = config.getInitParameter("onlyTrusted");
+        if (onlyTrustedConf != null) onlyTrusted = Boolean.valueOf(onlyTrustedConf);
+        
+        String mailAttributeConf = config.getInitParameter("mailAttribute");
+        if (mailAttributeConf != null) mailAttribute = mailAttributeConf;
+        
+        
+        String type = config.getInitParameter("keyStoreType");
+        String file = config.getInitParameter("keyStoreFileName");
+        String password = config.getInitParameter("keyStorePassword");
+        
+        try {
+            if (file != null) trustedCertificateStore = new KeyStoreHolder(file, password, type);
+            else {
+                log("No trusted store path specified, using default store.");
+                trustedCertificateStore = new KeyStoreHolder(password);
+            }
+        } catch (Exception e) {
+            throw new MessagingException("Error loading the trusted certificate store", e);
+        }
+
+    }
+    /**
+     * @see org.apache.mailet.Matcher#match(org.apache.mailet.Mail)
+     */
+    public void service(Mail mail) throws MessagingException {
+        // I extract the MimeMessage from the mail object and I check if the
+        // mime type of the mail is one of the mime types that can contain a
+        // signature.
+        MimeMessage message = mail.getMessage();
+
+        // strippedMessage will contain the signed content of the message 
+        MimeBodyPart strippedMessage =null;
+        
+        List<SMIMESignerInfo> signers=null;
+        
+        try {
+            Object obj = message.getContent();
+            SMIMESigned signed;
+            if (obj instanceof MimeMultipart) signed = new SMIMESigned((MimeMultipart)message.getContent());
+            else if (obj instanceof SMIMESigned) signed = (SMIMESigned) obj;                
+            else if (obj instanceof byte[]) signed = new SMIMESigned(message);
+            else signed = null;
+            
+            if (signed != null) {
+                signers = trustedCertificateStore.verifySignatures(signed);
+                strippedMessage = signed.getContent();
+            } else log("Content not identified as signed");
+            
+            // These errors are logged but they don't cause the 
+            // message to change its state. The message 
+            // is considered as not signed and the process will
+            // go on.
+        } catch (CMSException e) {
+            log("Error during the analysis of the signed message", e);
+            signers = null;
+        } catch (IOException e) {
+            log("IO error during the analysis of the signed message", e);
+            signers = null;
+        } catch (SMIMEException e) {
+            log("Error during the analysis of the signed message", e);
+            signers = null;
+        } catch (Exception e) {
+            e.printStackTrace();
+            log("Generic error occured during the analysis of the message", e);
+            signers = null;
+        }
+        
+        // If at least one mail signer is found 
+        // the mail attributes are set.
+        if (signers != null) {
+            ArrayList<X509Certificate> signerinfolist = new ArrayList<X509Certificate>();
+
+            for (SMIMESignerInfo info : signers) {
+                if (info.isSignValid()
+                        && (!onlyTrusted || info.getCertPath() != null)) {
+                    signerinfolist.add(info.getSignerCertificate());
+                }
+            }
+
+            if (signerinfolist.size() > 0) {
+                mail.setAttribute(mailAttribute, signerinfolist);
+            } else {
+                // if no valid signers are found the message is not modified.
+                strippedMessage = null;
+            }
+        }
+
+        if (stripSignature && strippedMessage != null) {
+            try {
+                Object obj = strippedMessage.getContent();
+                if (obj instanceof Multipart) {
+                    message.setContent((Multipart) obj);
+                } else {
+                    message.setContent(obj, strippedMessage.getContentType());
+                }
+                message.saveChanges();
+                mail.setMessage(message);
+            } catch (Exception e) {
+                throw new MessagingException(
+                        "Error during the extraction of the signed content from the message.",
+                        e);
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/a72d89b4/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMEDecrypt.java
----------------------------------------------------------------------
diff --git a/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMEDecrypt.java b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMEDecrypt.java
new file mode 100644
index 0000000..88b4890
--- /dev/null
+++ b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMEDecrypt.java
@@ -0,0 +1,169 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+package org.apache.james.transport.mailets;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Part;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.transport.SMIMEKeyHolder;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetConfig;
+import org.apache.mailet.base.GenericMailet;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.RecipientId;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
+import org.bouncycastle.mail.smime.SMIMEEnveloped;
+import org.bouncycastle.mail.smime.SMIMEUtil;
+
+/**
+ * This mailet decrypts a s/mime encrypted message. It takes as input an
+ * encrypted message and it tries to dechiper it using the key specified in its
+ * configuration. If the decryption is successful the mail will be changed and
+ * it will contain the decrypted message. The mail attribute
+ * <code>org.apache.james.SMIMEDecrypt</code> will contain the public
+ * certificate of the key used in the process. 
+ * 
+ * The configuration parameters of this mailet are summarized below. The firsts
+ * define the keystore where the key that will be used to decrypt messages is
+ * saved.
+ * <ul>
+ * <li>keyStoreType (default: system dependent): defines the type of the store.
+ * Usually jks, pkcs12 or pkcs7</li>
+ * <li>keyStoreFileName (mandatory): private key store path.</li>
+ * <li>keyStorePassword (default: ""): private key store password</li>
+ * </ul>
+ * The other parameters define which private key have to be used. (if the store
+ * contains more than one key).
+ * <ul>
+ * <li>keyAlias: private key alias.</li>
+ * <li>keyPass: private key password</li>
+ * </ul>
+ * 
+ */
+public class SMIMEDecrypt extends GenericMailet {
+
+    private SMIMEKeyHolder keyHolder;
+    protected String mailAttribute = "org.apache.james.SMIMEDecrypt";
+    
+    public void init() throws MessagingException {
+        super.init();
+        
+        MailetConfig config = getMailetConfig();
+        
+        String privateStoreType = config.getInitParameter("keyStoreType");
+        
+        String privateStoreFile = config.getInitParameter("keyStoreFileName");
+        if (privateStoreFile == null) throw new MessagingException("No keyStoreFileName specified");
+        
+        String privateStorePass = config.getInitParameter("keyStorePassword");
+        
+        String keyAlias= config.getInitParameter("keyAlias");
+        String keyPass = config.getInitParameter("keyAliasPassword");
+        
+        String mailAttributeConf = config.getInitParameter("mailAttribute");
+        if (mailAttributeConf != null) mailAttribute = mailAttributeConf;
+        
+        try {
+            keyHolder = new SMIMEKeyHolder(privateStoreFile, privateStorePass, keyAlias, keyPass, privateStoreType);
+        } catch (IOException e) {
+            throw new MessagingException("Error loading keystore", e);
+        } catch (GeneralSecurityException e) {
+            throw new MessagingException("Error loading keystore", e);
+        }
+
+        
+    }
+    
+    /**
+     * @see org.apache.mailet.Mailet#service(org.apache.mailet.Mail)
+     */
+    public void service(Mail mail) throws MessagingException {
+        MimeMessage message = mail.getMessage();
+        Part strippedMessage = null;
+        log("Starting message decryption..");
+        if (message.isMimeType("application/x-pkcs7-mime") || message.isMimeType("application/pkcs7-mime")) {
+            try {
+                SMIMEEnveloped env = new SMIMEEnveloped(message);
+                RecipientInformationStore informationStore = env.getRecipientInfos();
+                @SuppressWarnings("unchecked")
+                Collection<RecipientInformation> recipients = informationStore.getRecipients();
+                for (RecipientInformation info : recipients) {
+                    RecipientId id = info.getRID();
+                    if (id.match(keyHolder.getCertificate())) {
+                        try {
+                            JceKeyTransEnvelopedRecipient recipient = new JceKeyTransEnvelopedRecipient(keyHolder.getPrivateKey());
+                            // strippedMessage contains the decrypted message.
+                            strippedMessage = SMIMEUtil.toMimeBodyPart(info.getContent(recipient));
+                            log("Encrypted message decrypted");
+                        } catch (Exception e) {
+                            throw new MessagingException("Error during the decryption of the message", e);
+                        }
+                    } else {
+                        log("Found an encrypted message but it isn't encrypted for the supplied key");
+                    }
+                }
+            } catch (CMSException e) {
+                throw new MessagingException("Error during the decryption of the message",e);
+            }
+        }
+
+        // if the decryption has been successful..
+        if (strippedMessage != null) {
+            // I put the private key's public certificate as a mailattribute.
+            // I create a list of certificate because I want to minic the
+            // behavior of the SMIMEVerifySignature mailet. In that way
+            // it is possible to reuse the same matchers to analyze
+            // the result of the operation.
+            ArrayList<X509Certificate> list = new ArrayList<X509Certificate>(1);
+            list.add(keyHolder.getCertificate());
+            mail.setAttribute(mailAttribute, list);
+
+            // I start the message stripping.
+            try {
+                MimeMessage newmex = new MimeMessage(message);
+                Object obj = strippedMessage.getContent();
+                if (obj instanceof Multipart) {
+                    log("The message is multipart, content type "+((Multipart)obj).getContentType());
+                    newmex.setContent((Multipart)obj);
+                } else {
+                    newmex.setContent(obj, strippedMessage.getContentType());
+                    newmex.setDisposition(null);
+                }
+                newmex.saveChanges();
+                mail.setMessage(newmex);
+            } catch (IOException e) { 
+                log("Error during the strip of the encrypted message");
+                throw new MessagingException("Error during the stripping of the encrypted message",e);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/a72d89b4/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMESign.java
----------------------------------------------------------------------
diff --git a/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMESign.java b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMESign.java
new file mode 100644
index 0000000..e875ab9
--- /dev/null
+++ b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMESign.java
@@ -0,0 +1,216 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+package org.apache.james.transport.mailets;
+
+import org.apache.mailet.Mail;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import java.io.IOException;
+
+/**
+ * <p>Puts a <I>server-side</I> SMIME signature on a message.
+ * It is a concrete subclass of {@link Sign}, with very few modifications to it,
+ * to specialize for SMIME.</p>
+ *
+ *  <P>Handles the following init parameters (will comment only the differences from {@link AbstractSign}):</P>
+ * <ul>
+ * <li>&lt;debug&gt;.</li>
+ * <li>&lt;keyStoreFileName&gt;.</li>
+ * <li>&lt;keyStorePassword&gt;.</li>
+ * <li>&lt;keyAlias&gt;.</li>
+ * <li>&lt;keyAliasPassword&gt;.</li>
+ * <li>&lt;keyStoreType&gt;.</li>
+ * <li>&lt;postmasterSigns&gt;. The default is <CODE>true</CODE>.</li>
+ * <li>&lt;rebuildFrom&gt;. The default is <CODE>true</CODE>.</li>
+ * <li>&lt;signerName&gt;.</li>
+ * <li>&lt;explanationText&gt;. There is a default explanation string template in English,
+ * displaying also all the headers of the original message (see {@link #getExplanationText}).</li>
+ * </ul>
+ * @version CVS $Revision$ $Date$
+ * @since 2.3.0
+ */
+public class SMIMESign extends Sign {
+    
+    /**
+     * Return a string describing this mailet.
+     *
+     * @return a string describing this mailet
+     */
+    public String getMailetInfo() {
+        return "SMIME Signature Mailet";
+    }
+    
+    /**
+     *
+     */
+    protected  String[] getAllowedInitParameters() {
+        return new String[]{
+            "debug",
+            "keyStoreFileName",
+            "keyStorePassword",
+            "keyStoreType",
+            "keyAlias",
+            "keyAliasPassword",
+            "signerName",
+            "postmasterSigns",
+            "rebuildFrom",
+            "explanationText"
+        };
+    }
+    
+     /* ******************************************************************** */
+    /* ****************** Begin of setters and getters ******************** */
+    /* ******************************************************************** */    
+    
+    /**
+     * Gets text offering an explanation.
+     * If the <CODE>&lt;explanationText&gt;</CODE> init parameter is missing
+     * returns the following default explanation template string:
+     * <pre><code>
+     * The message this file is attached to has been signed on the server by
+     *     "[signerName]" <[signerAddress]>
+     * to certify that the sender is known and truly has the following address (reverse-path):
+     *     [reversePath]
+     * and that the original message has the following message headers:
+     *
+     * [headers]
+     *
+     * The signature envelopes this attachment too.
+     * Please check the signature integrity.
+     *
+     *     "[signerName]" <[signerAddress]>
+     * </code></pre>
+     */
+    public String getExplanationText() {
+        String explanationText = super.getExplanationText();
+        if (explanationText == null) {
+            explanationText = "The message this file is attached to has been signed on the server by\r\n"
+            + "\t\"[signerName]\" <[signerAddress]>"
+            + "\r\nto certify that the sender is known and truly has the following address (reverse-path):\r\n"
+            + "\t[reversePath]"
+            + "\r\nand that the original message has the following message headers:\r\n"
+            + "\r\n[headers]"
+            + "\r\n\r\nThe signature envelopes this attachment too."
+            + "\r\nPlease check the signature integrity."
+            + "\r\n\r\n"
+            + "\t\"[signerName]\" <[signerAddress]>";
+        }
+        
+        return explanationText;
+    }
+    
+    /**
+     * Initializer for property keyHolderClass.
+     * Hardcodes it to {@link org.apache.james.transport.SMIMEKeyHolder}.
+     */
+    protected void initKeyHolderClass() throws MessagingException {
+        String keyHolderClassName = "org.apache.james.security.SMIMEKeyHolder";
+        try {
+            setKeyHolderClass(Class.forName(keyHolderClassName));
+        } catch (ClassNotFoundException cnfe) {
+            throw new MessagingException(keyHolderClassName + "does not exist.");
+        }
+        if (isDebug()) {
+            log("keyHolderClass: " + getKeyHolderClass());
+        }
+    }
+    
+    /**
+     * If the <CODE>&lt;postmasterSigns&gt;</CODE> init parameter is missing sets it to <I>true</I>.
+     */
+    protected void initPostmasterSigns() {
+        setPostmasterSigns((getInitParameter("postmasterSigns") == null) ? true : Boolean.valueOf(getInitParameter("postmasterSigns")));
+    }
+    
+    /**
+     * If the <CODE>&lt;rebuildFrom&gt;</CODE> init parameter is missing sets it to <I>true</I>.
+     */
+    protected void initRebuildFrom() throws MessagingException {
+        setRebuildFrom((getInitParameter("rebuildFrom") == null) ? true : Boolean.valueOf(getInitParameter("rebuildFrom")));
+        if (isDebug()) {
+            if (isRebuildFrom()) {
+                log("Will modify the \"From:\" header.");
+            } else {
+                log("Will leave the \"From:\" header unchanged.");
+            }
+        }
+    }
+    
+    /* ******************************************************************** */
+    /* ****************** End of setters and getters ********************** */
+    /* ******************************************************************** */
+    
+    /**
+     * A text file with the massaged contents of {@link #getExplanationText}
+     * is attached to the original message.
+     */    
+    protected MimeBodyPart getWrapperBodyPart(Mail mail) throws MessagingException, IOException {
+        
+        String explanationText = getExplanationText();
+        
+        // if there is no explanation text there should be no wrapping
+        if (explanationText == null) {
+            return null;
+        }
+
+            MimeMessage originalMessage = mail.getMessage();
+
+            MimeBodyPart messagePart = new MimeBodyPart();
+            MimeBodyPart signatureReason = new MimeBodyPart();
+            
+            String contentType = originalMessage.getContentType();
+            Object content = originalMessage.getContent();
+            
+            if (contentType != null && content != null) {
+            messagePart.setContent(content, contentType);
+            } else {
+                throw new MessagingException("Either the content type or the content is null");
+            }
+            
+            String headers = getMessageHeaders(originalMessage);
+            
+            signatureReason.setText(getReplacedExplanationText(getExplanationText(),
+                                                               getSignerName(),
+                                                               getKeyHolder().getSignerAddress(),
+                                                               mail.getSender().toString(),
+                                                               headers));
+            
+            signatureReason.setFileName("SignatureExplanation.txt");
+            
+            MimeMultipart wrapperMultiPart = new MimeMultipart();
+            
+            wrapperMultiPart.addBodyPart(messagePart);
+            wrapperMultiPart.addBodyPart(signatureReason);
+            
+            MimeBodyPart wrapperBodyPart = new MimeBodyPart();
+            
+            wrapperBodyPart.setContent(wrapperMultiPart);
+            
+            return wrapperBodyPart;
+    }
+        
+}
+

http://git-wip-us.apache.org/repos/asf/james-project/blob/a72d89b4/mailet/crypto/src/main/java/org/apache/james/transport/mailets/Sign.java
----------------------------------------------------------------------
diff --git a/mailet/crypto/src/main/java/org/apache/james/transport/mailets/Sign.java b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/Sign.java
new file mode 100644
index 0000000..66cf4c9
--- /dev/null
+++ b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/Sign.java
@@ -0,0 +1,207 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+package org.apache.james.transport.mailets;
+
+import org.apache.mailet.Mail;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import java.io.IOException;
+
+/**
+ * <p>Puts a <I>server-side</I> signature on a message.
+ * It is a concrete subclass of {@link AbstractSign}, with very few modifications to it.</p>
+ * <p>A text file with an explanation text is attached to the original message,
+ * and the resulting message with all its attachments is signed.
+ * The resulting appearence of the message is almost unchanged: only an extra attachment
+ * and the signature are added.</p>
+ *<p>The kind of signuture depends on the value of the &lt;keyHolderClass&gt; init parameter.
+ *
+ *  <P>Handles the following init parameters (will comment only the differences from {@link AbstractSign}):</P>
+ * <ul>
+ * <li>&lt;keyHolderClass&gt;: Sets the class of the KeyHolder object that will handle the cryptography functions,
+ * for example org.apache.james.security.SMIMEKeyHolder for SMIME.</li>
+ * <li>&lt;debug&gt;.</li>
+ * <li>&lt;keyStoreFileName&gt;.</li>
+ * <li>&lt;keyStorePassword&gt;.</li>
+ * <li>&lt;keyAlias&gt;.</li>
+ * <li>&lt;keyAliasPassword&gt;.</li>
+ * <li>&lt;keyStoreType&gt;.</li>
+ * <li>&lt;postmasterSigns&gt;. The default is <CODE>true</CODE>.</li>
+ * <li>&lt;rebuildFrom&gt;. The default is <CODE>true</CODE>.</li>
+ * <li>&lt;signerName&gt;.</li>
+ * <li>&lt;explanationText&gt;. There is a default explanation string template in English,
+ * displaying also all the headers of the original message (see {@link #getExplanationText}).</li>
+ * </ul>
+ * @version CVS $Revision$ $Date$
+ * @since 2.2.1
+ */
+public class Sign extends AbstractSign {
+    
+    /**
+     * Return a string describing this mailet.
+     *
+     * @return a string describing this mailet
+     */
+    public String getMailetInfo() {
+        return "Signature Mailet";
+    }
+    
+    /**
+     *
+     */
+    protected  String[] getAllowedInitParameters() {
+        return new String[]{
+            "keyHolderClass",
+            "debug",
+            "keyStoreFileName",
+            "keyStorePassword",
+            "keyStoreType",
+            "keyAlias",
+            "keyAliasPassword",
+            "signerName",
+            "postmasterSigns",
+            "rebuildFrom",
+            "explanationText"
+        };
+    }
+    
+     /* ******************************************************************** */
+    /* ****************** Begin of setters and getters ******************** */
+    /* ******************************************************************** */    
+    
+    /**
+     * Gets text offering an explanation.
+     * If the <CODE>&lt;explanationText&gt;</CODE> init parameter is missing
+     * returns the following default explanation template string:
+     * <pre><code>
+     * The message this file is attached to has been signed on the server by
+     *     "[signerName]" <[signerAddress]>
+     * to certify that the sender is known and truly has the following address (reverse-path):
+     *     [reversePath]
+     * and that the original message has the following message headers:
+     *
+     * [headers]
+     *
+     * The signature envelopes this attachment too.
+     * Please check the signature integrity.
+     *
+     *     "[signerName]" <[signerAddress]>
+     * </code></pre>
+     */
+    public String getExplanationText() {
+        String explanationText = super.getExplanationText();
+        if (explanationText == null) {
+            explanationText = "The message this file is attached to has been signed on the server by\r\n"
+            + "\t\"[signerName]\" <[signerAddress]>"
+            + "\r\nto certify that the sender is known and truly has the following address (reverse-path):\r\n"
+            + "\t[reversePath]"
+            + "\r\nand that the original message has the following message headers:\r\n"
+            + "\r\n[headers]"
+            + "\r\n\r\nThe signature envelopes this attachment too."
+            + "\r\nPlease check the signature integrity."
+            + "\r\n\r\n"
+            + "\t\"[signerName]\" <[signerAddress]>";
+        }
+        
+        return explanationText;
+    }
+    
+    /**
+     * If the <CODE>&lt;postmasterSigns&gt;</CODE> init parameter is missing sets it to <I>true</I>.
+     */
+    protected void initPostmasterSigns() {
+        setPostmasterSigns((getInitParameter("postmasterSigns") == null) ? true : Boolean.valueOf(getInitParameter("postmasterSigns")));
+    }
+    
+    /**
+     * If the <CODE>&lt;rebuildFrom&gt;</CODE> init parameter is missing sets it to <I>true</I>.
+     */
+    protected void initRebuildFrom() throws MessagingException {
+        setRebuildFrom((getInitParameter("rebuildFrom") == null) ? true : Boolean.valueOf(getInitParameter("rebuildFrom")));
+        if (isDebug()) {
+            if (isRebuildFrom()) {
+                log("Will modify the \"From:\" header.");
+            } else {
+                log("Will leave the \"From:\" header unchanged.");
+            }
+        }
+    }
+    
+    /* ******************************************************************** */
+    /* ****************** End of setters and getters ********************** */
+    /* ******************************************************************** */
+    
+    /**
+     * A text file with the massaged contents of {@link #getExplanationText}
+     * is attached to the original message.
+     */    
+    protected MimeBodyPart getWrapperBodyPart(Mail mail) throws MessagingException, IOException {
+        
+        String explanationText = getExplanationText();
+        
+        // if there is no explanation text there should be no wrapping
+        if (explanationText == null) {
+            return null;
+        }
+
+            MimeMessage originalMessage = mail.getMessage();
+
+            MimeBodyPart messagePart = new MimeBodyPart();
+            MimeBodyPart signatureReason = new MimeBodyPart();
+            
+            String contentType = originalMessage.getContentType();
+            Object content = originalMessage.getContent();
+            
+            if (contentType != null && content != null) {
+            messagePart.setContent(content, contentType);
+            } else {
+                throw new MessagingException("Either the content type or the content is null");
+            }
+            
+            String headers = getMessageHeaders(originalMessage);
+            
+            signatureReason.setText(getReplacedExplanationText(getExplanationText(),
+                                                               getSignerName(),
+                                                               getKeyHolder().getSignerAddress(),
+                                                               mail.getSender().toString(),
+                                                               headers));
+            
+            signatureReason.setFileName("SignatureExplanation.txt");
+            
+            MimeMultipart wrapperMultiPart = new MimeMultipart();
+            
+            wrapperMultiPart.addBodyPart(messagePart);
+            wrapperMultiPart.addBodyPart(signatureReason);
+            
+            MimeBodyPart wrapperBodyPart = new MimeBodyPart();
+            
+            wrapperBodyPart.setContent(wrapperMultiPart);
+            
+            return wrapperBodyPart;
+    }
+        
+}
+

http://git-wip-us.apache.org/repos/asf/james-project/blob/a72d89b4/mailet/crypto/src/main/java/org/apache/james/transport/mailets/package.html
----------------------------------------------------------------------
diff --git a/mailet/crypto/src/main/java/org/apache/james/transport/mailets/package.html b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/package.html
new file mode 100644
index 0000000..2dcfc17
--- /dev/null
+++ b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/package.html
@@ -0,0 +1,21 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.    
+-->
+<body>
+<p>Cryptographic mail processing agents.</p>
+</body>


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org