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 }