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

COVERAGE SUMMARY FOR SOURCE FILE [DKIMSigner.java]

nameclass, %method, %block, %line, %
DKIMSigner.java100% (1/1)100% (7/7)67%  (130/195)77%  (27.8/36)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DKIMSigner100% (1/1)100% (7/7)67%  (130/195)77%  (27.8/36)
sign (Headers, BodyHasher): String 100% (1/1)44%  (35/80)50%  (6/12)
sign (InputStream): String 100% (1/1)59%  (29/49)76%  (6.8/9)
DKIMSigner (String, PrivateKey): void 100% (1/1)100% (9/9)100% (4/4)
getPrivateKey (String): PrivateKey 100% (1/1)100% (18/18)100% (5/5)
newBodyHasher (SignatureRecord): BodyHasher 100% (1/1)100% (5/5)100% (1/1)
newSignatureRecordTemplate (String): SignatureRecord 100% (1/1)100% (5/5)100% (1/1)
signatureSign (Headers, SignatureRecord, PrivateKey, List): byte [] 100% (1/1)100% (29/29)100% (4/4)

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;
21 
22import java.io.IOException;
23import java.io.InputStream;
24import java.security.InvalidKeyException;
25import java.security.KeyFactory;
26import java.security.NoSuchAlgorithmException;
27import java.security.PrivateKey;
28import java.security.Signature;
29import java.security.SignatureException;
30import java.security.spec.InvalidKeySpecException;
31import java.security.spec.PKCS8EncodedKeySpec;
32import java.util.List;
33 
34import org.apache.commons.codec.binary.Base64;
35import org.apache.james.jdkim.api.BodyHasher;
36import org.apache.james.jdkim.api.Headers;
37import org.apache.james.jdkim.api.SignatureRecord;
38import org.apache.james.jdkim.exceptions.FailException;
39import org.apache.james.jdkim.exceptions.PermFailException;
40import org.apache.james.jdkim.impl.BodyHasherImpl;
41import org.apache.james.jdkim.impl.Message;
42import org.apache.james.jdkim.tagvalue.SignatureRecordImpl;
43import org.apache.james.mime4j.MimeException;
44 
45public class DKIMSigner extends DKIMCommon {
46 
47    private PrivateKey privateKey;
48    private String signatureRecordTemplate;
49 
50    public DKIMSigner(String signatureRecordTemplate, PrivateKey privateKey) {
51        this.privateKey = privateKey;
52        this.signatureRecordTemplate = signatureRecordTemplate;
53    }
54 
55    public SignatureRecord newSignatureRecordTemplate(String record) {
56        return new SignatureRecordImpl(record);
57    }
58 
59    public BodyHasher newBodyHasher(SignatureRecord signRecord)
60            throws PermFailException {
61        return new BodyHasherImpl(signRecord);
62    }
63 
64    public String sign(InputStream is) throws IOException, FailException {
65        Message message;
66        try {
67            try {
68                message = new Message(is);
69            } catch (MimeException e1) {
70                throw new PermFailException("MIME parsing exception: "
71                        + e1.getMessage(), e1);
72            }
73            SignatureRecord srt = newSignatureRecordTemplate(signatureRecordTemplate);
74 
75            BodyHasher bhj = newBodyHasher(srt);
76 
77            // computation of the body hash.
78            DKIMCommon.streamCopy(message.getBodyInputStream(), bhj
79                    .getOutputStream());
80 
81            return sign(message, bhj);
82 
83        } finally {
84            is.close();
85        }
86    }
87 
88    public String sign(Headers message, BodyHasher bhj)
89            throws PermFailException {
90        byte[] computedHash = bhj.getDigest();
91        
92        bhj.getSignatureRecord().setBodyHash(computedHash);
93        
94        List headers = bhj.getSignatureRecord().getHeaders();
95        try {
96            // TODO handle b= in SignatureRecord.
97            // whenever any tag is changed the b should be invalidated and the
98            // text representation lost.
99            // whenever the b value is regenerated it should also be associated
100            // with the right test representation.
101            // we need a method to "regenerate the text representation" and to
102            // retrieve it when it is valid.
103            byte[] signatureHash = signatureSign(message, bhj
104                    .getSignatureRecord(), privateKey, headers);
105 
106            bhj.getSignatureRecord().setSignature(signatureHash);
107 
108            return "DKIM-Signature:"+bhj.getSignatureRecord().toString();
109        } catch (InvalidKeyException e) {
110            throw new PermFailException("Invalid key: " + e.getMessage(), e);
111        } catch (NoSuchAlgorithmException e) {
112            throw new PermFailException("Unknown algorythm: " + e.getMessage(),
113                    e);
114        } catch (SignatureException e) {
115            throw new PermFailException("Signing exception: " + e.getMessage(),
116                    e);
117        }
118    }
119 
120    private byte[] signatureSign(Headers h, SignatureRecord sign, PrivateKey key, List headers)
121            throws NoSuchAlgorithmException, InvalidKeyException,
122            SignatureException, PermFailException {
123 
124        Signature signature = Signature.getInstance(sign.getHashMethod()
125                .toString().toUpperCase()
126                + "with" + sign.getHashKeyType().toString().toUpperCase());
127        signature.initSign(key);
128 
129        signatureCheck(h, sign, headers, signature);
130        return signature.sign();
131    }
132 
133    /**
134     * Generate a PrivateKey from a Base64 encoded private key.
135     * 
136     * In order to generate a valid PKCS8 key when you have a PEM key you can do
137     * this: <code>
138     * openssl pkcs8 -topk8 -inform PEM -in rsapriv.pem -outform DER -nocrypt -out rsapriv.der
139     * </code>
140     * And then base64 encode the content.
141     * 
142     * @param privateKeyPKCS8
143     *                a Base64 encoded string of the RSA key in PKCS8 format
144     * @return the PrivateKey
145     * @throws NoSuchAlgorithmException
146     *                 if RSA is unknown
147     * @throws InvalidKeySpecException
148     *                 on bad input key
149     */
150    public static PrivateKey getPrivateKey(String privateKeyPKCS8)
151            throws NoSuchAlgorithmException, InvalidKeySpecException {
152        byte[] encKey = Base64.decodeBase64(privateKeyPKCS8.getBytes());
153        // byte[] encKey = privateKey.getBytes();
154        PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(encKey);
155        KeyFactory keyFactory;
156        keyFactory = KeyFactory.getInstance("RSA");
157        PrivateKey privKey = keyFactory.generatePrivate(privSpec);
158        return privKey;
159    }
160 
161}

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