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
.util
;
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 public class PasswordEncryption
{
37 public final static Integer DEFAULT_ITERATION_COUNT
= 1024;
38 /** Stronger with 256, but causes problem with Oracle JVM */
39 public final static Integer DEFAULT_SECRETE_KEY_LENGTH
= 256;
40 public final static Integer DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED
= 128;
41 public final static String DEFAULT_SECRETE_KEY_FACTORY
= "PBKDF2WithHmacSHA1";
42 public final static String DEFAULT_SECRETE_KEY_ENCRYPTION
= "AES";
43 public final static String DEFAULT_CIPHER_NAME
= "AES/CBC/PKCS5Padding";
44 public final static String DEFAULT_CHARSET
= "UTF-8";
46 private Integer iterationCount
= DEFAULT_ITERATION_COUNT
;
47 private Integer secreteKeyLength
= DEFAULT_SECRETE_KEY_LENGTH
;
48 private String secreteKeyFactoryName
= DEFAULT_SECRETE_KEY_FACTORY
;
49 private String secreteKeyEncryption
= DEFAULT_SECRETE_KEY_ENCRYPTION
;
50 private String cipherName
= DEFAULT_CIPHER_NAME
;
52 private static byte[] DEFAULT_SALT_8
= { (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56,
53 (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
54 private static byte[] DEFAULT_IV_16
= { (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56,
55 (byte) 0x35, (byte) 0xE3, (byte) 0x03, (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56,
56 (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
59 private Cipher ecipher
;
60 private Cipher dcipher
;
62 private String securityProviderName
= null;
65 * This is up to the caller to clear the passed array. Neither copy of nor
66 * reference to the passed array is kept
68 public PasswordEncryption(char[] password
) {
69 this(password
, DEFAULT_SALT_8
, DEFAULT_IV_16
);
73 * This is up to the caller to clear the passed array. Neither copies of nor
74 * references to the passed arrays are kept
76 public PasswordEncryption(char[] password
, byte[] passwordSalt
, byte[] initializationVector
) {
78 initKeyAndCiphers(password
, passwordSalt
, initializationVector
);
79 } catch (InvalidKeyException e
) {
80 Integer previousSecreteKeyLength
= secreteKeyLength
;
81 secreteKeyLength
= DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED
;
82 System
.err
.println("'" + e
.getMessage() + "', will use " + secreteKeyLength
83 + " secrete key length instead of " + previousSecreteKeyLength
);
85 initKeyAndCiphers(password
, passwordSalt
, initializationVector
);
86 } catch (Exception e1
) {
87 throw new UtilsException("Cannot get secret key (with restricted length)", e1
);
89 } catch (Exception e
) {
90 throw new UtilsException("Cannot get secret key", e
);
94 protected void initKeyAndCiphers(char[] password
, byte[] passwordSalt
, byte[] initializationVector
)
95 throws GeneralSecurityException
{
96 byte[] salt
= new byte[8];
97 System
.arraycopy(passwordSalt
, 0, salt
, 0, salt
.length
);
98 // for (int i = 0; i < password.length && i < salt.length; i++)
99 // salt[i] = (byte) password[i];
100 byte[] iv
= new byte[16];
101 System
.arraycopy(initializationVector
, 0, iv
, 0, iv
.length
);
103 SecretKeyFactory keyFac
= SecretKeyFactory
.getInstance(getSecretKeyFactoryName());
104 PBEKeySpec keySpec
= new PBEKeySpec(password
, salt
, getIterationCount(), getKeyLength());
105 String secKeyEncryption
= getSecretKeyEncryption();
106 if (secKeyEncryption
!= null) {
107 SecretKey tmp
= keyFac
.generateSecret(keySpec
);
108 key
= new SecretKeySpec(tmp
.getEncoded(), getSecretKeyEncryption());
110 key
= keyFac
.generateSecret(keySpec
);
112 if (securityProviderName
!= null)
113 ecipher
= Cipher
.getInstance(getCipherName(), securityProviderName
);
115 ecipher
= Cipher
.getInstance(getCipherName());
116 ecipher
.init(Cipher
.ENCRYPT_MODE
, key
, new IvParameterSpec(iv
));
117 dcipher
= Cipher
.getInstance(getCipherName());
118 dcipher
.init(Cipher
.DECRYPT_MODE
, key
, new IvParameterSpec(iv
));
121 public void encrypt(InputStream decryptedIn
, OutputStream encryptedOut
) throws IOException
{
123 CipherOutputStream out
= new CipherOutputStream(encryptedOut
, ecipher
);
124 StreamUtils
.copy(decryptedIn
, out
);
125 StreamUtils
.closeQuietly(out
);
126 } catch (IOException e
) {
128 } catch (Exception e
) {
129 throw new UtilsException("Cannot encrypt", e
);
131 StreamUtils
.closeQuietly(decryptedIn
);
135 public void decrypt(InputStream encryptedIn
, OutputStream decryptedOut
) throws IOException
{
137 CipherInputStream decryptedIn
= new CipherInputStream(encryptedIn
, dcipher
);
138 StreamUtils
.copy(decryptedIn
, decryptedOut
);
139 } catch (IOException e
) {
141 } catch (Exception e
) {
142 throw new UtilsException("Cannot decrypt", e
);
144 StreamUtils
.closeQuietly(encryptedIn
);
148 public byte[] encryptString(String str
) {
149 ByteArrayOutputStream out
= null;
150 ByteArrayInputStream in
= null;
152 out
= new ByteArrayOutputStream();
153 in
= new ByteArrayInputStream(str
.getBytes(DEFAULT_CHARSET
));
155 return out
.toByteArray();
156 } catch (Exception e
) {
157 throw new UtilsException("Cannot encrypt", e
);
159 StreamUtils
.closeQuietly(out
);
163 /** Closes the input stream */
164 public String
decryptAsString(InputStream in
) {
165 ByteArrayOutputStream out
= null;
167 out
= new ByteArrayOutputStream();
169 return new String(out
.toByteArray(), DEFAULT_CHARSET
);
170 } catch (Exception e
) {
171 throw new UtilsException("Cannot decrypt", e
);
173 StreamUtils
.closeQuietly(out
);
177 protected Key
getKey() {
181 protected Cipher
getEcipher() {
185 protected Cipher
getDcipher() {
189 protected Integer
getIterationCount() {
190 return iterationCount
;
193 protected Integer
getKeyLength() {
194 return secreteKeyLength
;
197 protected String
getSecretKeyFactoryName() {
198 return secreteKeyFactoryName
;
201 protected String
getSecretKeyEncryption() {
202 return secreteKeyEncryption
;
205 protected String
getCipherName() {
209 public void setIterationCount(Integer iterationCount
) {
210 this.iterationCount
= iterationCount
;
213 public void setSecreteKeyLength(Integer keyLength
) {
214 this.secreteKeyLength
= keyLength
;
217 public void setSecreteKeyFactoryName(String secreteKeyFactoryName
) {
218 this.secreteKeyFactoryName
= secreteKeyFactoryName
;
221 public void setSecreteKeyEncryption(String secreteKeyEncryption
) {
222 this.secreteKeyEncryption
= secreteKeyEncryption
;
225 public void setCipherName(String cipherName
) {
226 this.cipherName
= cipherName
;
229 public void setSecurityProviderName(String securityProviderName
) {
230 this.securityProviderName
= securityProviderName
;