001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.util;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.util.Base64;
020    import com.liferay.portal.kernel.util.Digester;
021    import com.liferay.portal.kernel.util.DigesterUtil;
022    import com.liferay.portal.kernel.util.GetterUtil;
023    import com.liferay.portal.kernel.util.PropsKeys;
024    import com.liferay.portal.kernel.util.PropsUtil;
025    import com.liferay.portal.kernel.util.ServerDetector;
026    import com.liferay.portal.kernel.util.StringPool;
027    import com.liferay.portal.kernel.util.StringUtil;
028    import com.liferay.portal.kernel.util.SystemProperties;
029    
030    import java.security.Key;
031    import java.security.Provider;
032    import java.security.SecureRandom;
033    import java.security.Security;
034    
035    import java.util.Map;
036    import java.util.concurrent.ConcurrentHashMap;
037    
038    import javax.crypto.Cipher;
039    import javax.crypto.KeyGenerator;
040    import javax.crypto.spec.SecretKeySpec;
041    
042    /**
043     * @author Brian Wing Shun Chan
044     * @author Shuyang Zhou
045     * @see com.liferay.petra.encryptor.Encryptor
046     */
047    public class Encryptor {
048    
049            public static final String ENCODING = Digester.ENCODING;
050    
051            public static final String IBM_PROVIDER_CLASS =
052                    "com.ibm.crypto.provider.IBMJCE";
053    
054            public static final String KEY_ALGORITHM = StringUtil.toUpperCase(
055                    GetterUtil.getString(
056                            PropsUtil.get(PropsKeys.COMPANY_ENCRYPTION_ALGORITHM)));
057    
058            public static final int KEY_SIZE = GetterUtil.getInteger(
059                    PropsUtil.get(PropsKeys.COMPANY_ENCRYPTION_KEY_SIZE));
060    
061            public static final String PROVIDER_CLASS = GetterUtil.getString(
062                    SystemProperties.get(Encryptor.class.getName() + ".provider.class"),
063                    Encryptor.SUN_PROVIDER_CLASS);
064    
065            public static final String SUN_PROVIDER_CLASS =
066                    "com.sun.crypto.provider.SunJCE";
067    
068            public static String decrypt(Key key, String encryptedString)
069                    throws EncryptorException {
070    
071                    byte[] encryptedBytes = Base64.decode(encryptedString);
072    
073                    return decryptUnencodedAsString(key, encryptedBytes);
074            }
075    
076            public static byte[] decryptUnencodedAsBytes(Key key, byte[] encryptedBytes)
077                    throws EncryptorException {
078    
079                    String algorithm = key.getAlgorithm();
080    
081                    String cacheKey = algorithm.concat(StringPool.POUND).concat(
082                            key.toString());
083    
084                    Cipher cipher = _decryptCipherMap.get(cacheKey);
085    
086                    try {
087                            if (cipher == null) {
088                                    Security.addProvider(getProvider());
089    
090                                    cipher = Cipher.getInstance(algorithm);
091    
092                                    cipher.init(Cipher.DECRYPT_MODE, key);
093    
094                                    _decryptCipherMap.put(cacheKey, cipher);
095                            }
096    
097                            synchronized (cipher) {
098                                    return cipher.doFinal(encryptedBytes);
099                            }
100                    }
101                    catch (Exception e) {
102                            throw new EncryptorException(e);
103                    }
104            }
105    
106            public static String decryptUnencodedAsString(
107                            Key key, byte[] encryptedBytes)
108                    throws EncryptorException {
109    
110                    try {
111                            byte[] decryptedBytes = decryptUnencodedAsBytes(
112                                    key, encryptedBytes);
113    
114                            return new String(decryptedBytes, ENCODING);
115                    }
116                    catch (Exception e) {
117                            throw new EncryptorException(e);
118                    }
119            }
120    
121            public static Key deserializeKey(String base64String) {
122                    byte[] bytes = Base64.decode(base64String);
123    
124                    return new SecretKeySpec(bytes, Encryptor.KEY_ALGORITHM);
125            }
126    
127            public static String digest(String text) {
128                    return DigesterUtil.digest(text);
129            }
130    
131            public static String digest(String algorithm, String text) {
132                    return DigesterUtil.digest(algorithm, text);
133            }
134    
135            public static String encrypt(Key key, String plainText)
136                    throws EncryptorException {
137    
138                    if (key == null) {
139                            if (_log.isWarnEnabled()) {
140                                    _log.warn("Skip encrypting based on a null key");
141                            }
142    
143                            return plainText;
144                    }
145    
146                    byte[] encryptedBytes = encryptUnencoded(key, plainText);
147    
148                    return Base64.encode(encryptedBytes);
149            }
150    
151            public static byte[] encryptUnencoded(Key key, byte[] plainBytes)
152                    throws EncryptorException {
153    
154                    String algorithm = key.getAlgorithm();
155    
156                    String cacheKey = algorithm.concat(StringPool.POUND).concat(
157                            key.toString());
158    
159                    Cipher cipher = _encryptCipherMap.get(cacheKey);
160    
161                    try {
162                            if (cipher == null) {
163                                    Security.addProvider(getProvider());
164    
165                                    cipher = Cipher.getInstance(algorithm);
166    
167                                    cipher.init(Cipher.ENCRYPT_MODE, key);
168    
169                                    _encryptCipherMap.put(cacheKey, cipher);
170                            }
171    
172                            synchronized (cipher) {
173                                    return cipher.doFinal(plainBytes);
174                            }
175                    }
176                    catch (Exception e) {
177                            throw new EncryptorException(e);
178                    }
179            }
180    
181            public static byte[] encryptUnencoded(Key key, String plainText)
182                    throws EncryptorException {
183    
184                    try {
185                            byte[] decryptedBytes = plainText.getBytes(ENCODING);
186    
187                            return encryptUnencoded(key, decryptedBytes);
188                    }
189                    catch (Exception e) {
190                            throw new EncryptorException(e);
191                    }
192            }
193    
194            public static Key generateKey() throws EncryptorException {
195                    return generateKey(KEY_ALGORITHM);
196            }
197    
198            public static Key generateKey(String algorithm) throws EncryptorException {
199                    try {
200                            Security.addProvider(getProvider());
201    
202                            KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
203    
204                            keyGenerator.init(KEY_SIZE, new SecureRandom());
205    
206                            Key key = keyGenerator.generateKey();
207    
208                            return key;
209                    }
210                    catch (Exception e) {
211                            throw new EncryptorException(e);
212                    }
213            }
214    
215            public static Provider getProvider()
216                    throws ClassNotFoundException, IllegalAccessException,
217                               InstantiationException {
218    
219                    Class<?> providerClass = null;
220    
221                    try {
222                            providerClass = Class.forName(PROVIDER_CLASS);
223                    }
224                    catch (ClassNotFoundException cnfe) {
225                            if (ServerDetector.isWebSphere() &&
226                                    PROVIDER_CLASS.equals(SUN_PROVIDER_CLASS)) {
227    
228                                    if (_log.isWarnEnabled()) {
229                                            _log.warn(
230                                                    "WebSphere does not have " + SUN_PROVIDER_CLASS +
231                                                            ", using " + IBM_PROVIDER_CLASS + " instead");
232                                    }
233    
234                                    providerClass = Class.forName(IBM_PROVIDER_CLASS);
235                            }
236                            else if (System.getProperty("java.vm.vendor").equals(
237                                                    "IBM Corporation")) {
238    
239                                    if (_log.isWarnEnabled()) {
240                                            _log.warn(
241                                                    "IBM JVM does not have " + SUN_PROVIDER_CLASS +
242                                                            ", using " + IBM_PROVIDER_CLASS + " instead");
243                                    }
244    
245                                    providerClass = Class.forName(IBM_PROVIDER_CLASS);
246                            }
247                            else {
248                                    throw cnfe;
249                            }
250                    }
251    
252                    return (Provider)providerClass.newInstance();
253            }
254    
255            public static String serializeKey(Key key) {
256                    return Base64.encode(key.getEncoded());
257            }
258    
259            private static final Log _log = LogFactoryUtil.getLog(Encryptor.class);
260    
261            private static final Map<String, Cipher> _decryptCipherMap =
262                    new ConcurrentHashMap<>(1, 1f, 1);
263            private static final Map<String, Cipher> _encryptCipherMap =
264                    new ConcurrentHashMap<>(1, 1f, 1);
265    
266    }