package de.eisfeldj.augendiagnosefx.tools;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.security.Key;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;
/**
* Utility for handling user keys.
*/
@SuppressWarnings("unchecked")
public final class EncryptionUtil {
// JAVADOC:OFF
// parameters for encryption
private static Cipher mCipherEncrypt;
private static MessageDigest mMessageDigest;
private static final String DUMMY_HASH = "dummy";
private static final int HASH_LENGTH = 8;
private static final String ALGORITHM = "DES";
// JAVADOC:ON
/**
* Hide default constructor.
*/
private EncryptionUtil() {
throw new UnsupportedOperationException();
}
/**
* Special keys that get are set as valid.
*/
private static final List<String> SPECIAL_KEYS;
/**
* The private key to be used for user key generation.
*/
private static final String KEY;
/**
* Flag to store initialization status.
*/
private static boolean mIsInitialized = false;
static {
boolean foundPrivateConstants = false;
List<String> specialKeys = new ArrayList<>();
String key = "";
try {
// Looking for a class PrivateConstants with fields SPECIAL_KEYS and KEY_STRING - not in repository
Class<?> privateConstants = Class.forName("de.eisfeldj.augendiagnosefx.tools.PrivateConstants");
Field specialKeysField = privateConstants.getDeclaredField("SPECIAL_KEYS");
specialKeys = (List<String>) specialKeysField.get(null);
Field keyField = privateConstants.getDeclaredField("KEY_STRING");
key = (String) keyField.get(null);
foundPrivateConstants = true;
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
SPECIAL_KEYS = specialKeys;
KEY = key;
if (foundPrivateConstants) {
try {
mCipherEncrypt = Cipher.getInstance(ALGORITHM);
Key symKey = new SecretKeySpec(Base64.getDecoder().decode(KEY), ALGORITHM);
mCipherEncrypt.init(Cipher.ENCRYPT_MODE, symKey);
mMessageDigest = MessageDigest.getInstance("MD5");
mIsInitialized = true;
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
/**
* Validate a user key.
*
* @param key
* the user key to be validated.
* @return true if the user key is valid.
*/
public static boolean validateUserKey(final String key) {
if (key == null || key.length() == 0 || !mIsInitialized) {
return false;
}
if (SPECIAL_KEYS.contains(key)) {
return true;
}
int index = key.lastIndexOf('-');
if (index > 0) {
String name = key.substring(0, index);
String hash = key.substring(index + 1);
return createCryptoHash(name).equals(hash);
}
else {
return false;
}
}
/**
* Generate a user key, which is a concatenation of user name and hash.
*
* @param input
* the user key without hash.
* @return the user key including hash.
*/
public static String createUserKey(final String input) {
return input + "-" + createCryptoHash(input);
}
/**
* Create a cryptographic hash from a String.
*
* @param input
* the input for creating the hash. (Will be username.)
* @return the cryptocraphic hash.
*/
private static String createCryptoHash(final String input) {
try {
return convertBase64(createHash(encrypt(input))).substring(0, HASH_LENGTH);
}
catch (Exception e) {
return DUMMY_HASH;
}
}
/**
* Create a hash value from an input.
*
* @param input
* the input for the hash creation.
* @return the hash.
*/
private static byte[] createHash(final byte[] input) {
return mMessageDigest.digest(input);
}
/**
* Do base 64 encoding of a message.
*
* @param bytes
* the bytes to be encoded.
* @return the base 64 encoded String.
*/
private static String convertBase64(final byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
/**
* Encrypt a String using DES.
*
* @param input
* the String to be encrypted.
* @return the encrypted String.
* @throws BadPaddingException
* if this cipher is in decryption mode, and (un)padding has been requested, but the decrypted data is
* not bounded by the appropriate padding bytes
* @throws IllegalBlockSizeException
* if this cipher is a block cipher, no padding has been requested (only in encryption mode), and the
* total input length of the data processed by this cipher is not a multiple of block size; or if this
* encryption algorithm is unable to process the input data provided.
*/
private static byte[] encrypt(final String input) throws BadPaddingException, IllegalBlockSizeException {
return mCipherEncrypt.doFinal(input.getBytes());
}
/**
* Main method - retrieves the user name and returns the user key.
*
* @param args
* Command line arguments - not used.
* @throws IOException
* thrown if there are issues with text input.
*/
public static void main(final String[] args) throws IOException {
System.out.print("Enter name: "); // SYSTEMOUT
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input = br.readLine();
System.out.println("User key: " + createUserKey(input)); // SYSTEMOUT
/*
* Used keys: Augoustides-rox11m49
*/
}
}