Hello I have following Problem:
I have an Class/Object Access wich is Serializable.
public class Access implements Serializable {
private static final long serialVersionUID = 1L;
private URL website;
private String username;
private String password;
// + some methods
}
Now when writing them to a File i encrypt them using a Cipher. Looks like this:
WRITING:
ObservableList<Access> userData;
userData = FXCollections.observableArrayList();
...
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key128);
File file = new File("./resources/saves" + username);
file.createNewFile();
CipherOutputStream cipherOut = new CipherOutputStream(
new BufferedOutputStream(new FileOutputStream(file, true)), cipher);
ObjectOutputStream out = new ObjectOutputStream(cipherOut);
userData.forEach((item) -> {
try {
out.writeObject(new SealedObject(item, cipher));
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
});
out.flush();
out.close();
READING:
ObservableList<Access> access = FXCollections.observableArrayList();
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key128);
CipherInputStream cipherIn = new CipherInputStream(
new BufferedInputStream(new FileInputStream("./resources/saves" + username)), cipher);
ObjectInputStream in = new ObjectInputStream(cipherIn);
SealedObject sealed;
while ((sealed = (SealedObject) in.readObject()) != null) {
access.add((Access) sealed.getObject(cipher));
}
If i now Load the File it seems corrupted. I find it hard to find the mistake. I think the Problem is in the load function. Am i missing something obvious here?
Error:
java.io.StreamCorruptedException: invalid stream header: 3D23898C
Thank you for your time & help! :*
Okay, The problem is the interaction between the cipher stream and the object stream.
When you read the file, the ObjectInputStream asks to the underlying stream (the cipher stream) to read a very specific header. But the cipher stream has zero knowledge of that - he reads the normal amount of bytes he is supposed to, according his own protocol, in order to make sense of the encrypted data. The result is that the ObjectInputStream gets a truncated/altered header and can't process the stream correctly.
Good news is, you don't actually need it ! The SealedObject will take care of encryption/decryption for you. Simply remove the cipher stream and it should work.
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file, true));
and to read:
FileInputStream inputStream = new FileInputStream(fileName);
ObjectInputStream ois = new ObjectInputStream(inputStream);
Now, if you really want to use the cipher stream (and thus effectively encrypt/decrypt the data twice with the same key), you need first to make a "first pass" on the file to decrypt it, and then open an object stream on the new decrypted file.
Did you forget to call out.flush() and out.close() ?
If you did, some data may not be actually be written to the disk and it'll be impossible to read it again.
Related
In my project a textfile is chosen and become encrypted. The encrypted text is saved seperatly as well as the key. Now I try to create a program which is decrypting the file when the right keyfile is available. I think the decrypting program needs to look pretty like the encrypting program just in DECRYPT_MODE. When I read in the key I don't know how to do the next step at it to decrypt the textfile. Maybe anyone can help me how I use the key from .txt file and use it to decrypt the encoded file.
The encrypting program:
public class encrypt {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException {
//Key is created and saved in File
KeyGenerator keygenerator = KeyGenerator.getInstance("AES");
SecretKey myDesKey = keygenerator.generateKey();
String encodedKey = Base64.getEncoder().encodeToString(myDesKey.getEncoded());
Path keypath = Paths.get("C:/xxx/key.txt");
Path keyfile = Files.createFile(keypath);
Files.write(keyfile, encodedKey.getBytes(), StandardOpenOption.WRITE);
Cipher desalgCipher;
desalgCipher = Cipher.getInstance("AES");
desalgCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
Path target = Paths.get("C:/xxx/encrypted.txt");
Path file = Files.createFile(target);
Path path = Paths.get("test.txt");
try(InputStream is = Files.newInputStream(path);
CipherInputStream cipherIS = new CipherInputStream(is, desalgCipher);
BufferedReader reader = new BufferedReader(new InputStreamReader(cipherIS));){
String line;
while((line = reader.readLine()) != null){
System.out.println(line);
Files.write(file, line.getBytes(), StandardOpenOption.WRITE);
}
}
}
}
Decrypt: read in the key and decrypt it
public class decrypt {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException {
try {
File fileDir = new File("C:/Users/JT/Desktop/key.txt");
BufferedReader in = new BufferedReader(
new InputStreamReader(new FileInputStream(fileDir), "UTF-8"));
String str;
while ((str = in.readLine()) != null) {
System.out.println(str);
}
in.close();
}
catch (UnsupportedEncodingException e)
{
System.out.println(e.getMessage());
}
catch (IOException e)
{
System.out.println(e.getMessage());
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
byte[] decodedKey = Base64.getDecoder().decode(sb.toString());
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
SecretKeySpec key = new SecretKeySpec(sb.toString().getBytes(), "Base64");
Cipher desalgCipher;
desalgCipher = Cipher.getInstance("AES");
desalgCipher.init(Cipher.DECRYPT_MODE, key);
Path path = Paths.get("encrypted.txt"); // path to your file
try(InputStream is = Files.newInputStream(path); // get an IS on your file
CipherInputStream cipherIS = new CipherInputStream(is, desalgCipher); // wraps stream using cipher
BufferedReader reader = new BufferedReader(new InputStreamReader(cipherIS));){ // init reader.
String line;
while((line = reader.readLine()) != null){
System.out.println(line);
}
}
}
}
Your application is not being programmed the right way. Currently you try to encrypt by wrapping the input stream with a CipherInputStream instance. Then this instance again is wrapped with a BufferedReader instance.
So what you are doing is to first convert the bytes of the input file - probably text - into ciphertext. This ciphertext can contain any byte value. Then you try to read those bytes in line-by-line using the default character set and line endings. Obviously after encryption even the notion of lines doesn't exist anymore, so you'll loose data in that final step.
Then you convert back to bytes, which you then (somehow) try to decrypt. This will obviously fail as you lost data during the readLine statement.
What you should do is to read in the file using bytes. You can then write to a CipherOutputStream. If the file with the ciphertext needs to be actual text you can use a Base64 stream which the new java.util.Base64 nicely provides.
Only once you programmed the encryption correctly you can try and reverse the process. As long as data is lost obviously the decryption will fail (with an error or garbage output, depending on the mode and your luck).
If you're unlucky you will end up with code that works 99% of the time. So good luck and heed the comments: don't try and perform encryption without understanding what you're doing. It will end with tears - or a smashed keyboard.
In my java program I would like to read a .txt file in and encode it afterwards. I know how to read a File in and tried to learn how to encode an array. The problem I have is that I don't know how to combine it, it doesn't work the way I tried it.
Here's the part I can read in my text file with:
public class ReadFile {
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("test.txt");
BufferedReader br = new BufferedReader(fr);
String zeile = "";
do
{
zeile = br.readLine();
System.out.println(zeile);
}
while (zeile != null);
br.close();
}
}
In this part I can encrypt and decrypt bytes:
public class Crypt {
public static void main(String[] args) {
try{
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
SecretKey myDesKey = keygenerator.generateKey();
Cipher desalgCipher;
desalgCipher = Cipher.getInstance("DES");
byte[] text = "test".getBytes("UTF8");
desalgCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desalgCipher.doFinal(text);
String s = new String(textEncrypted);
System.out.println(s);
desalgCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textDecrypted = desalgCipher.doFinal(textEncrypted);
s = new String(textDecrypted);
System.out.println(s);
}
catch(Exception e)
{
System.out.println("Error");
}
}
}
I thought to read the text file in and put it in a string to encode it, but I think it is way too complex. Is there another way to connect them, or is another way for encoding required?
I strongly advise you to use Streams ( see https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html & https://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html) rather than directly using a FileReader.
Encryption happens at a lower level (on bytes) than what you're trying to do.
Java ciphers offer the convenient CipherInputStream (and CipherOutputStream ) to encrypt byte streams on the fly. It's much cheaper and more scalable than trying to dump a whole file in a single byte[] (moreso because you're decoding and re-encoding the file content).
If you want an example of use, please look at the following snippet :
public static void encrypt(Path inputFile, OutputStream output) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException {
// init cipher
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
SecretKey myDesKey = keygenerator.generateKey();
Cipher desalgCipher;
desalgCipher = Cipher.getInstance("DES");
desalgCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
try(InputStream is = Files.newInputStream(inputFile); // get an IS on your file
CipherInputStream cipherIS = new CipherInputStream(is, desalgCipher)){ // wraps input Stream with cipher
copyStreams(cipherIS, output); // copyStream is let to the implementer's choice.
}
}
And I'll let you figure out how to decrypt.
EDIT :
A common way to communicate encrypted bytes without fear for encoding issues is to encode the raw bytes with base 64.
You can wrap the outputStream with Base64.getEncoder().wrap(os)
FileReader/FileWriter are the wrong (old utility) classes, as they use the current platform encoding, and a file encrypted on one computer (Greek Windows) would not be decryptable on another computer (Linux server).
Text in java, String, is in Unicode. One cannot (should not) throw arbitrary bytes into a String.
So the following cannot be done
new String(textEncrypted); // Uses the default platform encoding
new String(textEncrypted, "UTF-8"); // Probably the bytes are not valid UTF-8
So do:
Path path = Paths.get("text.txt");
byte[] content = Files.readAllBytes(path);
content = encrypt(content);
Files.write(path, content);
I am wanting to create a functional Java chat application.
So I have a small application which allows users to connect via server classes and talk with each other via client classes and I have started to add Encryption. I am having trouble decrypting output from other clients in my Java chat application.
can someone help me please?
snippet of my code is included below:
THE CLIENTGUI.JAVA CLASS (encrypt is a button which is clicked)
if(o == encrypt) {
String change = null;
try{
change = tf.getText();
change = FileEncryption.encryptString(change);
tf.setText("" + change);
return;
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
finally{
}
THE FILEENCRYPTION.JAVA
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();
// in encrypt method
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 = DatatypeConverter.parseHexBinary(src);;
//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();
dst = new String(baos.toByteArray());
return dst;
}
}
the problem is that when i try to decrypt the code entering the following code:
if(o == decrypt) {
try{
msg = tf.getText();
msg = FileEncryption.decryptString(msg);
fop.
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}finally{
}
Currently, it ALLOWS me to encrypt what I type into text field.
It does not allow me to decrypt the output of what the users have said in the chat. The current code I have included for the decrypt does not function.
Can anyone help me? or have any suggestions that I could make to my program to help it decrypt?
Thanks
EDIT:
Your best bet would probably be to simply use SSL sockets for your network communications, rather than writing the encryption code yourself. While your question isn't exactly a duplicate of this one, you'd likely be well served by the answers here:
Secret Key SSL Socket connections in Java
I suspect that the problem is not passing the encrypted status between the 2 clients.
If the "encrypt" object is a button then it is a button on only one side of the client-client connection. You will need to pass the encrypted state to the other client, so that it knows to decrypt the message.
A short cut to confirming this would be to automatically show the plaintext and decrypted message on the receiving end. One of them will always be gibberish but it should change depending on the use of the encrypt button.
Good luck :)
My program sends a string encrypted (AES) with received session key to Client to prove the key is correct.
Client should decrypt it, get the string and verify it with original one.
Program works fine. It encrypts and decrypts the string. It prints the string I need, but gives me false when I do String.equals(string).
I can figure out why.
There is the encryption part of my code:
// ----create a challenge for Client (to check if the session key is correct)--------
public void sessionKeyVer(String challenge, File out) throws Exception{
aesCipher.init(Cipher.ENCRYPT_MODE, aeskeySpec); // switching mode for encryption
CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), aesCipher); //output stream to another file
os.write(challenge.getBytes("UTF-8"));// function to copy String to outputstream
os.close(); //close the stream
}
There is the decryption part:
public boolean sessionKeyVer(File file) throws Exception{
aesCipher.init(Cipher.DECRYPT_MODE, aeskeySpec); // switching mode for decryption
CipherInputStream is = new CipherInputStream(new FileInputStream(file), aesCipher); //output stream to another file
ByteArrayOutputStream os = new ByteArrayOutputStream();
int i;
byte[] b = new byte[1024];
while((i=is.read(b))!=-1) {
os.write(b, 0, i);
}
is.close();
os.close();
String file_string = new String(b,"UTF-8");
System.out.print(file_string);
return file_string.equals(challenge); //return false
}
Thank you.
The first part is the encryption part. The second part is the decryption part.
The second part is wrong. You are decrypting the last part of the still-encrypted buffer, rather than the entire, decrypted ByteArrayOutputStream, and committing a size error in the process too.
String file_string = new String(b,"UTF-8");
should be
String file_string = new String(os.toByteArray(), "UTF-8");
I am using some java code that encrypts the contents of a text file using Blowfish. When I convert the encrypted file back (i.e. decrypt it) the string is missing a character from the end. Any ideas why? I am very new to Java and have been fiddling with this for hours with no luck.
The file war_and_peace.txt just contains the string "This is some text". decrypted.txt contains "This is some tex" (with no t on the end). Here is the java code:
public static void encrypt(String key, InputStream is, OutputStream os) throws Throwable {
encryptOrDecrypt(key, Cipher.ENCRYPT_MODE, is, os);
}
public static void decrypt(String key, InputStream is, OutputStream os) throws Throwable {
encryptOrDecrypt(key, Cipher.DECRYPT_MODE, is, os);
}
private static byte[] getBytes(String toGet)
{
try
{
byte[] retVal = new byte[toGet.length()];
for (int i = 0; i < toGet.length(); i++)
{
char anychar = toGet.charAt(i);
retVal[i] = (byte)anychar;
}
return retVal;
}catch(Exception e)
{
String errorMsg = "ERROR: getBytes :" + e;
return null;
}
}
public static void encryptOrDecrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {
String iv = "12345678";
byte[] IVBytes = getBytes(iv);
IvParameterSpec IV = new IvParameterSpec(IVBytes);
byte[] KeyData = key.getBytes();
SecretKeySpec blowKey = new SecretKeySpec(KeyData, "Blowfish");
//Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding");
if (mode == Cipher.ENCRYPT_MODE) {
cipher.init(Cipher.ENCRYPT_MODE, blowKey, IV);
CipherInputStream cis = new CipherInputStream(is, cipher);
doCopy(cis, os);
} else if (mode == Cipher.DECRYPT_MODE) {
cipher.init(Cipher.DECRYPT_MODE, blowKey, IV);
CipherOutputStream cos = new CipherOutputStream(os, cipher);
doCopy(is, cos);
}
}
public static void doCopy(InputStream is, OutputStream os) throws IOException {
byte[] bytes = new byte[4096];
//byte[] bytes = new byte[64];
int numBytes;
while ((numBytes = is.read(bytes)) != -1) {
os.write(bytes, 0, numBytes);
}
os.flush();
os.close();
is.close();
}
public static void main(String[] args) {
//Encrypt the reports
try {
String key = "squirrel123";
FileInputStream fis = new FileInputStream("war_and_peace.txt");
FileOutputStream fos = new FileOutputStream("encrypted.txt");
encrypt(key, fis, fos);
FileInputStream fis2 = new FileInputStream("encrypted.txt");
FileOutputStream fos2 = new FileOutputStream("decrypted.txt");
decrypt(key, fis2, fos2);
} catch (Throwable e) {
e.printStackTrace();
}
}
`
There is a couple of things not optimal here.
But let's first solve your problem. The reason why the last portion of your input is somehow missing is the padding you specify: none! Without specifying a padding, the Cipher can just operate on full-length blocks (8 bytes for Blowfish). Excess input that is less than a block long will be silently discarded, and there's your missing text. In detail: "This is some text" is 17 bytes long, so two full blocks will be decrypted, and the final 17th byte, "t", will be discarded.
Always use a padding in combination with symmetric block ciphers, PKCS5Padding is fine.
Next, when operating with Cipher, you don't need to implement your own getBytes() - there's String#getBytes already doing the job for you. Just be sure to operate on the same character encoding when getting the bytes and when reconstructing a String from bytes later on, it's a common source of errors.
You should have a look at the JCE docs, they will help you avoiding some of the common mistakes.
For example, using String keys directly is a no-go for symmetric cryptography, they do not contain enough entropy, which would make it easier to brute-force such a key. The JCE gives you theKeyGenerator class and you should always use it unless you know exactly what you are doing. It generates a securely random key of the appropriate size for you, but in addition, and that is something people tend to forget, it will also ensure that it doesn't create a weak key. For example, there are known weak keys for Blowfish that should be avoided in practical use.
Finally, you shouldn't use a deterministic IV when doing CBC encryption. There are some recent attacks that make it possible to exploit this, resulting in total recovery of the message, and that's obviously not cool. The IV should always be chosen at random (using a SecureRandom) in order to make it unpredictable. Cipher does this for you by default, you can simply obtain the used IV after encryption with Cipher#getIV.
On another note, less security-relevant: you should close streams in a finally block to ensure they're closed at all cost - otherwise you will be left with an open file handle in case of an exception.
Here's an updated version of your code that takes all these aspects into account (had to use Strings instead of files in main, but you can simply replace it with what you had there):
private static final String ALGORITHM = "Blowfish/CBC/PKCS5Padding";
/* now returns the IV that was used */
private static byte[] encrypt(SecretKey key,
InputStream is,
OutputStream os) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
CipherInputStream cis = new CipherInputStream(is, cipher);
doCopy(cis, os);
return cipher.getIV();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static void decrypt(SecretKey key,
byte[] iv,
InputStream is,
OutputStream os)
{
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
CipherInputStream cis = new CipherInputStream(is, cipher);
doCopy(cis, os);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static void doCopy(InputStream is, OutputStream os)
throws IOException {
try {
byte[] bytes = new byte[4096];
int numBytes;
while ((numBytes = is.read(bytes)) != -1) {
os.write(bytes, 0, numBytes);
}
} finally {
is.close();
os.close();
}
}
public static void main(String[] args) {
try {
String plain = "I am very secret. Help!";
KeyGenerator keyGen = KeyGenerator.getInstance("Blowfish");
SecretKey key = keyGen.generateKey();
byte[] iv;
InputStream in = new ByteArrayInputStream(plain.getBytes("UTF-8"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
iv = encrypt(key, in, out);
in = new ByteArrayInputStream(out.toByteArray());
out = new ByteArrayOutputStream();
decrypt(key, iv, in, out);
String result = new String(out.toByteArray(), "UTF-8");
System.out.println(result);
System.out.println(plain.equals(result)); // => true
} catch (Exception e) {
e.printStackTrace();
}
}
You have your CipherInputStream and CipherOutputStream mixed up. To encrypt, you read from a plain inputstream and write to a CipherOutputStream. To decrypt ... you get the idea.
EDIT:
What is happening is that you have specified NOPADDING and you are attempting to encrypt using a CipherInputStream. The first 16 bytes form two valid complete blocks and so are encrypted correctly. Then there is only 1 byte left over, and when the CipherInputStream class receives the end-of-file indication it performs a Cipher.doFinal() on the cipher object and receives an IllegalBlockSizeException. This exception is swallowed, and read returns -1 indicating end-of-file. If however you use PKCS5PADDING everything should work.
EDIT 2:
emboss is correct in that the real issue is simply that it is tricky and error-prone to use the CipherStream classes with the NOPADDING option. In fact, these classes explicitly state that they silently swallow every Security exception thrown by the underlying Cipher instance, so they are perhaps not a good choice for beginners.
Keys are binary, and String is not a container for binary data. Use a byte[].
When I had this problem I had to call doFinal on the cipher:
http://docs.oracle.com/javase/1.4.2/docs/api/javax/crypto/Cipher.html#doFinal()