Getting different results while computing md5 for Serializable object vs string - java

I want to compute md5 of any Serilizable object, which is done by following function.
public static String getMd5Hash(Serializable object) {
try {
return getChecksum(object, "MD5");
} catch (Exception e) {
throw new RmsException("Exception while generating md5 hash", e);
}
}
public static String getMd5Hash(Serializable object) {
try {
return getChecksum(object, "MD5");
} catch (Exception e) {
throw new RuntimeException("Exception while generating md5 hash", e);
}
}
private static String getChecksum(Serializable object, String algorithm)
throws IOException, NoSuchAlgorithmException {
try (
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)
) {
oos.writeObject(object);
MessageDigest md = MessageDigest.getInstance(algorithm);
byte[] theDigest = md.digest(baos.toByteArray());
return DatatypeConverter.printHexBinary(theDigest);
}
}
Test
#Test
public void getMd5Hash() {
String actual = CryptoUtils.getMd5Hash("water");
Assert.assertEquals("9460370bb0ca1c98a779b1bcc6861c2c", actual);
}
OP
Expected :9460370bb0ca1c98a779b1bcc6861c2c (actual md5 for string water)
Actual :37F7DBD142DABF05ACAA6759C4D9E96C (Why the diff?)

The ObjectOutputStream adds a header, so the Serializable you are passing does not actually represent "water" when you get the byte array. Print out baos.toString() to verify. You can either extend ObjectOutputStream and override the writeStreamHeader method, or call md.digest with the substring of the data, ie md.digest(baos.substring(7).getBytes()) (or something thereabouts). Once the actual data digested is "water", the hash will be correct.

As Terje says,
ObjectOutPutStream adds a header, you can verify that using
public static String getChecksum(Serializable object, String algorithm)
throws IOException, NoSuchAlgorithmException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(object);
oos.flush();
MessageDigest md = MessageDigest.getInstance(algorithm);
md.update(object.toString().getBytes());
byte[] theDigest = md.digest();
System.out.println("Without Object output stream="+DatatypeConverter.printHexBinary(theDigest));
md.reset();
System.out.println("object="+object+ " Written to ByteArray is="+baos.toString());
md.update(baos.toByteArray());
theDigest = md.digest();
return DatatypeConverter.printHexBinary(theDigest);
}
}
public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
System.out.println(getChecksum("water", "MD5"));
}
Instead of creating all ByteArrayOutputStream and ObjectOutputStream, you can simple use
md.update(object.toString().getBytes());
To write byte array to MessageDigest.
Thanks

Related

ObjectInputStream header problems [duplicate]

I am trying to convert a ArrayList object to a byte string so it can be sent via sockets. When I run this code it converts to a string properly but when I try to convert it back I get the exception "java.io.StreamCorruptedException: invalid stream header: EFBFBDEF". Other answers I looked at on here didn't really help as I am using the matching ObjectOutputStream and ObjectInputStream. Sorry if there is a simple fix as I am new to working with stream objects.
try {
ArrayList<String> text = new ArrayList<>();
text.add("Hello World!");
String byteString = Utils.StringUtils.convertToByteString(text);
ArrayList<String> convertedSet = (ArrayList<String>) Utils.StringUtils.convertFromByteString(byteString);
VCS.getServiceManager().addConsoleLog(convertedSet.get(0));
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
public static String convertToByteString(Object object) throws IOException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos)) {
out.writeObject(object);
final byte[] byteArray = bos.toByteArray();
return new String(byteArray);
}
}
public static Object convertFromByteString(String byteString) throws IOException, ClassNotFoundException {
final byte[] bytes = byteString.getBytes();
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInput in = new ObjectInputStream(bis)) {
return in.readObject();
}
}
I figured it out. I had to use Base64 encoding. The conversion methods have to be changed to the following:
public static String convertToByteString(Object object) throws IOException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos)) {
out.writeObject(object);
final byte[] byteArray = bos.toByteArray();
return Base64.getEncoder().encodeToString(byteArray);
}
}
public static Object convertFromByteString(String byteString) throws IOException, ClassNotFoundException {
final byte[] bytes = Base64.getDecoder().decode(byteString);
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInput in = new ObjectInputStream(bis)) {
return in.readObject();
}
}
String is not a container for binary data. You need to pass around the original byte array, or hex- or base64-encode it.
Better still, serialize directly to the socket and get rid of this altogether.

Checksum generation with the same algorithm returns different output

