1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.accumulo.core.security.crypto;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.DataInputStream;
22 import java.io.DataOutputStream;
23 import java.io.IOException;
24 import java.net.URI;
25 import java.net.URISyntaxException;
26 import java.security.InvalidAlgorithmParameterException;
27 import java.security.InvalidKeyException;
28 import java.security.SecureRandom;
29 import java.util.HashMap;
30 import java.util.Map;
31
32 import javax.crypto.Cipher;
33 import javax.crypto.CipherOutputStream;
34 import javax.crypto.spec.IvParameterSpec;
35 import javax.crypto.spec.SecretKeySpec;
36
37 import org.apache.accumulo.core.conf.Property;
38 import org.apache.accumulo.core.util.CachedConfiguration;
39 import org.apache.hadoop.fs.FileSystem;
40 import org.apache.hadoop.fs.Path;
41 import org.apache.log4j.Logger;
42
43 public class DefaultSecretKeyEncryptionStrategy implements SecretKeyEncryptionStrategy {
44
45 private static final Logger log = Logger.getLogger(DefaultSecretKeyEncryptionStrategy.class);
46
47 public static class DefaultSecretKeyEncryptionStrategyContext implements SecretKeyEncryptionStrategyContext {
48
49 private byte[] plaintextSecretKey;
50 private byte[] encryptedSecretKey;
51 private Map<String, String> context;
52 private String opaqueKeyId;
53
54 @Override
55 public String getOpaqueKeyEncryptionKeyID() {
56 return opaqueKeyId;
57 }
58
59 @Override
60 public void setOpaqueKeyEncryptionKeyID(String id) {
61 this.opaqueKeyId = id;
62 }
63
64 @Override
65 public byte[] getPlaintextSecretKey() {
66 return plaintextSecretKey;
67 }
68
69 @Override
70 public void setPlaintextSecretKey(byte[] key) {
71 this.plaintextSecretKey = key;
72 }
73
74 @Override
75 public byte[] getEncryptedSecretKey() {
76 return encryptedSecretKey;
77 }
78
79 @Override
80 public void setEncryptedSecretKey(byte[] key) {
81 this.encryptedSecretKey = key;
82 }
83
84 @Override
85 public Map<String,String> getContext() {
86 return context;
87 }
88
89 @Override
90 public void setContext(Map<String,String> context) {
91 this.context = context;
92 }
93 }
94
95
96 @Override
97 public SecretKeyEncryptionStrategyContext encryptSecretKey(SecretKeyEncryptionStrategyContext context) {
98 String hdfsURI = context.getContext().get(Property.CRYPTO_DEFAULT_KEY_STRATEGY_HDFS_URI.getKey());
99 String pathToKeyName = context.getContext().get(Property.CRYPTO_DEFAULT_KEY_STRATEGY_KEY_LOCATION.getKey());
100 Path pathToKey = new Path(pathToKeyName);
101
102 FileSystem fs = getHadoopFileSystem(hdfsURI);
103 try {
104
105 doKeyEncryptionOperation(Cipher.ENCRYPT_MODE, context, pathToKeyName, pathToKey, fs);
106
107 } catch (IOException e) {
108 log.error(e);
109 throw new RuntimeException(e);
110 }
111
112 return context;
113
114 }
115
116 private void initializeKeyEncryptingKey(FileSystem fs, Path pathToKey, SecretKeyEncryptionStrategyContext context) throws IOException {
117 Map<String, String> cryptoContext = context.getContext();
118 DataOutputStream out = fs.create(pathToKey);
119
120 fs.setReplication(pathToKey, (short) 5);
121
122
123 out.writeInt(cryptoContext.size());
124
125 for (String key : cryptoContext.keySet()) {
126 out.writeUTF(key);
127 out.writeUTF(cryptoContext.get(key));
128 }
129
130 SecureRandom random = DefaultCryptoModuleUtils.getSecureRandom(cryptoContext.get(Property.CRYPTO_SECURE_RNG.getKey()), cryptoContext.get(Property.CRYPTO_SECURE_RNG_PROVIDER.getKey()));
131 int keyLength = Integer.parseInt(cryptoContext.get(Property.CRYPTO_CIPHER_KEY_LENGTH.getKey()));
132 byte[] newRandomKeyEncryptionKey = new byte[keyLength / 8];
133
134 random.nextBytes(newRandomKeyEncryptionKey);
135
136 Cipher cipher = DefaultCryptoModuleUtils.getCipher(cryptoContext.get(Property.CRYPTO_CIPHER_SUITE.getKey()));
137 try {
138 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(newRandomKeyEncryptionKey, cryptoContext.get(Property.CRYPTO_CIPHER_ALGORITHM_NAME.getKey())), random);
139 } catch (InvalidKeyException e) {
140 log.error(e);
141 throw new RuntimeException(e);
142 }
143
144 byte[] initVector = cipher.getIV();
145
146 out.writeInt(initVector.length);
147 out.write(initVector);
148
149 out.writeInt(newRandomKeyEncryptionKey.length);
150 out.write(newRandomKeyEncryptionKey);
151
152 out.flush();
153 out.close();
154
155 }
156
157 private FileSystem getHadoopFileSystem(String hdfsURI) {
158 FileSystem fs = null;
159
160 if (hdfsURI != null && !hdfsURI.equals("")) {
161 try {
162 fs = FileSystem.get(CachedConfiguration.getInstance());
163 } catch (IOException e) {
164 log.error(e);
165 throw new RuntimeException(e);
166 }
167 }
168 else {
169 try {
170 fs = FileSystem.get(new URI(hdfsURI), CachedConfiguration.getInstance());
171 } catch (URISyntaxException e) {
172 log.error(e);
173 throw new RuntimeException(e);
174 } catch (IOException e) {
175 log.error(e);
176 throw new RuntimeException(e);
177 }
178
179
180 }
181 return fs;
182 }
183
184 @Override
185 public SecretKeyEncryptionStrategyContext decryptSecretKey(SecretKeyEncryptionStrategyContext context) {
186 String hdfsURI = context.getContext().get(Property.CRYPTO_DEFAULT_KEY_STRATEGY_HDFS_URI.getKey());
187 String pathToKeyName = context.getContext().get(Property.CRYPTO_DEFAULT_KEY_STRATEGY_KEY_LOCATION.getKey());
188 Path pathToKey = new Path(pathToKeyName);
189
190 FileSystem fs = getHadoopFileSystem(hdfsURI);
191 try {
192 doKeyEncryptionOperation(Cipher.DECRYPT_MODE, context, pathToKeyName, pathToKey, fs);
193
194
195 } catch (IOException e) {
196 log.error(e);
197 throw new RuntimeException(e);
198 }
199
200 return context;
201 }
202
203 private void doKeyEncryptionOperation(int encryptionMode, SecretKeyEncryptionStrategyContext context, String pathToKeyName, Path pathToKey, FileSystem fs)
204 throws IOException {
205 DataInputStream in = null;
206 try {
207 if (!fs.exists(pathToKey)) {
208
209 if (encryptionMode == Cipher.DECRYPT_MODE) {
210 log.error("There was a call to decrypt the session key but no key encryption key exists. Either restore it, reconfigure the conf file to point to it in HDFS, or throw the affected data away and begin again.");
211 throw new RuntimeException("Could not find key encryption key file in configured location in HDFS ("+pathToKeyName+")");
212 } else {
213 initializeKeyEncryptingKey(fs, pathToKey, context);
214 }
215 }
216 in = fs.open(pathToKey);
217
218 int numOfOpts = in.readInt();
219 Map<String, String> optsFromFile = new HashMap<String, String>();
220
221 for (int i = 0; i < numOfOpts; i++) {
222 String key = in.readUTF();
223 String value = in.readUTF();
224
225 optsFromFile.put(key, value);
226 }
227
228 int ivLength = in.readInt();
229 byte[] iv = new byte[ivLength];
230 in.read(iv);
231
232
233 int keyEncryptionKeyLength = in.readInt();
234 byte[] keyEncryptionKey = new byte[keyEncryptionKeyLength];
235 in.read(keyEncryptionKey);
236
237 Cipher cipher = DefaultCryptoModuleUtils.getCipher(optsFromFile.get(Property.CRYPTO_CIPHER_SUITE.getKey()));
238
239 try {
240 cipher.init(encryptionMode, new SecretKeySpec(keyEncryptionKey, optsFromFile.get(Property.CRYPTO_CIPHER_ALGORITHM_NAME.getKey())), new IvParameterSpec(iv));
241 } catch (InvalidKeyException e) {
242 log.error(e);
243 throw new RuntimeException(e);
244 } catch (InvalidAlgorithmParameterException e) {
245 log.error(e);
246 throw new RuntimeException(e);
247 }
248
249 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
250 CipherOutputStream cipherStream = new CipherOutputStream(byteArrayOutputStream, cipher);
251
252
253 if (Cipher.DECRYPT_MODE == encryptionMode) {
254 cipherStream.write(context.getEncryptedSecretKey());
255 cipherStream.flush();
256 byte[] plaintextSecretKey = byteArrayOutputStream.toByteArray();
257
258 cipherStream.close();
259
260 context.setPlaintextSecretKey(plaintextSecretKey);
261 } else {
262 cipherStream.write(context.getPlaintextSecretKey());
263 cipherStream.flush();
264 byte[] encryptedSecretKey = byteArrayOutputStream.toByteArray();
265
266 cipherStream.close();
267
268 context.setEncryptedSecretKey(encryptedSecretKey);
269 context.setOpaqueKeyEncryptionKeyID(pathToKeyName);
270 }
271
272 } finally {
273 if (in != null) {
274 in.close();
275 }
276 }
277 }
278
279 @Override
280 public SecretKeyEncryptionStrategyContext getNewContext() {
281 return new DefaultSecretKeyEncryptionStrategyContext();
282 }
283
284 }