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