1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56:
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63: import ;
64: import ;
65: import ;
66:
67:
70: public class PasswordEncryptedEntry
71: extends MaskableEnvelopeEntry
72: implements PasswordProtectedEntry, Registry
73: {
74: private static final Logger log = Logger.getLogger(PasswordEncryptedEntry.class.getName());
75: public static final int TYPE = 1;
76:
77: public PasswordEncryptedEntry(String cipher, String mode, int keylen,
78: Properties properties)
79: {
80: super(TYPE, properties);
81: if ((cipher == null || cipher.length() == 0)
82: || (mode == null || mode.length() == 0))
83: throw new IllegalArgumentException("cipher nor mode can be empty");
84: this.properties.put("cipher", cipher);
85: this.properties.put("mode", mode);
86: this.properties.put("keylen", String.valueOf(keylen));
87: setMasked(false);
88: }
89:
90: private PasswordEncryptedEntry()
91: {
92: super(TYPE);
93: setMasked(true);
94: }
95:
96: public static PasswordEncryptedEntry decode(DataInputStream in,
97: char[] password)
98: throws IOException
99: {
100: PasswordEncryptedEntry entry = decode(in);
101: try
102: {
103: entry.decrypt(password);
104: }
105: catch (WrongPaddingException wpe)
106: {
107: throw new MalformedKeyringException("wrong padding in decrypted data");
108: }
109: return entry;
110: }
111:
112: public static PasswordEncryptedEntry decode(DataInputStream in)
113: throws IOException
114: {
115: PasswordEncryptedEntry entry = new PasswordEncryptedEntry();
116: entry.defaultDecode(in);
117: return entry;
118: }
119:
120: public void decrypt(char[] password) throws IllegalArgumentException,
121: WrongPaddingException
122: {
123: if (Configuration.DEBUG)
124: log.entering(this.getClass().getName(), "decrypt");
125: if (isMasked() && payload != null)
126: {
127: long tt = -System.currentTimeMillis();
128: IMode mode = getMode(password, IMode.DECRYPTION);
129: IPad padding = PadFactory.getInstance("PKCS7");
130: padding.init(mode.currentBlockSize());
131: byte[] buf = new byte[payload.length];
132: int count = 0;
133: while (count + mode.currentBlockSize() <= payload.length)
134: {
135: mode.update(payload, count, buf, count);
136: count += mode.currentBlockSize();
137: }
138: int padlen = padding.unpad(buf, 0, buf.length);
139: setMasked(false);
140: int len = buf.length - padlen;
141: ByteArrayInputStream baos = new ByteArrayInputStream(buf, 0, len);
142: DataInputStream in = new DataInputStream(baos);
143: try
144: {
145: decodeEnvelope(in);
146: }
147: catch (IOException ioe)
148: {
149: throw new IllegalArgumentException("decryption failed");
150: }
151: tt += System.currentTimeMillis();
152: log.fine("Decrypted in " + tt + "ms.");
153: }
154: else if (Configuration.DEBUG)
155: log.fine("Skip decryption; " + (isMasked() ? "null payload" : "unmasked"));
156: if (Configuration.DEBUG)
157: log.exiting(this.getClass().getName(), "decrypt");
158: }
159:
160: public void encrypt(char[] password) throws IOException
161: {
162: if (Configuration.DEBUG)
163: log.entering(this.getClass().getName(), "encrypt", String.valueOf(password));
164: long tt = -System.currentTimeMillis();
165: long t1 = -System.currentTimeMillis();
166: byte[] salt = new byte[8];
167: PRNG.getInstance().nextBytes(salt);
168: t1 += System.currentTimeMillis();
169: if (Configuration.DEBUG)
170: log.fine("-- Generated salt in " + t1 + "ms.");
171: properties.put("salt", Util.toString(salt));
172: IMode mode = getMode(password, IMode.ENCRYPTION);
173: IPad pad = PadFactory.getInstance("PKCS7");
174: pad.init(mode.currentBlockSize());
175: ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
176: DataOutputStream out2 = new DataOutputStream(bout);
177: for (Iterator it = entries.iterator(); it.hasNext();)
178: {
179: Entry entry = (Entry) it.next();
180: if (Configuration.DEBUG)
181: log.fine("-- About to encode one " + entry);
182: t1 = -System.currentTimeMillis();
183: entry.encode(out2);
184: t1 += System.currentTimeMillis();
185: if (Configuration.DEBUG)
186: log.fine("-- Encoded an Entry in " + t1 + "ms.");
187: }
188: byte[] plaintext = bout.toByteArray();
189: byte[] padding = pad.pad(plaintext, 0, plaintext.length);
190: payload = new byte[plaintext.length + padding.length];
191: byte[] lastBlock = new byte[mode.currentBlockSize()];
192: int l = mode.currentBlockSize() - padding.length;
193: System.arraycopy(plaintext, plaintext.length - l, lastBlock, 0, l);
194: System.arraycopy(padding, 0, lastBlock, l, padding.length);
195: int count = 0;
196: while (count + mode.currentBlockSize() < plaintext.length)
197: {
198: mode.update(plaintext, count, payload, count);
199: count += mode.currentBlockSize();
200: }
201: mode.update(lastBlock, 0, payload, count);
202: setMasked(true);
203: tt += System.currentTimeMillis();
204: if (Configuration.DEBUG)
205: {
206: log.fine("Encrypted in " + tt + "ms.");
207: log.exiting(this.getClass().getName(), "encrypt");
208: }
209: }
210:
211: public void encode(DataOutputStream out, char[] password) throws IOException
212: {
213: encrypt(password);
214: encode(out);
215: }
216:
217: protected void encodePayload() throws IOException
218: {
219: if (payload == null)
220: {
221: if (Configuration.DEBUG)
222: log.fine("Null payload: " + this);
223: throw new IllegalStateException("not encrypted");
224: }
225: }
226:
227: private IMode getMode(char[] password, int state)
228: {
229: String s = properties.get("salt");
230: if (s == null)
231: throw new IllegalArgumentException("no salt");
232: byte[] salt = Util.toBytesFromString(s);
233: IBlockCipher cipher = CipherFactory.getInstance(properties.get("cipher"));
234: if (cipher == null)
235: throw new IllegalArgumentException("no such cipher: "
236: + properties.get("cipher"));
237: int blockSize = cipher.defaultBlockSize();
238: if (properties.containsKey("block-size"))
239: try
240: {
241: blockSize = Integer.parseInt(properties.get("block-size"));
242: }
243: catch (NumberFormatException nfe)
244: {
245: throw new IllegalArgumentException("bad block size: "
246: + nfe.getMessage());
247: }
248: String modeName = properties.get("mode");
249: IMode mode = ModeFactory.getInstance(modeName, cipher, blockSize);
250: if (mode == null)
251: throw new IllegalArgumentException("no such mode: " + modeName);
252: HashMap pbAttr = new HashMap();
253: pbAttr.put(IPBE.PASSWORD, password);
254: pbAttr.put(IPBE.SALT, salt);
255: pbAttr.put(IPBE.ITERATION_COUNT, ITERATION_COUNT);
256: IRandom kdf = PRNGFactory.getInstance("PBKDF2-HMAC-SHA");
257: kdf.init(pbAttr);
258: int keylen = 0;
259: if (! properties.containsKey("keylen"))
260: throw new IllegalArgumentException("no key length");
261: try
262: {
263: keylen = Integer.parseInt(properties.get("keylen"));
264: }
265: catch (NumberFormatException nfe)
266: {
267: }
268: byte[] dk = new byte[keylen];
269: byte[] iv = new byte[blockSize];
270: try
271: {
272: kdf.nextBytes(dk, 0, keylen);
273: kdf.nextBytes(iv, 0, blockSize);
274: }
275: catch (LimitReachedException shouldNotHappen)
276: {
277: throw new Error(shouldNotHappen.toString());
278: }
279: HashMap modeAttr = new HashMap();
280: modeAttr.put(IMode.KEY_MATERIAL, dk);
281: modeAttr.put(IMode.STATE, Integer.valueOf(state));
282: modeAttr.put(IMode.IV, iv);
283: try
284: {
285: mode.init(modeAttr);
286: }
287: catch (InvalidKeyException ike)
288: {
289: throw new IllegalArgumentException(ike.toString());
290: }
291: return mode;
292: }
293: }