Java: write and read password based encrypted private key - java

I am trying to read a password based encrypted private key from a file, but I'm getting the following exception:
java.io.IOException: DerInputStream.getLength(): lengthTag=109, too big.
at sun.security.util.DerInputStream.getLength(DerInputStream.java:561)
at sun.security.util.DerValue.init(DerValue.java:365)
at sun.security.util.DerValue.<init>(DerValue.java:294)
at javax.crypto.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:84) ...
This is how I encrypt and write to file the private key:
public static void savePrivateKeyToDisk(PrivateKey privateKey, String passord){
try {
// unencrypted PKCS#8 private key
byte[] encodedPrivateKey = privateKey.getEncoded();
String MYPBEALG = "PBEWithSHA1AndDESede";
int count = 20;
SecureRandom random = new SecureRandom();
byte[] salt = new byte[8];
random.nextBytes(salt);
// Create PBE parameter set
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(MYPBEALG);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher pbeCipher = Cipher.getInstance(MYPBEALG);
// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
// Encrypt the encoded Private Key with the PBE key
byte[] cipherText = pbeCipher.doFinal(encodedPrivateKey);
// Now construct PKCS #8 EncryptedPrivateKeyInfo object
AlgorithmParameters algparms = AlgorithmParameters.getInstance(MYPBEALG);
algparms.init(pbeParamSpec);
EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(algparms, cipherText);
// DER encoded PKCS#8 encrypted key
byte[] encryptedPkcs8 = encinfo.getEncoded();
File encryptedPrivate = new File(PRIVATE_KEY_FILE);
if (encryptedPrivate.getParentFile() != null) {
encryptedPrivate.getParentFile().mkdirs();
}
encryptedPrivate.createNewFile();
ObjectOutputStream publicKeyOS = new ObjectOutputStream(
new FileOutputStream(encryptedPrivate));
publicKeyOS.writeObject(encryptedPkcs8);
publicKeyOS.close();
}
catch (Exception e){
e.printStackTrace();
}
}
... and this is how I'm trying to read the encrypted private key:
public static PrivateKey getPrivateKey(String passwd){
try {
byte[] encodedPrivateKey = getFileBytes(PRIVATE_KEY_FILE);
// exception thrown from here
EncryptedPrivateKeyInfo encryptPKInfo = new EncryptedPrivateKeyInfo(encodedPrivateKey);
Cipher cipher = Cipher.getInstance(encryptPKInfo.getAlgName());
PBEKeySpec pbeKeySpec = new PBEKeySpec(passwd.toCharArray());
SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptPKInfo.getAlgName());
Key pbeKey = secFac.generateSecret(pbeKeySpec);
AlgorithmParameters algParams = encryptPKInfo.getAlgParameters();
cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
KeySpec pkcs8KeySpec = encryptPKInfo.getKeySpec(cipher);
KeyFactory kf = KeyFactory.getInstance(ALGORITHM);
return kf.generatePrivate(pkcs8KeySpec);
}
catch (Exception e){
e.printStackTrace();
return null;
}
}
... the getFileBytes method:
private static byte[] getFileBytes(String infile){
File f = new File(infile) ;
int sizecontent = ((int) f.length());
byte[] data = new byte[sizecontent];
try
{
FileInputStream freader = new FileInputStream(f);
freader.read(data, 0, sizecontent) ;
freader.close();
return data;
}
catch(IOException ioe)
{
System.out.println(ioe.toString());
return null;
}
}
It seems like the encrypted private key is not in the right format, but I save it in DER PKCS#8 format.
So, the question: What is the mistake in this code?

