Java tutorial
/* * 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(); } }