I have some .net code that generates a SHA1 hash on an xml document and need it to match a SHA1 hash generated on the same xml document in java code. The xml doc is sent to the java system and they generate a hash and match against the one i send to verify they are getting the document I intended them to. Below are the snippets in use for this and they both consistently generate the same different hashes. Any ideas why the following code would not generate the same hash?
.NET
String fileName = #"D:\Projects\CHIEP\hasherror\cdadoc.xml";
byte[] buff = null;
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
long numBytes = new FileInfo(fileName).Length;
buff = br.ReadBytes((int)numBytes);
HashAlgorithm hash = new SHA1Managed();
byte[] hashBytes = hash.ComputeHash(buff);
string hex = BitConverter.ToString(hashBytes);
hex = hex.Replace("-", "").ToLower();
Java
public static String SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md;
md = MessageDigest.getInstance("SHA-1");
byte[] sha1hash = new byte[40];
md.update(text.getBytes("UTF-8"), 0, text.length());
sha1hash = md.digest();
//String converted = convertToHex(sha1hash);
String converted = getHexString(sha1hash);
return converted;
}
.NET output
587691443778c1da54c3fd04bb35ec68a5a7fecd
Java output:
89665a8268d7d1901aba529dc8c9cea0f910c1bd
The input is a UTF-8 encoded CDA document that gets created here:
XmlSerializer serializer = new XmlSerializer(obj.GetType());
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, new UTF8Encoding(false));
XmlDocument xmlDoc = new XmlDocument();
serializer.Serialize(xmlTextWriter, obj);
memoryStream = (System.IO.MemoryStream)xmlTextWriter.BaseStream;
String xml = UTF8Encoding.UTF8.GetString(memoryStream.ToArray());
UPDATE:
Getting close to a solution. I found in the document there is a character that is being interpreted differently in the java than in the .net code.
Java reads in this:
value="21.9456" unit="kg/m²"
.net reads in this:
value="21.9456" unit="kg/m²"
If I open in the xml editor of my choice it looks like the what .net reads in. I suspect it has something to do with java doing a conversion and .net simply assuming..
I had the following java code:
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(password);
and I converted it to C# as follows:
var sha1 = SHA1Managed.Create();
byte[] outputBytes = sha1.ComputeHash(password);
In order to get my hashes to match, I did NOT include the following code that you had in your example:
string hex = BitConverter.ToString(hashBytes);
hex = hex.Replace("-", "").ToLower();
Related
I have got to make the same function in Java and C#, but the result are not the same.
My code in C# :
string xmlString = System.IO.File.ReadAllText(#"crc.xml");
byte[] bytes = Encoding.ASCII.GetBytes(xmlString);
// step 1, calculate MD5 hash from input
MD5 md5 = System.Security.Cryptography.MD5.Create();
byte[] hash = md5.ComputeHash(bytes);
// step 2, convert byte array to hex string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
Console.WriteLine(sb.ToString());
And my code in Java :
string xmlstring = Files.readString(Paths.get("crc.xml"));
MessageDigest m = MessageDigest.getInstance("MD5");
byte[] digest = m.digest(xmlstring.getbytes());
String hash = new BigInteger(1, digest).toString(16);
System.out.println(hash);
In C# I have this result :
F5F8B2F361FEA6EA30F24BEBAA5BDE3A
But in Java I have this result :
8fb40aad49fbf796b82a2faa11cda764
What I'm doing wrong?
Codebrane say it. Use
byte[] bytes = Encoding.UFT8.GetBytes(xmlString);
instead of
byte[] bytes = Encoding.ASCII.GetBytes(xmlString);
So I am not the Crypto wizard by any means but here is some code I have that works in C# but does not return the same b64 string in Java.
c#
string _Cert = "long b64 string here";
string _Pass = "my password";
string lvreturn = "Test";
byte[] lvCertBytes = Convert.FromBase64String(_Cert);
X509Certificate2 lvCertFromBytes = new X509Certificate2(lvCertBytes, _Pass);
SHA1Managed lvSHA1 = new SHA1Managed();
byte[] lvData = Encoding.Unicode.GetBytes(lvReturn);
byte[] lvHash = lvSHA1.ComputeHash(lvData);
RSACryptoServiceProvider lvCryptoProvider = (RSACryptoServiceProvider)lvCertFromBytes.PrivateKey;
byte[] lvSignedBytes = lvCryptoProvider.SignHash(lvHash, CryptoConfig.MapNameToOID("SHA1"));
string lvToken = Convert.ToBase64String(lvSignedBytes);
Java
String certB64 = "long b64 string here";
char[] Pass = "text password".toCharArray();
String alias = "guid looking ID here";
String plaintext = "Test";
byte[] certbytes = Base64.getDecoder().decode(certB64);
InputStream in = new ByteArrayInputStream(certbytes);
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(in,Pass);
KeyStore.PrivateKeyEntry pvk = (KeyStore.PrivateKeyEntry)keystore.getEntry(alias, new KeyStore.PasswordProtection(Pass));
PrivateKey pkey = (PrivateKey)pvk.getPrivateKey();
Signature rsa = Signature.getInstance("SHA1withRSA");
rsa.initSign(pkey);
rsa.update(plaintext.getBytes());
System.out.println("Hash: " + Base64.getEncoder().encodeToString(rsa.sign()));
I have Cert.pfx file that I want to use to use the privatekey to encrypt a https auth segment. I am just ripping the file to a base64 string and stuffing it into the "_Cert" var in C#. I do the same in Java. I want to sign the plaintext message using the private key of the cert and SHA1. The C# code below works and the https server provides a response. Java however is not spitting out the same base64 encoded string. Thanks for some help!
Update: I found a link to another post that is the same as mine but with a couple small diffs, and I didn't want to necro post on it. I followed it exactly removing the messagedigest piece of my original code. I tried reading directly from the pfx file or using the b64 string directly in the code. I am still not getting the same between Java and C#. At this point it has to be something small I am missing with encoding in Java because the C# is basically identical to mine.
Java Digital Signature different to C#
I'm trying to implement digital signature in php as in java sample code below:
Signature rsaSig = Signature.getInstance("MD5withRSA");
RSAPrivateKey clientPrivateKey = readPrivateKeyFromFile(fileName);
rsaSig.initSign(clientPrivateKey);
String source = msg;
byte temp[] = source.getBytes();
rsaSig.update(temp);
byte sig[] = rsaSig.sign();
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(sig);
My php code :
$rsa = new Crypt_RSA();
$rsa->loadKey('...'); // in xml format
$plaintext = '...';
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($plaintext);
But looks like some thing is missing. We should get same signature as java code returns.Can anybody guide me in this?
By default phpseclib uses sha1 as the hash. You probably need to do $rsa->setHash('md5').
I am trying to read data from password protected PEM file to generate public and private key from those data. I have the password and I need to pass that password during opening the file. But I haven't any option in java to do that.
Need emergency assistance on this. It would have been much better if I could get a code example for this.
EDITED:
My current code is -
public void readPrivateKey(String filePath)
{
File fp = new File(filePath);
FileInputStream fis = new FileInputStream(fp);
DataInputStrean dis = new DataInputStream(fis);
byte [] keyBytes = new byte [(int) fp.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String privateKeyPem = removeUnnecessaryTags(temp); //this function returns a string after removing String like this "BEGIN......."
byte[] decoded = Base64.decode(privateKeyPem);
ASN1Sequence primitive = (ASN1Sequence) ASN1Sequence.fromByteArray(decoded);
Enumeration <?> e = primitive.getObjects();
BigInteger bigInt = ((DERInteger)e.NextElement()).getValue();
int version = bigInt.intValue();
BigInteger modulus = ((DERInteger)e.NextElement()).getValue();
BigInteger publicExp = ((DERInteger)e.NextElement()).getValue();
//... all the values has been retrieved in this way
}
I believe you are taking about RSA key-pair.
You can do it without using Java like this,
openssl rsa -in MYFILE.pem -pubout > MYFILE.pub
ssh-keygen -f MYFILE.pub -i -m PKCS8
If you need to do this in Java, refer to link given by #m0skit0 in comments.
I'm converting a php script to java (for android) but find myself stuck converting the hmac signature process.
PHP which gives correct sign:
$secret = "lT4fhviR7ILvwGeiBJgolfYji1uz/f7B6HQWaWQWVl/sWEz3Kwt4QjzCHWE+MBENOmtgBS6PlN87s+1d7/8bRw==";
$nonce = "1388256620813308";
$postdata = "nonce=1388256620813308";
$path = "/0/private/Balance";
$sign = hash_hmac('sha512', $path . hash('sha256', $nonce . $postdata, true), base64_decode($this->secret), true);
echo $sign;
Hmac = 2IVoBCoadCEivxKVRB/4quJET4DoZV4JdY6bMC2oEYJZuygF5JiAhGrxVMyw2yPhz+KdiwvbzV43cicGamzr2A==
Which is correct and accepted signature
Java (with invalid sign):
String secret = "lT4fhviR7ILvwGeiBJgolfYji1uz/f7B6HQWaWQWVl/sWEz3Kwt4QjzCHWE+MBENOmtgBS6PlN87s+1d7/8bRw==";
String nonce = "1388256620813308";
String postdata = "nonce=1388256620813308";
String path = "/0/private/Balance";
// hash nonce + data
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update((nonce+postdata).getBytes());
byte[] digest = md.digest();
String baseString = path + new String(digest); //this is probably root of evil
// HMAC
Mac mac = Mac.getInstance("HmacSHA512");
SecretKey secretKey = new SecretKeySpec(Base64.decode(secret, Base64.DEFAULT), "HmacSHA512");
mac.init(secretKey);
String sign = new String(Base64.encodeToString(mac.doFinal(baseString.getBytes()), Base64.DEFAULT)).trim();
Log.d(TAG, sign);
Hmac = 7ZQfn+fqMpMEFN5Z/T5UwcqP1uo0JOyAVSn4HEBeE/KotnEf4a5bPOWriiC//gdoEg2kOe60EIr3Lv7irXuejw==
The problem is in the java string conversion of the bytes (even if I add "UTF-8" as characted encoding in getBytes). I know this because if I don add path to the hmac, and just feed it with digest without the string conversion the signature matches.
After posting question I did a quick and dirty test to add bytes from path manually to a new bytes array
byte[] digest = md.digest();
byte[] pBytes = path.getBytes();
int L = digest.length + pBytes.length;
byte[] message = new byte[L];
for (int i=0;i<pBytes.length;i++) {
message[i] = pBytes[i];
}
for (int i=pBytes.length,n=0; n<digest.length; n++) {
message[i+n] = digest[n];
}
String sign = new String(Base64.encodeToString(mac.doFinal(message), Base64.NO_WRAP));
And voilà; the hmac sign matches!
I have solved my problem but keeping question unanswered for some day to say if a better answer is provided that sheds light on this.