]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.core/src/org/argeo/security/crypto/PasswordBasedEncryption.java
Use standard JAAS login context for RAP login
[lgpl/argeo-commons.git] / org.argeo.security.core / src / org / argeo / security / crypto / 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.security.crypto;
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 import java.security.Security;
27
28 import javax.crypto.Cipher;
29 import javax.crypto.CipherInputStream;
30 import javax.crypto.CipherOutputStream;
31 import javax.crypto.SecretKey;
32 import javax.crypto.SecretKeyFactory;
33 import javax.crypto.spec.IvParameterSpec;
34 import javax.crypto.spec.PBEKeySpec;
35 import javax.crypto.spec.SecretKeySpec;
36
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.argeo.ArgeoException;
40 import org.argeo.StreamUtils;
41 import org.bouncycastle.jce.provider.BouncyCastleProvider;
42
43 /** Simple password based encryption / decryption */
44 public class PasswordBasedEncryption {
45 private final static Log log = LogFactory
46 .getLog(PasswordBasedEncryption.class);
47
48 static {
49 Security.addProvider(new BouncyCastleProvider());
50 }
51
52 public final static Integer DEFAULT_ITERATION_COUNT = 1024;
53 /** Stronger with 256, but causes problem with Oracle JVM */
54 public final static Integer DEFAULT_SECRETE_KEY_LENGTH = 256;
55 public final static Integer DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED = 128;
56 public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
57 public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
58 public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
59 public final static String DEFAULT_CHARSET = "UTF-8";
60
61 private Integer iterationCount = DEFAULT_ITERATION_COUNT;
62 private Integer secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
63 private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
64 private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
65 private String cipherName = DEFAULT_CIPHER_NAME;
66
67 private static byte[] DEFAULT_SALT_8 = { (byte) 0xA9, (byte) 0x9B,
68 (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
69 (byte) 0x03 };
70 private static byte[] DEFAULT_IV_16 = { (byte) 0xA9, (byte) 0x9B,
71 (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
72 (byte) 0x03, (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
73 (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
74
75 private Key key;
76 private Cipher ecipher;
77 private Cipher dcipher;
78
79 /**
80 * Default provider is bouncy castle, in order to have consistent behaviour
81 * across implementations
82 */
83 private String securityProviderName = "BC";
84
85 /**
86 * This is up to the caller to clear the passed array. Neither copy of nor
87 * reference to the passed array is kept
88 */
89 public PasswordBasedEncryption(char[] password) {
90 this(password, DEFAULT_SALT_8, DEFAULT_IV_16);
91 }
92
93 /**
94 * This is up to the caller to clear the passed array. Neither copies of nor
95 * references to the passed arrays are kept
96 */
97 public PasswordBasedEncryption(char[] password, byte[] passwordSalt,
98 byte[] initializationVector) {
99 try {
100 initKeyAndCiphers(password, passwordSalt, initializationVector);
101 } catch (InvalidKeyException e) {
102 Integer previousSecreteKeyLength = secreteKeyLength;
103 secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED;
104 log.warn("'" + e.getMessage() + "', will use " + secreteKeyLength
105 + " secrete key length instead of "
106 + previousSecreteKeyLength);
107 try {
108 initKeyAndCiphers(password, passwordSalt, initializationVector);
109 } catch (Exception e1) {
110 throw new ArgeoException(
111 "Cannot get secret key (with restricted length)", e1);
112 }
113 } catch (Exception e) {
114 throw new ArgeoException("Cannot get secret key", e);
115 }
116 }
117
118 protected void initKeyAndCiphers(char[] password, byte[] passwordSalt,
119 byte[] initializationVector) throws GeneralSecurityException {
120 byte[] salt = new byte[8];
121 System.arraycopy(passwordSalt, 0, salt, 0, salt.length);
122 // for (int i = 0; i < password.length && i < salt.length; i++)
123 // salt[i] = (byte) password[i];
124 byte[] iv = new byte[16];
125 System.arraycopy(initializationVector, 0, iv, 0, iv.length);
126
127 SecretKeyFactory keyFac = SecretKeyFactory
128 .getInstance(getSecretKeyFactoryName());
129 PBEKeySpec keySpec = new PBEKeySpec(password, salt,
130 getIterationCount(), getKeyLength());
131 String secKeyEncryption = getSecretKeyEncryption();
132 if (secKeyEncryption != null) {
133 SecretKey tmp = keyFac.generateSecret(keySpec);
134 key = new SecretKeySpec(tmp.getEncoded(), getSecretKeyEncryption());
135 } else {
136 key = keyFac.generateSecret(keySpec);
137 }
138 ecipher = Cipher.getInstance(getCipherName(), securityProviderName);
139 ecipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
140 dcipher = Cipher.getInstance(getCipherName());
141 dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
142 }
143
144 public void encrypt(InputStream decryptedIn, OutputStream encryptedOut)
145 throws IOException {
146 try {
147 CipherOutputStream out = new CipherOutputStream(encryptedOut,
148 ecipher);
149 StreamUtils.copy(decryptedIn, out);
150 StreamUtils.closeQuietly(out);
151 } catch (IOException e) {
152 throw e;
153 } catch (Exception e) {
154 throw new ArgeoException("Cannot encrypt", e);
155 } finally {
156 StreamUtils.closeQuietly(decryptedIn);
157 }
158 }
159
160 public void decrypt(InputStream encryptedIn, OutputStream decryptedOut)
161 throws IOException {
162 try {
163 CipherInputStream decryptedIn = new CipherInputStream(encryptedIn,
164 dcipher);
165 StreamUtils.copy(decryptedIn, decryptedOut);
166 } catch (IOException e) {
167 throw e;
168 } catch (Exception e) {
169 throw new ArgeoException("Cannot decrypt", e);
170 } finally {
171 StreamUtils.closeQuietly(encryptedIn);
172 }
173 }
174
175 public byte[] encryptString(String str) {
176 ByteArrayOutputStream out = null;
177 ByteArrayInputStream in = null;
178 try {
179 out = new ByteArrayOutputStream();
180 in = new ByteArrayInputStream(str.getBytes(DEFAULT_CHARSET));
181 encrypt(in, out);
182 return out.toByteArray();
183 } catch (Exception e) {
184 throw new ArgeoException("Cannot encrypt", e);
185 } finally {
186 StreamUtils.closeQuietly(out);
187 }
188 }
189
190 /** Closes the input stream */
191 public String decryptAsString(InputStream in) {
192 ByteArrayOutputStream out = null;
193 try {
194 out = new ByteArrayOutputStream();
195 decrypt(in, out);
196 return new String(out.toByteArray(), DEFAULT_CHARSET);
197 } catch (Exception e) {
198 throw new ArgeoException("Cannot decrypt", e);
199 } finally {
200 StreamUtils.closeQuietly(out);
201 }
202 }
203
204 protected Key getKey() {
205 return key;
206 }
207
208 protected Cipher getEcipher() {
209 return ecipher;
210 }
211
212 protected Cipher getDcipher() {
213 return dcipher;
214 }
215
216 protected Integer getIterationCount() {
217 return iterationCount;
218 }
219
220 protected Integer getKeyLength() {
221 return secreteKeyLength;
222 }
223
224 protected String getSecretKeyFactoryName() {
225 return secreteKeyFactoryName;
226 }
227
228 protected String getSecretKeyEncryption() {
229 return secreteKeyEncryption;
230 }
231
232 protected String getCipherName() {
233 return cipherName;
234 }
235
236 public void setIterationCount(Integer iterationCount) {
237 this.iterationCount = iterationCount;
238 }
239
240 public void setSecreteKeyLength(Integer keyLength) {
241 this.secreteKeyLength = keyLength;
242 }
243
244 public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
245 this.secreteKeyFactoryName = secreteKeyFactoryName;
246 }
247
248 public void setSecreteKeyEncryption(String secreteKeyEncryption) {
249 this.secreteKeyEncryption = secreteKeyEncryption;
250 }
251
252 public void setCipherName(String cipherName) {
253 this.cipherName = cipherName;
254 }
255
256 public void setSecurityProviderName(String securityProviderName) {
257 this.securityProviderName = securityProviderName;
258 }
259 }