KeyObject.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.common.crypto;
import org.waarp.common.digest.FilesystemBasedDigest;
import org.waarp.common.exception.CryptoException;
import org.waarp.common.file.FileUtils;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.WaarpStringUtils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Key;
/**
* This class handles method to crypt and decrypt using the chosen
* algorithm.<br>
*
* <br>
* Usage:<br>
* <ul>
* <li>Create a Key object: KeyObject key = new KeyObject();</li>
* <li>Create a key:
* <ul>
* <li>Generate: key.generateKey();<br>
* The method key.getSecretKeyInBytes() allow getting the key in Bytes.</li>
* <li>From an external source: key.setSecretKey(arrayOfBytes);</li>
* </ul>
* </li>
* <li>To crypt a String in a Hex format: String myStringCrypt =
* key.cryptToHex(myString);</li>
* <li>To decrypt one string from Hex format to the original String: String
* myStringDecrypt =
* key.decryptHexInString(myStringCrypte);</li>
* </ul>
*/
public abstract class KeyObject {
/**
* Internal Logger
*/
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(KeyObject.class);
/**
* The True Key associated with this object
*/
Key secretKey;
/**
* Empty constructor
*/
protected KeyObject() {
}
/**
* @return the algorithm used (Java name)
*/
public abstract String getAlgorithm();
/**
* @return the instance used (Java name)
*/
public abstract String getInstance();
/**
* @return the size for the algorithm key
*/
public abstract int getKeySize();
/**
* @return the filename extension to use for this kind of key
*/
public abstract String getFileExtension();
/**
* @return the key associated with this object
*/
public final Key getSecretKey() {
return secretKey;
}
/**
* @return True if this key is ready to be used
*/
public final boolean keyReady() {
return secretKey != null;
}
/**
* Returns the key as an array of bytes in order to be stored somewhere else
* and retrieved using the
* setSecretKey(byte[] keyData) method.
*
* @return the key as an array of bytes (or null if not ready)
*/
public final byte[] getSecretKeyInBytes() {
if (keyReady()) {
return secretKey.getEncoded();
} else {
return null;
}
}
/**
* Set the secretKey
*
* @param secretKey
*/
public final void setSecretKey(final Key secretKey) {
this.secretKey = secretKey;
}
/**
* Reconstruct a key from an array of bytes
*/
public final void setSecretKey(final byte[] keyData) {
secretKey = new SecretKeySpec(keyData, getAlgorithm());
}
/**
* Create a Key from a File
*
* @param file
*
* @throws CryptoException
* @throws IOException
*/
public final void setSecretKey(final File file)
throws CryptoException, IOException {
if (file.canRead()) {
final int len = (int) file.length();
final byte[] key = new byte[len];
final FileInputStream inputStream;
inputStream = new FileInputStream(file);
final DataInputStream dis = new DataInputStream(inputStream);
try {
dis.readFully(key);
} finally {
FileUtils.close(dis);
}
setSecretKey(key);
} else {
throw new CryptoException("Cannot read crypto file: " + file);
}
}
/**
* Save a Key to a File
*
* @param file
*
* @throws CryptoException
* @throws IOException
*/
public final void saveSecretKey(final File file)
throws CryptoException, IOException {
if (keyReady() && (!file.exists() || file.canWrite())) {
final byte[] key = getSecretKeyInBytes();
final FileOutputStream outputStream = new FileOutputStream(file);
try {
outputStream.write(key);
outputStream.flush();
} finally {
FileUtils.close(outputStream);
}
} else {
throw new CryptoException("Cannot read crypto file");
}
}
/**
* Generate a key from nothing
*
* @throws Exception
*/
public final void generateKey() throws Exception {
try {
final KeyGenerator keyGen = KeyGenerator.getInstance(getAlgorithm());
keyGen.init(getKeySize());
secretKey = keyGen.generateKey();
} catch (final Exception e) {
logger.warn("GenerateKey Error", e);
throw e;
}
}
/**
* Returns a cipher for encryption associated with the key
*
* @return the cipher for encryption or null if it fails in case Encryption
* method or key is incorrect
*/
public Cipher toCrypt() {
final Cipher cipher;
try {
cipher = Cipher.getInstance(getInstance());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
} catch (final Exception e) {
logger.warn("Crypt Error", e);
return null;
}
return cipher;
}
/**
* Crypt one array of bytes and returns the crypted array of bytes
*
* @param plaintext
*
* @return the crypted array of bytes
*
* @throws Exception
*/
public byte[] crypt(final byte[] plaintext) throws Exception {
if (!keyReady()) {
throw new CryptoException("Key not Ready");
}
try {
final Cipher cipher = Cipher.getInstance(getInstance());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(plaintext);
} catch (final Exception e) {
logger.warn("Crypt Error", e);
throw e;
}
}
/**
* Crypt one array of bytes and returns the crypted String as HEX format
*
* @param plaintext
*
* @return the crypted String as HEX format
*
* @throws Exception
*/
public final String cryptToHex(final byte[] plaintext) throws Exception {
final byte[] result = crypt(plaintext);
return encodeHex(result);
}
/**
* Crypt one String and returns the crypted array of bytes
*
* @param plaintext
*
* @return the crypted array of bytes
*
* @throws Exception
*/
public final byte[] crypt(final String plaintext) throws Exception {
return crypt(plaintext.getBytes(WaarpStringUtils.UTF8));
}
/**
* Crypt one String and returns the crypted String as HEX format
*
* @param plaintext
*
* @return the crypted String as HEX format
*
* @throws Exception
*/
public final String cryptToHex(final String plaintext) throws Exception {
return cryptToHex(plaintext.getBytes(WaarpStringUtils.UTF8));
}
/**
* Returns a cipher for decryption associated with the key
*
* @return the cipher for decryption or null if it fails in case Encryption
* method or key is incorrect
*/
public Cipher toDecrypt() {
final Cipher cipher;
try {
cipher = Cipher.getInstance(getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
} catch (final Exception e) {
logger.warn("Uncrypt Error", e);
return null;
}
return cipher;
}
/**
* Decrypt an array of bytes and returns the uncrypted array of bytes
*
* @param ciphertext
*
* @return the uncrypted array of bytes
*
* @throws Exception
*/
public byte[] decrypt(final byte[] ciphertext) throws Exception {
if (!keyReady()) {
throw new CryptoException("Key not Ready");
}
try {
final Cipher cipher = Cipher.getInstance(getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(ciphertext);
} catch (final Exception e) {
logger.warn("Decrypt Error", e);
throw e;
}
}
/**
* Decrypt an array of bytes and returns the uncrypted String
*
* @param ciphertext
*
* @return the uncrypted array of bytes
*
* @throws Exception
*/
public final String decryptInString(final byte[] ciphertext)
throws Exception {
return new String(decrypt(ciphertext), WaarpStringUtils.UTF8);
}
/**
* Decrypt a String as HEX format representing a crypted array of bytes and
* returns the uncrypted array of
* bytes
*
* @param ciphertext
*
* @return the uncrypted array of bytes
*
* @throws Exception
*/
public final byte[] decryptHexInBytes(final String ciphertext)
throws Exception {
final byte[] arrayBytes = decodeHex(ciphertext);
return decrypt(arrayBytes);
}
/**
* Decrypt an array of bytes as HEX format representing a crypted array of
* bytes and returns the uncrypted
* array of bytes
*
* @param ciphertext
*
* @return the uncrypted array of bytes
*
* @throws Exception
*/
public final byte[] decryptHexInBytes(final byte[] ciphertext)
throws Exception {
final byte[] arrayBytes =
decodeHex(new String(ciphertext, WaarpStringUtils.UTF8));
return decrypt(arrayBytes);
}
/**
* Decrypt a String as HEX format representing a crypted array of bytes and
* returns the uncrypted String
*
* @param ciphertext
*
* @return the uncrypted String
*
* @throws Exception
*/
public final String decryptHexInString(final String ciphertext)
throws Exception {
return new String(decryptHexInBytes(ciphertext), WaarpStringUtils.UTF8);
}
/**
* Decrypt a String as HEX format representing a crypted String and
* returns the uncrypted String
*
* @param ciphertext
*
* @return the uncrypted String
*
* @throws Exception
*/
public final String decryptHexInString(final byte[] ciphertext)
throws Exception {
return new String(decryptHexInBytes(ciphertext), WaarpStringUtils.UTF8);
}
/**
* Decode from a file containing a HEX crypted string
*
* @param file
*
* @return the decoded uncrypted content of the file
*
* @throws Exception
*/
public final byte[] decryptHexFile(final File file) throws Exception {
if (file.length() > Integer.MAX_VALUE) {
throw new IOException(
"File too big to be decoded into an array of bytes");
}
byte[] byteKeys = new byte[(int) file.length()];
FileInputStream inputStream = null;
DataInputStream dis = null;
try {
inputStream = new FileInputStream(file);
dis = new DataInputStream(inputStream);
dis.readFully(byteKeys);
FileUtils.close(dis);
final String skey = new String(byteKeys, WaarpStringUtils.UTF8);
// decrypt it
byteKeys = decryptHexInBytes(skey);
return byteKeys;
} finally {
FileUtils.close(dis);
FileUtils.close(inputStream);
}
}
/**
* @param encoded
*
* @return the array of bytes from encoded String (HEX)
*/
public final byte[] decodeHex(final String encoded) {
return FilesystemBasedDigest.getFromHex(encoded);
}
/**
* @param bytes
*
* @return The encoded array of bytes in HEX
*/
public final String encodeHex(final byte[] bytes) {
return FilesystemBasedDigest.getHex(bytes);
}
}