1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.security.pwd;
16  
17  import com.liferay.portal.PwdEncryptorException;
18  import com.liferay.portal.kernel.util.Base64;
19  import com.liferay.portal.kernel.util.Digester;
20  import com.liferay.portal.kernel.util.DigesterUtil;
21  import com.liferay.portal.kernel.util.GetterUtil;
22  import com.liferay.portal.kernel.util.PropsKeys;
23  import com.liferay.portal.kernel.util.Validator;
24  import com.liferay.portal.util.PropsUtil;
25  
26  import java.io.UnsupportedEncodingException;
27  
28  import java.security.MessageDigest;
29  import java.security.NoSuchAlgorithmException;
30  import java.security.SecureRandom;
31  
32  import java.util.Random;
33  
34  import org.vps.crypt.Crypt;
35  
36  /**
37   * <a href="PwdEncryptor.java.html"><b><i>View Source</i></b></a>
38   *
39   * @author Brian Wing Shun Chan
40   * @author Scott Lee
41   */
42  public class PwdEncryptor {
43  
44      public static final String PASSWORDS_ENCRYPTION_ALGORITHM =
45          GetterUtil.getString(PropsUtil.get(
46              PropsKeys.PASSWORDS_ENCRYPTION_ALGORITHM)).toUpperCase();
47  
48      public static final String TYPE_CRYPT = "CRYPT";
49  
50      public static final String TYPE_MD2 = "MD2";
51  
52      public static final String TYPE_MD5 = "MD5";
53  
54      public static final String TYPE_NONE = "NONE";
55  
56      public static final String TYPE_SHA = "SHA";
57  
58      public static final String TYPE_SHA_256 = "SHA-256";
59  
60      public static final String TYPE_SHA_384 = "SHA-384";
61  
62      public static final String TYPE_SSHA = "SSHA";
63  
64      public static final char[] saltChars =
65          "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
66              .toCharArray();
67  
68      public static String encrypt(String clearTextPassword)
69          throws PwdEncryptorException {
70  
71          return encrypt(PASSWORDS_ENCRYPTION_ALGORITHM, clearTextPassword, null);
72      }
73  
74      public static String encrypt(
75              String clearTextPassword, String currentEncryptedPassword)
76          throws PwdEncryptorException {
77  
78          return encrypt(
79              PASSWORDS_ENCRYPTION_ALGORITHM, clearTextPassword,
80              currentEncryptedPassword);
81      }
82  
83      public static String encrypt(
84              String algorithm, String clearTextPassword,
85              String currentEncryptedPassword)
86          throws PwdEncryptorException {
87  
88          if (algorithm.equals(TYPE_CRYPT)) {
89              byte[] saltBytes = _getSaltFromCrypt(currentEncryptedPassword);
90  
91              return encodePassword(algorithm, clearTextPassword, saltBytes);
92          }
93          else if (algorithm.equals(TYPE_NONE)) {
94              return clearTextPassword;
95          }
96          else if (algorithm.equals(TYPE_SSHA)) {
97              byte[] saltBytes = _getSaltFromSSHA(currentEncryptedPassword);
98  
99              return encodePassword(algorithm, clearTextPassword, saltBytes);
100         }
101         else {
102             return encodePassword(algorithm, clearTextPassword, null);
103         }
104     }
105 
106     protected static String encodePassword(
107             String algorithm, String clearTextPassword, byte[] saltBytes)
108         throws PwdEncryptorException {
109 
110         try {
111             if (algorithm.equals(TYPE_CRYPT)) {
112                 return Crypt.crypt(
113                     saltBytes, clearTextPassword.getBytes(Digester.ENCODING));
114             }
115             else if (algorithm.equals(TYPE_SSHA)) {
116                 byte[] clearTextPasswordBytes =
117                     clearTextPassword.getBytes(Digester.ENCODING);
118 
119                 // Create a byte array of salt bytes appeneded to password bytes
120 
121                 byte[] pwdPlusSalt =
122                     new byte[clearTextPasswordBytes.length + saltBytes.length];
123 
124                 System.arraycopy(
125                     clearTextPasswordBytes, 0, pwdPlusSalt, 0,
126                     clearTextPasswordBytes.length);
127 
128                 System.arraycopy(
129                     saltBytes, 0, pwdPlusSalt, clearTextPasswordBytes.length,
130                     saltBytes.length);
131 
132                 // Digest byte array
133 
134                 MessageDigest sha1Digest = MessageDigest.getInstance("SHA-1");
135 
136                 byte[] pwdPlusSaltHash = sha1Digest.digest(pwdPlusSalt);
137 
138                 // Appends salt bytes to the SHA-1 digest.
139 
140                 byte[] digestPlusSalt =
141                     new byte[pwdPlusSaltHash.length + saltBytes.length];
142 
143                 System.arraycopy(
144                     pwdPlusSaltHash, 0, digestPlusSalt, 0,
145                     pwdPlusSaltHash.length);
146 
147                 System.arraycopy(
148                     saltBytes, 0, digestPlusSalt, pwdPlusSaltHash.length,
149                     saltBytes.length);
150 
151                 // Base64 encode and format string
152 
153                 return Base64.encode(digestPlusSalt);
154             }
155             else {
156                 return DigesterUtil.digest(algorithm, clearTextPassword);
157             }
158         }
159         catch (NoSuchAlgorithmException nsae) {
160             throw new PwdEncryptorException(nsae.getMessage());
161         }
162         catch (UnsupportedEncodingException uee) {
163             throw new PwdEncryptorException(uee.getMessage());
164         }
165     }
166 
167     private static byte[] _getSaltFromCrypt(String cryptString)
168         throws PwdEncryptorException {
169 
170         byte[] saltBytes = null;
171 
172         try {
173             if (Validator.isNull(cryptString)) {
174 
175                 // Generate random salt
176 
177                 Random random = new Random();
178 
179                 int numSaltChars = saltChars.length;
180 
181                 StringBuilder sb = new StringBuilder();
182 
183                 int x = random.nextInt(Integer.MAX_VALUE) % numSaltChars;
184                 int y = random.nextInt(Integer.MAX_VALUE) % numSaltChars;
185 
186                 sb.append(saltChars[x]);
187                 sb.append(saltChars[y]);
188 
189                 String salt = sb.toString();
190 
191                 saltBytes = salt.getBytes(Digester.ENCODING);
192             }
193             else {
194 
195                 // Extract salt from encrypted password
196 
197                 String salt = cryptString.substring(0, 2);
198 
199                 saltBytes = salt.getBytes(Digester.ENCODING);
200             }
201         }
202         catch (UnsupportedEncodingException uee) {
203             throw new PwdEncryptorException(
204                 "Unable to extract salt from encrypted password: " +
205                     uee.getMessage());
206         }
207 
208         return saltBytes;
209     }
210 
211     private static byte[] _getSaltFromSSHA(String sshaString)
212         throws PwdEncryptorException {
213 
214         byte[] saltBytes = new byte[8];
215 
216         if (Validator.isNull(sshaString)) {
217 
218             // Generate random salt
219 
220             Random random = new SecureRandom();
221 
222             random.nextBytes(saltBytes);
223         }
224         else {
225 
226             // Extract salt from encrypted password
227 
228             try {
229                 byte[] digestPlusSalt = Base64.decode(sshaString);
230                 byte[] digestBytes = new byte[digestPlusSalt.length - 8];
231 
232                 System.arraycopy(
233                     digestPlusSalt, 0, digestBytes, 0, digestBytes.length);
234 
235                 System.arraycopy(
236                     digestPlusSalt, digestBytes.length, saltBytes, 0,
237                     saltBytes.length);
238             }
239             catch (Exception e) {
240                 throw new PwdEncryptorException(
241                     "Unable to extract salt from encrypted password: " +
242                         e.getMessage());
243             }
244         }
245 
246         return saltBytes;
247     }
248 
249 }