EMMA Coverage Report (generated Thu Nov 19 17:07:02 CET 2009)
[all classes][org.apache.james.jdkim.mailets]

COVERAGE SUMMARY FOR SOURCE FILE [DKIMSign.java]

nameclass, %method, %block, %line, %
DKIMSign.java100% (2/2)86%  (6/7)69%  (210/303)82%  (54.9/67)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DKIMSign100% (1/1)100% (4/4)63%  (135/213)78%  (35.9/46)
init (): void 100% (1/1)40%  (32/80)53%  (6.9/13)
service (Mail): void 100% (1/1)62%  (48/78)75%  (12/16)
DKIMSign (): void 100% (1/1)100% (3/3)100% (2/2)
prependHeader (MimeMessage, String): void 100% (1/1)100% (52/52)100% (15/15)
     
class DKIMSign$MimeMessageHeaders100% (1/1)67%  (2/3)83%  (75/90)90%  (19/21)
getFields (): List 0%   (0/1)0%   (0/3)0%   (0/1)
DKIMSign$MimeMessageHeaders (DKIMSign, MimeMessage): void 100% (1/1)85%  (69/81)95%  (18/19)
getFields (String): List 100% (1/1)100% (6/6)100% (1/1)

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 
20package org.apache.james.jdkim.mailets;
21 
22import java.io.ByteArrayInputStream;
23import java.io.IOException;
24import java.security.GeneralSecurityException;
25import java.security.NoSuchAlgorithmException;
26import java.security.PrivateKey;
27import java.security.spec.InvalidKeySpecException;
28import java.util.Enumeration;
29import java.util.HashMap;
30import java.util.Iterator;
31import java.util.LinkedList;
32import java.util.List;
33import java.util.Map;
34 
35import javax.mail.Header;
36import javax.mail.MessagingException;
37import javax.mail.internet.MimeMessage;
38 
39import org.apache.commons.ssl.PKCS8Key;
40import org.apache.james.jdkim.DKIMSigner;
41import org.apache.james.jdkim.api.BodyHasher;
42import org.apache.james.jdkim.api.Headers;
43import org.apache.james.jdkim.api.SignatureRecord;
44import org.apache.james.jdkim.exceptions.PermFailException;
45import org.apache.mailet.Mail;
46import org.apache.mailet.base.GenericMailet;
47 
48/**
49 * This mailet sign a message using the DKIM protocol
50 * If the privateKey is encoded using a password then you can pass
51 * the password as privateKeyPassword parameter.
52 * 
53 * Sample configuration:
54 * 
55 * <pre><code>
56 * &lt;mailet match=&quot;All&quot; class=&quot;DKIMSign&quot;&gt;
57 *   &lt;signatureTemplate&gt;v=1; s=selector; d=example.com; h=from:to:received:received; a=rsa-sha256; bh=; b=;&lt;/signatureTemplate&gt;
58 *   &lt;privateKey&gt;
59 *   -----BEGIN RSA PRIVATE KEY-----
60 *   MIICXAIBAAKBgQDYDaYKXzwVYwqWbLhmuJ66aTAN8wmDR+rfHE8HfnkSOax0oIoT
61 *   M5zquZrTLo30870YMfYzxwfB6j/Nz3QdwrUD/t0YMYJiUKyWJnCKfZXHJBJ+yfRH
62 *   r7oW+UW3cVo9CG2bBfIxsInwYe175g9UjyntJpWueqdEIo1c2bhv9Mp66QIDAQAB
63 *   AoGBAI8XcwnZi0Sq5N89wF+gFNhnREFo3rsJDaCY8iqHdA5DDlnr3abb/yhipw0I
64 *   /1HlgC6fIG2oexXOXFWl+USgqRt1kTt9jXhVFExg8mNko2UelAwFtsl8CRjVcYQO
65 *   cedeH/WM/mXjg2wUqqZenBmlKlD6vNb70jFJeVaDJ/7n7j8BAkEA9NkH2D4Zgj/I
66 *   OAVYccZYH74+VgO0e7VkUjQk9wtJ2j6cGqJ6Pfj0roVIMUWzoBb8YfErR8l6JnVQ
67 *   bfy83gJeiQJBAOHk3ow7JjAn8XuOyZx24KcTaYWKUkAQfRWYDFFOYQF4KV9xLSEt
68 *   ycY0kjsdxGKDudWcsATllFzXDCQF6DTNIWECQEA52ePwTjKrVnLTfCLEG4OgHKvl
69 *   Zud4amthwDyJWoMEH2ChNB2je1N4JLrABOE+hk+OuoKnKAKEjWd8f3Jg/rkCQHj8
70 *   mQmogHqYWikgP/FSZl518jV48Tao3iXbqvU9Mo2T6yzYNCCqIoDLFWseNVnCTZ0Q
71 *   b+IfiEf1UeZVV5o4J+ECQDatNnS3V9qYUKjj/krNRD/U0+7eh8S2ylLqD3RlSn9K
72 *   tYGRMgAtUXtiOEizBH6bd/orzI9V9sw8yBz+ZqIH25Q=
73 *   -----END RSA PRIVATE KEY-----
74 *   &lt;/privateKey&gt;
75 * &lt;/mailet&gt;
76 * </code></pre>
77 * 
78 * @version CVS $Revision: 713949 $ $Date: 2008-11-14 08:40:21 +0100 (ven, 14
79 *          nov 2008) $
80 * @since 2.2.0
81 */
82public class DKIMSign extends GenericMailet {
83 
84    /**
85     * An adapter to let DKIMSigner read headers from MimeMessage
86     */
87    private final class MimeMessageHeaders implements Headers {
88 
89        private Map/* String, String */headers;
90        private List/* String */fields;
91 
92        public MimeMessageHeaders(MimeMessage message)
93                throws MessagingException {
94            headers = new HashMap();
95            fields = new LinkedList();
96            for (Enumeration e = message.getAllHeaderLines(); e
97                    .hasMoreElements();) {
98                String head = (String) e.nextElement();
99                int p = head.indexOf(':');
100                if (p <= 0)
101                    throw new MessagingException("Bad header line: " + head);
102                String headerName = head.substring(0, p).trim();
103                String headerNameLC = headerName.toLowerCase();
104                fields.add(headerName);
105                List/* String */strings = (List) headers.get(headerNameLC);
106                if (strings == null) {
107                    strings = new LinkedList();
108                    headers.put(headerNameLC, strings);
109                }
110                strings.add(head);
111            }
112        }
113 
114        public List getFields() {
115            return fields;
116        }
117 
118        public List getFields(String name) {
119            return (List) headers.get(name);
120        }
121    }
122 
123    private String signatureTemplate;
124    private PrivateKey privateKey;
125 
126    public void init() throws MessagingException {
127        signatureTemplate = getInitParameter("signatureTemplate");
128        String privateKeyString = getInitParameter("privateKey");
129        String privateKeyPassword = getInitParameter("privateKeyPassword", null);
130        try {
131            PKCS8Key pkcs8 = new PKCS8Key(new ByteArrayInputStream(
132                    privateKeyString.getBytes()),
133                    privateKeyPassword != null ? privateKeyPassword
134                            .toCharArray() : null);
135            privateKey = pkcs8.getPrivateKey();
136            // privateKey = DKIMSigner.getPrivateKey(privateKeyString);
137        } catch (NoSuchAlgorithmException e) {
138            throw new MessagingException("Unknown private key algorythm: "
139                    + e.getMessage(), e);
140        } catch (InvalidKeySpecException e) {
141            throw new MessagingException(
142                    "PrivateKey should be in base64 encoded PKCS8 (der) format: "
143                            + e.getMessage(), e);
144        } catch (GeneralSecurityException e) {
145            throw new MessagingException("General security exception: "
146                    + e.getMessage(), e);
147        }
148    }
149 
150    public void service(Mail mail) throws MessagingException {
151        DKIMSigner signer = new DKIMSigner(signatureTemplate, privateKey);
152        SignatureRecord signRecord = signer
153                .newSignatureRecordTemplate(signatureTemplate);
154        try {
155            BodyHasher bhj = signer.newBodyHasher(signRecord);
156            MimeMessage message = mail.getMessage();
157            Headers headers = new MimeMessageHeaders(message);
158            try {
159                message.writeTo(new HeaderSkippingOutputStream(bhj
160                        .getOutputStream()));
161                bhj.getOutputStream().close();
162            } catch (IOException e) {
163                throw new MessagingException("Exception calculating bodyhash: "
164                        + e.getMessage(), e);
165            }
166            String signatureHeader = signer.sign(headers, bhj);
167            // Unfortunately JavaMail does not give us a method to add headers
168            // on top.
169            // message.addHeaderLine(signatureHeader);
170            prependHeader(message, signatureHeader);
171        } catch (PermFailException e) {
172            throw new MessagingException("PermFail while signing: "
173                    + e.getMessage(), e);
174        }
175 
176    }
177 
178    private void prependHeader(MimeMessage message, String signatureHeader)
179            throws MessagingException {
180        List prevHeader = new LinkedList();
181        // read all the headers
182        for (Enumeration e = message.getAllHeaderLines(); e.hasMoreElements();) {
183            String headerLine = (String) e.nextElement();
184            prevHeader.add(headerLine);
185        }
186        // remove all the headers
187        for (Enumeration e = message.getAllHeaders(); e.hasMoreElements();) {
188            Header header = (Header) e.nextElement();
189            message.removeHeader(header.getName());
190        }
191        // add our header
192        message.addHeaderLine(signatureHeader);
193        // add the remaining headers using "addHeaderLine" that won't alter the
194        // insertion order.
195        for (Iterator i = prevHeader.iterator(); i.hasNext();) {
196            String header = (String) i.next();
197            message.addHeaderLine(header);
198        }
199    }
200 
201}

[all classes][org.apache.james.jdkim.mailets]
EMMA 2.0.5312 (C) Vladimir Roubtsov