001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.crypto; 029 030import org.opencms.configuration.CmsParameterConfiguration; 031import org.opencms.file.CmsObject; 032import org.opencms.main.CmsLog; 033 034import java.nio.charset.StandardCharsets; 035import java.util.Arrays; 036 037import javax.crypto.Cipher; 038import javax.crypto.SecretKey; 039import javax.crypto.spec.IvParameterSpec; 040import javax.crypto.spec.SecretKeySpec; 041 042import org.apache.commons.logging.Log; 043 044import org.bouncycastle.crypto.digests.SHA256Digest; 045import org.bouncycastle.crypto.generators.HKDFBytesGenerator; 046import org.bouncycastle.crypto.params.HKDFParameters; 047 048import com.google.common.io.BaseEncoding; 049 050/** 051 * Default text encryption class using AES, where the encryption key is generated from a string passed in as a parameter. 052 */ 053public class CmsAESCBCTextEncryption implements I_CmsTextEncryption { 054 055 /** The name of the algorithm. */ 056 public static final String AES = "AES"; 057 058 /** URL parameter safe base64 encoder. */ 059 public static final BaseEncoding BASE64 = BaseEncoding.base64Url().withPadChar('.'); 060 061 /** The configuration parameter for configuring the secret. */ 062 public static final String PARAM_SECRET = "secret"; 063 064 /** Logger instance for this class. */ 065 private static final Log LOG = CmsLog.getLog(CmsAESCBCTextEncryption.class); 066 067 /** The cipher name. */ 068 protected String m_aesVariant = "AES/CBC/PKCS5Padding"; 069 070 /** The parameter configuration. */ 071 private CmsParameterConfiguration m_config = new CmsParameterConfiguration(); 072 073 /** The key used for encryption / decryption. */ 074 private SecretKey m_key; 075 076 /** The name under which this is registered. */ 077 private String m_name; 078 079 /** 080 * Default constructor (used when instantiated automatically during OpenCms configuration). 081 */ 082 public CmsAESCBCTextEncryption() {} 083 084 /** 085 * Constructor used to manually, conveniently create a new encryption object with a given secret. 086 * 087 * <p>When using this constructor, it is not necessary to call initialize() to make the object usable. 088 * 089 * @param secret the secret used to generate the key 090 */ 091 public CmsAESCBCTextEncryption(String secret) { 092 093 m_key = generateAESKey(secret); 094 } 095 096 /** 097 * Helper method for generating an AES key from a secret string. 098 * 099 * @param secret the secret string 100 * @return the AES key 101 */ 102 public static SecretKey generateAESKey(String secret) { 103 104 HKDFParameters params = HKDFParameters.defaultParameters(secret.getBytes(StandardCharsets.UTF_8)); 105 HKDFBytesGenerator keyGenerator = new HKDFBytesGenerator(new SHA256Digest()); 106 keyGenerator.init(params); 107 byte[] keyBytes = new byte[16]; 108 keyGenerator.generateBytes(keyBytes, 0, 16); 109 SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES); 110 return keySpec; 111 } 112 113 /** 114 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String) 115 */ 116 public void addConfigurationParameter(String paramName, String paramValue) { 117 118 m_config.add(paramName, paramValue); 119 } 120 121 /** 122 * @see org.opencms.crypto.I_CmsTextEncryption#decrypt(java.lang.String) 123 */ 124 public String decrypt(String input) throws CmsEncryptionException { 125 126 byte[] encryptedBytes = BASE64.decode(input); 127 try { 128 Cipher cipher = Cipher.getInstance(m_aesVariant); 129 130 byte[] ciphertext = Arrays.copyOfRange(encryptedBytes, 16, encryptedBytes.length); 131 byte[] iv = Arrays.copyOfRange(encryptedBytes, 0, 16); 132 IvParameterSpec ivSpec = new IvParameterSpec(iv); 133 cipher.init(Cipher.DECRYPT_MODE, m_key, ivSpec); 134 byte[] decData = cipher.doFinal(ciphertext); 135 String result = new String(decData, StandardCharsets.UTF_8); 136 return result; 137 } catch (Exception e) { 138 throw new CmsEncryptionException(e.getLocalizedMessage(), e); 139 } 140 } 141 142 /** 143 * @see org.opencms.crypto.I_CmsTextEncryption#encrypt(java.lang.String) 144 */ 145 public String encrypt(String input) throws CmsEncryptionException { 146 147 try { 148 Cipher cipher = Cipher.getInstance(m_aesVariant); 149 cipher.init(Cipher.ENCRYPT_MODE, m_key); 150 byte[] encData = cipher.doFinal(input.getBytes(StandardCharsets.UTF_8)); 151 byte[] l = new byte[encData.length + 16]; 152 System.arraycopy(cipher.getIV(), 0, l, 0, 16); 153 System.arraycopy(encData, 0, l, 16, encData.length); 154 String lit = BASE64.encode(l); 155 return lit; 156 } catch (Exception e) { 157 throw new CmsEncryptionException(e.getLocalizedMessage(), e); 158 } 159 } 160 161 /** 162 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration() 163 */ 164 public CmsParameterConfiguration getConfiguration() { 165 166 return m_config; 167 } 168 169 /** 170 * @see org.opencms.crypto.I_CmsTextEncryption#getName() 171 */ 172 public String getName() { 173 174 return m_name; 175 176 } 177 178 /** 179 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration() 180 */ 181 public void initConfiguration() { 182 183 // never called. 184 } 185 186 /** 187 * @see org.opencms.crypto.I_CmsTextEncryption#initialize(org.opencms.file.CmsObject) 188 */ 189 public void initialize(CmsObject cms) { 190 191 String secret = m_config.get(PARAM_SECRET); 192 if (secret == null) { 193 throw new IllegalArgumentException("Parameter 'secret' must be set!"); 194 } 195 m_key = generateAESKey(secret); 196 } 197 198 /** 199 * @see org.opencms.crypto.I_CmsTextEncryption#setName(java.lang.String) 200 */ 201 public void setName(String name) { 202 203 if (m_name != null) { 204 throw new IllegalStateException("Can't call setName twice!"); 205 } 206 m_name = name; 207 } 208 209}