I guess the problem is that you write an Object, but then you read byte[] (not an Object)
I would suggest that you either read the whole object and then get the required bytes or even better write byte[] directly (don't use ObjectOutputStream) and then load these bytes, eg:
FileOutputStream fos = new FileOutputStream(PRIVATE_KEY_FILE);
fos.write(myByteArray);
fos.close();
and then to retrieve it:
byte[] bytes = Files.readAllBytes(Paths.get(PRIVATE_KEY_FILE));

Related

.NET equivalent of Java KeyFactory.getInstance "RSA"/"RSA/ECB/PKCS1Padding"

I have the following code,
public static String encrypt(String plainText, String key){
try{
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.decode(key, Base64.DEFAULT)));
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")),Base64.DEFAULT);
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
I want to convert this to C#. I have tried CryptUtils but it doesn't work https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Common/CryptUtils.cs
Sample key,
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ9AMIIBCgKCAQEAoqB1N9kugk4UKYnbh0fcg3qYyYKP0m4B
MjWd05ReeAdj+7JRYDEKO6xthDdVSdRO1/2V+YtY8DnXWnxRaICwu8235s3imZCyqgYnomPWdY+F
K540oTz/zug+9wbrlzt/WQFUU4lPlQbzm/Gjw8XfaCozT0e3bnWQcD7rORCOyuJgwSGgREjTv1ss
pgEaKTMknii9vpGZLeAXwoeIYROhuT4IoIkPDhtY0/UZiCi6v7Ja2dmy53VlWIkcm3rcnSJdvpXr
OgiHvaNABHmeymNycNqd6WUaysBRheluQ86nq/2nZPW0gcvmYt5zbMMYX3yY/n2WtAKeNQBAEW1q
b0s6MwIDAQAB
Possible encryped value,
Y3VTjghDnTrCeG8C/RklKsJ3Y0Mt89sSGGin28E4iQPQvKqeZBws7rBQEZaRamDWftxCkEYZs4Qh
V2l4IVlrawdtRmQlcQh8McrpqP/97Gz8pEDEYnqA7kqBTqZw0Z5o0WsshGSwiAQ9wNSym4xHejkq
zrKxWP8XCMkcT0NlKlRMoqKKICFKZbqWeSQkQM5y9OEcmB6inNNkJCoM1Ip48+cK3cOE6dqXNVrl
sSTZ8WQKwoB3dJmcYqexR3kAvBYdX6ZxEF+2+6b9h8+tc5G7Y5R2eqycyUossdkCcI3fNVhyc72P
axCjZFWZUgfDGCxg1WNhStrH9L8c59P35JKKug==
Since i don't have the private key, i can't decrypt, but at least this produces the right lengthed values.
So try this (you need bouncycastle for reading pem):
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
var keyBytes =
Convert.FromBase64String(
"MIIBI...."); // your key here
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
byte[] plaintext = Encoding.UTF8.GetBytes("amount=1&currency=AED");
byte[] ciphertext = rsa.Encrypt(plaintext, false);
string cipherresult = Convert.ToBase64String(ciphertext);

How to extract data from a CipherInputStream?

I decrypt a file on the internal memory, as follows:
CipherInputStream cip = new CipherInputStream(context.openFileInput("filename"), cipher);
How can I get the decoded result and convert it, for example, to the string?
UPDATE:
File, which is stored in the phone's internal memory is encrypted json-file. The method should read this file, decrypt and return the result as a string:
public String getJSON() {
String jsonString = "";
try {
String algorithm = "DESede";
jsonString = "";
SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
DESedeKeySpec kspec = new DESedeKeySpec(readKey().getBytes());
SecretKey key = skf.generateSecret(kspec);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key);
CipherInputStream cip = new CipherInputStream(context.openFileInput("filename"), cipher);
// here I have to get the result of decoding and write it to the jsonString
cip.close();
} catch (Exception e) {
e.printStackTrace();
}
return jsonString;
}

Can't restore privateKey RSA

