Вектор инициализации и соль сохраняются вместе с зашифрованными данными, поэтому вам нужно будет только указать пароль. Он нужен мне для распространения моих личных ключей подписи кода, чтобы у меня был доступ с любого компьютера, на котором я работаю, для расшифровки и подписания любых изменений. Цель состоит в том, чтобы перейти на AES-256, но у него были проблемы, как описано в заголовке. Вот (ИСПРАВЛЕНО) код:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.io.IOUtils;
/**
*
* @author Tim
*/
public class CryptoTool {
public static final String className = "CryptoTool";
public static boolean debug = false;
public CryptoTool() {
}
public byte[] encryptFile(String filename,char[] pass, byte[] salt, byte[] iv) {
// generate key
SecretKeySpec secretKeySpec = generateKeyFromPassword(pass,salt);
// erase local password, don't worry, it's fast enough, (local to the function)
for(int i = 0; i < pass.length;i++) pass[i] = '\0'; // NULL character (end of string for C/C++)
byte[] data = getBytesUTF8(filename);
byte[] output = null;
if (!new File(filename).exists()) {
System.out.println(className + "encryptFile: non-existent file: " + filename);
// if you don't have a file to operate on, I mean, C'mon, get it together
return null;
}
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv);
byte[] aadData = "symService".getBytes();
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec, new SecureRandom());
cipher.updateAAD(aadData);
output = cipher.doFinal(data);
byte[] encrypted = new byte[output.length + 20];
System.arraycopy(salt , 0, encrypted, 0 , 8);
System.arraycopy(iv , 0, encrypted, 8 , 12);
System.arraycopy(output, 0, encrypted, 20,output.length);
writeBytesTo(encrypted,filename + ".encrypted");
//System.out.println("Encrypted To: " + filename + ".encrypted");
new File(filename).delete();
} catch (NoSuchAlgorithmException nsae) {
System.out.println("No Such Algorithm: " + nsae);
} catch (InvalidKeyException ike) {
System.out.println("Invalid Key: " + ike);
} catch (InvalidAlgorithmParameterException iape) {
System.out.println("InvalidAlgorithmParameterException: " + iape);
} catch (NoSuchPaddingException nspe) {
System.out.println("NoSuchPaddingException: " + nspe);
} catch (IllegalBlockSizeException ibse) {
System.out.println("IllegalBlockSizeException: " + ibse);
} catch (BadPaddingException bpe) {
System.out.println("BadPaddingException: " + bpe);
}
return iv;
}
public boolean decryptFile(char[] pass,String outPath) {
// must pass a valid file, note that outPath should end in ".encrypted"
if (outPath == null || !new File(outPath + ".encrypted").exists()) return false;
// get the contents of the file
byte[] input = getBytesUTF8(outPath + ".encrypted");
// from the contents, get the salt and generate the key, so we can erase the password
byte[] salt = new byte[8];
System.arraycopy(input,0,salt,0,8);
if (debug) {
for(int i = 0; i < salt.length;i++) System.out.print((char)salt[i]);
System.out.println();
}
// generate the key
SecretKeySpec secretKeySpec = generateKeyFromPassword(pass,salt);
for(int i = 0; i < pass.length;i++) pass[i] = '\0';
// get the initalization vector
byte[] iv = new byte[12];
System.arraycopy(input, 8, iv, 0, 12);
if (debug) {
for(int i = 0; i < salt.length;i++) System.out.print((char)salt[i]);
System.out.println();
}
// copy the encrypted data to a byte array
byte[] encrypted = new byte[input.length - 20];
System.arraycopy(input, 20, encrypted, 0, input.length - 20);
// get the authenticated additional data (the tag we're having trouble with)
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv);
byte[] aadData = "symService".getBytes();
// decrypt
byte[] decrypted = null;
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec, new SecureRandom());
cipher.updateAAD(aadData);
decrypted = cipher.doFinal(encrypted);
// save the decrypted file to its' original filename (removes the .encrypted from the filename)
writeBytesTo(decrypted,outPath);
// delete the encrypted file
new File(outPath + ".encrypted").delete();
// if we made it this far, nothing failed, so return true; decryption successful
return true;
} catch (NoSuchAlgorithmException nsae) {
System.out.println("No Such Algorithm: " + nsae);
} catch (InvalidKeyException ike) {
System.out.println("Invalid Key: " + ike);
} catch (InvalidAlgorithmParameterException iape) {
System.out.println("InvalidAlgorithmParameterException: " + iape);
} catch (NoSuchPaddingException nspe) {
System.out.println("NoSuchPaddingException: " + nspe);
} catch (IllegalBlockSizeException ibse) {
System.out.println("IllegalBlockSizeException: " + ibse);
} catch (BadPaddingException bpe) {
System.out.println("BadPaddingException: " + bpe);
bpe.printStackTrace();
}
// otherwise, something went wrong, return failure code
return false;
}
public static void main(String[] args) {
/* The purpose of this test is to encrypt and decrypt a keystore file so we
may distribute our keys, without fear of someone hacking the data and
signing code we don't authorize.
If it works, we can distribute our keys and have immediate access to them
without fear that anyone else can gain access to them. This allows us to
deploy from any machine attached to the Internet.
*/
// toggle 'test' in order to test both encryption and decryption, or separately.
boolean test = true;
CryptoTool tool = new CryptoTool();
char[] pass = new RequestPassword().getPassword();
SecureRandom sr = new SecureRandom();
byte[] salt = new byte[8];
sr.nextBytes(salt);
byte[] iv = new byte[12];
// maybe we could skip this next line, so delete that and just use one instance of SecureRandom???
sr = new SecureRandom();
sr.nextBytes(iv);
if (debug) {
for(int i = 0; i < salt.length;i++) System.out.print((char)salt[i]);
System.out.println();
for(int i = 0; i < iv.length;i++) System.out.print((char)iv[i]);
System.out.println();
}
String plainFile = "c:\\java\\keystore_private";
// if test is true, encrypt also, otherwise, just decrypt (in this case,
// salt and IV should be generated, else, get them from the encrypted file.
if (test) {
tool.encryptFile(plainFile, pass, salt, iv);
}
if (new File(plainFile + ".encrypted").exists())
System.out.println("Encrypted To: " + plainFile + ".encrypted");
pass = new RequestPassword().getPassword();
if (tool.decryptFile(pass,plainFile)) {
System.out.println("Decrypted To: " + plainFile);
} else {
System.out.println("Couldn't Decrypt");
}
// does erasing in a function(method) also erase original? Local to method call.
if (debug) {
System.out.print("Pass: '");
for(int i = 0; i < pass.length;i++) System.out.print(pass[i]);
System.out.println("'");
}
System.exit(0);
}
public byte[] getBytesUTF8(String filename) {
FileInputStream in = null;
try {
in = new FileInputStream(filename);
} catch (FileNotFoundException fnfe) {
// So using this package, that shouldn't happend, but anyway
System.out.println(className + ".getBytesUTF8: File not found: " + filename);
}
// okay, so load the class
byte[] data = null;
try {
data = IOUtils.toByteArray(in);
} catch (IOException ioe) {
System.err.println("IOException: reading class data.");
}
try {
in.close();
} catch (IOException ioe) {
System.out.println("Unable to close: " + filename);
}
return data;
}
public void writeBytesTo(byte[] data, String filename) {
FileOutputStream output = null;
try {
output = new FileOutputStream(new File(filename));
} catch (FileNotFoundException fnfe) {
System.err.println("Couldn't create: " + filename);
}
try {
IOUtils.write(data, output);
//System.out.println("Wrote file: " + filename);
} catch (IOException ioe) {
System.err.println("Couldn't write data to: " + filename);
}
try {
output.close();
} catch (IOException ioe) {
System.out.println("Couldn't close: " + filename);
}
}
public SecretKeySpec generateKeyFromPassword(char[] password, byte[] salt) {
/* Derive the key, given password and salt. */
SecretKeyFactory factory = null;
try {
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
} catch (NoSuchAlgorithmException nsae) {
System.out.println("No Such Algorithm: " + nsae);
}
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = null;
try {
tmp = factory.generateSecret(spec);
} catch (InvalidKeySpecException ikse) {
System.out.println("Inavalid Key Spec Exception: " + ikse);
}
SecretKeySpec keySpec = new SecretKeySpec(tmp.getEncoded(), "AES");
return keySpec;
}
}