001
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.CharPool;
020 import com.liferay.portal.kernel.util.GetterUtil;
021 import com.liferay.portal.kernel.util.Validator;
022
023 import java.nio.ByteBuffer;
024
025 import java.security.SecureRandom;
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 public String[] getSupportedAlgorithmTypes() {
042 return new String[] {PasswordEncryptorUtil.TYPE_PBKDF2};
043 }
044
045 @Override
046 protected String doEncrypt(
047 String algorithm, String plainTextPassword,
048 String encryptedPassword)
049 throws PwdEncryptorException {
050
051 try {
052 PBKDF2EncryptionConfiguration pbkdf2EncryptionConfiguration =
053 new PBKDF2EncryptionConfiguration();
054
055 pbkdf2EncryptionConfiguration.configure(
056 algorithm, encryptedPassword);
057
058 byte[] saltBytes = pbkdf2EncryptionConfiguration.getSaltBytes();
059
060 PBEKeySpec pbeKeySpec = new PBEKeySpec(
061 plainTextPassword.toCharArray(), saltBytes,
062 pbkdf2EncryptionConfiguration.getRounds(),
063 pbkdf2EncryptionConfiguration.getKeySize());
064
065 String algorithmName = algorithm;
066
067 int index = algorithm.indexOf(CharPool.SLASH);
068
069 if (index > -1) {
070 algorithmName = algorithm.substring(0, index);
071 }
072
073 SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(
074 algorithmName);
075
076 SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
077
078 byte[] secretKeyBytes = secretKey.getEncoded();
079
080 ByteBuffer byteBuffer = ByteBuffer.allocate(
081 2 * 4 + saltBytes.length + secretKeyBytes.length);
082
083 byteBuffer.putInt(pbkdf2EncryptionConfiguration.getKeySize());
084 byteBuffer.putInt(pbkdf2EncryptionConfiguration.getRounds());
085 byteBuffer.put(saltBytes);
086 byteBuffer.put(secretKeyBytes);
087
088 return Base64.encode(byteBuffer.array());
089 }
090 catch (Exception e) {
091 throw new PwdEncryptorException(e.getMessage(), e);
092 }
093 }
094
095 private static final int _KEY_SIZE = 160;
096
097 private static final int _ROUNDS = 128000;
098
099 private static final int _SALT_BYTES_LENGTH = 8;
100
101 private static Pattern _pattern = Pattern.compile(
102 "^.*/?([0-9]+)?/([0-9]+)$");
103
104 private class PBKDF2EncryptionConfiguration {
105
106 public void configure(String algorithm, String encryptedPassword)
107 throws PwdEncryptorException {
108
109 if (Validator.isNull(encryptedPassword)) {
110 Matcher matcher = _pattern.matcher(algorithm);
111
112 if (matcher.matches()) {
113 _keySize = GetterUtil.getInteger(
114 matcher.group(1), _KEY_SIZE);
115
116 _rounds = GetterUtil.getInteger(matcher.group(2), _ROUNDS);
117 }
118
119 SecureRandom random = new SecureRandom();
120
121 random.nextBytes(_saltBytes);
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 }