]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.util/src/org/argeo/util/security/PasswordBasedEncryption.java
d7866a810255992040f300373a643baf1d46b028
[lgpl/argeo-commons.git] / org.argeo.util / src / org / argeo / util / security / PasswordBasedEncryption.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.argeo.util.security;
17
18 import java.io.ByteArrayInputStream;
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.security.GeneralSecurityException;
24 import java.security.InvalidKeyException;
25 import java.security.Key;
26
27 import javax.crypto.Cipher;
28 import javax.crypto.CipherInputStream;
29 import javax.crypto.CipherOutputStream;
30 import javax.crypto.SecretKey;
31 import javax.crypto.SecretKeyFactory;
32 import javax.crypto.spec.IvParameterSpec;
33 import javax.crypto.spec.PBEKeySpec;
34 import javax.crypto.spec.SecretKeySpec;
35
36 import org.argeo.ArgeoException;
37 import org.argeo.StreamUtils;
38
39 /** Simple password based encryption / decryption */
40 public class PasswordBasedEncryption {
41 public final static Integer DEFAULT_ITERATION_COUNT = 1024;
42 /** Stronger with 256, but causes problem with Oracle JVM */
43 public final static Integer DEFAULT_SECRETE_KEY_LENGTH = 256;
44 public final static Integer DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED = 128;
45 public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
46 public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
47 public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
48 public final static String DEFAULT_CHARSET = "UTF-8";
49
50 private Integer iterationCount = DEFAULT_ITERATION_COUNT;
51 private Integer secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
52 private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
53 private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
54 private String cipherName = DEFAULT_CIPHER_NAME;
55
56 private static byte[] DEFAULT_SALT_8 = { (byte) 0xA9, (byte) 0x9B,
57 (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
58 (byte) 0x03 };
59 private static byte[] DEFAULT_IV_16 = { (byte) 0xA9, (byte) 0x9B,
60 (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
61 (byte) 0x03, (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
62 (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
63
64 private Key key;
65 private Cipher ecipher;
66 private Cipher dcipher;
67
68 private String securityProviderName = null;
69
70 /**
71 * This is up to the caller to clear the passed array. Neither copy of nor
72 * reference to the passed array is kept
73 */
74 public PasswordBasedEncryption(char[] password) {
75 this(password, DEFAULT_SALT_8, DEFAULT_IV_16);
76 }
77
78 /**
79 * This is up to the caller to clear the passed array. Neither copies of nor
80 * references to the passed arrays are kept
81 */
82 public PasswordBasedEncryption(char[] password, byte[] passwordSalt,
83 byte[] initializationVector) {
84 try {
85 initKeyAndCiphers(password, passwordSalt, initializationVector);
86 } catch (InvalidKeyException e) {
87 Integer previousSecreteKeyLength = secreteKeyLength;
88 secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED;
89 System.err.println("'" + e.getMessage() + "', will use "
90 + secreteKeyLength + " secrete key length instead of "
91 + previousSecreteKeyLength);
92 try {
93 initKeyAndCiphers(password, passwordSalt, initializationVector);
94 } catch (Exception e1) {
95 throw new ArgeoException(
96 "Cannot get secret key (with restricted length)", e1);
97 }
98 } catch (Exception e) {
99 throw new ArgeoException("Cannot get secret key", e);
100 }
101 }
102
103 protected void initKeyAndCiphers(char[] password, byte[] passwordSalt,
104 byte[] initializationVector) throws GeneralSecurityException {
105 byte[] salt = new byte[8];
106 System.arraycopy(passwordSalt, 0, salt, 0, salt.length);
107 // for (int i = 0; i < password.length && i < salt.length; i++)
108 // salt[i] = (byte) password[i];
109 byte[] iv = new byte[16];
110 System.arraycopy(initializationVector, 0, iv, 0, iv.length);
111
112 SecretKeyFactory keyFac = SecretKeyFactory
113 .getInstance(getSecretKeyFactoryName());
114 PBEKeySpec keySpec = new PBEKeySpec(password, salt,
115 getIterationCount(), getKeyLength());
116 String secKeyEncryption = getSecretKeyEncryption();
117 if (secKeyEncryption != null) {
118 SecretKey tmp = keyFac.generateSecret(keySpec);
119 key = new SecretKeySpec(tmp.getEncoded(), getSecretKeyEncryption());
120 } else {
121 key = keyFac.generateSecret(keySpec);
122 }
123 if (securityProviderName != null)
124 ecipher = Cipher.getInstance(getCipherName(), securityProviderName);
125 else
126 ecipher = Cipher.getInstance(getCipherName());
127 ecipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
128 dcipher = Cipher.getInstance(getCipherName());
129 dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
130 }
131
132 public void encrypt(InputStream decryptedIn, OutputStream encryptedOut)
133 throws IOException {
134 try {
135 CipherOutputStream out = new CipherOutputStream(encryptedOut,
136 ecipher);
137 StreamUtils.copy(decryptedIn, out);
138 StreamUtils.closeQuietly(out);
139 } catch (IOException e) {
140 throw e;
141 } catch (Exception e) {
142 throw new ArgeoException("Cannot encrypt", e);
143 } finally {
144 StreamUtils.closeQuietly(decryptedIn);
145 }
146 }
147
148 public void decrypt(InputStream encryptedIn, OutputStream decryptedOut)
149 throws IOException {
150 try {
151 CipherInputStream decryptedIn = new CipherInputStream(encryptedIn,
152 dcipher);
153 StreamUtils.copy(decryptedIn, decryptedOut);
154 } catch (IOException e) {
155 throw e;
156 } catch (Exception e) {
157 throw new ArgeoException("Cannot decrypt", e);
158 } finally {
159 StreamUtils.closeQuietly(encryptedIn);
160 }
161 }
162
163 public byte[] encryptString(String str) {
164 ByteArrayOutputStream out = null;
165 ByteArrayInputStream in = null;
166 try {
167 out = new ByteArrayOutputStream();
168 in = new ByteArrayInputStream(str.getBytes(DEFAULT_CHARSET));
169 encrypt(in, out);
170 return out.toByteArray();
171 } catch (Exception e) {
172 throw new ArgeoException("Cannot encrypt", e);
173 } finally {
174 StreamUtils.closeQuietly(out);
175 }
176 }
177
178 /** Closes the input stream */
179 public String decryptAsString(InputStream in) {
180 ByteArrayOutputStream out = null;
181 try {
182 out = new ByteArrayOutputStream();
183 decrypt(in, out);
184 return new String(out.toByteArray(), DEFAULT_CHARSET);
185 } catch (Exception e) {
186 throw new ArgeoException("Cannot decrypt", e);
187 } finally {
188 StreamUtils.closeQuietly(out);
189 }
190 }
191
192 protected Key getKey() {
193 return key;
194 }
195
196 protected Cipher getEcipher() {
197 return ecipher;
198 }
199
200 protected Cipher getDcipher() {
201 return dcipher;
202 }
203
204 protected Integer getIterationCount() {
205 return iterationCount;
206 }
207
208 protected Integer getKeyLength() {
209 return secreteKeyLength;
210 }
211
212 protected String getSecretKeyFactoryName() {
213 return secreteKeyFactoryName;
214 }
215
216 protected String getSecretKeyEncryption() {
217 return secreteKeyEncryption;
218 }
219
220 protected String getCipherName() {
221 return cipherName;
222 }
223
224 public void setIterationCount(Integer iterationCount) {
225 this.iterationCount = iterationCount;
226 }
227
228 public void setSecreteKeyLength(Integer keyLength) {
229 this.secreteKeyLength = keyLength;
230 }
231
232 public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
233 this.secreteKeyFactoryName = secreteKeyFactoryName;
234 }
235
236 public void setSecreteKeyEncryption(String secreteKeyEncryption) {
237 this.secreteKeyEncryption = secreteKeyEncryption;
238 }
239
240 public void setCipherName(String cipherName) {
241 this.cipherName = cipherName;
242 }
243
244 public void setSecurityProviderName(String securityProviderName) {
245 this.securityProviderName = securityProviderName;
246 }
247 }