How to properly validate PNG header in java? - java

I'm trying to write a function which will check if PNG file is not corrupted. Here I found a function, also provided below, which stores file bytes into its own byte array. I know that the first eight bytes of a PNG file always contain the same decimal values 137 80 78 71 13 10 26 10 (hex: 89 50 4e 47 0d 0a 1a 0a).
When I print the context of the byte array which starts with -1-40-1-3201674707370011007207200-1-370-124022222232235333565555686666681088888810101010101 ... and then convert it to decimal values I don't see the magic number in the beginning. Please, what have I misunderstood? I would like to read the image and compare its header to either decimal or hex values.
public static void main(String[] args)
{
File file = new File("src/resources/dog.png");
readContentIntoByteArray(file);
}
private static byte[] readContentIntoByteArray(File file)
{
FileInputStream fileInputStream = null;
byte[] bFile = new byte[(int) file.length()];
try
{
//convert file into array of bytes
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
for (int i = 0; i < bFile.length; i++)
{
System.out.print((char) bFile[i]);
}
}
catch (Exception e)
{
e.printStackTrace();
}
return bFile;
}

You are printing the actual bytes as characters to the terminal, not the representation of those bytes in decimal or hex, as #Andreas says.
You can check the header with:
byte[] data = readContentIntoByteArray(file);
byte[] expected = new byte[] {-119, 80, 78, 71, 13, 10, 26, 10};
for (int i = 0; i < expected.length; i++) {
if (expected[i] != data[i]) {
System.out.println("mismatch at " + i);
}
}

Related

File management and sizes

I have a task in Java generated from an exercise randomizer application and it's quite a pain. The instructions are:
Write a class named Stearyl with a public identifier which must
contain two public, static methods named mAmatorial and mSamanid. The
first method mAmatorial should accept as an argument string (String).
The string will contain the name of the file to be created. Caution:
you have to set the path of the location of the file This file should
have the following characteristics:
Size: 1077 bytes
List of allowed bytes: byte [] aRetooth = {97, 98, 99, 100, 101, 102,
103, 104, 105}. The list of allowed bytes should be set as static
public feature of class Stearyl named aRetooth. File data must be
random and be Evenly divisible. (All the bytes have about the same
number of appearances) .
The method mAmatorial should return a variable type long containing
the sum of the first 500 bytes. The second method mSamanid must accept
as argument one String (String) which will contain the name of a file.
The file is read into memory ( set the path as above) and for this the
following metrics will be recorded :
Size
Number of occurrences of every byte included the table defined in
aRetooth table. The data should be returned as an array of integers
(int) from mSamanid. In position 1 of this table (eg a [0]) will be
the size of the file and in the remaining there will be the number of
occurrences for each byte (eg in position 2 the number of occurrences
of the first byte and so on ) .
When I have it to check task it says:
java.lang.Exception: File was not made correctly in method mAmatorial (wrong size, it should be 1077 bytes)
Do you know where the error is? This is an excercise from a java excercise randomizer. That means the generator has the main and u post the methods so it can check your answers. Maybe u can use sth like this:
public static void main(String args[]){
Stearyl sl=new Stearyl();
long a=sl.mAmatorial("C:\Users\***\Desktop\java_programming_2\exercises\temp.txt");
System.out.println(a);
int f[]=sl.mSamanid("C:\Users\***\Desktop\java_programming_2\exercises\temp.txt");
for (int i=0;i<f.length;i++){
System.out.println(f[i]);
}
}
My code is:
import java.util.Random;
import java.io.*;
public class Stearyl{
public static int fileSize=1077;
public static byte[] aRetooth = {97, 98, 99, 100, 101, 102, 103, 104, 105};
public static long mAmatorial(String filename){
Random random=new Random();
try {
File file1=new File(filename);
boolean success = file1.createNewFile();
if (!success){
file1.delete();
file1.createNewFile();
}
} catch (IOException e) {
}
BufferedOutputStream out = null;
long sumFirst500=0;
try {
out = new BufferedOutputStream(new FileOutputStream(filename));
for (int i=0;i<fileSize;i++){
byte a=aRetooth[random.nextInt(aRetooth.length)];
if (i<500) {
sumFirst500+=a;
}
out.write(a);
}
out.close();
} catch (IOException e) {
}
return sumFirst500;
}
public static int[]mSamanid(String filename){
int[]pinakas=new int[aRetooth.length+1];
int length=0;
byte[] bytes=null;
try {
File file1=new File(filename);
InputStream is = new FileInputStream(file1);
length = (int) file1.length();
bytes = new byte[(int)length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
offset += numRead;
}
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file "+file1.getName());
}
is.close();
} catch (IOException e) {
}
pinakas[0]=length;
for (int j=0;j<aRetooth.length;j++){
pinakas[j+1]=0;
}
for (int i=0;i<bytes.length;i++){
for (int j=0;j<aRetooth.length;j++){
if (bytes[i]==aRetooth[j]) pinakas[j+1]++;
}
}
return pinakas;
}
}