I have two programs, with the same identical checksum generation method(SHA-256).
The difference is, that in the first program the path is (windowspath/folder/file), in the second one the path is (./folder/file).
Here is the code:
Program 1
String address = fileAddr.getText();
File file = new File(address);
try
{
checksum = getChecksum(file.getAbsolutePath());
data = generateByte(address);
}
catch (NoSuchAlgorithmException | IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
Checksum Generation Method
public static String getChecksum(Serializable object) throws IOException, NoSuchAlgorithmException
{
ByteArrayOutputStream baos = null;
ObjectOutputStream oos = null;
System.out.println(object.toString());
try
{
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] thedigest = md.digest(baos.toByteArray());
return DatatypeConverter.printHexBinary(thedigest);
}
finally
{
oos.close();
baos.close();
}
}
Program 2
File folder = new File(".\\Plugins");
File[] listOfFiles = folder.listFiles();
listChecksum.add(getChecksum(listOfFiles[i].getPath()));
The problem is as described in the title, the two checksums don't correspond.
I think that you are not reading the content of the file, just comparing the file names. And these differ for sure, so you get different checksum values.
I would suggest to read the content of the file in the function which generates the checksum:
public class Main {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
Path x = Paths.get("D:\\work\\some.zip");
System.out.println(getChecksum(x));
}
public static String getChecksum(Path path) throws IOException, NoSuchAlgorithmException {
byte[] bytes = Files.readAllBytes(path);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] thedigest = md.digest(bytes);
return Base64.getEncoder().encodeToString(thedigest);
}
}

How to (de)serialize correctly to a byte-array using a ObjectOutputStream & ObjectInputStream?

I am aware that an ObjectOutputStream/ObjectInputStream uses headers and this is not really a proper use-case. But anyway I need to wrap some data into it using the interfaces DataInput and DataOutput.
import java.util.*;
import java.lang.*;
import java.io.*;
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
byte[] array = serialize("test");
String deserialized = deserialize(array);
System.out.println(deserialized);
}
private static byte[] serialize(String test) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeUTF(test);
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return byteArrayOutputStream.toByteArray();
}
private static String deserialize(byte[] array) {
String temp = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(array));
temp = objectInputStream.readUTF();
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return temp;
}
}
I don't really get how to get that working. Am I right, that the problem are those headers currently?
You should call objectOutputStream.flush(); before closing byteArrayOutputStream.
ObjectOutputStream have its internal buffer so you got only beginning of string in your byte array.

encrypt and serialize by one --- deserialize and decrypt by another

I have serializable object:
import java.io.Serializable;
public class ConfigObject implements Serializable{
private String url;
private String user;
private String pass;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
}
and 2 method in SerializableEncryptDecrypt class:
public static void encrypt(Serializable object, OutputStream ostream, byte[] keyy, String transformationnn) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
try {
// Length is 16 byte
SecretKeySpec sks = new SecretKeySpec(keyy, transformationnn);
// Create cipher
Cipher cipher = Cipher.getInstance(transformationnn);
cipher.init(Cipher.ENCRYPT_MODE, sks);
SealedObject sealedObject = new SealedObject(object, cipher);
// Wrap the output stream
CipherOutputStream cos = new CipherOutputStream(ostream, cipher);
ObjectOutputStream outputStream = new ObjectOutputStream(cos);
outputStream.writeObject(sealedObject);
outputStream.close();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
public static Object decrypt(InputStream istream, byte[] keyy, String transformationnn) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
SecretKeySpec sks = new SecretKeySpec(keyy, transformationnn);
Cipher cipher = Cipher.getInstance(transformationnn);
cipher.init(Cipher.DECRYPT_MODE, sks);
CipherInputStream cipherInputStream = new CipherInputStream(istream, cipher);
ObjectInputStream inputStream = new ObjectInputStream(cipherInputStream);
SealedObject sealedObject;
try {
sealedObject = (SealedObject) inputStream.readObject();
return sealedObject.getObject(cipher);
} catch (ClassNotFoundException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
return null;
}
}
I had made 2 softwares(soft1 and soft2) which use this class (SerializableEncryptDecrypt). That softwares encrypt and serialize input data(the same input data). when I compare the output data with the input which I give is totally different data. But I need the same output data.
thanks in advance for your help.
It is a good practice to randomize encryption using some salt (nonce, IV, ...) so even if you encrypt with the same values with the same key, you may (and should) get different output. Having the same output lowers the security in some cases.
I cannot be sure, but I'd bet that's what the "SealedObject" does. If you really need "the same output", you could serialize the object directly (not using the SealedObject). But then - you are responsible for storing the salt, authentication tag, etc..
Please note - if you're using this code in some real project, you should not store the passwords (even encrypted), only their salted cryptographic hashes if neccessary.

Transfer Multiple File problems via socket

