001// -------------------------------------------------------------------------------- 002// Copyright 2002-2026 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.model.control.core.common.transfer.BaseEncryptionKeyTransfer; 022import com.echothree.model.data.user.common.pk.UserVisitPK; 023import com.echothree.util.common.persistence.BaseKey; 024import com.echothree.util.common.persistence.BaseKeys; 025import com.echothree.util.common.persistence.EncryptionConstants; 026import com.google.common.io.BaseEncoding; 027import java.io.File; 028import java.io.FileInputStream; 029import java.io.FileOutputStream; 030import java.io.IOException; 031import java.net.InetAddress; 032import java.util.HashMap; 033import java.util.Map; 034import java.util.Properties; 035import javax.crypto.spec.SecretKeySpec; 036import javax.naming.NamingException; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040public class BaseKeysHelper { 041 042 private BaseKeysHelper() { 043 super(); 044 } 045 046 private static class BaseKeysHelperHolder { 047 static BaseKeysHelper instance = new BaseKeysHelper(); 048 } 049 050 public static BaseKeysHelper getInstance() { 051 return BaseKeysHelperHolder.instance; 052 } 053 054 protected final Logger log = LoggerFactory.getLogger(this.getClass()); 055 056 private void storeBaseKeyToProperties(String baseEncryptionKeyName, BaseKey baseKey, String which) 057 throws IOException { 058 var baseEncoding = BaseEncoding.base64(); 059 var keyProperties = new Properties(); 060 061 keyProperties.setProperty("key", baseEncoding.encode(baseKey.getKey().getEncoded())); 062 keyProperties.setProperty("iv", baseEncoding.encode(baseKey.getIv())); 063 keyProperties.setProperty("which", which); 064 065 var hostName = InetAddress.getLocalHost().getHostName(); 066 var propertiesPath = new StringBuilder(System.getProperty("user.home")) 067 .append(File.separator).append("keys/media").append(which) 068 .append(File.separator).append(hostName) 069 .append(File.separator).append(baseEncryptionKeyName); 070 071 if(new File(propertiesPath.toString()).mkdirs()) { 072 var fileName = propertiesPath.append(File.separator).append("key.xml").toString(); 073 074 keyProperties.storeToXML(new FileOutputStream(propertiesPath.toString()), null); 075 log.info("Key #" + which + " stored to " + fileName); 076 } else { 077 // Save key into current user's home directory, and let them sort it out. 078 log.error("Storage of key #" + which + " failed!"); 079 keyProperties.storeToXML(new FileOutputStream(System.getProperty("user.home") + File.separator + "key" + which + ".xml"), null); 080 } 081 } 082 083 private void writeBaseKeysToFiles(BaseKeys baseKeys) 084 throws IOException { 085 var baseEncryptionKeyName = baseKeys.getBaseEncryptionKeyName(); 086 087 storeBaseKeyToProperties(baseEncryptionKeyName, baseKeys.getBaseKey1(), "1"); 088 storeBaseKeyToProperties(baseEncryptionKeyName, baseKeys.getBaseKey2(), "2"); 089 storeBaseKeyToProperties(baseEncryptionKeyName, baseKeys.getBaseKey3(), "3"); 090 } 091 092 public void handleGenerateBaseKeys(UserVisitPK userVisitPK) 093 throws NamingException, IOException { 094 var commandResult = CoreUtil.getHome().generateBaseKeys(userVisitPK); 095 var executionResult = commandResult.getExecutionResult(); 096 var result = executionResult.getResult(); 097 var baseKeys = result.getBaseKeys(); 098 099 if(baseKeys != null) { 100 writeBaseKeysToFiles(baseKeys); 101 } 102 } 103 104 private String getActiveBaseEncryptionKeyName(UserVisitPK userVisitPK) 105 throws NamingException { 106 BaseEncryptionKeyTransfer baseEncryptionKey; 107 var commandForm = CoreFormFactory.getGetBaseEncryptionKeyForm(); 108 var commandResult = CoreUtil.getHome().getBaseEncryptionKey(userVisitPK, commandForm); 109 var executionResult = commandResult.getExecutionResult(); 110 var result = executionResult.getResult(); 111 112 baseEncryptionKey = result.getBaseEncryptionKey(); 113 114 return baseEncryptionKey == null ? null : baseEncryptionKey.getBaseEncryptionKeyName(); 115 } 116 117 private void loadBaseKeyFromProperties(Map<String, BaseKey> baseKeyMap, String baseEncryptionKeyName, String whichMedia) 118 throws IOException { 119 var baseEncoding = BaseEncoding.base64(); 120 var keyProperties = new Properties(); 121 var hostName = InetAddress.getLocalHost().getHostName(); 122 var fileName = System.getProperty("user.home") + 123 File.separator + "keys/media" + whichMedia + 124 File.separator + hostName + 125 File.separator + baseEncryptionKeyName + 126 File.separator + "key.xml"; 127 128 keyProperties.loadFromXML(new FileInputStream(fileName)); 129 130 var secretKey1 = new SecretKeySpec(baseEncoding.decode(keyProperties.getProperty("key")), EncryptionConstants.AES_ALGORITHM); 131 var iv1 = baseEncoding.decode(keyProperties.getProperty("iv")); 132 133 var baseKey = new BaseKey(secretKey1, iv1); 134 var which = keyProperties.getProperty("which"); 135 baseKeyMap.put(which, baseKey); 136 log.info("Key #" + which + " restored from " + fileName); 137 } 138 139 private BaseKeys getBaseKeysFromFiles(UserVisitPK userVisitPK) 140 throws NamingException, IOException { 141 var baseKeyMap = new HashMap<String, BaseKey>(); 142 var baseEncryptionKeyName = getActiveBaseEncryptionKeyName(userVisitPK); 143 144 loadBaseKeyFromProperties(baseKeyMap, baseEncryptionKeyName, "1"); 145 loadBaseKeyFromProperties(baseKeyMap, baseEncryptionKeyName, "2"); 146 loadBaseKeyFromProperties(baseKeyMap, baseEncryptionKeyName, "3"); 147 148 var baseKey1 = baseKeyMap.get("1"); 149 var baseKey2 = baseKeyMap.get("2"); 150 var baseKey3 = baseKeyMap.get("3"); 151 152 return new BaseKeys(baseKey1, baseKey2, baseKey3); 153 } 154 155 public void handleLoadBaseKeys(UserVisitPK userVisitPK) 156 throws NamingException, IOException { 157 var baseKeys = getBaseKeysFromFiles(userVisitPK); 158 159 if(baseKeys.getBaseKeyCount() > 1) { 160 log.info("Two or more Base Encryption Keys found, loading keys."); 161 var commandForm = CoreFormFactory.getLoadBaseKeysForm(); 162 163 commandForm.setBaseKeys(baseKeys); 164 165 CoreUtil.getHome().loadBaseKeys(userVisitPK, commandForm); 166 } else { 167 log.error("A minimum of two Base Encryption Keys are required, not loading key."); 168 } 169 } 170 171 public void handleChangeBaseKeys(UserVisitPK userVisitPK) 172 throws NamingException, IOException { 173 var baseKeys = getBaseKeysFromFiles(userVisitPK); 174 175 if(baseKeys.getBaseKeyCount() == 3) { 176 log.info("Three Base Encryption Keys found, changing keys."); 177 var commandForm = CoreFormFactory.getChangeBaseKeysForm(); 178 179 commandForm.setBaseKeys(baseKeys); 180 181 var commandResult = CoreUtil.getHome().changeBaseKeys(userVisitPK, commandForm); 182 var executionResult = commandResult.getExecutionResult(); 183 var result = executionResult.getResult(); 184 185 writeBaseKeysToFiles(result.getBaseKeys()); 186 } else { 187 log.error("Three Base Encryption Keys are required, not changing keys."); 188 } 189 } 190 191}