SHA1 hashing not working as expected in Java - java

I am trying to write a small Java code to see how to properly use SHA1.
Following is the code snippet I came up with:
package dummyJavaExp;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Exp1 {
public static void main(String[] args) throws NoSuchAlgorithmException {
// TODO Auto-generated method stub
String str = "Hello there";
String hashstr = new String(MessageDigest.getInstance("SHA1").digest(str.getBytes()));
System.out.println("Encrypted value of " + str + " is: " + hashstr);
}
}
But the above code gives some weird characters as shown in the following output message when I run the above code:
Encrypted value of Hello there is: rlvU>?Þ¢‘4ónjòêì\Î
I thought the encrypted message will be some alphanumeric string.
Am I missing something in my code?

When you use String sample = new String(byte[] bytes) it will create a string with platform's default charset, your digest bytes may not have alphanumeric representation in that charset.
Try to use Base64 or HexString to display digest message.
For example in JAVA8:
You can encode your digest bytes to string with:
String hashstr = Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA1").digest(str.getBytes("UTF-8")));
You can decode your Base64 with:
byte [] digest = Base64.getDecoder().decode(hashstr);

Related

String of byte array to byte array (rsa and java)

I am working on a web service, and I want to send a byte array as a String, then get the original byte array.
I explain again, my server side has the role of encrypting a message, so I have a byte array.
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(cipher.ENCRYPT_MODE,clefPrivee);
byte[] cipherText= cipher.doFinal(msgEnOctets);
then to send this encrypted message, I send it as a String because I am sending an entire data frame
code :
cipherText.toString();
So I have the array as a string but nothing has changed.
How can I get my original painting back?
thanks
A common way to send byte array is to encode it in Base64 before sending it, on the other side when receiving the string it must be decoded the Base64 to get the original byte array. For example:
Sender:
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(cipher.ENCRYPT_MODE,clefPrivee);
byte[] cipherText= cipher.doFinal(msgEnOctets);
return Base64.getEncoder().encodeToString(cipherText);
Receiver:
public void getMessage(String message) {
byte[] decodeMessage = Base64.getDecoder().decode(message);
//...
}
Please do NOT use the conversion from #andy jason (https://stackoverflow.com/a/63489562/8166854) as a byte array (especially when used with data used for
encryption) cannot get converted to a string and vice verse with new String(bytes, charset).
One method for a byte array -> String -> byte array conversion is to use the Base64-encoding:
result:
ByteToString and reverse test
bytes: ee99c01c47185dbd6b62dd9bcfed94d7
method as by comment andy jason
s: ��G]�kbݛ���
tab: efbfbdefbfbd1c47185defbfbd6b62dd9befbfbdefbfbdefbfbd
bytes equal to tab: false
method with base64
s2: 7pnAHEcYXb1rYt2bz+2U1w==
tab2: ee99c01c47185dbd6b62dd9bcfed94d7
bytes equal to tab2: true
code:
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
public class ByteToString {
public static void main(String[] args) {
System.out.println("https://stackoverflow.com/questions/63489517/string-of-byte-array-to-byte-array-rsa-and-java");
System.out.println("ByteToString and reverse test");
byte[] bytes = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(bytes);
System.out.println("bytes: " + bytesToHex(bytes));
// method by andy jason
System.out.println("\nmethod as by comment andy jason");
Charset charset = StandardCharsets.UTF_8;
String s = new String(bytes, charset);
System.out.println("s: " + s);
byte [] tab = s.getBytes (charset);
System.out.println("tab: " + bytesToHex(tab));
System.out.println("bytes equal to tab: " + Arrays.equals(bytes, tab));
// method with base64
System.out.println("\nmethod with base64");
String s2 = Base64.getEncoder().encodeToString(bytes);
System.out.println("s2: " + s2);
byte[] tab2 = Base64.getDecoder().decode(s2);
System.out.println("tab2: " + bytesToHex(tab2));
System.out.println("bytes equal to tab2: " + Arrays.equals(bytes, tab2));
}
private static String bytesToHex(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return result.toString();
}
}
If you want to convert your byte array to String reversibly, you have to use the String constructor which expects a byte array:
String s = new String(bytes, charset);
Then to find your byte array, you have to be careful to use the same charset:
byte [] tab = s.getBytes (charset);

