1
/****************************************************************
2
* Licensed to the Apache Software Foundation (ASF) under one *
3
* or more contributor license agreements. See the NOTICE file *
4
* distributed with this work for additional information *
5
* regarding copyright ownership. The ASF licenses this file *
6
* to you under the Apache License, Version 2.0 (the *
7
* "License"); you may not use this file except in compliance *
8
* with the License. You may obtain a copy of the License at *
10
* http://www.apache.org/licenses/LICENSE-2.0 *
12
* Unless required by applicable law or agreed to in writing, *
13
* software distributed under the License is distributed on an *
14
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15
* KIND, either express or implied. See the License for the *
16
* specific language governing permissions and limitations *
17
* under the License. *
18
****************************************************************/
20
package org.apache.james.mime4j.storage;
22
import java.io.IOException;
23
import java.io.InputStream;
24
import java.security.GeneralSecurityException;
25
import java.security.NoSuchAlgorithmException;
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;
34
* A {@link StorageProvider} that transparently scrambles and unscrambles the
35
* data stored by another <code>StorageProvider</code>.
41
* StorageProvider mistrusted = new TempFileStorageProvider();
42
* StorageProvider enciphered = new CipherStorageProvider(mistrusted);
43
* StorageProvider provider = new ThresholdStorageProvider(enciphered);
44
* DefaultStorageProvider.setInstance(provider);
47
public class CipherStorageProvider extends AbstractStorageProvider {
49
private final StorageProvider backend;
50
private final String algorithm;
51
private final KeyGenerator keygen;
54
* Creates a new <code>CipherStorageProvider</code> for the given back-end
55
* using the Blowfish cipher algorithm.
58
* back-end storage strategy to encrypt.
60
public CipherStorageProvider(StorageProvider backend) {
61
this(backend, "Blowfish");
65
* Creates a new <code>CipherStorageProvider</code> for the given back-end
66
* and cipher algorithm.
69
* back-end storage strategy to encrypt.
71
* the name of the symmetric block cipher algorithm such as
72
* "Blowfish", "AES" or "RC2".
74
public CipherStorageProvider(StorageProvider backend, String algorithm) {
76
throw new IllegalArgumentException();
79
this.backend = backend;
80
this.algorithm = algorithm;
81
this.keygen = KeyGenerator.getInstance(algorithm);
82
} catch (NoSuchAlgorithmException e) {
83
throw new IllegalArgumentException(e);
87
public StorageOutputStream createStorageOutputStream() throws IOException {
88
SecretKeySpec skeySpec = getSecretKeySpec();
90
return new CipherStorageOutputStream(backend
91
.createStorageOutputStream(), algorithm, skeySpec);
94
private SecretKeySpec getSecretKeySpec() {
95
byte[] raw = keygen.generateKey().getEncoded();
96
return new SecretKeySpec(raw, algorithm);
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;
106
public CipherStorageOutputStream(StorageOutputStream out,
107
String algorithm, SecretKeySpec skeySpec) throws IOException {
109
this.storageOut = out;
110
this.algorithm = algorithm;
111
this.skeySpec = skeySpec;
113
Cipher cipher = Cipher.getInstance(algorithm);
114
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
116
this.cipherOut = new CipherOutputStream(out, cipher);
117
} catch (GeneralSecurityException e) {
118
throw (IOException) new IOException().initCause(e);
123
public void close() throws IOException {
129
protected void write0(byte[] buffer, int offset, int length)
131
cipherOut.write(buffer, offset, length);
135
protected Storage toStorage0() throws IOException {
136
// cipherOut has already been closed because toStorage calls close
137
Storage encrypted = storageOut.toStorage();
138
return new CipherStorage(encrypted, algorithm, skeySpec);
142
private static final class CipherStorage implements Storage {
143
private Storage encrypted;
144
private final String algorithm;
145
private final SecretKeySpec skeySpec;
147
public CipherStorage(Storage encrypted, String algorithm,
148
SecretKeySpec skeySpec) {
149
this.encrypted = encrypted;
150
this.algorithm = algorithm;
151
this.skeySpec = skeySpec;
154
public void delete() {
155
if (encrypted != null) {
161
public InputStream getInputStream() throws IOException {
162
if (encrypted == null)
163
throw new IllegalStateException("storage has been deleted");
166
Cipher cipher = Cipher.getInstance(algorithm);
167
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
169
InputStream in = encrypted.getInputStream();
170
return new CipherInputStream(in, cipher);
171
} catch (GeneralSecurityException e) {
172
throw (IOException) new IOException().initCause(e);