/* * Sakuli - Testing and Monitoring-Tool for Websites and common UIs. * * Copyright 2013 - 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sakuli.actions.environment; import org.apache.commons.codec.binary.Base64; import org.sakuli.datamodel.properties.ActionProperties; import org.sakuli.exceptions.SakuliCipherException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.spec.SecretKeySpec; import java.net.NetworkInterface; import java.util.Enumeration; import static java.net.NetworkInterface.getNetworkInterfaces; /** * a util class to encrypt and decrypt secrets based on the MAC address of a network interface. * * @author tschneck * Date: 06.08.13 */ @Component public class CipherUtil { private static byte[] keyPart1 = { 0x63, 0x6f, 0x6e, 0x31, 0x33, 0x53, 0x61, 0x6b, 0x53, 0x6f };//"con13SakSo" private static String algorithm = "AES/ECB/PKCS5Padding"; private String interfaceName; private boolean autodetect; private byte[] macOfEncryptionInterface; private String interfaceLog = ""; public CipherUtil() { } @Autowired public CipherUtil(ActionProperties cipherProps) { interfaceName = cipherProps.getEncryptionInterface(); autodetect = cipherProps.isEncryptionInterfaceAutodetect(); } /** * Determines a valid network interfaces. * * @return ethernet interface name * @throws Exception */ static String autodetectValidInterfaceName() throws Exception { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface anInterface = interfaces.nextElement(); if (anInterface.getHardwareAddress() != null && anInterface.getHardwareAddress().length == 6) { return anInterface.getName(); } } throw new Exception("No network interface with a MAC address is present, please check your os settings!"); } /** * fetch the local network interfaceLog and reads out the MAC of the chosen encryption interface. * Must be called before the methods {@link #encrypt(String)} or {@link #decrypt(String)}. * * @throws SakuliCipherException for wrong interface names and MACs. */ @PostConstruct public void scanNetworkInterfaces() throws SakuliCipherException { Enumeration<NetworkInterface> networkInterfaces; try { interfaceName = checkEthInterfaceName(); networkInterfaces = getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface anInterface = networkInterfaces.nextElement(); if (anInterface.getHardwareAddress() != null) { interfaceLog = interfaceLog + "\nNET-Interface " + anInterface.getIndex() + " - Name: " + anInterface.getName() + "\t MAC: " + formatMAC(anInterface.getHardwareAddress()) + "\t VirtualAdapter: " + anInterface.isVirtual() + "\t Loopback: " + anInterface.isLoopback() + "\t Desc.: " + anInterface.getDisplayName(); } if (anInterface.getName().equals(interfaceName)) { macOfEncryptionInterface = anInterface.getHardwareAddress(); } } if (macOfEncryptionInterface == null) { throw new SakuliCipherException("Cannot resolve MAC address ... please check your config of the property: " + ActionProperties.ENCRYPTION_INTERFACE + "=" + interfaceName, interfaceLog); } } catch (Exception e) { throw new SakuliCipherException(e, interfaceLog); } } /** * checks if {@link #autodetect} is enabled and returns: * <ul> * <li>true: the first valid interface at this computer</li> * <li>false: the interface name defined at the property {@link ActionProperties#ENCRYPTION_INTERFACE}</li> * </ul> */ private String checkEthInterfaceName() throws Exception { if (autodetect) { return autodetectValidInterfaceName(); } return interfaceName; } private String formatMAC(byte[] mac) { StringBuilder b = new StringBuilder(); for (int i = 0; i < mac.length; i++) { b.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : "")); } return b.toString(); } /** * Encrypts the secret into a encrypted {@link String}, based on the MAC address of the first network interface of a machine. * Therewith it should be secured, that an encrypted secret is only valid on one physical machine. * * @param strToEncrypt the secret * @return a encrypted String, which is coupled to one physical machine * @throws SakuliCipherException if the encryption fails. */ public String encrypt(String strToEncrypt) throws SakuliCipherException { try { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, getKey()); return Base64.encodeBase64String(cipher.doFinal(strToEncrypt.getBytes())); } catch (Exception e) { throw new SakuliCipherException(e, interfaceLog); } } /** * Decrypts a String to the secret. The decryption must be take place on the same physical machine like the encryption, see {@link #encrypt(String)}. * * @param strToDecrypt String to encrypt * @return the decrypted secret * @throws SakuliCipherException if the decryption fails. */ public String decrypt(String strToDecrypt) throws SakuliCipherException { try { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE, getKey()); return new String(cipher.doFinal(Base64.decodeBase64(strToDecrypt))); } catch (IllegalBlockSizeException e) { throw new SakuliCipherException("Maybe this secret hasn't been encrypted correctly! Maybe encrypt it again!", interfaceLog, e); } catch (Exception e) { throw new SakuliCipherException(e, interfaceLog); } } /** * generates the key for encryption from salt (keyPart1) and the MAC address of the choosen interface. * * @return valid {@link SecretKeySpec} */ private SecretKeySpec getKey() { // the length of the MAC address must be 6, to get secrect key length of 16 bytes assert (macOfEncryptionInterface.length == 6); byte[] keyPar2 = macOfEncryptionInterface; byte[] key = new byte[keyPart1.length + keyPar2.length]; System.arraycopy(keyPart1, 0, key, 0, keyPart1.length); System.arraycopy(keyPar2, 0, key, keyPart1.length, keyPar2.length); return new SecretKeySpec(key, "AES"); } /** * @return the name of the currently used encryption interface */ public String getInterfaceName() { return interfaceName; } }