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