1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
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 clearTextPwd)
69          throws PwdEncryptorException {
70  
71          return encrypt(PASSWORDS_ENCRYPTION_ALGORITHM, clearTextPwd, null);
72      }
73  
74      public static String encrypt(String clearTextPwd, String currentEncPwd)
75          throws PwdEncryptorException {
76  
77          return encrypt(
78              PASSWORDS_ENCRYPTION_ALGORITHM, clearTextPwd, currentEncPwd);
79      }
80  
81      public static String encrypt(
82              String algorithm, String clearTextPwd, String currentEncPwd)
83          throws PwdEncryptorException {
84  
85          if (algorithm.equals(TYPE_CRYPT)) {
86              byte[] saltBytes = _getSaltFromCrypt(currentEncPwd);
87  
88              return encodePassword(algorithm, clearTextPwd, saltBytes);
89          }
90          else if (algorithm.equals(TYPE_NONE)) {
91              return clearTextPwd;
92          }
93          else if (algorithm.equals(TYPE_SSHA)) {
94              byte[] saltBytes = _getSaltFromSSHA(currentEncPwd);
95  
96              return encodePassword(algorithm, clearTextPwd, saltBytes);
97          }
98          else {
99              return encodePassword(algorithm, clearTextPwd, null);
100         }
101     }
102 
103     protected static String encodePassword(
104             String algorithm, String clearTextPwd, byte[] saltBytes)
105         throws PwdEncryptorException {
106 
107         try {
108             if (algorithm.equals(TYPE_CRYPT)) {
109                 return Crypt.crypt(
110                     clearTextPwd.getBytes(Digester.ENCODING), saltBytes);
111             }
112             else if (algorithm.equals(TYPE_SSHA)) {
113                 byte[] clearTextPwdBytes =
114                     clearTextPwd.getBytes(Digester.ENCODING);
115 
116                 // Create a byte array of salt bytes appeneded to password bytes
117 
118                 byte[] pwdPlusSalt =
119                     new byte[clearTextPwdBytes.length + saltBytes.length];
120 
121                 System.arraycopy(
122                     clearTextPwdBytes, 0, pwdPlusSalt, 0,
123                     clearTextPwdBytes.length);
124 
125                 System.arraycopy(
126                     saltBytes, 0, pwdPlusSalt, clearTextPwdBytes.length,
127                     saltBytes.length);
128 
129                 // Digest byte array
130 
131                 MessageDigest sha1Digest = MessageDigest.getInstance("SHA-1");
132 
133                 byte[] pwdPlusSaltHash = sha1Digest.digest(pwdPlusSalt);
134 
135                 // Appends salt bytes to the SHA-1 digest.
136 
137                 byte[] digestPlusSalt =
138                     new byte[pwdPlusSaltHash.length + saltBytes.length];
139 
140                 System.arraycopy(
141                     pwdPlusSaltHash, 0, digestPlusSalt, 0,
142                     pwdPlusSaltHash.length);
143 
144                 System.arraycopy(
145                     saltBytes, 0, digestPlusSalt, pwdPlusSaltHash.length,
146                     saltBytes.length);
147 
148                 // Base64 encode and format string
149 
150                 return Base64.encode(digestPlusSalt);
151             }
152             else {
153                 return DigesterUtil.digest(algorithm, clearTextPwd);
154             }
155         }
156         catch (NoSuchAlgorithmException nsae) {
157             throw new PwdEncryptorException(nsae.getMessage());
158         }
159         catch (UnsupportedEncodingException uee) {
160             throw new PwdEncryptorException(uee.getMessage());
161         }
162     }
163 
164     private static byte[] _getSaltFromCrypt(String cryptString)
165         throws PwdEncryptorException {
166 
167         byte[] saltBytes = new byte[2];
168 
169         try {
170             if (Validator.isNull(cryptString)) {
171 
172                 // Generate random salt
173 
174                 Random random = new Random();
175 
176                 int numSaltChars = saltChars.length;
177 
178                 StringBuilder sb = new StringBuilder();
179 
180                 int x = random.nextInt(Integer.MAX_VALUE) % numSaltChars;
181                 int y = random.nextInt(Integer.MAX_VALUE) % numSaltChars;
182 
183                 sb.append(saltChars[x]);
184                 sb.append(saltChars[y]);
185 
186                 String salt = sb.toString();
187 
188                 saltBytes = salt.getBytes(Digester.ENCODING);
189             }
190             else {
191 
192                 // Extract salt from encrypted password
193 
194                 String salt = cryptString.substring(0, 3);
195 
196                 saltBytes = salt.getBytes(Digester.ENCODING);
197             }
198         }
199         catch (UnsupportedEncodingException uee) {
200             throw new PwdEncryptorException(
201                 "Unable to extract salt from encrypted password: " +
202                     uee.getMessage());
203         }
204 
205         return saltBytes;
206     }
207 
208     private static byte[] _getSaltFromSSHA(String sshaString)
209         throws PwdEncryptorException {
210 
211         byte[] saltBytes = new byte[8];
212 
213         if (Validator.isNull(sshaString)) {
214 
215             // Generate random salt
216 
217             Random random = new SecureRandom();
218 
219             random.nextBytes(saltBytes);
220         }
221         else {
222 
223             // Extract salt from encrypted password
224 
225             try {
226                 byte[] digestPlusSalt = Base64.decode(sshaString);
227                 byte[] digestBytes = new byte[digestPlusSalt.length - 8];
228 
229                 System.arraycopy(
230                     digestPlusSalt, 0, digestBytes, 0, digestBytes.length);
231 
232                 System.arraycopy(
233                     digestPlusSalt, digestBytes.length, saltBytes, 0,
234                     saltBytes.length);
235             }
236             catch (Exception e) {
237                 throw new PwdEncryptorException(
238                     "Unable to extract salt from encrypted password: " +
239                         e.getMessage());
240             }
241         }
242 
243         return saltBytes;
244     }
245 
246 }