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; 019 020 import java.io.UnsupportedEncodingException; 021 import java.util.HashMap; 022 import java.util.Map; 023 024 import javax.crypto.BadPaddingException; 025 import javax.crypto.Cipher; 026 import javax.crypto.IllegalBlockSizeException; 027 import javax.crypto.spec.IvParameterSpec; 028 import javax.crypto.spec.SecretKeySpec; 029 030 import org.cumulus4j.store.crypto.AbstractCryptoManager; 031 import org.cumulus4j.store.crypto.AbstractCryptoSession; 032 import org.cumulus4j.store.crypto.Ciphertext; 033 import org.cumulus4j.store.crypto.CryptoContext; 034 import org.cumulus4j.store.crypto.CryptoSession; 035 import org.cumulus4j.store.crypto.Plaintext; 036 037 /** 038 * Dummy crypto-manager for debugging and testing. 039 * 040 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 041 */ 042 public class DummyCryptoManager extends AbstractCryptoManager 043 { 044 public static final String KEY_STORE_ID_COMPATIBILITY_TEST = "COMPATIBILITYTEST"; // no special characters! 045 046 @Override 047 protected CryptoSession createCryptoSession() { 048 return new DummySession(); 049 } 050 051 private static final class DummySession 052 extends AbstractCryptoSession 053 { 054 // // key length: 128 bits 055 // private static final byte[] dummyKey = { 'D', 'e', 'r', ' ', 'F', 'e', 'r', 'd', ' ', 'h', 'a', 't', ' ', 'v', 'i', 'e' }; 056 // initialization vector length: 128 bits 057 private static final IvParameterSpec iv = new IvParameterSpec(new byte[] {'b', 'l', 'a', 't', 'r', 'u', 'l', 'l', 'a', 'l', 'a', 't', 'r', 'a', 'r', 'a'}); 058 059 private static final String ALGORITHM = "AES"; 060 private static final String ALGORITHM_WITH_PARAMS = ALGORITHM + "/CBC/PKCS5Padding"; 061 062 // private Cipher encrypter; 063 // private Cipher decrypter; 064 // { 065 // try { 066 // SecretKeySpec key = new SecretKeySpec(dummyKey, ALGORITHM); 067 // encrypter = Cipher.getInstance(ALGORITHM_WITH_PARAMS); 068 // encrypter.init(Cipher.ENCRYPT_MODE, key, iv); 069 // decrypter = Cipher.getInstance(ALGORITHM_WITH_PARAMS); 070 // decrypter.init(Cipher.DECRYPT_MODE, key, iv); 071 // } catch (Exception ex) { 072 // throw new RuntimeException(ex); 073 // } 074 // } 075 076 private Map<Integer, Map<String, Cipher>> mode2KeyStoreID2Cipher = new HashMap<Integer, Map<String,Cipher>>(); 077 078 protected Cipher getEncrypter() { 079 return getCipher(Cipher.ENCRYPT_MODE); 080 } 081 082 protected Cipher getDecrypter() { 083 return getCipher(Cipher.DECRYPT_MODE); 084 } 085 086 protected Cipher getCipher(int mode) { 087 String keyStoreID = getKeyStoreID(); 088 089 synchronized (mode2KeyStoreID2Cipher) { 090 Map<String, Cipher> keyStoreID2Cipher = mode2KeyStoreID2Cipher.get(mode); 091 if (keyStoreID2Cipher == null) { 092 keyStoreID2Cipher = new HashMap<String, Cipher>(); 093 mode2KeyStoreID2Cipher.put(mode, keyStoreID2Cipher); 094 } 095 096 Cipher cipher = keyStoreID2Cipher.get(keyStoreID); 097 if (cipher == null) { 098 // key length: 128 bits 099 byte[] dummyKey = { 'D', 'e', 'r', ' ', 'F', 'e', 'r', 'd', ' ', 'h', 'a', 't', ' ', 'v', 'i', 'e' }; 100 101 if (!KEY_STORE_ID_COMPATIBILITY_TEST.equals(keyStoreID)) { 102 try { 103 byte[] keyStoreIDBytes = keyStoreID.getBytes("UTF-8"); 104 int keyIdx = -1; 105 for (int i = 0; i < keyStoreIDBytes.length; ++i) { 106 if (++keyIdx >= dummyKey.length) 107 keyIdx = 0; 108 109 dummyKey[keyIdx] ^= keyStoreIDBytes[i]; 110 } 111 } catch (UnsupportedEncodingException e) { 112 throw new RuntimeException(e); 113 } 114 } 115 116 try { 117 SecretKeySpec key = new SecretKeySpec(dummyKey, ALGORITHM); 118 cipher = Cipher.getInstance(ALGORITHM_WITH_PARAMS); 119 cipher.init(mode, key, iv); 120 } catch (Exception ex) { 121 throw new RuntimeException(ex); 122 } 123 keyStoreID2Cipher.put(keyStoreID, cipher); 124 } 125 return cipher; 126 } 127 } 128 129 @Override 130 public Ciphertext encrypt(CryptoContext cryptoContext, Plaintext plaintext) 131 { 132 // First get the required resources (that are cleared in close()). 133 Cipher c = getEncrypter(); 134 135 // Then assert that we are not yet closed. This makes sure that we definitely can continue 136 // even if close() is called right now simultaneously. 137 assertNotClosed(); 138 139 Ciphertext result = new Ciphertext(); 140 result.setKeyID(12345); 141 142 synchronized (c) { 143 try { 144 result.setData( 145 c.doFinal(plaintext.getData()) 146 ); 147 } catch (IllegalBlockSizeException e) { 148 throw new RuntimeException(e); 149 } catch (BadPaddingException e) { 150 throw new RuntimeException(e); 151 } 152 } 153 154 return result; 155 } 156 157 @Override 158 public Plaintext decrypt(CryptoContext cryptoContext, Ciphertext ciphertext) 159 { 160 if (ciphertext.getKeyID() != 12345) 161 throw new IllegalArgumentException("No key with this keyID: " + ciphertext.getKeyID()); 162 163 // First get the required resources (that are cleared in close()). 164 Cipher c = getDecrypter(); 165 166 // Then assert that we are not yet closed. This makes sure that we definitely can continue 167 // even if close() is called right now simultaneously. 168 assertNotClosed(); 169 170 Plaintext result = new Plaintext(); 171 172 synchronized (c) { 173 try { 174 result.setData( 175 c.doFinal(ciphertext.getData()) 176 ); 177 } catch (IllegalBlockSizeException e) { 178 throw new RuntimeException(e); 179 } catch (BadPaddingException e) { 180 throw new RuntimeException(e); 181 } 182 } 183 184 return result; 185 } 186 187 @Override 188 public void close() { 189 super.close(); 190 synchronized (mode2KeyStoreID2Cipher) { 191 mode2KeyStoreID2Cipher.clear(); 192 } 193 } 194 } 195 196 }