Reading in a file and then hashing it using SHA-256 I found a tutorial which showed two separate ways to do it.
When comparing the file that I hashed (Which was a PDF) against both methods, they did not match up. I am following the code properly, not understanding why it isn't matching up.
Here is my result:
Hex format: b050692edb134da209adf76347f6c5e49db8734edeaa44876606ec8e5559ab4e
Hex format: b050692edb134da29adf76347f6c5e49db8734edeaa4487666ec8e5559ab4e
It looks like it is lopping off the two zeros in the middle, I just don't understand why
Java Code
import java.io.FileInputStream;
import java.security.MessageDigest;
public class SHAHash{
public static void main(String[] args)throws Exception{
MessageDigest md = MessageDigest.getInstance("SHA-256");
FileInputStream fis = new FileInputStream("myfile");
byte[] dataBytes = new byte[1024];
int nread = 0;
while((nread = fis.read(dataBytes))!= -1){
md.update(dataBytes, 0, nread);
};
byte[] mdbytes = md.digest();
StringBuffer sb1 = new StringBuffer();
for(int i = 0; i < mdbytes.length; i++){
sb1.append(Integer.toString((mdbytes[i] & 0xFF) + 0x100, 16).substring(1));
}
System.out.println("Hex format: " + sb1.toString());
StringBuffer sb2 = new StringBuffer();
for(int i = 0; i < mdbytes.length; i++){
sb2.append(Integer.toHexString(0xFF & mdbytes[i]));
}
System.out.println("Hex format: " + sb2.toString());
}
}
Instead of StringBuffer you should be using StringBuilder (the methods aren't synchronized and you aren't using multiple threads, it's like the difference between a Vector and an ArrayList). Also, you need to preserve leading 0(s). You could correct your second loop with something like String.format(String, Object...). Putting that together like,
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < mdbytes.length; i++) {
sb2.append(String.format("%02x", 0xFF & mdbytes[i]));
}
System.out.println("Hex format: " + sb2.toString());
The difference between both is that the first adds 0x100 and then only takes a substring.
That way the toString call in the first loop always returns a string with a leading 1 followed by the actual value. That way the zero is preserved.
The second loop produces wrong output because the zero digit gets lost.
Related
Java: I'm encrypting and decrypting a text file using a key of any two ASCII characters on the keyboard. I have them working correctly, except when I read the encrypted file to a string for decryption. It replaces some specific letter with a different incorrect letter, but not all of correct letters are replaced. Some t's are replaced with s's for example. I've also seen some b's be replaced with e's when I use a different key.
I've already looked through my encrypted/decryption algorithm. I copy and pasted the encrypted text file into my code and ran the algorithm again, it came out perfect. The only time the letters are replaced is when the encrypted algorithm is read from a text file to be decrypted.
public static String readFileToString(string filePath) {
StringBuilder builder = new StringBuilder();
try (Stream<String> stream = Files.get(filePath), StandardCharsets.UTF_8)){
stream.forEach(s->builder.append(s).append("\n");
}
catch(IOException e){
e.printStackTrace();
}
return builder.toString();
}
public static void writeFile(String crypt) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.println("New file name: ");
String fileName = sc.nextLine();
String writtenString = crypt;
String userHome = System.getProperty("user.home");
File textFile = new File(userHome, fileName + ".txt");
BufferedWriter out = new BufferedWriter(new FileWriter(textFile));
out.write(writtenString);
out.close();
//Converts string and key into binary characters for 1-to-1 xOr to prevent any possible translation errors.
public static String crypt(String input, String key) throws UnsupportedEncodingException {
if (input.length() % 2 == 1) {
input = input + " ";
}
int n = input.length() / 2;
key = new String(new char[n]).replace("\0", key);
byte[] a = input.getBytes();
byte[] c = key.getBytes();
StringBuilder binaryBuilder = new StringBuilder();
StringBuilder binaryKeyBuilder = new StringBuilder();
//Creates a StringBuilder of bits using the file text
for(byte b: a) {
int value = b;
for(int i = 0; i < 8; i++) {
binaryBuilder.append((value & 128) == 0 ? 0 : 1);
value <<= 1;
}
binaryBuilder.append(' ');
}
//Converts binary StringBuilder to String
String binary = binaryBuilder.toString();
//Creates a StringBuilder of bits using the provided key
for(byte d: c) {
int keyValue = d;
for(int j = 0; j < 8; j++) {
binaryKeyBuilder.append((keyValue & 128) == 0 ? 0 : 1);
keyValue <<= 1;
}
binaryKeyBuilder.append(' ');
}
//Converts binaryKey StringBuilder to String
String binaryKey = binaryKeyBuilder.toString();
//Creates StringBuilder of bits using the provided key
StringBuilder xOr = new StringBuilder();
for(int q = 0; q < binary.length();q++) {
xOr.append(binary.charAt(q) ^ binaryKey.charAt(q));
}
String xOrResult = xOr.toString();
String cryptedString = "";
char next;
//Iterates through binary string to convert to ASCII characters 8 bits at a time.
for(int k = 0; k <= xOrResult.length()-8; k+=9) {
next = (char)Integer.parseInt(xOrResult.substring(k,k+8), 2);
cryptedString += next;
}
return cryptedString;
}
When I use the key "ty"
"Four score and seven years ago our fathers brought forth on this" is the correct phrasing.
However, I'm getting: "Four score and seven years ago our fashers broughs forth on this"
I would use binary file for encrypted text. It will save you from dealing with UTF-8 encoding/decoding some unusual code points. For example - when you xor 't' and 't' you get character with code 0.
You can also get unexpected new line characters. You actually replace all of them with '\n', but there are other options - '\r', or even two characters in sequence "\r\n". All of them will be replaced with '\n' in your code, and lead to mistakes after decryption.
What happened here:
Binary ASCII (or UTF-8) code for t is 01110100, and for y it is 01111001. When character y from key meets character t from text you get 01110100 xor 01111001 = 00001101 = 0x0D = '\r'. This character is written to file. When you read that file line by line, this '\r' is skipped as line separator. You replace it with '\n'=00001010 in line
stream.forEach(s->builder.append(s).append("\n");
When decrypting that text we get 00001010 (\n) xor 01111001 (y) = 01110011 (s).
I wish to convert following python code into Java. This code encodes credit card data using hash algorithms and keys. I have written some Java code based on my understanding below that. I think mainly its about panmackey. I am not sure how to generate its value for Java.
Python Code:
panmackey = bytes.fromhex('449E5A196233A43819A028770880E814DC420BFFC428295787302E6285FDD685')
def pandgstsha(track2, pan_mac_key=panmackey):
h = hmac.HMAC(pan_mac_key, hashes.SHA256(), backend=default_backend())
h. (bytes(track2.split('=')[0]+track2.split('=')[1][:4],'UTF-8'))
return base64.b64encode(h.finalize())
Java code:
String tokenPan = ccNum + expiryStr;
String panmackey= "?????????????????????";//pan_mac_key from python code
Mac mac = Mac.getInstance("HmacSHA512");
byte[] decodedBytes = Base64.decodeBase64(panmackey.getBytes("UTF-16LE"));
SecretKeySpec sk = new SecretKeySpec(decodedBytes, mac.getAlgorithm());
mac.init(sk);
byte[] resultBase64 = Base64.encodeBase64(mac.doFinal(tokenPan.getBytes("ASCII")));
String sB64 = new String(resultBase64, "UTF-8");
I need to figure out panmackey and if the 2 codes do exactly same function.
Your best bet would be to use:
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/binary/Hex.html
import org.apache.commons.codec.binary.Hex;
...
byte[] panmackey= Hex.decodeHex("Hex string goes here");
Otherwise, you'll have to implement your own conversion function like this;
public static byte[] hexStringToByteArray(String hex) {
byte[] decoded = new byte[hex.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int value = Integer.parseInt(hex.substring(index, index + 2), 16);
decoded[i] = (byte) value;
}
return decoded;
}
I am trying to fill a byte array, 2 bytes at a time. My hw is a cryptography hw. I am to find the plain text from the given cipher text using a key. The key is 2 bytes long. The plain text was encrypted with many time pad. The Key is repeated until its as long as the plain text. I got the first 2 bytes of the key, now i just need to repeat it until its as long as the cipher text/plain text.
The cipher text length which is the same as the plain text length is 640138. The key is supposed to be this length as well, but what i got for key length after i applied the solution below was 640144. Doing Math.min below instead of Math.max gives an error String.checkBoundsBeginEnd.
I had to convert it to string because the builder is of type StringBuilder. "result" in this instance is key.
How do i make the key as long as the cipher text length?
byte[] cipherText = Files.readAllBytes(Paths.get("src/cipher3.bmp"));
byte[] plainText = new byte[cipherText.length];
byte[] pText = new byte[]{'B', 'M'};
byte[] key = new byte[pText.length];
for(int i = 0; i < pText.length; i++){
key[i] = (byte)(cipherText[i] ^ pText[i]);
}
String keyString = Arrays.toString(key);
System.out.println("The key " + keyString);
System.out.println("First two in ptext"+ Arrays.toString(pText));
System.out.println(plainText.length);
String plainlength = (String) Arrays.toString(new int[]{plainText.length});
System.out.println(plainlength);
StringBuilder builder = new StringBuilder(cipherText.length);
while(builder.length() < cipherText.length){
builder.append(keyString.substring(0, Math.max(keyString.length(), builder.length() -cipherText.length)));
}
String result = builder.toString();
System.out.println(result);
System.out.println(result.length());//this gives 640144
Math.min should be used to fill correctly. The
java.lang.StringIndexOutOfBoundsException: String index out of range: -640138
gives you clue on what is wrong. Negative input is applied on the subString method, obviously it must be Math.min(keyString.length(), builder.length() -cipherText.length. Since cipherText length must greater than or equals to builder length, you should swap the arguments to give correct result.
public class Fill2ByteToArray {
public static void main(String[] args) throws IOException {
int targetLength = 640138;
byte[] cipherText = new byte[targetLength];
for (int i = 0; i < cipherText.length; i++) {
cipherText[i] = Integer.valueOf(i).byteValue();
}
byte[] plainText = new byte[cipherText.length];
byte[] pText = new byte[] { 'B', 'M' };
byte[] key = new byte[pText.length];
for (int i = 0; i < pText.length; i++) {
key[i] = (byte) (cipherText[i] ^ pText[i]);
}
String keyString = Arrays.toString(key);
System.out.println("The key " + keyString);
System.out.println("First two in ptext" + Arrays.toString(pText));
System.out.println(plainText.length);
String plainlength = (String) Arrays.toString(new int[] { plainText.length });
System.out.println(plainlength);
StringBuilder builder = new StringBuilder(cipherText.length);
while (builder.length() < cipherText.length) {
builder.append(keyString.substring(0, Math.min(keyString.length(), cipherText.length-builder.length())));
}
String result = builder.toString();
System.out.println(result);
System.out.println(result.length());
}
}
i'm creating the md5 hash generator. i first test it with an original file, then i altered the file to see whether the md5 hash codes is changed or not. the hash code did not change even after i altered the same file. what is the problem?
public class MD5CheckSum {
public byte [] createChecksum (String filename) throws Exception {
InputStream fis = new FileInputStream(filename);
byte[] buffer = new byte[1024];
MessageDigest complete = MessageDigest.getInstance("MD5");
int numRead;
do {
numRead = fis.read(buffer);
if (numRead > 0){
complete.update(buffer,0,numRead);
}
}while (numRead !=1);
fis.close();
return complete.digest();
}
public String getMD5Checksum(String filename) throws Exception {
/*byte[] b = createChecksum(filename);
String result = "";
for (int i=0; i < b.length; i++){
result += Integer.toString(( b[i] & 0xff) + 0x100, 16).substring( 1 );
}
return result;*/
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(filename.getBytes());
BigInteger number = new BigInteger(1, messageDigest);
String hashtext = number.toString(16);
// Now we need to zero pad it if you actually want the full 32 chars.
while (hashtext.length() < 32) {
hashtext = "0" + hashtext;
}
return hashtext;
}
public MD5CheckSum() throws Exception{
String path = "C:/Users/user/Downloads/Documents/ECOMM SUMMER BLOSSOM.docx";
System.out.println("MD5 Hash Succeed");
System.out.println(getMD5Checksum(path));
}
EDITED: I changed some code
public static String getMD5Checksum(String filename) throws Exception {
byte[] b = createChecksum(filename);
String result = "";
for (int i=0; i < b.length; i++) {
result += Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
}
return result;
}
public static void main(String args[]) {
try {
System.out.println("Start hashing....");
System.out.println(getMD5Checksum("C:/Users/user/Downloads/Documents/21.pdf"));
System.out.println("Done hashing....");
}
catch (Exception e) {
e.printStackTrace();
}
}
But it takes too long to generate the hash and currently the hash still not generated till now.
filename.getBytes() gets bytes of the filename, not the file contents.
I could tell you how to load the entire file into a byte array, but that would be bad, because it could take up huge amounts of memory when it just isn't necessary to keep the entire file in memory while the hash is calculated.
Instead you should open a stream and get the hash of that. See this answer for that: https://stackoverflow.com/a/304350/360211
You're seem to calculate the MD5-sum of the filename not the content of the file. What you should have done to avoid this is to use a file with a known MD5-sum (by for example run md5sum on it) and check if your code yields the same result.
Also I can't help noting that your createCheckSum seem to be a better candidate to be working as it seem to actually work on the content of the file.
Just verifying that you get different value for different input may show that you've got a candidate for check summing, but it's a poor check that it's actually the correct algorithm used.
I have a problem.
I sent some data over a socket in string form. With some substringing, I managed to get the byte[] in String form [B#58596d12. Now, my question is, this is a string. How do I convert it back to a byte array after which I can use String result = new String(byteArray);?
I tried casting the String to byte[], but that's not allowed.
Google only came up with the usual .getBytes() (which I can't use since they already are bytes) and new String(someByteArray)
This is what's being sent:
byte[] all = Files.readAllBytes(f.toPath());
this.out.println(destinationOfFile + "/" + file.getName() + "*" + all);
And this is what's being received:
private void writeToFile(String str) {
String file = str.substring(0, str.indexOf("*"));
String write = str.substring(str.indexOf("*")+1);
EDIT: I managed to do it in another way.
Instead of getting all the bytes from the file, I wen't through it line by line, sending each line over the socket. In the client, I store the data in a HashMap and when it receives "Done sending File" it writes all the strings to the file.
If you want to store raw bytes in a String, you should use an encoding designed for this purpose, such as Base64. Take a look at the Commons Codec library and Base64 class.
I'm using this code to convert String-Byte[], but I'm not sure if it will help you because I don't know if you have rawBytes as source or a Java-String. Could you clarify?
public static String byteToHexString(byte b) {
String result = "";
result += Integer.toHexString((int)(b >> 4) & 0xf);
result += Integer.toHexString((int)(b) & 0xf);
return result;
}
public static String bytesToHexString(final byte[] byteArray) {
if (byteArray == null) {
return null;
}
String result = "";
for (int i = 0; i < byteArray.length; ++i) {
result += byteToHexString(byteArray[i]);
}
return result;
}
public static byte[] hexStringToBytes(final String hexString) {
if (hexString == null) {
return null;
}
byte[] result = new byte[hexString.length() / 2];
for (int idx = 0; idx < result.length; ++idx) {
int strIdx = idx * 2;
result[idx] = (byte) ((Character.digit(hexString.charAt(strIdx), 16) << 4)
+ Character.digit(hexString.charAt(strIdx + 1), 16));
}
return result;
}
Edit:
If you are converting String-byte[] and byte[]-String I would recommend using the charset or charsetname parameter for String.getBytes() and for the String-constructor. For example:
byte[] bytes = "text".getBytes("UTF-8");
String text = new String(bytes, "UTF-8");
Remember not every platform or jvm may support the same charsets. For a list of charsets have a look here.
If you are reading bytes from a File and want to interpret them as String, you also have to care about using the right charset.