mitm.application.djigzo.james.mailets.PDFReplyURLBuilder.java Source code

Java tutorial

Introduction

Here is the source code for mitm.application.djigzo.james.mailets.PDFReplyURLBuilder.java

Source

/*
 * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo.
 * 
 * This file is part of Djigzo email encryption.
 *
 * Djigzo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License 
 * version 3, 19 November 2007 as published by the Free Software 
 * Foundation.
 *
 * Djigzo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public 
 * License along with Djigzo. If not, see <http://www.gnu.org/licenses/>
 *
 * Additional permission under GNU AGPL version 3 section 7
 * 
 * If you modify this Program, or any covered work, by linking or 
 * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, 
 * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, 
 * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, 
 * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, 
 * wsdl4j-1.6.1.jar (or modified versions of these libraries), 
 * containing parts covered by the terms of Eclipse Public License, 
 * tyrex license, freemarker license, dom4j license, mx4j license,
 * Spice Software License, Common Development and Distribution License
 * (CDDL), Common Public License (CPL) the licensors of this Program grant 
 * you additional permission to convey the resulting work.
 */
package mitm.application.djigzo.james.mailets;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.json.JSONException;
import org.json.JSONObject;

import mitm.application.djigzo.DjigzoRuntimeException;
import mitm.common.security.NoSuchProviderRuntimeException;
import mitm.common.security.SecurityFactory;
import mitm.common.security.SecurityFactoryFactory;
import mitm.common.security.SecurityFactoryFactoryException;
import mitm.common.security.crypto.RandomGenerator;
import mitm.common.util.Base32;
import mitm.common.util.Check;
import mitm.common.util.MiscStringUtils;
import mitm.common.util.URLBuilder;
import mitm.common.util.URLBuilderException;

/**
 * Used for building the reply URL for PDFEncrypt. Can also be used to validate an existing reply.
 * 
 * This class is not thread safe.
 * 
 * @author Martijn Brinkers
 *
 */
public class PDFReplyURLBuilder {
    /**
     * Thrown when the key is null
     */
    public static class NullKeyURLBuilderException extends URLBuilderException {
        private static final long serialVersionUID = -3993250347651565167L;

        public NullKeyURLBuilderException(String message) {
            super(message);
        }

        public NullKeyURLBuilderException(Throwable cause) {
            super(cause);
        }

        public NullKeyURLBuilderException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    /**
     * Used by PDFReplyURLBuilder#loadFromEnvelope for getting the key used to calculate the hmac. The key can 
     * be based upon the enveloped data.
     *
     */
    public static interface KeyProvider {
        public String getKey(PDFReplyURLBuilder builder) throws URLBuilderException;
    }

    /*
     * The HTTP parameters names
     */
    public final static String REPLY_ENVELOPE_PARAMETER = "env";
    public final static String HMAC_PARAMETER = "hmac";

    /*
     * JSON parameter names
     */
    private final static String USER_EMAIL_PARAMETER = "u";
    private final static String RECIPIENT_EMAIL_PARAMETER = "r";
    private final static String FROM_EMAIL_PARAMETER = "f";
    private final static String SUBJECT_PARAMETER = "s";
    private final static String TIME_PARAMETER = "t";
    private final static String MESSAGE_ID_PARAMETER = "id";

    /*
     * Number of bytes to used for the message ID if it should be generated
     */
    private final static int MESSAGE_ID_BYTES_RANDOM = 8;

    /*
     * Used to generate a message-id
     */
    private final RandomGenerator randomGenerator;

    /*
     * Used for building the URL
     */
    private URLBuilder uRLBuilder;

    /*
     * The base URL for the final URL. example: http://www.example.com
     */
    private String baseURL;

    /*
     * The email address of the initiator of the message
     */
    private String user;

    /*
     * The recipient of the reply
     */
    private String recipient;

    /*
     * The sender of the reply
     */
    private String from;

    /*
     * The subject of the message
     */
    private String subject;

    /*
     * The time the reply was created
     */
    private Long time;

    /*
     * A unique id which will be added to the URL.
     */
    private String messageID;

    /*
     * The secret key used for calculating the HMAC
     */
    private String key;

    /*
     * The hmac algorithm. 
     */
    private String algorithm = "HmacSHA1";

    public PDFReplyURLBuilder(URLBuilder uRLBuilder) {
        Check.notNull(uRLBuilder, "uRLBuilder");

        this.uRLBuilder = uRLBuilder;

        try {
            randomGenerator = SecurityFactoryFactory.getSecurityFactory().createRandomGenerator();
        } catch (NoSuchAlgorithmException e) {
            throw new DjigzoRuntimeException("Crypto provider does not support the requested random generator.");
        } catch (SecurityFactoryFactoryException e) {
            throw new DjigzoRuntimeException("Crypto provider does not support the requested random generator.");
        } catch (NoSuchProviderException e) {
            throw new NoSuchProviderRuntimeException(e);
        }
    }

    public String getBaseURL() {
        return baseURL;
    }

