001 /*
002 * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org
003 * Copyright (C) 2011 NightLabs Consulting GmbH
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program. If not, see <http://www.gnu.org/licenses/>.
017 */
018 package org.cumulus4j.store.crypto;
019
020 import java.util.Date;
021
022 import org.slf4j.Logger;
023 import org.slf4j.LoggerFactory;
024
025 /**
026 * Abstract base-class for implementing {@link CryptoSession}s.
027 *
028 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
029 */
030 public abstract class AbstractCryptoSession implements CryptoSession
031 {
032 private static final Logger logger = LoggerFactory.getLogger(AbstractCryptoSession.class);
033
034 private Date creationTimestamp = new Date();
035 private volatile Date lastUsageTimestamp = creationTimestamp;
036 private CryptoManager cryptoManager;
037 private String cryptoSessionID;
038 private volatile String keyStoreID;
039
040 private volatile boolean closed;
041
042 @Override
043 public CryptoManager getCryptoManager() {
044 return cryptoManager;
045 }
046
047 @Override
048 public void setCryptoManager(CryptoManager cryptoManager)
049 {
050 if (cryptoManager == null)
051 throw new IllegalArgumentException("cryptoManager == null");
052
053 if (cryptoManager == this.cryptoManager)
054 return;
055
056 if (this.cryptoManager != null)
057 throw new IllegalStateException("this.cryptoManager already assigned! Cannot modify!");
058
059 this.cryptoManager = cryptoManager;
060 }
061
062 @Override
063 public String getCryptoSessionID()
064 {
065 return cryptoSessionID;
066 }
067
068 @Override
069 public void setCryptoSessionID(String cryptoSessionID)
070 {
071 if (cryptoSessionID == null)
072 throw new IllegalArgumentException("cryptoSessionID == null");
073
074 if (cryptoSessionID.equals(this.cryptoSessionID))
075 return;
076
077 if (this.cryptoSessionID != null)
078 throw new IllegalStateException("this.cryptoSessionID already assigned! Cannot modify!");
079
080 this.cryptoSessionID = cryptoSessionID;
081 this.keyStoreID = null;
082 logger.trace("setCryptoSessionID: cryptoSessionID={}", cryptoSessionID);
083 }
084
085 @Override
086 public String getKeyStoreID() {
087 String keyStoreID = this.keyStoreID;
088 if (keyStoreID == null) {
089 String cryptoSessionID = getCryptoSessionID();
090 if (cryptoSessionID == null)
091 throw new IllegalStateException("cryptoSessionID == null :: setCryptoSessionID(...) was not yet called!");
092
093 // Our default format for a cryptoSessionID is:
094 //
095 // "${cryptoSessionIDPrefix}*${serial}*${random1}"
096 //
097 // ${cryptoSessionIDPrefix} is: "${keyStoreID}_${random2}"
098 // The ${cryptoSessionIDPrefix} is used for routing key-request-messages to the right key manager
099 // and for determining the key-store-id.
100 //
101 // ${serial} is a serial number to guarantee uniqueness (as ${random1} is pretty short).
102 //
103 // ${random1} is a random number making it much harder to guess a session-ID.
104
105 int colonIndex = cryptoSessionID.indexOf('_');
106 if (colonIndex < 0)
107 throw new IllegalStateException("cryptoSessionID does not contain an underscore ('_'): "+ cryptoSessionID);
108
109 keyStoreID = cryptoSessionID.substring(0, colonIndex);
110 this.keyStoreID = keyStoreID;
111 }
112 return keyStoreID;
113 }
114
115 @Override
116 public Date getCreationTimestamp()
117 {
118 return creationTimestamp;
119 }
120
121 @Override
122 public Date getLastUsageTimestamp() {
123 return lastUsageTimestamp;
124 }
125
126 @Override
127 public void updateLastUsageTimestamp() {
128 lastUsageTimestamp = new Date();
129 }
130
131 /**
132 * {@inheritDoc}
133 * <p>
134 * Implementors should use {@link #assertNotClosed()} in their subclasses to check
135 * whether the operation is still allowed.
136 * </p>
137 */
138 @Override
139 public boolean isClosed() {
140 return closed;
141 }
142
143 /**
144 * Throws an {@link IllegalStateException}, if this session is already closed.
145 */
146 protected void assertNotClosed()
147 {
148 if (isClosed())
149 throw new IllegalStateException("This session (cryptoSessionID=\"" + cryptoSessionID + "\") is already closed!");
150 }
151
152 /**
153 * {@inheritDoc}
154 * <p>
155 * When overriding this method, you should first call <code>super.close();</code> and
156 * then perform your own closing operations.
157 * </p>
158 */
159 @Override
160 public void close() {
161 logger.trace("close: cryptoSessionID={}", cryptoSessionID);
162 closed = true;
163 cryptoManager.onCloseCryptoSession(this);
164 }
165 }