001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.security.pwd;
016    
017    import com.liferay.portal.PwdEncryptorException;
018    import com.liferay.portal.kernel.util.Base64;
019    import com.liferay.portal.kernel.util.Digester;
020    import com.liferay.portal.kernel.util.DigesterUtil;
021    import com.liferay.portal.kernel.util.GetterUtil;
022    import com.liferay.portal.kernel.util.PropsKeys;
023    import com.liferay.portal.kernel.util.Validator;
024    import com.liferay.portal.util.PropsUtil;
025    
026    import java.io.UnsupportedEncodingException;
027    
028    import java.security.MessageDigest;
029    import java.security.NoSuchAlgorithmException;
030    import java.security.SecureRandom;
031    
032    import java.util.Random;
033    
034    import org.vps.crypt.Crypt;
035    
036    /**
037     * @author Brian Wing Shun Chan
038     * @author Scott Lee
039     */
040    public class PwdEncryptor {
041    
042            public static final String PASSWORDS_ENCRYPTION_ALGORITHM =
043                    GetterUtil.getString(PropsUtil.get(
044                            PropsKeys.PASSWORDS_ENCRYPTION_ALGORITHM)).toUpperCase();
045    
046            public static final String TYPE_CRYPT = "CRYPT";
047    
048            public static final String TYPE_MD2 = "MD2";
049    
050            public static final String TYPE_MD5 = "MD5";
051    
052            public static final String TYPE_NONE = "NONE";
053    
054            public static final String TYPE_SHA = "SHA";
055    
056            public static final String TYPE_SHA_256 = "SHA-256";
057    
058            public static final String TYPE_SHA_384 = "SHA-384";
059    
060            public static final String TYPE_SSHA = "SSHA";
061    
062            public static final char[] saltChars =
063                    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
064                            .toCharArray();
065    
066            public static String encrypt(String clearTextPassword)
067                    throws PwdEncryptorException {
068    
069                    return encrypt(PASSWORDS_ENCRYPTION_ALGORITHM, clearTextPassword, null);
070            }
071    
072            public static String encrypt(
073                            String clearTextPassword, String currentEncryptedPassword)
074                    throws PwdEncryptorException {
075    
076                    return encrypt(
077                            PASSWORDS_ENCRYPTION_ALGORITHM, clearTextPassword,
078                            currentEncryptedPassword);
079            }
080    
081            public static String encrypt(
082                            String algorithm, String clearTextPassword,
083                            String currentEncryptedPassword)
084                    throws PwdEncryptorException {
085    
086                    if (algorithm.equals(TYPE_CRYPT)) {
087                            byte[] saltBytes = _getSaltFromCrypt(currentEncryptedPassword);
088    
089                            return encodePassword(algorithm, clearTextPassword, saltBytes);
090                    }
091                    else if (algorithm.equals(TYPE_NONE)) {
092                            return clearTextPassword;
093                    }
094                    else if (algorithm.equals(TYPE_SSHA)) {
095                            byte[] saltBytes = _getSaltFromSSHA(currentEncryptedPassword);
096    
097                            return encodePassword(algorithm, clearTextPassword, saltBytes);
098                    }
099                    else {
100                            return encodePassword(algorithm, clearTextPassword, null);
101                    }
102            }
103    
104            protected static String encodePassword(
105                            String algorithm, String clearTextPassword, byte[] saltBytes)
106                    throws PwdEncryptorException {
107    
108                    try {
109                            if (algorithm.equals(TYPE_CRYPT)) {
110                                    return Crypt.crypt(
111                                            saltBytes, clearTextPassword.getBytes(Digester.ENCODING));
112                            }
113                            else if (algorithm.equals(TYPE_SSHA)) {
114                                    byte[] clearTextPasswordBytes =
115                                            clearTextPassword.getBytes(Digester.ENCODING);
116    
117                                    // Create a byte array of salt bytes appeneded to password bytes
118    
119                                    byte[] pwdPlusSalt =
120                                            new byte[clearTextPasswordBytes.length + saltBytes.length];
121    
122                                    System.arraycopy(
123                                            clearTextPasswordBytes, 0, pwdPlusSalt, 0,
124                                            clearTextPasswordBytes.length);
125    
126                                    System.arraycopy(
127                                            saltBytes, 0, pwdPlusSalt, clearTextPasswordBytes.length,
128                                            saltBytes.length);
129    
130                                    // Digest byte array
131    
132                                    MessageDigest sha1Digest = MessageDigest.getInstance("SHA-1");
133    
134                                    byte[] pwdPlusSaltHash = sha1Digest.digest(pwdPlusSalt);
135    
136                                    // Appends salt bytes to the SHA-1 digest.
137    
138                                    byte[] digestPlusSalt =
139                                            new byte[pwdPlusSaltHash.length + saltBytes.length];
140    
141                                    System.arraycopy(
142                                            pwdPlusSaltHash, 0, digestPlusSalt, 0,
143                                            pwdPlusSaltHash.length);
144    
145                                    System.arraycopy(
146                                            saltBytes, 0, digestPlusSalt, pwdPlusSaltHash.length,
147                                            saltBytes.length);
148    
149                                    // Base64 encode and format string
150    
151                                    return Base64.encode(digestPlusSalt);
152                            }
153                            else {
154                                    return DigesterUtil.digest(algorithm, clearTextPassword);
155                            }
156                    }
157                    catch (NoSuchAlgorithmException nsae) {
158                            throw new PwdEncryptorException(nsae.getMessage());
159                    }
160                    catch (UnsupportedEncodingException uee) {
161                            throw new PwdEncryptorException(uee.getMessage());
162                    }
163            }
164    
165            private static byte[] _getSaltFromCrypt(String cryptString)
166                    throws PwdEncryptorException {
167    
168                    byte[] saltBytes = null;
169    
170                    try {
171                            if (Validator.isNull(cryptString)) {
172    
173                                    // Generate random salt
174    
175                                    Random random = new Random();
176    
177                                    int numSaltChars = saltChars.length;
178    
179                                    StringBuilder sb = new StringBuilder();
180    
181                                    int x = random.nextInt(Integer.MAX_VALUE) % numSaltChars;
182                                    int y = random.nextInt(Integer.MAX_VALUE) % numSaltChars;
183    
184                                    sb.append(saltChars[x]);
185                                    sb.append(saltChars[y]);
186    
187                                    String salt = sb.toString();
188    
189                                    saltBytes = salt.getBytes(Digester.ENCODING);
190                            }
191                            else {
192    
193                                    // Extract salt from encrypted password
194    
195                                    String salt = cryptString.substring(0, 2);
196    
197                                    saltBytes = salt.getBytes(Digester.ENCODING);
198                            }
199                    }
200                    catch (UnsupportedEncodingException uee) {
201                            throw new PwdEncryptorException(
202                                    "Unable to extract salt from encrypted password: " +
203                                            uee.getMessage());
204                    }
205    
206                    return saltBytes;
207            }
208    
209            private static byte[] _getSaltFromSSHA(String sshaString)
210                    throws PwdEncryptorException {
211    
212                    byte[] saltBytes = new byte[8];
213    
214                    if (Validator.isNull(sshaString)) {
215    
216                            // Generate random salt
217    
218                            Random random = new SecureRandom();
219    
220                            random.nextBytes(saltBytes);
221                    }
222                    else {
223    
224                            // Extract salt from encrypted password
225    
226                            try {
227                                    byte[] digestPlusSalt = Base64.decode(sshaString);
228                                    byte[] digestBytes = new byte[digestPlusSalt.length - 8];
229    
230                                    System.arraycopy(
231                                            digestPlusSalt, 0, digestBytes, 0, digestBytes.length);
232    
233                                    System.arraycopy(
234                                            digestPlusSalt, digestBytes.length, saltBytes, 0,
235                                            saltBytes.length);
236                            }
237                            catch (Exception e) {
238                                    throw new PwdEncryptorException(
239                                            "Unable to extract salt from encrypted password: " +
240                                                    e.getMessage());
241                            }
242                    }
243    
244                    return saltBytes;
245            }
246    
247    }