1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.james.mime4j.storage;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.security.GeneralSecurityException;
25 import java.security.NoSuchAlgorithmException;
26
27 import javax.crypto.Cipher;
28 import javax.crypto.CipherInputStream;
29 import javax.crypto.CipherOutputStream;
30 import javax.crypto.KeyGenerator;
31 import javax.crypto.spec.SecretKeySpec;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public class CipherStorageProvider extends AbstractStorageProvider {
48
49 private final StorageProvider backend;
50 private final String algorithm;
51 private final KeyGenerator keygen;
52
53
54
55
56
57
58
59
60 public CipherStorageProvider(StorageProvider backend) {
61 this(backend, "Blowfish");
62 }
63
64
65
66
67
68
69
70
71
72
73
74 public CipherStorageProvider(StorageProvider backend, String algorithm) {
75 if (backend == null)
76 throw new IllegalArgumentException();
77
78 try {
79 this.backend = backend;
80 this.algorithm = algorithm;
81 this.keygen = KeyGenerator.getInstance(algorithm);
82 } catch (NoSuchAlgorithmException e) {
83 throw new IllegalArgumentException(e);
84 }
85 }
86
87 public StorageOutputStream createStorageOutputStream() throws IOException {
88 SecretKeySpec skeySpec = getSecretKeySpec();
89
90 return new CipherStorageOutputStream(backend
91 .createStorageOutputStream(), algorithm, skeySpec);
92 }
93
94 private SecretKeySpec getSecretKeySpec() {
95 byte[] raw = keygen.generateKey().getEncoded();
96 return new SecretKeySpec(raw, algorithm);
97 }
98
99 private static final class CipherStorageOutputStream extends
100 StorageOutputStream {
101 private final StorageOutputStream storageOut;
102 private final String algorithm;
103 private final SecretKeySpec skeySpec;
104 private final CipherOutputStream cipherOut;
105
106 public CipherStorageOutputStream(StorageOutputStream out,
107 String algorithm, SecretKeySpec skeySpec) throws IOException {
108 try {
109 this.storageOut = out;
110 this.algorithm = algorithm;
111 this.skeySpec = skeySpec;
112
113 Cipher cipher = Cipher.getInstance(algorithm);
114 cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
115
116 this.cipherOut = new CipherOutputStream(out, cipher);
117 } catch (GeneralSecurityException e) {
118 throw (IOException) new IOException().initCause(e);
119 }
120 }
121
122 @Override
123 public void close() throws IOException {
124 super.close();
125 cipherOut.close();
126 }
127
128 @Override
129 protected void write0(byte[] buffer, int offset, int length)
130 throws IOException {
131 cipherOut.write(buffer, offset, length);
132 }
133
134 @Override
135 protected Storage toStorage0() throws IOException {
136
137 Storage encrypted = storageOut.toStorage();
138 return new CipherStorage(encrypted, algorithm, skeySpec);
139 }
140 }
141
142 private static final class CipherStorage implements Storage {
143 private Storage encrypted;
144 private final String algorithm;
145 private final SecretKeySpec skeySpec;
146
147 public CipherStorage(Storage encrypted, String algorithm,
148 SecretKeySpec skeySpec) {
149 this.encrypted = encrypted;
150 this.algorithm = algorithm;
151 this.skeySpec = skeySpec;
152 }
153
154 public void delete() {
155 if (encrypted != null) {
156 encrypted.delete();
157 encrypted = null;
158 }
159 }
160
161 public InputStream getInputStream() throws IOException {
162 if (encrypted == null)
163 throw new IllegalStateException("storage has been deleted");
164
165 try {
166 Cipher cipher = Cipher.getInstance(algorithm);
167 cipher.init(Cipher.DECRYPT_MODE, skeySpec);
168
169 InputStream in = encrypted.getInputStream();
170 return new CipherInputStream(in, cipher);
171 } catch (GeneralSecurityException e) {
172 throw (IOException) new IOException().initCause(e);
173 }
174 }
175 }
176
177 }