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

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.

Related

SHA1 hashing not working as expected in 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);

how to compare sha1 encripted password in Java?

Let say i have encripted SHA1 password like this
String pass = "f6ce584e7b4ff5253eed4a2ea2b44247";
and i want make condition like this :
if (pass.equals("userinput")){
System.out.println("success");
}
please someone help me to make proper condition / function to compare those both value between user input and encripted password. Your help will be highly appreciated. thanks
SHA1 is a hash algorithm, which means that it is one-way. You can't get the original message after hashing it. Unlike encryption which is two-way (allows encryption and decryption).
This means that if you want to compare a hash, you don't try to get the original message. Instead, you hash the message-to-be-compared as well, then you perform the match:
So if the hashed pw is stored as:
String pass = "f6ce584e7b4ff5253eed4a2ea2b44247";
To match the subsequent input of the password, you do:
//check if hashed userInput is also "f6ce584e7b4ff5253eed4a2ea2b44247"
if(pass.equals(sha1(userInput))){
//do whatever
}
To implement a sha1() hash function, refer to: Java String to SHA1
To get your hashcode:
public static byte[] sha1(byte[] data)
Calculates the SHA-1 digest and returns the value as a byte[].
Parameters:
data - Data to digest
Returns:
SHA-1 digest
Found these at
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/DigestUtils.html#sha1Hex(java.lang.String)
This helps your process.
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jcajce.provider.asymmetric.rsa.DigestSignatureSpi.SHA1;
public class SHA1_test {
public static String sha1(String s, String keyString)
throws UnsupportedEncodingException, NoSuchAlgorithmException,
InvalidKeyException {
SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"),
"HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(key);
byte[] bytes = mac.doFinal(s.getBytes("UTF-8"));
return new String(Base64.encodeBase64(bytes));
}
public static void main(String[] args) throws InvalidKeyException,
UnsupportedEncodingException, NoSuchAlgorithmException {
Boolean validate = false;
String code = sha1("admin", "123456");
String your_user_inputString = "testpassword";
if (code.equals(sha1(your_user_inputString, "123456"))) {
System.out.println("Correct");
} else {
System.out.println("Bad password");
}
}
}
This works!!!

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";

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:

Java BouncyCastle Cast6Engine (CAST-256) encrypting

I'm trying to implement a function that receives a string and returns the encoded values of the String in CAST-256. The following code is what i implement following the example on BoncyCastle official web page (http://www.bouncycastle.org/specifications.html , point 4.1).
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.engines.CAST6Engine;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
public class Test {
static{
Security.addProvider(new BouncyCastleProvider());
}
public static final String UTF8 = "utf-8";
public static final String KEY = "CLp4j13gADa9AmRsqsXGJ";
public static byte[] encrypt(String inputString) throws UnsupportedEncodingException {
final BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CAST6Engine());
byte[] key = KEY.getBytes(UTF8);
byte[] input = inputString.getBytes(UTF8);
cipher.init(true, new KeyParameter(key));
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int outputLen = cipher.processBytes(input, 0, input.length, cipherText, 0);
try {
cipher.doFinal(cipherText, outputLen);
} catch (CryptoException ce) {
System.err.println(ce);
System.exit(1);
}
return cipherText;
}
public static void main(String[] args) throws UnsupportedEncodingException {
final String toEncrypt = "hola";
final String encrypted = new String(Base64.encode(test(toEncrypt)),UTF8);
System.out.println(encrypted);
}
}
But , when i run my code i get
QUrYzMVlbx3OK6IKXWq1ng==
and if you encode hola in CAST-256 with the same key ( try here if you want http://www.tools4noobs.com/online_tools/encrypt/) i should get
w5nZSYEyA8HuPL5V0J29Yg==.
What is happening? Why im getting a wront encrypted string?
I'm tired of find that on internet and didnt find a answer.
Bouncy Castle uses PKCS #7 padding by default, while PHP's mcrypt (and the web site you linked) uses zero padding by default. This causes the different ciphertexts.
Please note that the ECB mode used here is not secure for almost any use. Additionally, I hope the secret key you posted is not the real key, because now that it's not secret anymore, all this encryption is useless.
This doesn't really answer your question, but it does provide some pointers.
You need to do a little digging to ensure you are decrypting in exactly the same way as PHP's mcrypt(). You need to make sure your key generation, encoding/decoding and cipher algorithm match exactly.
Keys
"CLp4j13gADa9AmRsqsXGJ".getBytes("UTF-8");
is probably not the right way to create the key source bytes. The docs seem to indicate that mcrypt() pads the key and data with \0 if it isn't the right size. Note that your method produces a 168 bit key, which is not a valid key size and I'm not sure what java is going to do about it.
Algorithm
Make sure the cipher mode and padding are the same. Does mcrypt() use ECB, CBC, something else?
Encoding
Ciphers work on bytes, not Strings. Make sure your conversion between the two is the same in java and PHP.
Here is a reference test for CAST6 using test vectors from https://www.rfc-editor.org/rfc/rfc2612#page-10. Note the key, ciphertext and plaintext are hex encoded.
import java.security.Provider;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class Cast6 {
static final String KEY_ALGO = "CAST6";
static final String CIPHER_ALGO = "CAST6/ECB/NOPADDING";
static String keytext = "2342bb9efa38542c0af75647f29f615d";
static String plaintext = "00000000000000000000000000000000";
static String ciphertext = "c842a08972b43d20836c91d1b7530f6b";
static Provider bc = new BouncyCastleProvider();
public static void main(String[] args) throws Exception {
System.out.println("encrypting");
String actual = encrypt();
System.out.println("actual: " + actual);
System.out.println("expect: " + ciphertext);
System.out.println("decrypting");
actual = decrypt();
System.out.println("actual: " + actual);
System.out.println("expect: " + plaintext);
}
static String encrypt() throws Exception {
Cipher cipher = Cipher.getInstance(CIPHER_ALGO, bc);
byte[] keyBytes = Hex.decodeHex(keytext.toCharArray());
SecretKeySpec key = new SecretKeySpec(keyBytes, KEY_ALGO);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] input = Hex.decodeHex(plaintext.toCharArray());
byte[] output = cipher.doFinal(input);
String actual = Hex.encodeHexString(output);
return actual;
}
static String decrypt() throws Exception {
Cipher cipher = Cipher.getInstance(CIPHER_ALGO, bc);
byte[] keyBytes = Hex.decodeHex(keytext.toCharArray());
SecretKeySpec key = new SecretKeySpec(keyBytes, KEY_ALGO);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] output = cipher.doFinal(Hex.decodeHex(ciphertext.toCharArray()));
String actual = Hex.encodeHexString(output);
return actual;
}
}

Categories