Reading data from binary file correctly in Java

I have problem with reading understood data, when I used Parsing (char) the numbers became strange char in ASCII, here is the code :
static void doTestByteFiles() throws IOException {
File file = new File("sample1.data");
FileOutputStream outStream = new FileOutputStream(file); //Warning!!!!
byte[] outByteArray = {10,20,30,40,50,60,70,80,(byte)'J',(byte)'a',(byte)'v',(byte)'a'};
outStream.write(outByteArray);
outStream.close();
FileInputStream inStream = new FileInputStream(file);
int fileSize = (int) file.length();
byte[] inByteArray = new byte[fileSize];
inStream.read(inByteArray);
for (int i = 0; i < fileSize; i++) {
System.out.println((char) inByteArray[i]);
}
inStream.close();
}
the result:
(
2
<
F
P
J
a
v
a
result I expect :
10
20
30
40
50
60
70
80
J
a
v
a
I tried to use (byte) instead (char), same problem but the Java word became numbers in ASCII , Any help please ?
The numbers in the byte[] array are interpreted as byte values. You have to write Intergers and convert them to byte inside the array.

Convert hexadecimal file into unsigned int 32 Big-Endian in Java

It might be a question already asked, but I have not found a satisfactory answer yet out there. In particular because this conversion has always been done in c or C++.
Btw, how do you convert an hexadecimal file (200MB) into its UINT32 Big-endian representation in Java?
This an example of what I am trying to achieve:
54 00 00 00 -> 84
55 F1 2E 04 -> 70185301
A2 3F 32 01 -> 20070306
and so on
EDIT
File fileInputString = new File(inputFileField.getText());
FileInputStream fin = new FileInputStream(fileInputString);
FileOutputStream out = new FileOutputStream(fileDirectoryFolder.getText() +"/"+ fileInputString.getName());
byte[] fileContent = new byte[(int)fileInputString.length()];
fin.read(fileContent);
System.out.println("File Lenght" + fileContent.length);
for(int i = 0; i < fileContent.length; i++){
Byte b = fileContent[i]; // Boxing conversion converts `byte` to `Byte`
int value = b.intValue();
out.write(value);
}
close();
System.out.println("Done");
EDIT 2
File fileInputString = new File(inputFileField.getText());
FileInputStream fin = new FileInputStream(fileInputString);
FileOutputStream out = new FileOutputStream(fileDirectoryFolder.getText() +"/"+ fileInputString.getName());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] fileContent = new byte[(int)fileInputString.length()];
System.out.println("File Lenght" + fileContent.length);
int bytesRead;
while (( bytesRead = fin.read(fileContent)) != -1) {
ByteBuffer.wrap(fileContent).order(ByteOrder.LITTLE_ENDIAN).getLong();
bos.write(fileContent, 0, bytesRead);
}
out.write(bos.toByteArray());
System.out.println("Done");
EDIT 3
DataOutputStream out = new DataOutputStream(new FileOutputStream(output));
DataInputStream in = new DataInputStream(new FileInputStream(input))) {
int count = 0;
while (count < input.length() - 4) {
in.readFully(buffer, 4, 4);
String s=Long.toString(ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getLong());
out.writeBytes( s + " ");
count += 4;
}
Thanks
The following code should hopefully suffice. It uses long values to ensure we can fully represent the range of positive values that four bytes can represent.
Note: this code assumes the hex input is four bytes. You may want to add some more checks and measures in production code.
private static long toLong(String hex) {
hex = hex.replace(" ", "") + "00000000";
byte[] data = DatatypeConverter.parseHexBinary(hex);
return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getLong();
}
public static void main(String[] args) throws Exception {
System.out.println(toLong("54 00 00 00"));
System.out.println(toLong("55 F1 2E 04"));
System.out.println(toLong("A2 3F 32 01"));
System.out.println(toLong("FF FF FF FF"));
}
Output:
84
70185301
20070306
4294967295
Based on your recent edits, I propose some code such as the following. Note that it assumes your input is a multiple of four bytes in length. Any left-over bytes are ignored:
File input = new File("whatever");
byte[] buffer = new byte[8];
List<Long> result = new ArrayList<>();
try (DataInputStream in = new DataInputStream(new FileInputStream(input))) {
int count = 0;
// Note: any trailing bytes are ignored
while (count < input.length() - 4) {
in.readFully(buffer, 4, 4);
result.add(ByteBuffer.wrap(buffer)
.order(ByteOrder.LITTLE_ENDIAN).getLong());
count += 4;
}
}
You need to switch the byte order within the 4 bytes that form an int. The conversion is symetric, so when the input is little endian, output becomes big endian and vice versa.
Big Endian: 12 34 56 78
Little Endian: 78 56 34 12
So if you were doing that while processing an InputStream, read four bytes, and write them to output in reverse order.

I'm generating a SHA256 hash of few files and some of them return 63 characters instead of 64. How can it be possible?

I need the hash function always to return 64 char hex but sometimes, depending on a file, it returns 63 and that's a problem for me. Due to business reasons I need always 64 chars. And that happens completely random with Any kind and size of file. Does anyone know why it happens? Follow my code:
public static String geraHash(File f) throws NoSuchAlgorithmException, FileNotFoundException
{
MessageDigest digest = MessageDigest.getInstance("SHA-256");
InputStream is = new FileInputStream(f);
byte[] buffer = new byte[8192];
int read = 0;
String output = null;
try
{
while( (read = is.read(buffer)) > 0)
{
digest.update(buffer, 0, read);
}
byte[] md5sum = digest.digest();
BigInteger bigInt = new BigInteger(1,md5sum);
output = bigInt.toString(16);
}
catch(IOException e)
{
throw new RuntimeException("Não foi possivel processar o arquivo.", e);
}
finally
{
try
{
is.close();
}
catch(IOException e)
{
throw new RuntimeException("Não foi possivel fechar o arquivo", e);
}
}
return output;
}
In fact, there is 32 byte. Just first half of first byte is zero. (first byte looks like: 0000 xxxx)
So, when you are converting it to string, it has 63 hex value which is 31.5 bytes, so it is 32 bytes in bytes. This (32 byte) is exactly what it should to be.
You can just write 0 start of string when its length is 63.
if (output.length == 63){
output = "0" + output;
}
or
while (output.length < 64){
output = "0" + output;
}

Translate a byte sequence given an InputStream

In Java, is there a library anywhere that will, given a byte sequence (preferable expressed as hex), translate to another byte sequence given an InputStream? For example:
InputStream input = new FileInputStream(new File(...));
OutputStream output = new FileOutputStream(new File(...));
String fromHex = "C3BEAB";
String toHex = "EAF6"
MyMagicLibrary.translate(fromHex, toHex, input, output)
So if the input file (in hex looked like)
00 00 12 18 33 C3 BE AB 00 23 C3 BE AB 00
after translation, the result would be
00 00 12 18 33 EA F6 00 23 EA F6 00
Once I did something like this (for trivially patching exe-files) using regexes. I read the whole input into a byte[] and converted into String using latin1, then did the substitution and converted back. It wasn't efficient but it didn't matter at all. You don't need regexes, simple String.replace would do.
But in your case it can be done quite simply and very efficiently:
int count = 0;
while (true) {
int n = input.read();
if (n == (fromAsByteArray[count] & 255)) {
++count;
if (count==fromAsByteArray.length) { // match found
output.write(toAsByteArray);
count = 0;
}
} else { // mismatch
output.write(fromAsByteArray, 0, count); // flush matching chars so far
count = 0;
if (n == -1) break;
output.write(n);
}
}
}
If u mean that u want to use a class whch translate from hex and to hex
here's two methods I usualluy use, u can put them inside a class and reuse it any where u want
public static String toHex(byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10) {
strbuf.append("0");
}
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public static byte[] fromHexString(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
Actually I don't understand each line in the code, but I usually reuse them.
Since your input can have spaces then you first need to scrub your input to remove the spaces. After reading a pair of characters just use Byte.parseByte(twoCharString, 16) then use String.format to convert back to a String.
Doing it byte by byte would most likely be VERY inefficient, though easy to test. Once you get the result you want, you can tweak it by reading and parsing a whole buffer and spitting out more than one resulting byte a time, maybe 16 "byte" characters per line for formatting. It is all up to you at that point.
One way to implement this is to use IOUtils and String replace method.
public static void translate(byte[] fromHex, byte[] toHex, InputStream input, OutputStream output) throws IOException {
IOUtils.write(translate(fromHex, toHex, IOUtils.toByteArray(input)), output);
}
public static byte[] translate(byte[] fromHex, byte[] toHex, byte[] inBytes) throws UnsupportedEncodingException {
String inputText = new String(inBytes, "ISO-8859-1");
String outputText = inputText.replace(new String(fromHex, "ISO-8859-1"), new String(toHex, "ISO-8859-1"));
return outputText.getBytes("ISO-8859-1");
}

Categories