001
014
015 package com.liferay.portal.security.pwd;
016
017 import com.liferay.portal.kernel.exception.PwdEncryptorException;
018 import com.liferay.portal.kernel.io.BigEndianCodec;
019 import com.liferay.portal.kernel.security.SecureRandomUtil;
020 import com.liferay.portal.kernel.security.pwd.PasswordEncryptor;
021 import com.liferay.portal.kernel.security.pwd.PasswordEncryptorUtil;
022 import com.liferay.portal.kernel.util.Base64;
023 import com.liferay.portal.kernel.util.CharPool;
024 import com.liferay.portal.kernel.util.GetterUtil;
025 import com.liferay.portal.kernel.util.Validator;
026
027 import java.nio.ByteBuffer;
028
029 import java.util.regex.Matcher;
030 import java.util.regex.Pattern;
031
032 import javax.crypto.SecretKey;
033 import javax.crypto.SecretKeyFactory;
034 import javax.crypto.spec.PBEKeySpec;
035
036
040 public class PBKDF2PasswordEncryptor
041 extends BasePasswordEncryptor implements PasswordEncryptor {
042
043 @Override
044 public String[] getSupportedAlgorithmTypes() {
045 return new String[] {PasswordEncryptorUtil.TYPE_PBKDF2};
046 }
047
048 @Override
049 protected String doEncrypt(
050 String algorithm, String plainTextPassword,
051 String encryptedPassword)
052 throws PwdEncryptorException {
053
054 try {
055 PBKDF2EncryptionConfiguration pbkdf2EncryptionConfiguration =
056 new PBKDF2EncryptionConfiguration();
057
058 pbkdf2EncryptionConfiguration.configure(
059 algorithm, encryptedPassword);
060
061 byte[] saltBytes = pbkdf2EncryptionConfiguration.getSaltBytes();
062
063 PBEKeySpec pbeKeySpec = new PBEKeySpec(
064 plainTextPassword.toCharArray(), saltBytes,
065 pbkdf2EncryptionConfiguration.getRounds(),
066 pbkdf2EncryptionConfiguration.getKeySize());
067
068 String algorithmName = algorithm;
069
070 int index = algorithm.indexOf(CharPool.SLASH);
071
072 if (index > -1) {
073 algorithmName = algorithm.substring(0, index);
074 }
075
076 SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(
077 algorithmName);
078
079 SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
080
081 byte[] secretKeyBytes = secretKey.getEncoded();
082
083 ByteBuffer byteBuffer = ByteBuffer.allocate(
084 2 * 4 + saltBytes.length + secretKeyBytes.length);
085
086 byteBuffer.putInt(pbkdf2EncryptionConfiguration.getKeySize());
087 byteBuffer.putInt(pbkdf2EncryptionConfiguration.getRounds());
088 byteBuffer.put(saltBytes);
089 byteBuffer.put(secretKeyBytes);
090
091 return Base64.encode(byteBuffer.array());
092 }
093 catch (Exception e) {
094 throw new PwdEncryptorException(e.getMessage(), e);
095 }
096 }
097
098 private static final int _KEY_SIZE = 160;
099
100 private static final int _ROUNDS = 128000;
101
102 private static final int _SALT_BYTES_LENGTH = 8;
103
104 private static final Pattern _pattern = Pattern.compile(
105 "^.*/?([0-9]+)?/([0-9]+)$");
106
107 private static class PBKDF2EncryptionConfiguration {
108
109 public void configure(String algorithm, String encryptedPassword)
110 throws PwdEncryptorException {
111
112 if (Validator.isNull(encryptedPassword)) {
113 Matcher matcher = _pattern.matcher(algorithm);
114
115 if (matcher.matches()) {
116 _keySize = GetterUtil.getInteger(
117 matcher.group(1), _KEY_SIZE);
118
119 _rounds = GetterUtil.getInteger(matcher.group(2), _ROUNDS);
120 }
121
122 BigEndianCodec.putLong(
123 _saltBytes, 0, SecureRandomUtil.nextLong());
124 }
125 else {
126 byte[] bytes = new byte[16];
127
128 try {
129 byte[] encryptedPasswordBytes = Base64.decode(
130 encryptedPassword);
131
132 System.arraycopy(
133 encryptedPasswordBytes, 0, bytes, 0, bytes.length);
134 }
135 catch (Exception e) {
136 throw new PwdEncryptorException(
137 "Unable to extract salt from encrypted password " +
138 e.getMessage(),
139 e);
140 }
141
142 ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
143
144 _keySize = byteBuffer.getInt();
145 _rounds = byteBuffer.getInt();
146
147 byteBuffer.get(_saltBytes);
148 }
149 }
150
151 public int getKeySize() {
152 return _keySize;
153 }
154
155 public int getRounds() {
156 return _rounds;
157 }
158
159 public byte[] getSaltBytes() {
160 return _saltBytes;
161 }
162
163 private int _keySize = _KEY_SIZE;
164 private int _rounds = _ROUNDS;
165 private final byte[] _saltBytes = new byte[_SALT_BYTES_LENGTH];
166
167 }
168
169 }