Server side:
public class Server
{
public static final String ALGORITHM = "RSA";
public static final String PRIVATE_KEY_FILE = "C:/Users/mrarsenal10/Desktop/server/key/private.key";
public static final String PUBLIC_KEY_FILE = "C:/Users/mrarsenal10/Desktop/server/key/public.key";
public static void generateKey()
{
try
{
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(1024);
final KeyPair key = keyGen.generateKeyPair();
File privateKeyFile = new File(PRIVATE_KEY_FILE);
File publicKeyFile = new File(PUBLIC_KEY_FILE);
// Create files to store public and private key
if (privateKeyFile.getParentFile() != null) {
privateKeyFile.getParentFile().mkdirs();
}
privateKeyFile.createNewFile();
if (publicKeyFile.getParentFile() != null) {
publicKeyFile.getParentFile().mkdirs();
}
publicKeyFile.createNewFile();
// Saving the Public key in a file
ObjectOutputStream publicKeyOS = new ObjectOutputStream(
new FileOutputStream(publicKeyFile));
publicKeyOS.writeObject(key.getPublic());
publicKeyOS.close();
// Saving the Private key in a file
ObjectOutputStream privateKeyOS = new ObjectOutputStream(
new FileOutputStream(privateKeyFile));
privateKeyOS.writeObject(key.getPrivate());
privateKeyOS.close();
}catch (Exception e)
{
e.printStackTrace();
}
}
public static boolean areKeysPresent()
{
File privateKey = new File(PRIVATE_KEY_FILE);
File publicKey = new File(PUBLIC_KEY_FILE);
if (privateKey.exists() && publicKey.exists())
{
return true;
}
return false;
}
public static String decrypt(byte[] text, PrivateKey key) { // giải mã
byte[] dectyptedText = null;
try {
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance(ALGORITHM);
// decrypt the text using the private key
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
} catch (Exception ex) {
ex.printStackTrace();
}
return new String(dectyptedText);
}
static void sendFile(Socket sock, String fName) throws FileNotFoundException, IOException
{
File transferFile = new File (fName);
byte [] bytearray = new byte [(int)transferFile.length()];
FileInputStream fin = new FileInputStream(transferFile);
BufferedInputStream bin = new BufferedInputStream(fin);
bin.read(bytearray,0,bytearray.length); // luu vao bytearray
OutputStream os = sock.getOutputStream(); // goi outputstream de
System.out.println("Sending Files...");
os.write(bytearray,0,bytearray.length);
os.flush();
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
if (!areKeysPresent()) {
generateKey();
}
ServerSocket serverSocket = new ServerSocket(15124);
Socket sock = serverSocket.accept();
sendFile(sock, PUBLIC_KEY_FILE);
sendFile(sock, "lich.txt");
sock.close();
}
}
Client side:
public class Client
{
public static final String ALGORITHM = "RSA";
public static final String PUBLIC_KEY_FILE = "C:/Users/mrarsenal10/Desktop/Client/public.key";
static void recvFile(Socket sock, String fName) throws FileNotFoundException, IOException
{
int filesize=1022386;
int bytesRead;
int currentTot = 0;
byte [] bytearray = new byte [filesize];
InputStream is = sock.getInputStream();
FileOutputStream fos = new FileOutputStream(fName);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bytesRead = is.read(bytearray,0,bytearray.length);
currentTot = bytesRead;
do {
bytesRead = is.read(bytearray, currentTot, (bytearray.length-currentTot));
if(bytesRead >= 0) currentTot += bytesRead; }
while(bytesRead > -1);
bos.write(bytearray, 0 , currentTot);
bos.flush();
bos.close();
}
public static byte[] encrypt(String text, PublicKey key) {
byte[] cipherText = null;
try {
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance(ALGORITHM);
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, key);
cipherText = cipher.doFinal(text.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return cipherText;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Socket sock = new Socket("127.0.0.1",15124);
recvFile(sock, "public.key");
recvFile(sock, "lich.txt");
sock.close();
}
}
My problem is here i just can send "public.key" or "lich.txt" from server to client, now i want to send both "public.key" and "lich.txt". Thank for your help.
The problem is with the overall design of both the Server and the Client. On the server side it is sending two different files, but on the Client side it is just a stream of data. There is no distinction between one byte representing data from one file vs the next. So what is probably happening is you are calling recvFile, which receives ALL the data from BOTH files sent by the server. After sending the data, the server closes the connection. (You do this explicitly.) So now, on the client side, you have an invalid socket. However, you try to call recvFile again with the socket thinking that represents the second file. This will lead to the SocketException or more likely OutOfBoundsException you are seeing.
To fix this, you need to add more hand-shaking between the Server and Client. The simplest would be a delimiter representing the end of a file. A better approach would be to append a known-size header to the front of every "message" (aka file) before sending any data which lets the client know the size of the file. Then once the client receives the header it knows exactly how many bytes to read.
For now, to prevent the crash change you're recvFile method to something like this:
byte[] bytearray = new byte[4096];
InputStream is = sock.getInputStream();
FileOutputStream fos = new FileOutputStream(fName);
int bytesRead;
while ((bytesRead = is.read(bytearray)) >= 0) {
if (bytesRead > 0) {
fos.write(bytearray, 0, bytesRead);
}
}
fos.flush();
fos.close();

Categories

Resources