This is the Java code for the function which create a keyed hash digest, and I want to do the same but in PHP code.
Java code:
private String generateHash(final InputStream is, final int iteration,final String key) throws IOException,NoSuchAlgorithmException,InvalidKeyException {
Mac sha256_HMAC;
sha256_HMAC = Mac.getInstance(ALGORITHM);
final SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), ALGORITHM);
sha256_HMAC.init(secret_key);
byte[] bytesBuffer = new byte[2048];
int bytesRead = -1;
while ((bytesRead = is.read(bytesBuffer)) != -1) {
sha256_HMAC.update(bytesBuffer, 0, bytesRead);
}
byte[] digestValue = sha256_HMAC.doFinal();
for (int i = 0; i < iteration; i++) {
sha256_HMAC.reset();
digestValue = sha256_HMAC.doFinal(digestValue);
}
return Base64.encodeBase64String(digestValue);
}
This the PHP code I have tried, but the output of both code are not same.
And also that I think I'm missing something somewhere because I don't know alternative for each code statement of Java in PHP:
function generateHash($file, $iteration, $key){
//$hash = hash_hmac('sha256', 'hello, world!', 'mykey');
$inithash=hash_init('SHA256',1,$key);
//mb_strlen($string, '8bit')
while ($buffer=fread($file,"2048")) {
hash_update($inithash, $buffer);
}
$hash = hash_final($inithash);
$hashbyte = unpack('C*',$hash);
for($i=0;$i<$iteration;$i++)
{
//unset($hashbyte);
$inithash=hash_init('SHA256');
$inithash = hash_final($inithash);
}
return base64_encode($inithash);
}
Related
This is C# code. I want to implement it in JAVA exactly.
The result of encryption is as same as C#. If you know about it. Please keep me posted!!
public static byte[] HexStringToByteArray(string hex)
{
int hexLen = hex.Length;
byte[] ret = new byte[hexLen / 2];
for (int i = 0; i < hexLen; i += 2)
{
ret[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return ret;
}
public string doEncryption(string password)
{
// If needed, working sample values for mod and exp can be provided
string mod = "9e5787bead673a363178b9437030edf4ea6e26640c1f318e447d5685ddcf582002d15f459b392abffe0fd6001cb1f289f14be8b67da21e7982edf72b15ff8f7859e4c6bbb26abc99eb58ead851b4eec9f7a2a9b0c3dca3339c03f1c1a9a6d9a458728f8eb129cb52dee0606e5e6ffbfbf0fa777f8b3ca04a63da66ce4ef32b5aad4ee11928b8c3a6af10fedc2b76b0d7afc3b5c53d8572ee04cd16fcc364aa8486d53d4b773a20a2628f3db4483eea00fc441755e061b1184f2a21bd8753fb33982330eb0fc94f30960640dfa3876ddf7d8ef907f9eff23b67a225d40d6c15dea90649f51fd113c9f39467961962f1b9992140e3c10b003f0afeb8c95eec7235";
string exp = "010001";
RNGCryptoServiceProvider secureRandom = new RNGCryptoServiceProvider(); // Import System.Security.Cryptography
byte[] encryptedPasswordBytes;
using (var rsaEncryptor = new RSACryptoServiceProvider())
{
var passwordBytes = Encoding.ASCII.GetBytes(password);
var rsaParameters = rsaEncryptor.ExportParameters(false);
rsaParameters.Exponent = HexStringToByteArray(exp);
rsaParameters.Modulus = HexStringToByteArray(mod);
rsaEncryptor.ImportParameters(rsaParameters);
encryptedPasswordBytes = rsaEncryptor.Encrypt(passwordBytes, false);
}
// Encrypted password should be identical in the C# and Java versions
string encryptedPassword = Convert.ToBase64String(encryptedPasswordBytes);
return encryptedPassword;
}
I'm trying to convert the following C# code to Java, the code is using .NET library (System.Security.Cryptography), I want a work around to get the same results using Java:
internal static class CryptoHelper
{
// keys must be 16 characters long (and ASCII)
public const string DefaultKey = "2$8Kba19z23asd!#";
public const string IV = "0000000000000000";
enum CryptoTransformType
{
Encryptor,
Decryptor
}
public static byte[] Encrypt(bool FIPScompatible, string key, byte[] data)
{
if(key == null) throw new ArgumentNullException("key");
if(data == null) return null;
// Create the crypto
ICryptoTransform transform = CreateCryptoTransform(FIPScompatible,CryptoTransformType.Encryptor, key, IV);
// Encrypt the data
using(MemoryStream ms = new MemoryStream())
{
using(CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
}
byte[] encryptedData = ms.GetBuffer();
return encryptedData;
}
}
public static byte[] Decrypt(bool FIPScompatible, string key, byte[] data)
{
if(key == null) throw new ArgumentNullException("key");
if(data == null) return null;
// Create the crypto
ICryptoTransform transform = CreateCryptoTransform(FIPScompatible,CryptoTransformType.Decryptor, key, IV);
// Decrypt the data
using(MemoryStream ms = new MemoryStream(data))
{
using(CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Read))
{
byte[] decryptedData = new byte[data.Length];
cs.Read(decryptedData, 0, decryptedData.Length);
return decryptedData;
}
}
}
private static ICryptoTransform CreateCryptoTransform(bool FIPScompatible,CryptoTransformType transformType, string key, string iv)
{
if (!FIPScompatible)
{
RijndaelManaged rijndael = new RijndaelManaged();
rijndael.Mode = CipherMode.CBC;
rijndael.Padding = PaddingMode.Zeros;
rijndael.BlockSize = 128;
byte[] bykey = Encoding.ASCII.GetBytes(key);
byte[] byiv = Encoding.ASCII.GetBytes(iv);
ICryptoTransform transform = null;
if (transformType == CryptoTransformType.Encryptor)
transform = rijndael.CreateEncryptor(bykey, byiv);
else
transform = rijndael.CreateDecryptor(bykey, byiv);
return transform;
}
else
{
AesCryptoServiceProvider provider = new AesCryptoServiceProvider();
provider.Padding = PaddingMode.Zeros;
provider.BlockSize = 128;
byte[] bykey = Encoding.ASCII.GetBytes(key);
byte[] byiv = Encoding.ASCII.GetBytes(iv);
ICryptoTransform transform = null;
if (transformType == CryptoTransformType.Encryptor)
transform = provider.CreateEncryptor(bykey, byiv);
else
transform = provider.CreateDecryptor(bykey, byiv);
return transform;
}
}
}
I tried a method but I couldn't get the same results that comes out from the .NET code, the method I tried will be added in the comments.
My application requires the users to upload digitally signed pdf and then encrypt the file. This encrypted file is then uploaded on server, where it is decrypted. The decrypted file is then verified by matching the hash of file and digital signature. Now this file is encrypted with using AES algorithm. Once encryption is completed the file is then stored on file server. The size of file could go upto 80mb.
The challenge I am facing now is that when the encrypted file is stored on local drive of machine the files get saved instantly but when the file server is on another machine it takes upto 30 min to save a single file. I am not able to figure out the reason for it.
Following is the code which I am using. I have deployed and tried in tomcat 6 and IBM WAS. The file transfer takes the same time when transferring to file server. The file server is connected to Application server via SAN network.
Following is my encryption code
strSymAlg = rb.getString("SYM_KEY_ALG"); //SYM_KEY_ALG=AES
cipher = Cipher.getInstance(strSymAlg);
SecKey = new SecretKeySpec(hex2Byte(sSymmetricKey), strSymAlg);
cipher.init(Cipher.DECRYPT_MODE, SecKey);
baos = recoverFile(new FileInputStream(fileEnv), cipher);
if (baos != null && isRecoveredFileValid((InputStream) new ByteArrayInputStream(baos.toByteArray()))) {
fileRecovered = (InputStream) new ByteArrayInputStream(baos.toByteArray());
}
}
private ByteArrayOutputStream recoverFile(FileInputStream in, Cipher cipher) {
int blockSize = cipher.getBlockSize();
int outputSize = cipher.getOutputSize(blockSize);
byte[] inBytes = new byte[blockSize];
byte[] outBytes = new byte[outputSize];
int inLength = 0;
int outLength = 0;
boolean more = true;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
while (more) {
inLength = in.read(inBytes);
if (inLength == blockSize) {
outLength = cipher.update(inBytes, 0, blockSize, outBytes);
baos.write(outBytes, 0, outLength);
} else {
more = false;
}
}
if (inLength > 0) {
outBytes = cipher.doFinal(inBytes, 0, inLength);
} else {
outBytes = cipher.doFinal();
}
baos.write(outBytes);
} catch (Exception e) {
System.out.println("recoverFile1: " + e.getMessage());
// e.printStackTrace();
baos = null;
}
return baos;
}
my encryption code is
String strSymKey = "";
File fileToCreate = null;
KeyGenerator keygen = KeyGenerator.getInstance(strSymAlg);
random = new SecureRandom();
keygen.init(random);
SecretKey secKey = keygen.generateKey();
Key publicKey = getPublicKeyFromString(sPubKey.trim());
//encrypt Symmetric key with public key
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.WRAP_MODE, publicKey);
byte[] wrappedKey = cipher.wrap(secKey);
strSymKey = byte2hex(wrappedKey);
fileToCreate = new File(strFile);
if (fileToCreate.exists()) {
fileToCreate.delete();
}
//Encrypt Bidder file with symmetric key
DataOutputStream out = new DataOutputStream(new FileOutputStream(strFile));
cipher = Cipher.getInstance(strSymAlg);
cipher.init(Cipher.ENCRYPT_MODE, secKey);
crypt(fis, out, cipher);
fis.close();
out.close();
//blnDone=true;
// System.out.println("STRING SYMMETRIC KEY:"+ strSymKey);
return strSymKey;
public String byte2hex(byte[] b) {
// String Buffer can be used instead
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1) {
hs = hs + "0" + stmp;
} else {
hs = hs + stmp;
}
if (n < b.length - 1) {
hs = hs + "";
}
}
return hs;
}
New Function added
public void crypt(InputStream in, OutputStream out, Cipher cipher) throws IOException, GeneralSecurityException {
System.out.println("crypt start time :"+ new Date());
int blockSize = cipher.getBlockSize();
int outputSize = cipher.getOutputSize(blockSize);
byte[] inBytes = new byte[blockSize];
byte[] outBytes = new byte[outputSize];
int inLength = 0;
boolean more = true;
while (more) {
inLength = in.read(inBytes);
if (inLength == blockSize) {
int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
out.write(outBytes, 0, outLength);
} else {
more = false;
}
}
if (inLength > 0) {
outBytes = cipher.doFinal(inBytes, 0, inLength);
} else {
outBytes = cipher.doFinal();
}
System.out.println("crypt end time :"+ new Date());
out.write(outBytes);
}
Thanks in advance
You are making the classic mistake of assuming that every read fills the buffer, and another: that it won't do so at end of stream. Neither is correct.
while ((count = in.read(buffer)) >= 0)
{
out.write(cipher.update(buffer, 0, count));
}
out.write(cipher.doFinal());
You don't need a DataOutputStream for this.
EDIT
I changed $checksum = md5($someString+$bkey); to $checksum = md5($someString.$bkey);
I need to perform the following operations in Java:
$hexString = '90aa';#sample value
$bkey = pack('H*',$hexString);
$someString='qwe';#sample value
$checksum = md5($someString.$bkey);
echo $checksum;
I can't convert hexString to bkey in Java to get the same result as php script. Except bkey everything is working properly.
If I remove bkey then:
PHP:
$someString='qwe';#sample value
$checksum = md5($someString);
echo $checksum;
result: 76d80224611fc919a5d54f0ff9fba446
Java:
String someString = "qwe";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
String checksum = new BigInteger(1, messageDigest.digest(someString
.getBytes())).toString(16);
System.out.println(checksum);
result: 76d80224611fc919a5d54f0ff9fba446
As you can see, it works
With bkey:
PHP:
$hexString = '90aa';#sample value
$bkey = pack('H*',$hexString);
$someString='qwe';#sample value
$checksum = md5($someString.$bkey);
echo $checksum;
result: 18f5f1a9bf898131945dd9e315759fe4
Java:
public static void main(String[] args) throws NoSuchAlgorithmException {
String hexString = "90aa";
String bkey = hexToString(hexString);
String someString = "qwe";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
String input = someString + bkey;
String checksum = new BigInteger(1, messageDigest.digest(input
.getBytes())).toString(16);
System.out.println(checksum);
}
public static String hexToString(String hex) {
StringBuilder output = new StringBuilder();
for (int i = 0; i < hex.length(); i += 2) {
String str = hex.substring(i, i + 2);
output.append((char) Integer.parseInt(str, 16));
}
return output.toString();
}
result: 44bb634dee436833dd65caa5043ffeb9
As you can see results are different.
How to convert hex String to String to get the same result?
The problem is not actually in your Java code, but in the PHP code.
The line $checksum = md5($someString+$bkey); does not do what you think it does, it should be:
$checksum = md5($someString . $bkey); # use concatenate, not sum
Although, that gives abetter PHP MD5, but does not help make the Java code match the PHP
EDIT
The problem on the Java side is in the character encoding. The Java char values for the inoacked versions of 90aa are not valid unicode characters. Thus the toByteArray() ,ethods ar enot doing great things. If you treat all of the Java code at a byte level (and ignore any high-bytes in any chars in the Java), then you get the same result as the PHP:
public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String hexString = "90aa";
byte[] bkey = hexToString(hexString);
String someString = "qwe";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] input = join(stringToBytes(someString), bkey);
String checksum = new BigInteger(1, messageDigest.digest(input)).toString(16);
System.out.println(checksum);
System.out.println(Charset.defaultCharset().displayName());
}
private static byte[] join(byte[] a, byte[] b) {
// join two byte arrays
final byte[] ret = new byte[a.length + b.length];
System.arraycopy(a, 0, ret, 0, a.length);
System.arraycopy(b, 0, ret, a.length, b.length);
return ret;
}
public static byte[] hexToString(String hex) {
// hexToString that works at a byte level, not a character level
byte[] output = new byte[(hex.length() + 1) / 2];
for (int i = hex.length() - 1; i >= 0; i -= 2) {
int from = i - 1;
if (from < 0) {
from = 0;
}
String str = hex.substring(from, i + 1);
output[i/2] = (byte)Integer.parseInt(str, 16);
}
return output;
}
public static byte[] stringToBytes(final String input) {
// unlike Stirng.toByteArray(), we ignore any high-byte values of the characters.
byte[] ret = new byte[input.length()];
for (int i = input.length() - 1; i >=0; i--) {
ret[i] = (byte)input.charAt(i);
}
return ret;
}
The above Java code produces the MD5sum 18f5f1a9bf898131945dd9e315759fe4 which is what PHP gives too
Can someone figure out why the output of these (php and java) snippets of code don't return the same SHA512 for the same input?
$password = 'whateverpassword';
$salt = 'ieerskzcjy20ec8wkgsk4cc8kuwgs8g';
$salted = $password.'{'.$salt.'}';
$digest = hash('sha512', $salted, true);
echo "digest: ".base64_encode($digest);
for ($i = 1; $i < 5000; $i++) {
$digest = hash('sha512', $digest.$salted, true);
}
$encoded_pass = base64_encode($digest);
echo $encoded_pass;
This is the code on the android application:
public String processSHA512(String pw, String salt, int rounds)
{
try {
md = MessageDigest.getInstance("SHA-512");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("No Such Algorithm");
}
String result = hashPw(pw, salt, rounds);
System.out.println(result);
return result;
}
private static String hashPw(String pw, String salt, int rounds) {
byte[] bSalt;
byte[] bPw;
String appendedSalt = new StringBuilder().append('{').append(salt).append('}').toString();
try {
bSalt = appendedSalt.getBytes("ISO-8859-1");
bPw = pw.getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported Encoding", e);
}
byte[] digest = run(bPw, bSalt);
Log.d(LCAT, "first hash: " + Base64.encodeBytes(digest));
for (int i = 1; i < rounds; i++) {
digest = run(digest, bSalt);
}
return Base64.encodeBytes(digest);
}
private static byte[] run(byte[] input, byte[] salt) {
md.update(input);
return md.digest(salt);
}
The library for base64 encoding is this: base64lib
This java code is actually some modified code I found around another question in StackOverflow.
Although the Android code is running fine it doesn't match with the output from the php script. It doesn't even match the first hash!
Note 1: On php hash('sha512',$input, $raw_output) returns raw binary output
Note 2: On java I tried to change the charset (UTF-8, ASCII) but it also didn't work.
Note 3: The code from the server can not be changed, so I would appreciate any answer regarding how to change my android code.
The first hash should be the same on the server and in Java. But then in the loop what gets appended to the digest is password{salt} in the PHP code, but only {salt} in the Java code.
For the lazy ones, one example better than a thousand words ;). I finally understood what was happening. The method update appends bytes to the digest, so when you append $password.{$salt} is the same as doing mda.update(password bytes) and the mda.digest("{$salt}" bytes. I do that answer because I was going crazy finding why it was not working and it was all in this answer.
Thanks guys.
This is the example that works in a Java Server:
public static String hashPassword(String password, String salt) throws Exception {
String result = password;
String appendedSalt = new StringBuilder().append('{').append(salt).append('}').toString();
String appendedSalt2 = new StringBuilder().append(password).append('{').append(salt).append('}').toString();
if(password != null) {
//Security.addProvider(new BouncyCastleProvider());
MessageDigest mda = MessageDigest.getInstance("SHA-512");
byte[] pwdBytes = password.getBytes("UTF-8");
byte[] saltBytes = appendedSalt.getBytes("UTF-8");
byte[] saltBytes2 = appendedSalt2.getBytes("UTF-8");
byte[] digesta = encode(mda, pwdBytes, saltBytes);
//result = new String(digesta);
System.out.println("first hash: " + new String(Base64.encode(digesta),"UTF-8"));
for (int i = 1; i < ROUNDS; i++) {
digesta = encode(mda, digesta, saltBytes2);
}
System.out.println("last hash: " + new String(Base64.encode(digesta),"UTF-8"));
result = new String(Base64.encode(digesta));
}
return result;
}
private static byte[] encode(MessageDigest mda, byte[] pwdBytes,
byte[] saltBytes) {
mda.update(pwdBytes);
byte [] digesta = mda.digest(saltBytes);
return digesta;
}