c# and java - difference between hmacsha256 hash Redux

While testing the code in a previous post on the differences between the java and c# hmacsha256 implementation outputs, I noticed that the outputs were slightly different, i.e. when I ran java code the output was
ivEyFpkagEoghGnTw_LmfhDOsiNbcnEON50mFGzW9_w=
but in C# code I get
ivEyFpkagEoghGnTw/LmfhDOsiNbcnEON50mFGzW9/w=
Has anybody seen this, i.e. there is a _ in the java example but an / in the c# example
Java Code
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class Test {
public static void main (String[] args) throws Exception {
String secretAccessKey = "mykey";
String data = "my data";
byte[] secretKey = secretAccessKey.getBytes();
SecretKeySpec signingKey = new SecretKeySpec(secretKey, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] bytes = data.getBytes();
byte[] rawHmac = mac.doFinal(bytes);
System.out.println(Base64.getUrlEncoder().encodeToString(rawHmac));
}
}
C# Code
using System;
using System.Security.Cryptography;
using System.Text;
class Test
{
static void Main()
{
String secretAccessKey = "mykey";
String data = "my data";
byte[] secretKey = Encoding.UTF8.GetBytes(secretAccessKey);
HMACSHA256 hmac = new HMACSHA256(secretKey);
hmac.Initialize();
byte[] bytes = Encoding.UTF8.GetBytes(data);
byte[] rawHmac = hmac.ComputeHash(bytes);
Console.WriteLine(Convert.ToBase64String(rawHmac));
}
}
This seems to be a design choice for the Base64 encoders, one of them chose to use the _ character and one of them chose to use the / character (C# also uses the + character instead of the - character). If you need to use that string across languages you can use myString.replace(oldChar, newChar) (myString.Replace(oldChar, newChar) in C#) to replace the mismatch characters.
If you want C# Base64 strings to look like Java's Base64 strings you can use Microsoft.IdentityModel.Tokens.Base64UrlEncoder, but this is a Nuget package you would have to install. This uses - and _ instead of + and /.

GNU Crypto Encrypt returns blank string

I'm using GNU Crypto library to encrypt simple strings. I believe I have followed to documentation correctly, but the problem is that it just returns an blank string (in this case 5 characters) of spaces. I'm not sure whether I miss coded it or if its some encoding issue. I hope its not something embarrassingly simple.
import gnu.crypto.cipher.CipherFactory;
import gnu.crypto.cipher.IBlockCipher;
import java.util.HashMap;
import java.util.Map;
public class FTNSAMain {
public static void main(String[] args) throws Exception {
String data = "Apple";
String key = "ABCDEFGHIJKLMNOP";
byte[] temp = Encrypt(data.getBytes(), key.getBytes(), "AES");
System.out.println(new String(temp));
}
public static byte[] Encrypt(byte[] input, byte[] key, String algorithm) throws Exception {
byte[] output = new byte[input.length];
IBlockCipher cipher = CipherFactory.getInstance(algorithm);
Map attributes = new HashMap();
attributes.put(IBlockCipher.CIPHER_BLOCK_SIZE, 16);
attributes.put(IBlockCipher.KEY_MATERIAL, key);
cipher.init(attributes);
int bs = cipher.currentBlockSize();
for (int i = 0; i + bs < input.length; i += bs) {
cipher.encryptBlock(input, i, output, i);
}
return output;
}
}
GNU Crypto documentation have the following to say about the void encryptBlock(..) methode:
Encrypts a block of bytes from plaintext starting at inOffset, storing
the encrypted bytes in ciphertext, starting at outOffset. It is up to
the programmer to ensure that there is at least one full block in
plaintext from inOffset and space for one full block in ciphertext
from outOffset. A java.lang.IllegalStateException will be thrown if
the cipher has not been initialized.
Your input:
String data = "Apple";
Is not a full datablock as AES needs data in blocks of 16 bytes. Also, your output buffer is also too short.
For starters, try encrypting with an input that ends up as 16 bytes like:
String data = "Apple56789abcdef";

Hash Based Message Authentication Codes (MAC) does not match at receiver end

I am trying to create a basic demo application where one class will generate a message to be sent in the following format
SignedMessage_using_HMAC.BASE64encoded_message
At the receiving end (DecodeData.java) first I wan to compare if the message was signed using the right key by decrypting the signed message and then signing the message with the same key and then compare the signed message at the receiver end the signed message sent.
But these do not work.
When I try to decode the Base64 encoded message it does not give me the correct message.
Can anyone please guide me what's wrong here?
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class EncodeData {
public static void main(String[] args) throws Exception {
String myAppContext = "abc123def";
String consumerSecret = "959595";
String algorithm = "HMACSHA256";
byte[] encodedContext;
// Base64 encoded context;
encodedContext = new Base64(true).encode(myAppContext.getBytes());
System.out.print("Encoded Context : ");
System.out.println(encodedContext);
//Generate Signed context
SecretKey hmacKey = new SecretKeySpec(consumerSecret.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(hmacKey);
byte[] digest = mac.doFinal(myAppContext.getBytes());
System.out.print("Created digest : ");
System.out.println(digest);
// Signed Based64 context and Base64 encoded context
String messageToSend = digest.toString() + "." + encodedContext.toString();
System.out.println(messageToSend);
}
}
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;
public class DecodeData {
public static void main(String[] args) throws Exception {
byte[] myAppContext;
String consumerSecret = "959595";
String algorithm = "HMACSHA256";
String messageRecieved = args[0];
byte[] singedDecodedContext;
String recievedDigest = messageRecieved.split("[.]", 2)[0];
String encodedContext = messageRecieved.split("[.]", 2)[1];
myAppContext = new Base64(true).decode(encodedContext);
System.out.print("Decrypted message : ");
System.out.println(myAppContext);
//Check if the message is sent by the correct sender by signing the context and matching with signed context
SecretKey hmacKey = new SecretKeySpec(consumerSecret.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(hmacKey);
byte[] digest = mac.doFinal(myAppContext);
System.out.print("Created digest : ");
System.out.println(digest);
if (Arrays.equals(digest, recievedDigest.getBytes())) {
System.out.println("Message was not tempered and was sent by the correct sender");
} else {
System.out.println("Message was tempered or was not sent by the corrrect sender");
}
}
}
Output
Output of EncodeData.java
C:\Users\vivek.patel\Desktop\API\java\encoding>java -cp commons-codec-1.10.jar;. EncodeData
Encoded Context : [B#510bfe2c
Created digest : [B#73f025cb
[B#73f025cb.[B#510bfe2c
Output of DecodeData.java
C:\Users\vivek.patel\Desktop\API\java\encoding>java -cp commons-codec- 1.10.jar;. DecodeData [B#73f025cb.[B#510bfe2c
Decrypted message : [B#6726a408
Created digest : [B#7168bd8b
Message was tempered or was not sent by the correct sender
Before evaluate your code, you aren't actually comparing the values.
If you print like this:
System.out.print("Encoded Context : ");
System.out.println(encodedContext);
You are just printting the type of the array ([B) followed by its hashCode.
Initialize a String with the encoded bytes:
System.out.println(new String(encodedContext, "UTF8"));
You should also consider using an explicit charset instead of the default one (depending on your origin charset).
Try it and re-post your results.

sending rc4 encrypted api call in java

I am trying to make an api call in java using these steps:
json encode
RC4 encryption
base64 encoding
I am currently using the same system in php and its working correctly:
$enc_request = base64_encode(openssl_encrypt(json_encode($request_params), "rc4", $this->_app_key));
But when I use the same system in java, the results are not as expected. Here's my code:
//json encoding
JSONObject obj = new JSONObject();
obj.put("email", username);
obj.put("password", password);
obj.put("action", "login");
//function to encode base64
private String getBase64Encoded(String encryptedJsonString)
{
byte[] encoded = Base64.encodeBase64(encryptedJsonString.getBytes());
String encodedString = new String(encoded);
return encodedString;
}
//function to encrypt in RC4
private String getRC4EncryptedString2(String string, String key) throws Exception
{
Cipher cipher = Cipher.getInstance("RC4");
SecretKeySpec rc4Key = new SecretKeySpec(key.getBytes(), "RC4");
cipher.init(Cipher.ENCRYPT_MODE, rc4Key);
byte[] cipherText = cipher.update(string.getBytes());
return new String(cipherText);
}
I was able to identify the problem upto the RC4 encryption which is not returning the same result as the php version.
I've been battling this for 2 days now. I hope I have not missed any stupid thing because this should be straight-forward.
Thanks
You should use a byte[] not a String to hold intermediate byte array values. A String is for text, not raw data, and will attempt to decode the bytes as character data using your system's default character set (at least, the single-parameter String constructor will). Same with String.getBytes().
Just return cipherText directly from getRC4EncryptedString2(), and pass it directly to getBase64Encoded(). There's a reason those encoders operate on byte arrays, and that reason is not so that you can garble the data by applying a character encoding to it in between.
The same goes for the key you are passing to getRC4EncryptedString2(). At the bare minimum use String.getBytes("ISO-8859-1") or something (assuming that your key is actually text and not yet another garbled byte array). The no-parameter version of getBytes() returns the text encoded using your system's default character set, which is not guaranteed to be what you want.
That also all applies to the String you are returning from your base 64 encoder. I don't know what base 64 encoder you are using, but make sure you specify the character set to the String constructor. Most likely you will be OK, purely by coincidence, but you should always specify a character set when converting to/from String and raw bytes. And that, of course, assumes that your base 64 encoder returns text, rather than bytes in the range 0-63.
The general point here is you can't just convert back and forth from String to byte[]. A String is for text and it's representation as a byte[] depends on the character encoding.
I am able to achieve this using the following code. Hope this helps!
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.apache.commons.codec.DecoderException;
import org.bouncycastle.util.encoders.Hex;
import org.json.JSONException;
import org.json.JSONObject;
public class RC4Algo {
public static void main(String args[])throws IOException, NoSuchAlgorithmException, DecoderException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, JSONException
{
decryptRC4();
}
static String decryptRC4() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, JSONException{
//byte[] plainBytes = "testString".getBytes();
//json encoding
JSONObject obj = new JSONObject();
obj.put("email", "username");
obj.put("password", "password");
obj.put("action", "login");
byte [] plainBytes = obj.toString().getBytes();
String hashedKey = hashedData("thisismysecretkey");
//Generate a new key using KeyGenerator
/*KeyGenerator rc4KeyGenerator = KeyGenerator.getInstance("RC4");
SecretKey key = rc4KeyGenerator.generateKey();*/
Key key = new SecretKeySpec(Hex.decode(hashedKey), "RC4");
// Create Cipher instance and initialize it to encrytion mode
Cipher cipher = Cipher.getInstance("RC4"); // Transformation of the algorithm
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherBytes = cipher.doFinal(plainBytes);
String encoded = encodeBase64(cipherBytes);
String decoded = decodeBase64(encoded);
// Reinitialize the Cipher to decryption mode
cipher.init(Cipher.DECRYPT_MODE,key, cipher.getParameters());
byte[] plainBytesDecrypted = cipher.doFinal(Hex.decode(decoded));
System.out.println("Decrypted Data : "+new String(plainBytesDecrypted));
return new String(plainBytesDecrypted);
}
static String decodeBase64(String encodedData){
byte[] b = Base64.getDecoder().decode(encodedData);
String decodedData = DatatypeConverter.printHexBinary(b);
return decodedData;
}
static String encodeBase64(byte[] data){
byte[] b = Base64.getEncoder().encode(data);
String encodedData = new String(b);
/*String encodedData = DatatypeConverter.printHexBinary(b);*/
return encodedData;
}
static String hashedData(String key) throws NoSuchAlgorithmException{
String password = key;
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(password.getBytes());
byte byteData[] = md.digest();
//convert the byte to hex format method 1
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
//convert the byte to hex format method 2
StringBuffer hexString = new StringBuffer();
for (int i=0;i<byteData.length;i++) {
String hex=Integer.toHexString(0xff & byteData[i]);
if(hex.length()==1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}
Output:

Categories