2 * Copyright (C) 2007-2012 Argeo GmbH
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org
.argeo
.cms
.security
;
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
;
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
;
36 import org
.apache
.commons
.io
.IOUtils
;
37 import org
.argeo
.cms
.CmsException
;
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";
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
;
56 private static byte[] DEFAULT_SALT_8
= { (byte) 0xA9, (byte) 0x9B,
57 (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
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 };
65 private Cipher ecipher
;
66 private Cipher dcipher
;
68 private String securityProviderName
= null;
71 * This is up to the caller to clear the passed array. Neither copy of nor
72 * reference to the passed array is kept
74 public PasswordBasedEncryption(char[] password
) {
75 this(password
, DEFAULT_SALT_8
, DEFAULT_IV_16
);
79 * This is up to the caller to clear the passed array. Neither copies of nor
80 * references to the passed arrays are kept
82 public PasswordBasedEncryption(char[] password
, byte[] passwordSalt
,
83 byte[] initializationVector
) {
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
);
93 initKeyAndCiphers(password
, passwordSalt
, initializationVector
);
94 } catch (Exception e1
) {
95 throw new CmsException(
96 "Cannot get secret key (with restricted length)", e1
);
98 } catch (Exception e
) {
99 throw new CmsException("Cannot get secret key", e
);
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
);
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());
121 key
= keyFac
.generateSecret(keySpec
);
123 if (securityProviderName
!= null)
124 ecipher
= Cipher
.getInstance(getCipherName(), securityProviderName
);
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
));
132 public void encrypt(InputStream decryptedIn
, OutputStream encryptedOut
)
135 CipherOutputStream out
= new CipherOutputStream(encryptedOut
,
137 IOUtils
.copy(decryptedIn
, out
);
138 IOUtils
.closeQuietly(out
);
139 } catch (IOException e
) {
141 } catch (Exception e
) {
142 throw new CmsException("Cannot encrypt", e
);
144 IOUtils
.closeQuietly(decryptedIn
);
148 public void decrypt(InputStream encryptedIn
, OutputStream decryptedOut
)
151 CipherInputStream decryptedIn
= new CipherInputStream(encryptedIn
,
153 IOUtils
.copy(decryptedIn
, decryptedOut
);
154 } catch (IOException e
) {
156 } catch (Exception e
) {
157 throw new CmsException("Cannot decrypt", e
);
159 IOUtils
.closeQuietly(encryptedIn
);
163 public byte[] encryptString(String str
) {
164 ByteArrayOutputStream out
= null;
165 ByteArrayInputStream in
= null;
167 out
= new ByteArrayOutputStream();
168 in
= new ByteArrayInputStream(str
.getBytes(DEFAULT_CHARSET
));
170 return out
.toByteArray();
171 } catch (Exception e
) {
172 throw new CmsException("Cannot encrypt", e
);
174 IOUtils
.closeQuietly(out
);
178 /** Closes the input stream */
179 public String
decryptAsString(InputStream in
) {
180 ByteArrayOutputStream out
= null;
182 out
= new ByteArrayOutputStream();
184 return new String(out
.toByteArray(), DEFAULT_CHARSET
);
185 } catch (Exception e
) {
186 throw new CmsException("Cannot decrypt", e
);
188 IOUtils
.closeQuietly(out
);
192 protected Key
getKey() {
196 protected Cipher
getEcipher() {
200 protected Cipher
getDcipher() {
204 protected Integer
getIterationCount() {
205 return iterationCount
;
208 protected Integer
getKeyLength() {
209 return secreteKeyLength
;
212 protected String
getSecretKeyFactoryName() {
213 return secreteKeyFactoryName
;
216 protected String
getSecretKeyEncryption() {
217 return secreteKeyEncryption
;
220 protected String
getCipherName() {
224 public void setIterationCount(Integer iterationCount
) {
225 this.iterationCount
= iterationCount
;
228 public void setSecreteKeyLength(Integer keyLength
) {
229 this.secreteKeyLength
= keyLength
;
232 public void setSecreteKeyFactoryName(String secreteKeyFactoryName
) {
233 this.secreteKeyFactoryName
= secreteKeyFactoryName
;
236 public void setSecreteKeyEncryption(String secreteKeyEncryption
) {
237 this.secreteKeyEncryption
= secreteKeyEncryption
;
240 public void setCipherName(String cipherName
) {
241 this.cipherName
= cipherName
;
244 public void setSecurityProviderName(String securityProviderName
) {
245 this.securityProviderName
= securityProviderName
;