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