1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.mercury.accounts.spring;
14
15 import java.io.File;
16 import java.io.FileInputStream;
17 import java.io.FileOutputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.OutputStream;
21 import java.math.BigInteger;
22 import java.net.MalformedURLException;
23 import java.security.InvalidKeyException;
24 import java.security.Key;
25 import java.security.KeyPair;
26 import java.security.KeyPairGenerator;
27 import java.security.KeyStore;
28 import java.security.KeyStoreException;
29 import java.security.NoSuchAlgorithmException;
30 import java.security.NoSuchProviderException;
31 import java.security.Security;
32 import java.security.SignatureException;
33 import java.security.cert.Certificate;
34 import java.security.cert.CertificateEncodingException;
35 import java.security.cert.CertificateException;
36 import java.util.Date;
37 import java.util.HashMap;
38 import java.util.Map;
39
40 import org.abstracthorizon.mercury.common.ConfigurableStorageManager;
41 import org.abstracthorizon.mercury.common.SimpleStorageManager;
42 import org.bouncycastle.jce.X509Principal;
43 import org.bouncycastle.jce.provider.BouncyCastleProvider;
44 import org.bouncycastle.x509.X509V3CertificateGenerator;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48
49
50
51
52
53
54 public class MaildirKeystoreStorageManager extends SimpleStorageManager
55 implements ConfigurableStorageManager {
56
57
58 protected Logger logger = LoggerFactory.getLogger(getClass());
59
60
61 private File mailboxesPath;
62
63
64 private File keystoreFile;
65
66
67 private String keystorePassword;
68
69
70 private String keystoreType = KeyStore.getDefaultType();
71
72
73 private String keystoreProvider = "";
74
75
76 private Map<String, Object> options = new HashMap<String, Object>();
77
78
79
80
81 public MaildirKeystoreStorageManager() {
82 }
83
84
85
86
87
88 public void setKeyStorePassword(String password) {
89 this.keystorePassword = password;
90 if (password != null) {
91 options.put("keyStorePassword", password);
92 } else {
93 options.remove("keyStorePassword");
94 }
95 }
96
97
98
99
100
101
102 public void setKeyStoreFile(File keystoreFile) throws IOException {
103 this.keystoreFile = keystoreFile;
104 }
105
106
107
108
109
110 public File getKeyStoreFile() {
111 return keystoreFile;
112 }
113
114
115
116
117
118
119 public void setMailboxesPath(File mailboxesPath) throws IOException {
120 this.mailboxesPath = mailboxesPath;
121 }
122
123
124
125
126
127 public File getMailboxesPath() {
128 return mailboxesPath;
129 }
130
131
132
133
134
135 public void setKeyStoreType(String type) {
136 keystoreType = type;
137 if (type != null) {
138 options.put("keyStoreType", type);
139 } else {
140 options.remove("keyStoreType");
141 }
142 }
143
144
145
146
147
148 public String getKeyStoreType() {
149 return keystoreType;
150 }
151
152
153
154
155
156 public void setKeyStoreProvider(String provider) {
157 this.keystoreProvider = provider;
158 if (provider != null) {
159 options.put("keyStoreProvider", provider);
160 } else {
161 options.remove("keyStoreProvider");
162 }
163 }
164
165
166
167
168
169 public String getKeyStoreProvider() {
170 return keystoreProvider;
171 }
172
173
174
175
176
177
178
179
180
181
182
183 protected KeyStore loadKeyStore() throws KeyStoreException, NoSuchProviderException, MalformedURLException,
184 IOException, NoSuchAlgorithmException, CertificateException {
185
186 logger.debug("Loading keystore from " + keystoreFile.getAbsolutePath());
187 InputStream keystoreInputStream = new FileInputStream(keystoreFile);
188 try {
189 KeyStore keyStore;
190 if ((keystoreProvider == null) || (keystoreProvider.length() == 0)) {
191 keyStore = KeyStore.getInstance(keystoreType);
192 } else {
193 keyStore = KeyStore.getInstance(keystoreType, keystoreProvider);
194 }
195
196
197 keyStore.load(keystoreInputStream, keystorePassword.toCharArray());
198 return keyStore;
199
200 } finally {
201 keystoreInputStream.close();
202 }
203 }
204
205
206
207
208
209
210
211
212
213
214
215 protected void storeKeyStore(KeyStore keystore) throws KeyStoreException, NoSuchProviderException, MalformedURLException,
216 IOException, NoSuchAlgorithmException, CertificateException {
217
218 logger.debug("Storing keystore to " + keystoreFile.getAbsolutePath());
219 OutputStream keystoreOutputStream = new FileOutputStream(keystoreFile);
220
221 try {
222 keystore.store(keystoreOutputStream, keystorePassword.toCharArray());
223 } finally {
224 keystoreOutputStream.close();
225 }
226 }
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 protected void addEntryToStore(KeyStore keyStore, String entry, char[] password) throws NoSuchAlgorithmException, KeyStoreException, InvalidKeyException, SecurityException, SignatureException, CertificateEncodingException, IllegalStateException, NoSuchProviderException {
243 String name = "CN=" + entry + ", OU=, O=, L=, ST=, C=";
244
245
246 Security.addProvider(new BouncyCastleProvider());
247
248 X509V3CertificateGenerator generator = new X509V3CertificateGenerator();
249 KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
250 kpGen.initialize(1024);
251 KeyPair pair = kpGen.generateKeyPair();
252
253 generator.setSerialNumber(BigInteger.valueOf(1));
254 generator.setIssuerDN(new X509Principal(name));
255 generator.setNotBefore(new Date());
256 generator.setNotAfter(new Date(System.currentTimeMillis()+1000*60*60*24*365));
257 generator.setSubjectDN(new X509Principal(name));
258 generator.setPublicKey(pair.getPublic());
259 generator.setSignatureAlgorithm("MD5WithRSAEncryption");
260
261 Certificate cert = generator.generate(pair.getPrivate(), "BC");
262
263 keyStore.setKeyEntry(entry, pair.getPrivate(), password, new Certificate[]{cert});
264
265 }
266
267 protected String decorateStoreString(String mailbox, String domain, String storeString) {
268 if ((storeString == null) || (storeString.length() == 0)) {
269 File mailboxDir = ensureExists(mailbox, domain);
270 storeString = "maildir://" + mailbox + "@" + domain + "/" + mailboxDir.getAbsolutePath();
271 }
272 return storeString;
273 }
274
275
276
277
278
279
280
281 protected File ensureExists(String mailbox, String domain) {
282 File domainPath = null;
283 if (domain != null) {
284 domainPath = new File(mailboxesPath, domain);
285 } else {
286 domainPath = mailboxesPath;
287 }
288 if (!domainPath.exists()) {
289 domainPath.mkdirs();
290 }
291 File mailboxPath = new File(domainPath, mailbox);
292 if (!mailboxPath.exists()) {
293 mailboxPath.mkdirs();
294 }
295 makeMaildirLayout(mailboxPath);
296 return mailboxPath;
297 }
298
299
300
301
302
303 protected void makeMaildirLayout(File mailbox) {
304 File inbox = new File(mailbox, ".inbox");
305 File cur = new File(inbox, "cur");
306 File nw = new File(inbox, "new");
307 File tmp = new File(inbox, "tmp");
308 inbox.mkdirs();
309 cur.mkdirs();
310 nw.mkdirs();
311 tmp.mkdirs();
312
313 }
314
315
316
317
318
319
320
321 public void addMailbox(String mailbox, String domain, char[] password) {
322 try {
323
324
325
326 super.addMailbox(makeEntry(mailbox, domain), "");
327
328 String entry = makeEntry(mailbox, domain);
329
330 KeyStore keyStore = loadKeyStore();
331 addEntryToStore(keyStore, entry, password);
332 storeKeyStore(keyStore);
333 } catch (Exception e) {
334 throw new RuntimeException(e);
335 }
336 }
337
338
339
340
341
342
343
344 public boolean removeMailbox(String mailbox, String domain) {
345 boolean res = false;
346 try {
347 res = super.removeMailbox(mailbox, domain);
348 String entry = makeEntry(mailbox, domain);
349 KeyStore keyStore = loadKeyStore();
350 keyStore.deleteEntry(entry);
351 storeKeyStore(keyStore);
352 } catch (Exception e) {
353 throw new RuntimeException(e);
354 }
355 return res;
356 }
357
358
359
360
361
362
363
364
365
366 public void changeMailboxPassword(String mailbox, String domain, char[] newPassword) {
367 changeMailboxPassword(mailbox, domain, null, newPassword);
368 }
369
370
371
372
373
374
375
376
377 public void changeMailboxPassword(String mailbox, String domain, char[] oldPassword, char[] newPassword) {
378 try {
379 String entry = makeEntry(mailbox, domain);
380 KeyStore keyStore = loadKeyStore();
381 if (oldPassword != null) {
382 Key key = keyStore.getKey(entry, oldPassword);
383
384 Certificate[] certs = keyStore.getCertificateChain(entry);
385
386 keyStore.setKeyEntry(entry, key, newPassword, certs);
387 } else {
388 keyStore.deleteEntry(entry);
389 addEntryToStore(keyStore, entry, newPassword);
390 }
391 storeKeyStore(keyStore);
392 } catch (Exception e) {
393 throw new RuntimeException(e);
394 }
395 }
396
397
398
399
400
401 public String[] getMailboxNames() {
402 return super.getMailboxNames();
403 }
404
405
406
407
408
409
410 public String[] getMailboxNames(String domain) {
411 return super.getMailboxNames(domain);
412 }
413
414
415
416
417
418 public void addDomain(String domain) {
419 super.addDomain(domain);
420 }
421
422
423
424
425
426
427 public boolean removeDomain(String domain) {
428 return super.removeDomain(domain);
429 }
430
431
432
433
434
435 public String[] getDomains() {
436 return super.getDomains();
437 }
438
439
440
441
442
443 public void setMainDomain(String domain) {
444 super.setMainDomain(domain);
445 }
446
447 }