001// -------------------------------------------------------------------------------- 002// Copyright 2002-2024 Echo Three, LLC 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// -------------------------------------------------------------------------------- 016 017package com.echothree.control.user.core.client.helper; 018 019import com.echothree.control.user.core.common.CoreUtil; 020import com.echothree.control.user.core.common.form.CoreFormFactory; 021import com.echothree.control.user.core.common.result.ChangeBaseKeysResult; 022import com.echothree.control.user.core.common.result.GenerateBaseKeysResult; 023import com.echothree.control.user.core.common.result.GetBaseEncryptionKeyResult; 024import com.echothree.model.control.core.common.transfer.BaseEncryptionKeyTransfer; 025import com.echothree.model.data.user.common.pk.UserVisitPK; 026import com.echothree.util.common.persistence.BaseKey; 027import com.echothree.util.common.persistence.BaseKeys; 028import com.echothree.util.common.persistence.EncryptionConstants; 029import com.google.common.io.BaseEncoding; 030import java.io.File; 031import java.io.FileInputStream; 032import java.io.FileOutputStream; 033import java.io.IOException; 034import java.net.InetAddress; 035import java.util.HashMap; 036import java.util.Map; 037import java.util.Properties; 038import javax.crypto.spec.SecretKeySpec; 039import javax.naming.NamingException; 040import org.apache.commons.logging.Log; 041import org.apache.commons.logging.LogFactory; 042 043public class BaseKeysHelper { 044 045 private BaseKeysHelper() { 046 super(); 047 } 048 049 private static class BaseKeysHelperHolder { 050 static BaseKeysHelper instance = new BaseKeysHelper(); 051 } 052 053 public static BaseKeysHelper getInstance() { 054 return BaseKeysHelperHolder.instance; 055 } 056 057 private Log log = LogFactory.getLog(this.getClass()); 058 059 private void storeBaseKeyToProperties(String baseEncryptionKeyName, BaseKey baseKey, String which) 060 throws IOException { 061 var baseEncoding = BaseEncoding.base64(); 062 var keyProperties = new Properties(); 063 064 keyProperties.setProperty("key", baseEncoding.encode(baseKey.getKey().getEncoded())); 065 keyProperties.setProperty("iv", baseEncoding.encode(baseKey.getIv())); 066 keyProperties.setProperty("which", which); 067 068 String hostName = InetAddress.getLocalHost().getHostName(); 069 StringBuilder propertiesPath = new StringBuilder(System.getProperty("user.home")) 070 .append(File.separator).append("keys/media").append(which) 071 .append(File.separator).append(hostName) 072 .append(File.separator).append(baseEncryptionKeyName); 073 074 if(new File(propertiesPath.toString()).mkdirs()) { 075 String fileName = propertiesPath.append(File.separator).append("key.xml").toString(); 076 077 keyProperties.storeToXML(new FileOutputStream(propertiesPath.toString()), null); 078 log.info("Key #" + which + " stored to " + fileName); 079 } else { 080 // Save key into current user's home directory, and let them sort it out. 081 log.error("Storage of key #" + which + " failed!"); 082 keyProperties.storeToXML(new FileOutputStream(System.getProperty("user.home") + File.separator + "key" + which + ".xml"), null); 083 } 084 } 085 086 private void writeBaseKeysToFiles(BaseKeys baseKeys) 087 throws IOException { 088 var baseEncryptionKeyName = baseKeys.getBaseEncryptionKeyName(); 089 090 storeBaseKeyToProperties(baseEncryptionKeyName, baseKeys.getBaseKey1(), "1"); 091 storeBaseKeyToProperties(baseEncryptionKeyName, baseKeys.getBaseKey2(), "2"); 092 storeBaseKeyToProperties(baseEncryptionKeyName, baseKeys.getBaseKey3(), "3"); 093 } 094 095 public void handleGenerateBaseKeys(UserVisitPK userVisitPK) 096 throws NamingException, IOException { 097 var commandResult = CoreUtil.getHome().generateBaseKeys(userVisitPK); 098 var executionResult = commandResult.getExecutionResult(); 099 var result = (GenerateBaseKeysResult)executionResult.getResult(); 100 var baseKeys = result.getBaseKeys(); 101 102 if(baseKeys != null) { 103 writeBaseKeysToFiles(baseKeys); 104 } 105 } 106 107 private String getActiveBaseEncryptionKeyName(UserVisitPK userVisitPK) 108 throws NamingException { 109 BaseEncryptionKeyTransfer baseEncryptionKey; 110 var commandForm = CoreFormFactory.getGetBaseEncryptionKeyForm(); 111 var commandResult = CoreUtil.getHome().getBaseEncryptionKey(userVisitPK, commandForm); 112 var executionResult = commandResult.getExecutionResult(); 113 var result = (GetBaseEncryptionKeyResult)executionResult.getResult(); 114 115 baseEncryptionKey = result.getBaseEncryptionKey(); 116 117 return baseEncryptionKey == null ? null : baseEncryptionKey.getBaseEncryptionKeyName(); 118 } 119 120 private void loadBaseKeyFromProperties(Map<String, BaseKey> baseKeyMap, String baseEncryptionKeyName, String whichMedia) 121 throws IOException { 122 var baseEncoding = BaseEncoding.base64(); 123 var keyProperties = new Properties(); 124 var hostName = InetAddress.getLocalHost().getHostName(); 125 var propertiesPath = new StringBuilder(System.getProperty("user.home")) 126 .append(File.separator).append("keys/media").append(whichMedia) 127 .append(File.separator).append(hostName) 128 .append(File.separator).append(baseEncryptionKeyName); 129 var fileName = propertiesPath.append(File.separator).append("key.xml").toString(); 130 131 keyProperties.loadFromXML(new FileInputStream(fileName)); 132 133 var secretKey1 = new SecretKeySpec(baseEncoding.decode(keyProperties.getProperty("key")), EncryptionConstants.algorithm); 134 byte[] iv1 = baseEncoding.decode(keyProperties.getProperty("iv")); 135 136 var baseKey = new BaseKey(secretKey1, iv1); 137 var which = keyProperties.getProperty("which"); 138 baseKeyMap.put(which, baseKey); 139 log.info("Key #" + which + " restored from " + fileName); 140 } 141 142 private BaseKeys getBaseKeysFromFiles(UserVisitPK userVisitPK) 143 throws NamingException, IOException { 144 var baseKeyMap = new HashMap<String, BaseKey>(); 145 var baseEncryptionKeyName = getActiveBaseEncryptionKeyName(userVisitPK); 146 147 loadBaseKeyFromProperties(baseKeyMap, baseEncryptionKeyName, "1"); 148 loadBaseKeyFromProperties(baseKeyMap, baseEncryptionKeyName, "2"); 149 loadBaseKeyFromProperties(baseKeyMap, baseEncryptionKeyName, "3"); 150 151 var baseKey1 = baseKeyMap.get("1"); 152 var baseKey2 = baseKeyMap.get("2"); 153 var baseKey3 = baseKeyMap.get("3"); 154 155 return new BaseKeys(baseKey1, baseKey2, baseKey3); 156 } 157 158 public void handleLoadBaseKeys(UserVisitPK userVisitPK) 159 throws NamingException, IOException { 160 var baseKeys = getBaseKeysFromFiles(userVisitPK); 161 162 if(baseKeys.getBaseKeyCount() > 1) { 163 log.info("Two or more Base Encryption Keys found, loading keys."); 164 var commandForm = CoreFormFactory.getLoadBaseKeysForm(); 165 166 commandForm.setBaseKeys(baseKeys); 167 168 CoreUtil.getHome().loadBaseKeys(userVisitPK, commandForm); 169 } else { 170 log.error("A minimum of two Base Encryption Keys are required, not loading key."); 171 } 172 } 173 174 public void handleChangeBaseKeys(UserVisitPK userVisitPK) 175 throws NamingException, IOException { 176 var baseKeys = getBaseKeysFromFiles(userVisitPK); 177 178 if(baseKeys.getBaseKeyCount() == 3) { 179 log.info("Three Base Encryption Keys found, changing keys."); 180 var commandForm = CoreFormFactory.getChangeBaseKeysForm(); 181 182 commandForm.setBaseKeys(baseKeys); 183 184 var commandResult = CoreUtil.getHome().changeBaseKeys(userVisitPK, commandForm); 185 var executionResult = commandResult.getExecutionResult(); 186 var result = (ChangeBaseKeysResult)executionResult.getResult(); 187 188 writeBaseKeysToFiles(result.getBaseKeys()); 189 } else { 190 log.error("Three Base Encryption Keys are required, not changing keys."); 191 } 192 } 193 194}