    public void setBaseURL(String baseURL) {
        this.baseURL = baseURL;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getRecipient() {
        return recipient;
    }

    public void setRecipient(String recipient) {
        this.recipient = recipient;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public Long getTime() {
        return time;
    }

    public void setTime(Long time) {
        this.time = time;
    }

    public String getMessageID() {
        return messageID;
    }

    public void setMessageID(String messageID) {
        this.messageID = messageID;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    private Mac createMAC() throws URLBuilderException {
        SecurityFactory securityFactory = SecurityFactoryFactory.getSecurityFactory();

        try {
            Mac mac = securityFactory.createMAC(algorithm);

            SecretKeySpec keySpec = new SecretKeySpec(MiscStringUtils.toUTF8Bytes(key), "raw");

            mac.init(keySpec);

            return mac;
        } catch (NoSuchAlgorithmException e) {
            throw new URLBuilderException(e);
        } catch (NoSuchProviderException e) {
            throw new URLBuilderException(e);
        } catch (InvalidKeyException e) {
            throw new URLBuilderException(e);
        }
    }

    /**
     * Builds a PDF reply URL. baseURL, recipient, sender and key must be non-null. If time is not specified the current 
     * time in milliseconds will be used. If messageID is not specified a messageID will be created (using a UUID).
     */
    public String buildURL() throws URLBuilderException {
        if (baseURL == null) {
            throw new URLBuilderException("baseURL is not specified.");
        }

        if (user == null) {
            throw new URLBuilderException("user is not specified.");
        }

        if (recipient == null) {
            throw new URLBuilderException("recipient is not specified.");
        }

        if (from == null) {
            throw new URLBuilderException("from is not specified.");
        }

        if (subject == null) {
            throw new URLBuilderException("subject is not specified.");
        }

        if (key == null) {
            throw new URLBuilderException("key is not specified.");
        }

        if (time == null) {
            time = System.currentTimeMillis();
        }

        if (messageID == null) {
            messageID = Base32.encode(randomGenerator.generateRandom(MESSAGE_ID_BYTES_RANDOM));
        }

        uRLBuilder.reset();

        uRLBuilder.setBaseURL(baseURL);

        JSONObject json = new JSONObject();

        try {
            json.put(USER_EMAIL_PARAMETER, user);
            json.put(RECIPIENT_EMAIL_PARAMETER, recipient);
            json.put(FROM_EMAIL_PARAMETER, from);
            json.put(SUBJECT_PARAMETER, subject);
            json.put(TIME_PARAMETER, time);
            json.put(MESSAGE_ID_PARAMETER, messageID);
        } catch (JSONException e) {
            throw new URLBuilderException(e);
        }

        byte[] envelopeBlob = Base64.encodeBase64(MiscStringUtils.toUTF8Bytes(json.toString()));

        String envelope = MiscStringUtils.toUTF8String(envelopeBlob);

        uRLBuilder.addParameter(REPLY_ENVELOPE_PARAMETER, envelope);
        uRLBuilder.addHMAC(HMAC_PARAMETER, createMAC());

        return uRLBuilder.buildURL();
    }

    /**
     * Compares the given hmac with the newly calculated hmac of the base64 envelope to see whether the envelop 
     * is not changed and retrieves the values from the envelope (which should be a base64 encoded json object).
     */
    public void loadFromEnvelope(String envelope, String hmac, KeyProvider keyProvider) throws URLBuilderException {
        Check.notNull(keyProvider, "keyProvider");

        if (envelope == null) {
            throw new URLBuilderException("envelope must not be null.");
        }

        if (hmac == null) {
            throw new URLBuilderException("hmac must not be null.");
        }

        byte[] envelopeBlob = Base64.decodeBase64(MiscStringUtils.toUTF8Bytes(envelope));

        try {
            JSONObject json = new JSONObject(MiscStringUtils.toUTF8String(envelopeBlob));

            user = json.getString(USER_EMAIL_PARAMETER);
            recipient = json.getString(RECIPIENT_EMAIL_PARAMETER);
            from = json.getString(FROM_EMAIL_PARAMETER);
            subject = json.getString(SUBJECT_PARAMETER);
            time = json.getLong(TIME_PARAMETER);
            messageID = json.getString(MESSAGE_ID_PARAMETER);
        } catch (JSONException e) {
            throw new URLBuilderException(e);
        }

        uRLBuilder.reset();

        key = keyProvider.getKey(this);

        if (key == null) {
            throw new NullKeyURLBuilderException(user + " has no valid key.");
        }

        uRLBuilder.addParameter(REPLY_ENVELOPE_PARAMETER, envelope);

        String calculated = uRLBuilder.addHMAC("hmac", createMAC());

        if (!calculated.equals(hmac)) {
            throw new URLBuilderException("HMAC is not correct.");
        }
    }

    public void reset() throws URLBuilderException {
        recipient = null;
        from = null;
        time = null;
        messageID = null;
        key = null;

        uRLBuilder.reset();
    }
}