I try to test this code for encrypting and decrypting. By using dst = new String(baos.toByteArray()); return dst;
I can't decrypt the cipher text. However when I use byte[] encryptedBytes = DatatypeConverter.parseHexBinary(src);
I didn't manage to run the program. How can I fix this?
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class FileEncryption {
//Initial Vector
public static final byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
//EncryptAndDecrypt String -> Input : PlainText + Return : CipherText+DecipherText
public static String encryptString(String src) throws Exception
{
String dst="";
//Not Input!
if(src == null || src.length()==0)
return "";
//Encryption Setting
byte[] k="Multimediaproces".getBytes();
SecretKeySpec Key = new SecretKeySpec(k,"AES");
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
encryptCipher.init(Cipher.ENCRYPT_MODE,Key,ivspec);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
CipherOutputStream cout = new CipherOutputStream(baos,encryptCipher);
cout.write(src.getBytes());
cout.flush(); //ByteOutputStream -> Write Encryption Text
cout.close();
dst = DatatypeConverter.printHexBinary(baos.toByteArray());
return dst;
}
//String src -> EncryptedData
public static String decryptString(String src) throws Exception
{
//src value is Encrypted Value!
//So, src value -> Not Byte!
String dst="";
byte[] encryptedBytes = src.getBytes();
//Not Input!
if(src == null || src.length()==0)
return "";
//Decryption Setting
IvParameterSpec ivspec = new IvParameterSpec(iv);
byte[] k="Multimediaproces".getBytes();
SecretKeySpec Key = new SecretKeySpec(k,"AES");
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
decryptCipher.init(Cipher.DECRYPT_MODE,Key,ivspec);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteArrayInputStream bais = new ByteArrayInputStream(encryptedBytes);
CipherInputStream cin = new CipherInputStream(bais,decryptCipher);
byte[] buf = new byte[1024];
int read;
while((read=cin.read(buf))>=0) //reading encrypted data!
{
baos.write(buf,0,read); //writing decrypted data!
}
// closing streams
cin.close();
byte[] encryptedBytes = DatatypeConverter.parseHexBinary(src);
return dst;
}
}
There are three errors in your sample code:
It doesn't compile because of byte[] encryptedBytes = DatatypeConverter.parseHexBinary(src); at the end of decryptString method
Because you have printed out the encrypted string as a hexidecimal representation, you must convert it back to a normal byte array before trying to decrypt it. In decryptString you should call byte[] encryptedBytes = DatatypeConverter.parseHexBinary(src); instead. This will convert back to the byte[] instead of the hexidecimal representation.
You need a line which converts baos to a String at the end of decrpytString. This is as simple as dst = baos.toString();
With these three changes, I was able to encrypt and decrypt a String.
Related
I'm working on a file encryption benchmark for large files and tested ChaCha20-Poly1305, but received an error on decryption part:
java.lang.RuntimeException: javax.crypto.ShortBufferException: Output buffer too small
at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:703)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2085)
at ChaCha20.ChaCha20Poly1305Jre.main(ChaCha20Poly1305Jre.java:73)
Caused by: javax.crypto.ShortBufferException: Output buffer too small
at java.base/com.sun.crypto.provider.ChaCha20Cipher$EngineAEADDec.doFinal(ChaCha20Cipher.java:1360)
at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:701)
This is not an error in my program but in OpenJava 11 I'm using and should get fixed (known since 2019, see https://bugs.openjdk.java.net/browse/JDK-8224997).
Even with the newest "Early adopter"-version (OpenJDK11U-jdk_x64_windows_11.0.7_9_ea) the error still occurs. This test is run with java version: 11.0.6+8-b520.43.
My question is: is there any other way to perform file encryption with ChaCha20-Poly1305 with native JCE for large files ?
I do not want to use BouncyCastle (as I'm using BC allready for the counterpart benchmark) or reading the plainfile completly into memory (in my source the
testfile is only 1.024 bytes large but the benchmark will test up to 1 GB files). As well I do not want to use ChaCha20 as it does not provide any authentication.
You can find the sources for ChaCha20Poly1305Jce.java, ChaCha20Poly1305JceNoStream.java and ChaCha20Jce.java in my Github-Repo https://github.com/java-crypto/Stackoverflow/tree/master/ChaCha20Poly1305.
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
public class ChaCha20Poly1305Jce {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
System.out.println("File En-/Decryption with ChaCha20-Poly1305 JCE");
System.out.println("see: https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small");
System.out.println("\njava version: " + Runtime.version());
String filenamePlain = "test1024.txt";
String filenameEnc = "test1024enc.txt";
String filenameDec = "test1024dec.txt";
Files.deleteIfExists(new File(filenamePlain).toPath());
generateRandomFile(filenamePlain, 1024);
// setup chacha20-poly1305-cipher
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] key = new byte[32]; // 32 for 256 bit key or 16 for 128 bit
byte[] nonce = new byte[12]; // nonce = 96 bit
sr.nextBytes(key);
sr.nextBytes(nonce);
// Get Cipher Instance
Cipher cipherE = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
// Create parameterSpec
AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(nonce);
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key, "ChaCha20");
System.out.println("keySpec: " + keySpec.getAlgorithm() + " " + keySpec.getFormat());
System.out.println("cipher algorithm: " + cipherE.getAlgorithm());
// initialize the cipher for encryption
cipherE.init(Cipher.ENCRYPT_MODE, keySpec, algorithmParameterSpec);
// encryption
System.out.println("start encryption");
byte inE[] = new byte[8192];
byte outE[] = new byte[8192];
try (InputStream is = new FileInputStream(new File(filenamePlain));
OutputStream os = new FileOutputStream(new File(filenameEnc))) {
int len = 0;
while (-1 != (len = is.read(inE))) {
cipherE.update(inE, 0, len, outE, 0);
os.write(outE, 0, len);
}
byte[] outEf = cipherE.doFinal();
os.write(outEf, 0, outEf.length);
} catch (Exception e) {
e.printStackTrace();
}
// decryption
System.out.println("start decryption");
Cipher cipherD = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
// initialize the cipher for decryption
cipherD.init(Cipher.DECRYPT_MODE, keySpec, algorithmParameterSpec);
byte inD[] = new byte[8192];
byte outD[] = new byte[8192];
try (InputStream is = new FileInputStream(new File(filenameEnc));
OutputStream os = new FileOutputStream(new File(filenameDec))) {
int len = 0;
while (-1 != (len = is.read(inD))) {
cipherD.update(inD, 0, len, outD, 0);
os.write(outD, 0, len);
}
byte[] outDf = cipherD.doFinal();
os.write(outDf, 0, outDf.length);
} catch (Exception e) {
e.printStackTrace();
}
// file compare
System.out.println("compare plain <-> dec: " + Arrays.equals(sha256(filenamePlain), sha256(filenameDec)));
}
public static void generateRandomFile(String filename, int size) throws IOException, NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] data = new byte[size];
sr.nextBytes(data);
Files.write(Paths.get(filename), data, StandardOpenOption.CREATE);
}
public static byte[] sha256(String filenameString) throws IOException, NoSuchAlgorithmException {
byte[] buffer = new byte[8192];
int count;
MessageDigest md = MessageDigest.getInstance("SHA-256");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filenameString));
while ((count = bis.read(buffer)) > 0) {
md.update(buffer, 0, count);
}
bis.close();
return md.digest();
}
}
Using the Early Access version of OpenJDK 11.0.8 (get it here: https://adoptopenjdk.net/upstream.html?variant=openjdk11&ga=ea)
I was able to run the (edited) testprogram (now I'm using CipherInput/OutputStreams).
File En-/Decryption with ChaCha20-Poly1305 JCE
see: https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small
java version: 11.0.8-ea+8
start encryption
keySpec: ChaCha20 RAW
cipher algorithm: ChaCha20-Poly1305/None/NoPadding
start decryption
compare plain <-> dec: true
Edit: I missed the final version (GA) for one hour but it's working as well:
java version: 11.0.8+10
start encryption
keySpec: ChaCha20 RAW
cipher algorithm: ChaCha20-Poly1305/None/NoPadding
start decryption
compare plain <-> dec: true
new code:
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.*;
import java.util.Arrays;
public class ChaCha20Poly1305JceCis {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
System.out.println("File En-/Decryption with ChaCha20-Poly1305 JCE");
System.out.println("see: https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small");
System.out.println("\njava version: " + Runtime.version());
String filenamePlain = "test1024.txt";
String filenameEnc = "test1024enc.txt";
String filenameDec = "test1024dec.txt";
Files.deleteIfExists(new File(filenamePlain).toPath());
generateRandomFile(filenamePlain, 1024);
// setup chacha20-poly1305-cipher
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] key = new byte[32]; // 32 for 256 bit key or 16 for 128 bit
byte[] nonce = new byte[12]; // nonce = 96 bit
sr.nextBytes(key);
sr.nextBytes(nonce);
System.out.println("start encryption");
Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
try (FileInputStream in = new FileInputStream(filenamePlain);
FileOutputStream out = new FileOutputStream(filenameEnc);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "ChaCha20");
System.out.println("keySpec: " + secretKeySpec.getAlgorithm() + " " + secretKeySpec.getFormat());
System.out.println("cipher algorithm: " + cipher.getAlgorithm());
//AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(nonce);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(nonce));
byte[] buffer = new byte[8096];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
}
// decryption
System.out.println("start decryption");
Cipher cipherD = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
try (FileInputStream in = new FileInputStream(filenameEnc); // i don't care about the path as all is lokal
CipherInputStream cipherInputStream = new CipherInputStream(in, cipherD);
FileOutputStream out = new FileOutputStream(filenameDec)) // i don't care about the path as all is lokal
{
byte[] buffer = new byte[8192];
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "ChaCha20");
//AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(nonce);
cipherD.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(nonce));
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
// file compare
System.out.println("compare plain <-> dec: " + Arrays.equals(sha256(filenamePlain), sha256(filenameDec)));
}
public static void generateRandomFile(String filename, int size) throws IOException, NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] data = new byte[size];
sr.nextBytes(data);
Files.write(Paths.get(filename), data, StandardOpenOption.CREATE);
}
public static byte[] sha256(String filenameString) throws IOException, NoSuchAlgorithmException {
byte[] buffer = new byte[8192];
int count;
MessageDigest md = MessageDigest.getInstance("SHA-256");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filenameString));
while ((count = bis.read(buffer)) > 0) {
md.update(buffer, 0, count);
}
bis.close();
return md.digest();
}
}
Edit July 23 2020: Seems to be that not all Java versions with version nr 11.0.8 got fixed. I tested the "original" Oracle Java 1.0.8 for Windows x64 and the error still persists. I reported this to Oracle Bug tracker and it was assigned as a Bug ID: JDK-8249844 (https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8249844).
I am working on a class to encrypt/decrypt large files so I am trying to use the streams instead of byte arrays to avoid OutOfMemory exceptions.
In the encryption method I add random salt and iv to the beginning of the encrypted file and it works fine, and here is the code:
public File encryptFile(File inputFile, File outPutFile, String password, ProgressBar progressBar, Label progressPercentage){
//Create IV
byte[] ivBytes = new byte[16];
SecureRandom random1 = new SecureRandom();
random1.nextBytes(ivBytes);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
//Create the key with the salt
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_SIZE];
random.nextBytes(salt);
SecretKeySpec keySpec = generateAesKey(password, salt);
//Create and Init the cipher
Cipher c = Cipher.getInstance("AES/CBC/"+padding);
c.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] buf = new byte[8192];
FileInputStream in = new FileInputStream(inputFile);
FileOutputStream out = new FileOutputStream(outPutFile);
int nread;
int progress = 0;
byte[] ivAndSalt = new byte[ivBytes.length + salt.length];
System.arraycopy(ivBytes,0, ivAndSalt,0, ivBytes.length );
System.arraycopy(salt, 0, ivAndSalt, ivBytes.length, salt.length);
out.write(ivAndSalt);
while((nread = in.read(buf)) != -1) {
byte[] enc = c.update(buf, 0, nread);
out.write(enc);
progress++;
}
Then I try to get the iv and salt in decryption method and then decrypt the rest of the file into an output one with FileInputStream.getChannel.position():
public File decryptFile(File inputFile, File outPutFile, String password, ProgressBar progressBar, Label progressPercentage) {
//Create and Init Cipher
Cipher c = Cipher.getInstance("AES/CBC/" + padding);
FileInputStream in = new FileInputStream(inputFile);
FileOutputStream out = new FileOutputStream(outPutFile);
//Getting the iv and salt
byte[] ivBytes = new byte[16];
byte[] salt = new byte[SALT_SIZE];
byte[] ivAndSalt = new byte[ivBytes.length+SALT_SIZE];
in.read(ivAndSalt, 0, ivBytes.length+SALT_SIZE);
System.arraycopy(ivAndSalt, 0, ivBytes, 0, ivBytes.length);
System.arraycopy(ivAndSalt, ivBytes.length, salt, 0, SALT_SIZE);
IvParameterSpec iv =new IvParameterSpec(ivBytes);
SecretKeySpec keySpec = generateAesKey(password, salt);
c.init(Cipher.DECRYPT_MODE, keySpec, iv);
in.getChannel().position(ivAndSalt.length);
int nread;
int progress = 0;
byte[] buf = new byte[8192];
while((nread = in.read(buf)) != -1) {
byte[] enc = c.update(buf, 0, nread);
out.write(enc);
progress++;
/*if (enc.length / 8192 != 0)
System.out.println((nread*progress) + "%");*/
}
System.out.println("Size of out before doFinal(): " + out.getChannel().size());
byte[] enc = c.doFinal();
out.write(enc);
System.out.println("Size of out after doFinal(): " + out.getChannel().size());
return outPutFile;
}
I didn't get an error on calling decryptFile() but the produced file is corrupted and this means that there is an issue somewhere in the decrypting.
The problem was totally something silly, I forgot to close the input and output streams after doFinal() and writing the enc bytes array to output:
in.close();
out.close();
I am not sure what wrong I have done to this. To cut story short I want to decrypt a file with the given secretKey and using iv and I am using the following code to do so :
package com.Crypt.test;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.SecureRandom;
public class AES256CBCTest {
static String encoding = "UTF-8";
public static void main(String[] args) throws Exception {
String key = "BURP6070";
File inputFile = new File("/Users/jaynigam/Documents/workspace/EncryptDecrypt/files/test.xml.enc");
try {
BufferedReader br = new BufferedReader(new FileReader(inputFile));
String st;
File outputFile =null;
FileOutputStream outputStream = null;
try {
while ((st = br.readLine()) != null){
//decrypt(someString.getBytes(encoding), key);
String decrypted = decrypt(st.getBytes(), key);
outputFile = new File("/Users/jaynigam/Documents/workspace/EncryptDecrypt/files/decryptTest.xml.dec");
outputStream = new FileOutputStream(outputFile);
byte[] strToBytes = decrypted.getBytes(encoding);
outputStream.write(strToBytes);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
outputStream.close();
br.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//byte[] encrypted = encrypt(clean, key);
public static byte[] encrypt(String plainText, String key) throws Exception {
byte[] clean = plainText.getBytes();
// Generating IV.
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Hashing key.
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(key.getBytes());
byte[] keyBytes = new byte[32];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Encrypt.
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clean);
// Combine IV and encrypted part.
byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);
return encryptedIVAndText;
}
public static String decrypt(byte[] encryptedIvTextBytes, String key) throws Exception {
int ivSize = 16;
int keySize = 16;
// Extract IV.
byte[] iv = new byte[ivSize];
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Extract encrypted part.
int encryptedSize = encryptedIvTextBytes.length - ivSize;
byte[] encryptedBytes = new byte[encryptedSize];
System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);
// Hash key.
byte[] keyBytes = new byte[keySize];
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(key.getBytes());
System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Decrypt.
Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/NoPadding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decrypted = cipherDecrypt.doFinal(encryptedBytes);
return new String(decrypted);
}
}
This returns me an output like ?lm:#?ڤ?w?)P#?\?s????Ka???0??{???w|k???o?\?. I have already tried UTF-8 decoding. But still no luck till now. Does anyone have any clue on this one?
I've had exactly the same problem as you, and also used the same source code!
I had a problem using the return and input parameters as byte[], so i've converted it to String with Base64 encoding, so i didnt end up with problems with encodings.
My class:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;
public class PasswordManager {
private static final Logger LOGGER = LoggerFactory.getLogger(PasswordManager.class);
private static final String key = "DdFfGg998012jffW"; // 128 bit key
private PasswordManager() {
}
public static String encrypt(String plainText) {
if (plainText == null) {
return null;
}
byte[] clean = plainText.getBytes();
// Generating IV.
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
try {
// Hashing key.
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key.getBytes(StandardCharsets.UTF_8));
byte[] keyBytes = new byte[16];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Encrypt.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clean);
// Combine IV and encrypted part.
byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);
return Base64.getEncoder().encodeToString(encryptedIVAndText);
} catch (Exception e) {
LOGGER.error("Exception in decrypting a password. Returning null", e);
return null;
}
}
public static String decrypt(String encryptedString) {
if (encryptedString == null) {
return null;
}
byte[] encryptedIvTextBytes = Base64.getDecoder().decode(encryptedString);
int ivSize = 16;
int keySize = 16;
// Extract IV.
byte[] iv = new byte[ivSize];
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Extract encrypted part.
int encryptedSize = encryptedIvTextBytes.length - ivSize;
byte[] encryptedBytes = new byte[encryptedSize];
System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);
try {
// Hash key.
byte[] keyBytes = new byte[keySize];
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(key.getBytes());
System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Decrypt.
Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decrypted = cipherDecrypt.doFinal(encryptedBytes);
return new String(decrypted);
} catch (Exception e) {
LOGGER.error("Exception in decrypting a password. Returning null", e);
return null;
}
}
}
Also try using a key with the same length as mine, try that first, as it could be the problem. After try to use Base64 on string operations on your while loop.
Hope it helps you.
This is my first project in Java and I decided to make a simple text encryptor using AES.
The error I am getting is: The method init(int, Key) in the type Cipher is not applicable for the arguments (int, byte[])
Code:
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;
import javax.crypto.*;
import java.util.*;
public class Encryptor {
public static void main(String[] args) throws Exception {
String FileName = "encryptedtext.txt";
String FileName2 = "decryptedtext.txt";
Scanner input = new Scanner(System.in);
System.out.println("Enter your 16 character key here:");
String EncryptionKey = input.next();
KeyGenerator KeyGen = KeyGenerator.getInstance("AES");
KeyGen.init(128);
Cipher AesCipher = Cipher.getInstance("AES");
System.out.println("Enter text to encrypt or decrypt:");
String Text = input.next();
System.out.println("Do you want to encrypt or decrypt (e/d)");
String answer = input.next();
if (answer.equalsIgnoreCase("e")){
byte[] byteKey = (EncryptionKey.getBytes());
byte[] byteText = (Text).getBytes();
AesCipher.init(Cipher.ENCRYPT_MODE, byteKey); // ERROR LINE
byte[] byteCipherText = AesCipher.doFinal(byteText);
Files.write(Paths.get(FileName), byteCipherText);
}
else if (answer.equalsIgnoreCase("d")){
byte[] byteKey = (EncryptionKey.getBytes());
byte[] byteText = (Text).getBytes();
byte[] cipherText = Files.readAllBytes(Paths.get(FileName));
AesCipher.init(Cipher.DECRYPT_MODE, byteKey); // ERROR LINE
byte[] bytePlainText = AesCipher.doFinal(cipherText);
Files.write(Paths.get(FileName2), bytePlainText);
}
}
}
Thanks in advance! :)
This is Full code
Full code:
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
public class Encrypter {
public static void main(String[] args) throws Exception {
String FileName = "encryptedtext.txt";
String FileName2 = "decryptedtext.txt";
Scanner input = new Scanner(System.in);
System.out.println("Enter your 16 character key here:");
String EncryptionKey = input.next();
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
KeyGenerator KeyGen = KeyGenerator.getInstance("AES");
KeyGen.init(128);
Cipher AesCipher = Cipher.getInstance("AES/CFB/NoPadding");
System.out.println("Enter text to encrypt or decrypt:");
String Text = input.next();
System.out.println("Do you want to encrypt or decrypt (e/d)");
String answer = input.next();
if (answer.equalsIgnoreCase("e")){
byte[] byteKey = (EncryptionKey.getBytes());
byte[] byteText = (Text).getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(byteKey, "AES");
AesCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,ivspec );
AesCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,ivspec); // ERROR LINE
byte[] byteCipherText = AesCipher.doFinal(byteText);
Files.write(Paths.get(FileName), byteCipherText);
}
else if (answer.equalsIgnoreCase("d")){
byte[] byteKey = (EncryptionKey.getBytes());
byte[] byteText = (Text).getBytes();
byte[] cipherText = Files.readAllBytes(Paths.get(FileName));
SecretKeySpec secretKeySpec = new SecretKeySpec(byteKey, "AES");
AesCipher.init(Cipher.DECRYPT_MODE, secretKeySpec,ivspec); // ERROR LINE
byte[] bytePlainText = AesCipher.doFinal(cipherText);
Files.write(Paths.get(FileName2), bytePlainText);
}
}
}
You should not pass byte array directly to Cipher object, instead you need to create object of SecretKeySpecs.
This is complete Code
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
public class Encrypter {
public static void main(String[] args) throws Exception {
String FileName = "encryptedtext.txt";
String FileName2 = "decryptedtext.txt";
Scanner input = new Scanner(System.in);
System.out.println("Enter your 16 character key here:");
String EncryptionKey = input.next();
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
KeyGenerator KeyGen = KeyGenerator.getInstance("AES");
KeyGen.init(128);
Cipher AesCipher = Cipher.getInstance("AES/CFB/NoPadding");
System.out.println("Enter text to encrypt or decrypt:");
String Text = input.next();
System.out.println("Do you want to encrypt or decrypt (e/d)");
String answer = input.next();
if (answer.equalsIgnoreCase("e")) {
byte[] byteKey = (EncryptionKey.getBytes());
byte[] byteText = (Text).getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(byteKey, "AES");
AesCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivspec);
AesCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivspec); // ERROR LINE
byte[] byteCipherText = AesCipher.doFinal(byteText);
Files.write(Paths.get(FileName), byteCipherText);
} else if (answer.equalsIgnoreCase("d")) {
byte[] byteKey = (EncryptionKey.getBytes());
byte[] byteText = (Text).getBytes();
byte[] cipherText = Files.readAllBytes(Paths.get(FileName));
SecretKeySpec secretKeySpec = new SecretKeySpec(byteKey, "AES");
AesCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivspec); // ERROR LINE
byte[] bytePlainText = AesCipher.doFinal(cipherText);
Files.write(Paths.get(FileName2), bytePlainText);
}
}
}
I had to encrypt a URL for been exposed in a external link, and I have created a class to encrypt on AES. Maybe this can help someone.
I added a method to create my Random Initial Vector and it is on this class also.
package br.com.tokiomarine.captcha.encryption;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.text.CharacterPredicates;
import org.apache.commons.text.RandomStringGenerator;
public class Encryptor
{
public static String encrypt(String key, String initVector, String value) throws Exception
{
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64URLSafeString(encrypted);
}
public static String decrypt(String key, String initVector, String encrypted) throws Exception
{
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
}
public static String generateInitVector() {
RandomStringGenerator randomStringGenerator =
new RandomStringGenerator.Builder()
.withinRange('0', 'z')
.filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
.build();
return randomStringGenerator.generate(16);
}
}
And here is my Test Class:
public class EcryptionTest {
#Test
public void encryptionTest() {
try
{
String key = "Key#TokioMarineC";
for(int i = 0; i < 1000; i++)
{
String initVector = Encryptor.generateInitVector();
String encrypted = Encryptor.encrypt(key, initVector, "StringTeste");
assertTrue("StringTeste".equals(Encryptor.decrypt(key, initVector, encrypted)));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
We're trying to figure out how to do this in Java/scala:
use Crypt::CBC;
$aesKey = "some key"
$cipher = new Crypt::CBC($aesKey, "DES");
$encrypted = $cipher->encrypt("hello world");
print $encrypted // prints: Salted__�,%�8XL�/1�&�n;����쀍c
print encode_base64($encrypted); // prints: U2FsdGVkX19JwL/Dc4gwehTfZ1ahNlO6Jf41vALcshg=
$decrypted = $cipher->decrypt($encrypted);
print $decrypted // prints: hello world
The problem is that the perl code is something we can not chanage.
I tried a few things in scala but didn't really get it right, for example something like this:
val secretKey = new SecretKeySpec("some key".getBytes("UTF-8"), "DES")
val encipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
encipher.init(Cipher.ENCRYPT_MODE, secretKey)
val encrypted = encipher.doFinal("hello world".getBytes)
println(encrypted) // prints: [B#4896ceb3
println(java.util.Arrays.toString(encrypted)) // [-45, -126, -90, 36, 8, -73, 6, 85, -94, 108, 100, -120, 15, -8, 126, 76]
println(Hex.encodeHexString(encrypted)) //prints: 822c90f1116686e75160ff06c8faf4a4
What we eventually need to do is to be able to decrypt cookies in Java set by Perl.
Any help or direction in Java/scala would very much be appreciated
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
final class CrapEncryption
{
private static final byte[] MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
private static final int KEY_LEN = 8;
private static final int SALT_LEN = 8;
private static final SecureRandom random = new SecureRandom();
static byte[] pretendToEncrypt(byte[] password, byte[] msg)
throws GeneralSecurityException
{
byte[] salt = new byte[SALT_LEN];
random.nextBytes(salt);
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(password);
md5.update(salt);
byte[] dk = md5.digest();
Cipher des;
try {
SecretKey key = new SecretKeySpec(dk, 0, KEY_LEN, "DES");
AlgorithmParameterSpec iv = new IvParameterSpec(dk, KEY_LEN, SALT_LEN);
des = Cipher.getInstance("DES/CBC/PKCS5Padding");
des.init(Cipher.ENCRYPT_MODE, key, iv);
}
finally {
Arrays.fill(dk, (byte) 0);
}
byte[] pkg = new byte[des.getOutputSize(msg.length) + MAGIC.length + SALT_LEN];
System.arraycopy(MAGIC, 0, pkg, 0, MAGIC.length);
System.arraycopy(salt, 0, pkg, MAGIC.length, SALT_LEN);
des.doFinal(msg, 0, msg.length, pkg, MAGIC.length + SALT_LEN);
return pkg;
}
static byte[] decrypt(byte[] password, byte[] pkg)
throws GeneralSecurityException
{
if ((pkg.length < MAGIC.length) || !Arrays.equals(Arrays.copyOfRange(pkg, 0, MAGIC.length), MAGIC))
throw new IllegalArgumentException("Expected magic number \"Salted__\"");
if (pkg.length < MAGIC.length + SALT_LEN)
throw new IllegalArgumentException("Missing salt");
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(password); /* password */
md5.update(pkg, MAGIC.length, SALT_LEN); /* salt */
byte[] dk = md5.digest();
Cipher des;
try {
SecretKey secret = new SecretKeySpec(dk, 0, KEY_LEN, "DES");
des = Cipher.getInstance("DES/CBC/PKCS5Padding");
des.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(dk, KEY_LEN, SALT_LEN));
}
finally {
Arrays.fill(dk, (byte) 0);
}
return des.doFinal(pkg, MAGIC.length + SALT_LEN, pkg.length - MAGIC.length - SALT_LEN);
}
public static void main(String... argv)
throws Exception
{
byte[] password = "some key".getBytes(StandardCharsets.UTF_8);
byte[] message = "hello world".getBytes(StandardCharsets.UTF_8);
byte[] encrypted = pretendToEncrypt(password, message);
byte[] recovered = decrypt(password, encrypted);
System.out.println(new String(recovered, StandardCharsets.UTF_8));
}
}
Why "CrapEncryption" and "pretendToEncrypt"? Because the algorithms used here are the worst! DES is not secure. MD5 is not secure. The key derivation function uses only one iteration. This is all garbage. Use AES with PBKDF2 instead.