1 | /******************************************************************************
|
---|
2 | *
|
---|
3 | * Copyright (c) 1998,99 by Mindbright Technology AB, Stockholm, Sweden.
|
---|
4 | * www.mindbright.se, [email protected]
|
---|
5 | *
|
---|
6 | * This program is free software; you can redistribute it and/or modify
|
---|
7 | * it under the terms of the GNU General Public License as published by
|
---|
8 | * the Free Software Foundation; either version 2 of the License, or
|
---|
9 | * (at your option) any later version.
|
---|
10 | *
|
---|
11 | * This program is distributed in the hope that it will be useful,
|
---|
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
14 | * GNU General Public License for more details.
|
---|
15 | *
|
---|
16 | *****************************************************************************
|
---|
17 | * $Author: mats $
|
---|
18 | * $Date: 2000/08/01 20:37:09 $
|
---|
19 | * $Name: rel1-2-1 $
|
---|
20 | *****************************************************************************/
|
---|
21 | package mindbright.util;
|
---|
22 |
|
---|
23 | import java.io.InputStream;
|
---|
24 | import java.io.OutputStream;
|
---|
25 | import java.io.ByteArrayOutputStream;
|
---|
26 | import java.io.ByteArrayInputStream;
|
---|
27 | import java.io.IOException;
|
---|
28 | import java.util.Properties;
|
---|
29 | import java.util.Enumeration;
|
---|
30 |
|
---|
31 | import mindbright.security.*;
|
---|
32 |
|
---|
33 | public class EncryptedProperties extends Properties {
|
---|
34 | public final static String HASH_KEY = "EncryptedProperties.hash";
|
---|
35 | public final static String CIPHER_KEY = "EncryptedProperties.cipher";
|
---|
36 | public final static String CONTENTS_KEY = "EncryptedProperties.contents";
|
---|
37 | public final static String SIZE_KEY = "EncryptedProperties.size";
|
---|
38 | public final static String PROPS_HEADER = "Sealed with mindbright.util.EncryptedProperties" +
|
---|
39 | "(ver. $Name: rel1-2-1 $" + "$Date: 2000/08/01 20:37:09 $)";
|
---|
40 |
|
---|
41 | private boolean isNormalPropsFile;
|
---|
42 |
|
---|
43 | public EncryptedProperties() {
|
---|
44 | super();
|
---|
45 | isNormalPropsFile = false;
|
---|
46 | }
|
---|
47 |
|
---|
48 | public EncryptedProperties(Properties defaultProperties) {
|
---|
49 | super(defaultProperties);
|
---|
50 | isNormalPropsFile = false;
|
---|
51 | }
|
---|
52 |
|
---|
53 | public boolean isNormalPropsFile() {
|
---|
54 | return isNormalPropsFile;
|
---|
55 | }
|
---|
56 |
|
---|
57 | public synchronized void save(OutputStream out, String header, String password,
|
---|
58 | String cipherName) throws IOException {
|
---|
59 | ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
|
---|
60 | Properties encProps = new Properties();
|
---|
61 | byte[] contents, hash;
|
---|
62 | String hashStr;
|
---|
63 | Cipher cipher = Cipher.getInstance(cipherName);
|
---|
64 | int size;
|
---|
65 |
|
---|
66 | if(cipher == null)
|
---|
67 | throw new IOException("Unknown cipher '" + cipherName + "'");
|
---|
68 |
|
---|
69 | save(bytesOut, header);
|
---|
70 |
|
---|
71 | contents = bytesOut.toByteArray();
|
---|
72 | size = contents.length;
|
---|
73 | try {
|
---|
74 | MessageDigest md5 = MessageDigest.getInstance("MD5");
|
---|
75 | md5.update(contents);
|
---|
76 | hash = md5.digest();
|
---|
77 | } catch(Exception e) {
|
---|
78 | throw new IOException("MD5 not implemented, can't generate session-id");
|
---|
79 | }
|
---|
80 |
|
---|
81 | hash = Base64.encode(hash);
|
---|
82 | hashStr = new String(hash);
|
---|
83 |
|
---|
84 | // Assume cipher-block length no longer than 8
|
---|
85 | //
|
---|
86 | byte[] tmp = new byte[contents.length + (8 - (contents.length % 8))];
|
---|
87 | System.arraycopy(contents, 0, tmp, 0, contents.length);
|
---|
88 | contents = new byte[tmp.length];
|
---|
89 |
|
---|
90 | cipher.setKey(hashStr + password);
|
---|
91 | cipher.encrypt(tmp, 0, contents, 0, contents.length);
|
---|
92 |
|
---|
93 | contents = Base64.encode(contents);
|
---|
94 |
|
---|
95 | encProps.put(HASH_KEY, new String(hash));
|
---|
96 | encProps.put(CIPHER_KEY, cipherName);
|
---|
97 | encProps.put(CONTENTS_KEY, new String(contents));
|
---|
98 | encProps.put(SIZE_KEY, String.valueOf(size));
|
---|
99 | encProps.save(out, PROPS_HEADER);
|
---|
100 | out.flush();
|
---|
101 | }
|
---|
102 |
|
---|
103 | public synchronized void load(InputStream in, String password) throws IOException, AccessDeniedException {
|
---|
104 | Properties encProps = new Properties();
|
---|
105 | String hashStr, cipherName, contentsStr, sizeStr;
|
---|
106 | byte[] contents, hash, hashCalc;
|
---|
107 | Cipher cipher;
|
---|
108 | int size;
|
---|
109 |
|
---|
110 | encProps.load(in);
|
---|
111 |
|
---|
112 | hashStr = encProps.getProperty(HASH_KEY);
|
---|
113 | cipherName = encProps.getProperty(CIPHER_KEY);
|
---|
114 | contentsStr = encProps.getProperty(CONTENTS_KEY);
|
---|
115 | sizeStr = encProps.getProperty(SIZE_KEY);
|
---|
116 |
|
---|
117 | // Assume normal properties if our keys are not found (i.e. for
|
---|
118 | // "backwards compatible" reading of properties which will be encrypted
|
---|
119 | // if saved)
|
---|
120 | //
|
---|
121 | if(hashStr == null && cipherName == null && contentsStr == null && sizeStr == null) {
|
---|
122 | isNormalPropsFile = true;
|
---|
123 | Enumeration keys = encProps.keys();
|
---|
124 | while(keys.hasMoreElements()) {
|
---|
125 | String key = (String)keys.nextElement();
|
---|
126 | put(key, encProps.getProperty(key));
|
---|
127 | }
|
---|
128 | return;
|
---|
129 | }
|
---|
130 |
|
---|
131 | size = Integer.parseInt(sizeStr);
|
---|
132 |
|
---|
133 | hash = Base64.decode(hashStr.getBytes());
|
---|
134 | contents = Base64.decode(contentsStr.getBytes());
|
---|
135 |
|
---|
136 | cipher = Cipher.getInstance(cipherName);
|
---|
137 | if(cipher == null)
|
---|
138 | throw new IOException("Unknown cipher '" + cipherName + "'");
|
---|
139 |
|
---|
140 | cipher.setKey(hashStr + password);
|
---|
141 | cipher.decrypt(contents, 0, contents, 0, contents.length);
|
---|
142 |
|
---|
143 | byte[] tmp = new byte[size];
|
---|
144 | System.arraycopy(contents, 0, tmp, 0, size);
|
---|
145 | contents = tmp;
|
---|
146 |
|
---|
147 | try {
|
---|
148 | MessageDigest md5 = MessageDigest.getInstance("MD5");
|
---|
149 | md5.update(contents);
|
---|
150 | hashCalc = md5.digest();
|
---|
151 | } catch(Exception e) {
|
---|
152 | throw new IOException("MD5 not implemented, can't generate session-id");
|
---|
153 | }
|
---|
154 |
|
---|
155 | for(int i = 0; i < hash.length; i++) {
|
---|
156 | if(hash[i] != hashCalc[i])
|
---|
157 | throw new AccessDeniedException("Access denied");
|
---|
158 | }
|
---|
159 |
|
---|
160 | ByteArrayInputStream bytesIn = new ByteArrayInputStream(contents);
|
---|
161 | load(bytesIn);
|
---|
162 | }
|
---|
163 |
|
---|
164 | /* !!! DEBUG
|
---|
165 | public static void main(String[] argv) {
|
---|
166 | EncryptedProperties test = new EncryptedProperties();
|
---|
167 |
|
---|
168 | test.put("Foo", "bar");
|
---|
169 | test.put("foo", "bAR");
|
---|
170 | test.put("bar", "FOO");
|
---|
171 | test.put("BAR", "foo");
|
---|
172 |
|
---|
173 | try {
|
---|
174 | test.save(new java.io.FileOutputStream("/tmp/fooprops"), "These are some meaningless props...",
|
---|
175 | "foobar", "Blowfish");
|
---|
176 | test = new EncryptedProperties();
|
---|
177 | test.load(new java.io.FileInputStream("/tmp/fooprops"), "foobar");
|
---|
178 |
|
---|
179 | System.out.println("test: " + test.getProperty("BAR") + test.getProperty("Foo"));
|
---|
180 |
|
---|
181 | } catch (Exception e) {
|
---|
182 | System.out.println("Error:" + e);
|
---|
183 | e.printStackTrace();
|
---|
184 | }
|
---|
185 | }
|
---|
186 | */
|
---|
187 |
|
---|
188 | }
|
---|