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;
}
Related
I'm trying to hash data "text" to be transferred from Java Service to C# Service.
I'm using SHA256 as a Hashing algorithm, but despite the values and the salt being the same the result doesn't.
Here is my C# snippet
public string Sign(string textToHash, string salt){
byte[] convertedHash = new byte[salt.Length / 2];
for (int i = 0; i < salt.Length / 2; i++)
convertedHash[i] = (byte)int.Parse(salt.Substring(i * 2, 2), NumberStyles.HexNumber);
HMAC hasher = new HMACSHA256(convertedHash);
string hexHash = "";
using (hasher)
{
byte[] hashValue = hasher.ComputeHash(Encoding.UTF8.GetBytes(textToHash));
foreach (byte b in hashValue)
{
hexHash += b.ToString("X2");
}
}
return hexHash;
}
And, here is the Java snippet
public static String sign(String textToHash, String salt){
byte[] convertedHash = new byte[salt.length() / 2];
for (int i = 0; i < salt.length() / 2; i++)
{
convertedHash[i] = (byte)Integer.parseInt(salt.substring(i * 2, i * 2 + 2),16);
}
String hashedText = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(convertedHash);
byte[] bytes = md.digest(textToHash.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
}
hashedText = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return hashedText;
}
In Java, I also tried
convertedHash = salt.getBytes();
But I got different results also.
Tests:
salt = ABCDEFG
text = hashme
Result in C#
70B38047C28FFEDCF7275C428E65310671CADB65F11A5C9A8CFBB3CF52112BA3
Result in Java
a8bc36606aade01591a1d12c8b3c87aca1fe55def79740def03a90b49f2c6b7c
So, any help about why the results aren't the same.
Thanks in advance.
To mimic the Java hashing, I used SHA256Managed rather than HMACSHA256 in C#
public static string Sign(string data, string salt)
{
UTF8Encoding encoder = new UTF8Encoding();
SHA256Managed sha256hasher = new SHA256Managed();
byte[] convertedHash = new byte[salt.Length / 2];
for (int i = 0; i < salt.Length / 2; i++)
convertedHash[i] = (byte)int.Parse(salt.Substring(i * 2, 2), NumberStyles.HexNumber);
byte[] dataBytes = encoder.GetBytes(data);
byte[] bytes = new byte[convertedHash.Length + dataBytes.Length];
Array.Copy(convertedHash, bytes, convertedHash.Length);
Array.Copy(dataBytes, 0, bytes, convertedHash.Length, dataBytes.Length);
byte[] hashedBytes = sha256hasher.ComputeHash(bytes);
return hashedBytes.Aggregate("", (current, t) => current + t.ToString("X2"));
}
HMACSHA256 is not a pure SHA-256.
public static void main(String[] args) throws SignatureException {
String data = "GET"+"\n"+"webservices.amazon.com"+"\n"+"/onca/xml"+"\n"+"AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=ItemLookup&ResponeGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06";
String key = "1234567890";
String result = calculateRFC2104HMAC(data, key);
System.out.println(result);
}
private static final String HMAC_SHA_ALGORITHM = "HmacSHA256";
public static String calculateRFC2104HMAC(String data, String key)throws java.security.SignatureException{
String result;
try {
// get an hmac_sha256 key from the raw key bytes
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA_ALGORITHM);
// get an hmac_sha256 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance(HMAC_SHA_ALGORITHM);
mac.init(signingKey);
// compute the hmac256 on input data bytes
byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
// base64-encode the hmac256
result = Base64.encodeBase64String(rawHmac);
} catch (Exception e) {
throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
}
return result;
}
So I am trying to calculate this hmac with sha256 for AWS, but I do not get the excpected result, even though this example is taken from official AWS docs: http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AuthJavaSampleHMACSignature.html And the only thing changed is the algorithm, which did not break the program, so it should work, but it does not.
The result I get: k1T/qvVoXgEvmdFhTEh71vLDznqEVCyKcslA5RRSB6s=
The result I expect: M/y0+EAFFGaUAp4bWv/WEuXYah99pVsxvqtAuC8YN7I=
Does anyone have any idea what is wrong?
It may have to do with how the newline character is interpreted. \n can be a cr, lf, or cr-lf depending on your OS.
AWS uses to two different HMAC functions, the first returns the string representation, the other returns the binary representation. This is from my C++ implementation using OpenSSL, hope it helps:
string hmacHex(string key, string msg)
{
unsigned char hash[32];
HMAC_CTX hmac;
HMAC_CTX_init(&hmac);
HMAC_Init_ex(&hmac, &key[0], key.length(), EVP_sha256(), NULL);
HMAC_Update(&hmac, (unsigned char*)&msg[0], msg.length());
unsigned int len = 32;
HMAC_Final(&hmac, hash, &len);
HMAC_CTX_cleanup(&hmac);
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (int i = 0; i < len; i++)
{
ss << std::hex << std::setw(2) << (unsigned int)hash[i];
}
return (ss.str());
}
the string implementation
string hmac(string key, string msg)
{
unsigned char hash[32];
HMAC_CTX hmac;
HMAC_CTX_init(&hmac);
HMAC_Init_ex(&hmac, &key[0], key.length(), EVP_sha256(), NULL);
HMAC_Update(&hmac, ( unsigned char* )&msg[0], msg.length());
unsigned int len = 32;
HMAC_Final(&hmac, hash, &len);
HMAC_CTX_cleanup(&hmac);
std::stringstream ss;
ss << std::setfill('0');
for (int i = 0; i < len; i++)
{
ss << hash[i];
}
return (ss.str());
}
If you are using Java, I'd recommend using the corresponding SDK. I my experience the API's tend to change rather quickly.
I am trying to concatenate long and bytearray to another bytearray.
I tried like this :
byte[] value1= new byte[16];
byte[] value2= new byte[16];
byte[] finalvalue = new byte[value1.length + value2.length];
long ts = System.currentTimeMillis();
int val = 100;
ByteBuffer.wrap(value1).order(ByteOrder.LITTLE_ENDIAN).asLongBuffer().put(ts);
ByteBuffer.wrap(value2).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().put(val);
System.arraycopy(value1, 0, finalvalue, 0, value1.length);
System.arraycopy(value2, 0, finalvalue, value1.length,value2.length);
When I tried to print this, I am not getting the correct values. It printing like this
BYTEVALUE -95-15-4410659100000000002000000000000000
it should print like this
- BYTEVALUE- 1354707038625,100
Can anyone help me out where I am going wrong.
Help will be appreciated.
Update:
Use to print values using StringBuffer like this:
StringBuffer sb = new StringBuffer(finalvalue.length);
for (int i = 0; i < finalvalue.length; i++) {
sb.append(finalvalue[i]);
}
Your code is not doing what you think it is. Consider the following self-contained application:
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class ByteArrayTest {
public static void main(String[] args) {
byte[] value1 = new byte[16];
byte[] value2 = new byte[16];
byte[] finalvalue = new byte[value1.length + value2.length];
long ts = System.currentTimeMillis();
int val = 100;
ByteBuffer.wrap(value1).order(ByteOrder.LITTLE_ENDIAN).asLongBuffer()
.put(ts);
ByteBuffer.wrap(value2).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer()
.put(val);
System.arraycopy(value1, 0, finalvalue, 0, value1.length);
System.arraycopy(value2, 0, finalvalue, value1.length, value2.length);
printByteArray(finalvalue);
}
private static void printByteArray(final byte[] array) {
StringBuilder sb = new StringBuilder(array.length);
for (byte b : array) {
sb.append(String.format("%02X", b));
}
System.out.println(sb.toString());
}
}
The output of this is:
BE26086B3B010000000000000000000064000000000000000000000000000000
Splitting this into component parts, we can see why:
The first sixteen bytes are BE26086B3B0100000000000000000000. This is your timestamp in little endian order. If you ignore the zero bytes, this converts to 1,354,710,394,558 in decimal, which is correct.
The second sixteen bytes are 64000000000000000000000000000000, which is your hard-coded value 100.
The zeroes represent the space in the byte arrays that you didn't use.
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.
String str = "9B7D2C34A366BF890C730641E6CECF6F";
I want to convert str into byte array, but str.getBytes() returns 32 bytes instead of 16.
I think what the questioner is after is converting the string representation of a hexadecimal value to a byte array representing that hexadecimal value.
The apache commons-codec has a class for that, Hex.
String s = "9B7D2C34A366BF890C730641E6CECF6F";
byte[] bytes = Hex.decodeHex(s.toCharArray());
Java SE 6 or Java EE 5 provides a method to do this now so there is no need for extra libraries.
The method is DatatypeConverter.parseHexBinary
In this case it can be used as follows:
String str = "9B7D2C34A366BF890C730641E6CECF6F";
byte[] bytes = DatatypeConverter.parseHexBinary(str);
The class also provides type conversions for many other formats that are generally used in XML.
Use:
str.getBytes("UTF-16LE");
I know it's late but hope it will help someone else...
This is my code: It takes two by two hex representations contained in String and add those into byte array.
It works perfectly for me.
public byte[] stringToByteArray (String s) {
byte[] byteArray = new byte[s.length()/2];
String[] strBytes = new String[s.length()/2];
int k = 0;
for (int i = 0; i < s.length(); i=i+2) {
int j = i+2;
strBytes[k] = s.substring(i,j);
byteArray[k] = (byte)Integer.parseInt(strBytes[k], 16);
k++;
}
return byteArray;
}
That should do the trick :
byte[] bytes = toByteArray(Str.toCharArray());
public static byte[] toByteArray(char[] array) {
return toByteArray(array, Charset.defaultCharset());
}
public static byte[] toByteArray(char[] array, Charset charset) {
CharBuffer cbuf = CharBuffer.wrap(array);
ByteBuffer bbuf = charset.encode(cbuf);
return bbuf.array();
}
try this:
String str = "9B7D2C34A366BF890C730641E6CECF6F";
String[] temp = str.split(",");
bytesArray = new byte[temp.length];
int index = 0;
for (String item: temp) {
bytesArray[index] = Byte.parseByte(item);
index++;
}
I assume what you need is to convert a hex string into a byte array that equals that means the same thing as that hex string?
Adding this method should do it for you, without any extra library importing:
public static byte[] hexToByteArray(String s) {
String[] strBytes = s.split("(?<=\\G.{2})");
byte[] bytes = new byte[strBytes.length];
for(int i = 0; i < strBytes.length; i++)
bytes[i] = (byte)Integer.parseInt(strBytes[i], 16);
return bytes;
}