I have this issue about java cryptography.
I take a password from the user then I product a private key and a public key.From the public key I create the cipher and then I store the private key and the cipher.
Then from my second application I read the password again from the user , the cipher file and the private key and then I try to match the password with the decryption of the cipher and the private Key.
My first application:
private static byte[] encrypt(byte[] inpBytes, PublicKey key,
String xform) throws Exception {
Cipher cipher = Cipher.getInstance(xform);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(inpBytes);
}
String xform = "RSA";
// Generate a key-pair
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(Efarmogi_1.class.getName()).log(Level.SEVERE, null, ex);
}
kpg.initialize(512); // 512 is the keysize.
KeyPair kp = kpg.generateKeyPair();
/create public and private key
PublicKey pubk = kp.getPublic();
PrivateKey prvk = kp.getPrivate();
//password from user
String password = T_Password.getText();
byte[] dataBytes = password.getBytes();
//create cipher
byte[] encBytes = null;
try {
encBytes = encrypt(dataBytes, pubk, xform);
} catch (Exception ex) {
Logger.getLogger(Efarmogi_1.class.getName()).log(Level.SEVERE, null, ex);
}
//storing
//cipher
FileOutputStream cipher = null;
try {
cipher = new FileOutputStream( "Xrhstes\\"+T_Username.getText()+"\\hash_"+T_Username.getText());
cipher.write(encBytes);//write with bytes
cipher.close();
} catch (IOException ex) {
Logger.getLogger(Efarmogi_1.class.getName()).log(Level.SEVERE, null, ex);
}
//private key
byte[] key2 = prvk.getEncoded();
FileOutputStream keyfos2 = null;
try {
keyfos2 = new FileOutputStream("Xrhstes\\"+T_Username.getText()+"\\private_"+ T_Username.getText()+".pem");
keyfos2.write(key2);
keyfos2.close();
and this is the second application:
private static byte[] decrypt(byte[] inpBytes, PrivateKey key,String xform) throws Exception{
Cipher cipher = Cipher.getInstance(xform);
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(inpBytes);
}
//fetch private key
byte[] prvk1 = new byte[(int)new File("C:\\...\\"+T_Username.getText()+"\\private_"+ T_Username.getText()).length()];
//make it from bytes to private key
KeyFactory kf= KeyFactory.getInstance("RSA");
PrivateKey prvk=kf.generatePrivate(new PKCS8EncodedKeySpec(prvk1));
//fetch cipher
byte[] encBytes = new byte[(int)new File("C:\\...\\"+T_Username.getText()+"\\hash_"+ T_Username.getText()).length()];
//decrypt with our password given from user
String xform = "RSA";
byte[] decBytes = decrypt(encBytes,prvk, xform);
boolean expected = java.util.Arrays.equals(password, decBytes);
System.out.println("Test " + (expected ? "SUCCEEDED!" : "FAILED!"));
My issue is when I try to retransform the bytes saved , back to privateKey I get multiple errors that key type is invalid(problem starts at decoding PKCS8EncodedKeySpec it is noted that KeySpec is invalid).I tried numerous ways but still the same,could someone guide me where is my mistake ? thanks in advance!!!
With the following lines:
byte[] prvk1 = new byte[(int)new File("C:\\...\\"+T_Username.getText()+"\\private_"+ T_Username.getText()).length()];
and
byte[] encBytes = new byte[(int)new File("C:\\...\\"+T_Username.getText()+"\\hash_"+ T_Username.getText()).length()];
You actually create two empty byte arrays. You only indicate the size of the array using File.length(), which obviously is not correct. Try again after actually reading the files, e.g. using the readAllBytes method (requires Java 8 or higher).

Why always give me false in signature?

I try in this code to verify my code, I have public key , my data and signature.I am read my signature from file and convert my string to public key then get my data and verify to signature.
public static boolean verify () {
String publickey = "MIGfMA0GCSqGSIb3DQE";
byte[] encKey = Base64.decodeBase64(publickey.getBytes());
try {
byte[] MACaddress = GetData();
BufferedReader in = new BufferedReader(new FileReader(
"EndSignatuer.txt"));
FileInputStream keyfis = new FileInputStream("EndSignatuer.txt");
byte[] Signen = new byte[keyfis.available()];
keyfis.read(Signen);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pubKey);
byte[] deSignen = Base64.decodeBase64(Signen);
byte[] decrypted_digest = cipher.doFinal(deSignen);
MessageDigest md5_digest = MessageDigest.getInstance("MD5");
md5_digest.update(MACaddress);
byte[] digest = md5_digest.digest();
if (decrypted_digest == digest) {
return true;
}else {
return false;//her why give me false
}
Code encryption:
public static void GenarationKEY(byte[] data) {
try {
File fileEndSignatuer = new File("EndSignatuer.txt");
FileOutputStream fopEndSignatuer = new FileOutputStream(
fileEndSignatuer);
// /Read private key from file
FileInputStream keyfis = new FileInputStream("PiveteKey.txt");
byte[] PrivateKeyB = new byte[keyfis.available()];
keyfis.read(PrivateKeyB);
keyfis.close();
byte[] decodePrivetekey = Base64.decodeBase64(PrivateKeyB);
// /get private key
PKCS8EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(
decodePrivetekey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privKey = keyFactory.generatePrivate(pubKeySpec);
// / make hash
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privKey);
// /make encoding
MessageDigest md5_digest = MessageDigest.getInstance("MD5");
byte[] digest = md5_digest.digest(data);
byte[] cipherText = cipher.doFinal(digest);
byte[] degnatureencode = Base64.encodeBase64(cipherText);
fopEndSignatuer.write(degnatureencode);
fopEndSignatuer.flush();
fopEndSignatuer.close();
}
First of all, what you do is not quite RSA digital signature. Use Signature class instead of combination Cipher and MessageDigest. Or if you insist to do it on low level, then consult specification, particularly section 9.2.
Secondly, string MIGfMA0GCSqGSIb3DQE doesn't represent RSA public key neither it is proper Base64-encoded data.
Also you want to use Arrays.equals(byte[], byte[]) instead of equality operator, as the latter just ensures that array object is the same, while the former compares actual content of the arrays.

Using password-based encryption on a file in Java

I'm trying to encrypt the contents of one file into another file using a passphrase in Java. The file is getting read to a byte array, encrypted to another byte array, and then written to the new file. Unfortunately, when I try to reverse the encryption, the output file gets decrypted as garbage.
I strongly suspect that the issue has to do with generating an identical key every time the same passphrase is used. I wrote a testing method that dumps the key into a file whenever one gets generated. The key is recorded both directly and in encoded form. The former is identical every time, but the latter is always different for some reason.
In all honesty, I don't know a great deal about encryption methods, especially in Java. I only need the data to be moderately secure, and the encryption doesn't have to withstand an attack from anyone with significant time and skills. Thanks in advance to anyone who has advice on this.
Edit: Esailija was kind enough to point out that I was always setting the cipher with ENCRYPT_MODE. I corrected the problem using a boolean argument, but now I'm getting the following exception:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher
That sounds to me like the passphrase isn't being used properly. I was under the impression that "PBEWithMD5AndDES" would hash it into a 16 byte code, which most certainly is a multiple of 8. I'm wondering why the key generates and gets used just fine for encryption mode, but then it complains when trying to decrypt under the exact same conditions.
import java.various.stuff;
/**Utility class to encrypt and decrypt files**/
public class FileEncryptor {
//Arbitrarily selected 8-byte salt sequence:
private static final byte[] salt = {
(byte) 0x43, (byte) 0x76, (byte) 0x95, (byte) 0xc7,
(byte) 0x5b, (byte) 0xd7, (byte) 0x45, (byte) 0x17
};
private static Cipher makeCipher(String pass, Boolean decryptMode) throws GeneralSecurityException{
//Use a KeyFactory to derive the corresponding key from the passphrase:
PBEKeySpec keySpec = new PBEKeySpec(pass.toCharArray());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(keySpec);
//Create parameters from the salt and an arbitrary number of iterations:
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 42);
/*Dump the key to a file for testing: */
FileEncryptor.keyToFile(key);
//Set up the cipher:
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
//Set the cipher mode to decryption or encryption:
if(decryptMode){
cipher.init(Cipher.ENCRYPT_MODE, key, pbeParamSpec);
} else {
cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec);
}
return cipher;
}
/**Encrypts one file to a second file using a key derived from a passphrase:**/
public static void encryptFile(String fileName, String pass)
throws IOException, GeneralSecurityException{
byte[] decData;
byte[] encData;
File inFile = new File(fileName);
//Generate the cipher using pass:
Cipher cipher = FileEncryptor.makeCipher(pass, false);
//Read in the file:
FileInputStream inStream = new FileInputStream(inFile);
decData = new byte[(int)inFile.length()];
inStream.read(decData);
inStream.close();
//Encrypt the file data:
encData = cipher.doFinal(decData);
//Write the encrypted data to a new file:
FileOutputStream outStream = new FileOutputStream(new File(fileName + ".encrypted"));
outStream.write(encData);
outStream.close();
}
/**Decrypts one file to a second file using a key derived from a passphrase:**/
public static void decryptFile(String fileName, String pass)
throws GeneralSecurityException, IOException{
byte[] encData;
byte[] decData;
File inFile = new File(fileName);
//Generate the cipher using pass:
Cipher cipher = FileEncryptor.makeCipher(pass, true);
//Read in the file:
FileInputStream inStream = new FileInputStream(inFile);
encData = new byte[(int)inFile.length()];
inStream.read(encData);
inStream.close();
//Decrypt the file data:
decData = cipher.doFinal(encData);
//Write the decrypted data to a new file:
FileOutputStream target = new FileOutputStream(new File(fileName + ".decrypted.txt"));
target.write(decData);
target.close();
}
/**Record the key to a text file for testing:**/
private static void keyToFile(SecretKey key){
try {
File keyFile = new File("C:\\keyfile.txt");
FileWriter keyStream = new FileWriter(keyFile);
String encodedKey = "\n" + "Encoded version of key: " + key.getEncoded().toString();
keyStream.write(key.toString());
keyStream.write(encodedKey);
keyStream.close();
} catch (IOException e) {
System.err.println("Failure writing key to file");
e.printStackTrace();
}
}
}
You are using the Cipher.ENCRYPT_MODE for both, decrypting and encrypting. You should use Cipher.DECRYPT_MODE for decrypting the file.
That has been fixed, but your boolean is wrong. It should be true for encrypt and false for decrypt. I would strongly recommend against using false/true as function arguments and always use enum like Cipher.ENCRYPT... moving on
Then you are encrypting to .encrypted file, but trying to decrypt the original plain text file.
Then you are not applying padding to encryption. I am surprised this actually has to be done manually,
but padding is explained here. The padding scheme PKCS5 appeared to be implicitly used here.
This is full working code, writing encrypted file to test.txt.encrypted, and decrypted file to test.txt.decrypted.txt.
Adding padding in encryption and removing it in decryption is explained in the comments.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class FileEncryptor {
public static void main( String[] args ) {
try {
encryptFile( "C:\\test.txt", "password" );
decryptFile( "C:\\test.txt", "password" );
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//Arbitrarily selected 8-byte salt sequence:
private static final byte[] salt = {
(byte) 0x43, (byte) 0x76, (byte) 0x95, (byte) 0xc7,
(byte) 0x5b, (byte) 0xd7, (byte) 0x45, (byte) 0x17
};
private static Cipher makeCipher(String pass, Boolean decryptMode) throws GeneralSecurityException{
//Use a KeyFactory to derive the corresponding key from the passphrase:
PBEKeySpec keySpec = new PBEKeySpec(pass.toCharArray());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(keySpec);
//Create parameters from the salt and an arbitrary number of iterations:
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 42);
/*Dump the key to a file for testing: */
FileEncryptor.keyToFile(key);
//Set up the cipher:
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
//Set the cipher mode to decryption or encryption:
if(decryptMode){
cipher.init(Cipher.ENCRYPT_MODE, key, pbeParamSpec);
} else {
cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec);
}
return cipher;
}
/**Encrypts one file to a second file using a key derived from a passphrase:**/
public static void encryptFile(String fileName, String pass)
throws IOException, GeneralSecurityException{
byte[] decData;
byte[] encData;
File inFile = new File(fileName);
//Generate the cipher using pass:
Cipher cipher = FileEncryptor.makeCipher(pass, true);
//Read in the file:
FileInputStream inStream = new FileInputStream(inFile);
int blockSize = 8;
//Figure out how many bytes are padded
int paddedCount = blockSize - ((int)inFile.length() % blockSize );
//Figure out full size including padding
int padded = (int)inFile.length() + paddedCount;
decData = new byte[padded];
inStream.read(decData);
inStream.close();
//Write out padding bytes as per PKCS5 algorithm
for( int i = (int)inFile.length(); i < padded; ++i ) {
decData[i] = (byte)paddedCount;
}
//Encrypt the file data:
encData = cipher.doFinal(decData);
//Write the encrypted data to a new file:
FileOutputStream outStream = new FileOutputStream(new File(fileName + ".encrypted"));
outStream.write(encData);
outStream.close();
}
/**Decrypts one file to a second file using a key derived from a passphrase:**/
public static void decryptFile(String fileName, String pass)
throws GeneralSecurityException, IOException{
byte[] encData;
byte[] decData;
File inFile = new File(fileName+ ".encrypted");
//Generate the cipher using pass:
Cipher cipher = FileEncryptor.makeCipher(pass, false);
//Read in the file:
FileInputStream inStream = new FileInputStream(inFile );
encData = new byte[(int)inFile.length()];
inStream.read(encData);
inStream.close();
//Decrypt the file data:
decData = cipher.doFinal(encData);
//Figure out how much padding to remove
int padCount = (int)decData[decData.length - 1];
//Naive check, will fail if plaintext file actually contained
//this at the end
//For robust check, check that padCount bytes at the end have same value
if( padCount >= 1 && padCount <= 8 ) {
decData = Arrays.copyOfRange( decData , 0, decData.length - padCount);
}
//Write the decrypted data to a new file:
FileOutputStream target = new FileOutputStream(new File(fileName + ".decrypted.txt"));
target.write(decData);
target.close();
}
/**Record the key to a text file for testing:**/
private static void keyToFile(SecretKey key){
try {
File keyFile = new File("C:\\keyfile.txt");
FileWriter keyStream = new FileWriter(keyFile);
String encodedKey = "\n" + "Encoded version of key: " + key.getEncoded().toString();
keyStream.write(key.toString());
keyStream.write(encodedKey);
keyStream.close();
} catch (IOException e) {
System.err.println("Failure writing key to file");
e.printStackTrace();
}
}
}
These are some improvements to the #Esailija 's answer given some new features in Java.
By using the CipherInputStream and CipherOutputStream classes, the length and complexity of the code is greatly reduced.
I also use char[] instead of String for the password.
You can use System.console().readPassword("input password: ") to get the password as a char[] so that it is never a String.
public static void encryptFile(String inFileName, String outFileName, char[] pass) throws IOException, GeneralSecurityException {
Cipher cipher = PasswordProtectFile.makeCipher(pass, true);
try (CipherOutputStream cipherOutputStream = new CipherOutputStream(new FileOutputStream(outFileName), cipher);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inFileName))) {
int i;
while ((i = bis.read()) != -1) {
cipherOutputStream.write(i);
}
}
}
public static void decryptFile(String inFileName, String outFileName, char[] pass) throws GeneralSecurityException, IOException {
Cipher cipher = PasswordProtectFile.makeCipher(pass, false);
try (CipherInputStream cipherInputStream = new CipherInputStream(new FileInputStream(inFileName), cipher);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outFileName))) {
int i;
while ((i = cipherInputStream.read()) != -1) {
bos.write(i);
}
}
}
private static Cipher makeCipher(char[] pass, Boolean decryptMode) throws GeneralSecurityException {
// Use a KeyFactory to derive the corresponding key from the passphrase:
PBEKeySpec keySpec = new PBEKeySpec(pass);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(keySpec);
// Create parameters from the salt and an arbitrary number of iterations:
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 43);
// Set up the cipher:
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
// Set the cipher mode to decryption or encryption:
if (decryptMode) {
cipher.init(Cipher.ENCRYPT_MODE, key, pbeParamSpec);
} else {
cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec);
}
return cipher;
}

Categories