I am studying MD5 and also SHA with MessageDigest. Here is a code I have from class that implements MD5 with MessageDigest. I am having trouble understanding it.
So it gets the "instance" of MD5. I guess that is the MD5 algorithm? Then it updates the bytes. Why does it do this? Then it creates a variable bytes b with md.digest(), but I am not sure why it does this either? Then I think it uses the for statement to do the algorithm and maybe pad it (append 0?). If anyone could explain a little better, I'd appreciate!
try {
MessageDigest md = MessageDigest.getInstance("MD5"); // get the
// instance
// of md5
md.update(bytes); // get the digest updated
byte[] b = md.digest(); // calculate the final value
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
message = buf.toString(); // output as strings
} catch (NoSuchAlgorithmException e) {
e.printStackTrace(); // when certain algorithm is down, output the
// abnormal condition
}
return message;
}
md.update(bytes) just puts the bytes through MD5. byte[] b = md.digest() gets out the result of the MD5 hash as a series of bytes.
Then the whole rest of the code is a dreadfully awkward way to convert bytes into a hexadecimal string.
Related
I'm developing an Android app and I need to send some data from server to Android device.
To prevent app from downloading too much data,I wrote a php service, which takes hash (md5 hash of last downloaded data), provided by Android and compares it to latest data's hash on server. If hashes match each other, it prints 'no_new_data', otherwise it prints latest data. Php uses md5($string) method to calculate hash - this part seems to work fine.
The problem is that hash calculated on device never matches server's one - it is wrong, even though string seems to be same. I tried even with changing encoding but it didn't help.
My md5 java code:
public static String md5(String base){
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(base.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));
}
//System.out.println("Digest(in hex format):: " + sb.toString());
//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();
}catch (Exception e){
return "a";
}
}
Thnks :)
Sometimes md5 hash is different from serverside hash. Try this method.
public static String getMD5Hash(String s) throws NoSuchAlgorithmException {
String result = s;
if (s != null) {
MessageDigest md = MessageDigest.getInstance("MD5"); // or "SHA-1"
md.update(s.getBytes());
BigInteger hash = new BigInteger(1, md.digest());
result = hash.toString(16);
while (result.length() < 32) { // 40 for SHA-1
result = "0" + result;
}
}
return result;
}
Never, ever use String.getBytes(), which depends on the platform-default charset, which is almost never what you want. It seems likely that the platform default charset differs between Android and your server side.
Pass it a Charset instead, e.g.
myString.getBytes(StandardCharsets.UTF_8)
if you have Java 7, or
myString.getBytes("UTF-8")
if you cannot.
I need to access some data that used PHP encryption. The PHP encryption is like this.
base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($cipher), $text, MCRYPT_MODE_ECB));
As value of $text they pass the time() function value which will be different each time that the method is called in. I have implemented this in Java. Like this,
public static String md5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Huh, MD5 should be supported?", e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Huh, UTF-8 should be supported?", e);
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
int i = (b & 0xFF);
if (i < 0x10) hex.append('0');
hex.append(Integer.toHexString(i));
}
return hex.toString();
}
public static byte[] rijndael_256(String text, byte[] givenKey) throws DataLengthException, IllegalStateException, InvalidCipherTextException, IOException{
final int keysize;
if (givenKey.length <= 192 / Byte.SIZE) {
keysize = 192;
} else {
keysize = 256;
}
byte[] keyData = new byte[keysize / Byte.SIZE];
System.arraycopy(givenKey, 0, keyData, 0, Math.min(givenKey.length, keyData.length));
KeyParameter key = new KeyParameter(keyData);
BlockCipher rijndael = new RijndaelEngine(256);
ZeroBytePadding c = new ZeroBytePadding();
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
pbbc.init(true, key);
byte[] plaintext = text.getBytes(Charset.forName("UTF8"));
byte[] ciphertext = new byte[pbbc.getOutputSize(plaintext.length)];
int offset = 0;
offset += pbbc.processBytes(plaintext, 0, plaintext.length, ciphertext, offset);
offset += pbbc.doFinal(ciphertext, offset);
return ciphertext;
}
public static String encrypt(String text, String secretKey) throws Exception {
byte[] givenKey = String.valueOf(md5(secretKey)).getBytes(Charset.forName("ASCII"));
byte[] encrypted = rijndael_256(text,givenKey);
return new String(Base64.encodeBase64(encrypted));
}
I have referred this answer when creating MCRYPT_RIJNDAEL_256 method."
Encryption in Android equivalent to php's MCRYPT_RIJNDAEL_256
"I have used apache codec for Base64.Here's how I call the encryption function,
long time= System.currentTimeMillis()/1000;
String encryptedTime = EncryptionUtils.encrypt(String.valueOf(time), secretkey);
The problem is sometimes the output is not similar to PHP but sometimes it works fine.
I think that my MCRYPT_RIJNDAEL_256 method is unreliable.
I want to know where I went wrong and find a reliable method so that I can always get similar encrypted string as to PHP.
The problem is likely to be the ZeroBytePadding. The one of Bouncy always adds/removes at least one byte with value zero (a la PKCS5Padding, 1 to 16 bytes of padding) but the one of PHP only pads until the first block boundary is encountered (0 to 15 bytes of padding). I've discussed this with David of the legion of Bouncy Castle, but the PHP zero byte padding is an extremely ill fit for the way Bouncy does padding, so currently you'll have to do this yourself, and use the cipher without padding.
Of course, as a real solution, rewrite the PHP part to use AES (MCRYPT_RIJNDAEL_128), CBC mode encryption, HMAC authentication, a real Password Based Key Derivation Function (PBKDF, e.g. PBKDF2 or bcrypt) and PKCS#7 compatible padding instead of this insecure, incompatible code. Alternatively, go for OpenSSL compatibility or a known secure container format.
I need to calculate the SHA 256 for my password.
i already know that I can user the common codec from apache but this is not allowed in where i am working
I tried to make a simple function to return the sha 256 from a plain text, which is:
public static String getSHA1(String plainText) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
md.update(plainText.getBytes());
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < md.digest().length; i++) {
hexString.append(Integer.toHexString(0xFF & md.digest()[i]));
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
my problem is whatever the input is, the result is the same. i always got this result
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
I can calculate the sha 256 online using this website http://onlinemd5.com/
but i need to calculate it from my code.
your help is appreciated and lovely.
From the Javadoc for digest():
Completes the hash computation by performing final operations such as padding. The digest is reset after this call is made.
Call digest() once and put the results into a variable.
(By the way, had you searched for the digest, which is always a good idea whenever you get a fixed result, you would have seen that it's the SHA-256 digest for the empty string.)
I am new to blackberry development and got to complete a task of encryption and decryption with AES/ECB/NoPadding. I used below code, from internet.
Encryption method:
public static byte[] encrypt( byte[] keyData,String message )
throws Exception
{
byte[] data = message.getBytes("UTF-8");
// Create the AES key to use for encrypting the data.
// This will create an AES key using as much of the keyData
// as possible.
if ((data.length % 16) != 0 ) {
StringBuffer buffer = new StringBuffer(message);
int moduleOut = data.length % 16;
int padding = 16 - moduleOut;
for(int i = 0 ; i < padding; i++){
buffer.append(" ");
}
data = buffer.toString().getBytes("UTF-8");
}
AESKey key = new AESKey( keyData);
NoCopyByteArrayOutputStream out = new NoCopyByteArrayOutputStream(data.length);
AESEncryptorEngine engine = new AESEncryptorEngine(key);
BlockEncryptor encryptor = new BlockEncryptor(engine, out);
encryptor.write(data,0,data.length);
int finalLength = out.size();
byte[] cbytes = new byte[finalLength];
System.arraycopy(out.toByteArray(), 0, cbytes, 0, finalLength);
// encryptor.close();
// out.close();
return cbytes;
}
Decryption method:
public static byte[] decrypt(byte[] keyData, byte[] base64EncodedData)
throws CryptoException, IOException
{
// String base64EncodedData=new String(base64EncodedData);
byte[] cipherText =Base64ToBytes(new String(base64EncodedData));
// First, create the AESKey again.
AESKey key = new AESKey(keyData);
// Now, create the decryptor engine.
AESDecryptorEngine engine = new AESDecryptorEngine(key);
// Create the BlockDecryptor to hide the decryption details away.
ByteArrayInputStream input = new ByteArrayInputStream(cipherText);
BlockDecryptor decryptor = new BlockDecryptor(engine, input);
// Now, read in the data.
byte[] temp = new byte[100];
DataBuffer buffer = new DataBuffer();
for (;;)
{
int bytesRead = decryptor.read(temp);
buffer.write(temp, 0, bytesRead);
if (bytesRead < 100)
{
// We ran out of data.
break;
}
}
byte[] plaintext = buffer.getArray();
return plaintext;
}
Base64 to Bytes convert method:
private static byte[] Base64ToBytes(String code) {
byte[] aesString = null;
try
{
aesString = Base64InputStream.decode(code);
}
catch (IOException ioe)
{
}
return aesString;
}
Now the problem is when i encrypt my string with above method i get padded unicodes at the end of the string, which is not tolerable at Server side.
I know it is due to PKCS5FormatterEngine and its normal to have characters appended at end of the string while using this class.
But what if i want to encrypt and decrypt string with AES/ECB method and that too with NoPadding. I know ECB is not a secure mode and all that but server is with PHP and ready, works great in Android, and J2ME.
Please guide. How to bypass PKCS5FormatterEngine or encrypt without any padding.
Update:
I tried to use Cipher class in Blackberry as i used in Android and J2ME but it seems unavailable in net_rim_api.jar, even i tried downloading bouncy castle jar file and dependent class NoSuchAlogrithmException so java.security jar (org.osgi.foundation-1.0.0.jar), compiles but when i try to run it stops saying duplicate classes found. It has a problem with few duplicate classes in jar i have kept for java.security.
If you have solution towards this, please let me know.
Update for Answer:
I have update my code with full encryption and decryption code and also check the answer for better understanding.
Not sure this is really an answer in general, but it might help in this specific case, so I am adding it as such.
You can't really use AES without some padding, because the AES processing does not wish to assume the data you supply will be a multiple of 16 bytes. However if you actually always supply a buffer that is a multiple of 16 bytes, then you can just encrypt your data with code like this:
AESEncryptorEngine engine = new AESEncryptorEngine( key );
for ( int j = 0; j < ciphertext.length - 15; ) {
engine.encrypt(plainText, j, ciphertext, j);
j = j+16;
}
So how do you make sure this works OK at the other end? It may or may not be possible to do this - it actually depends on what is being transferred.
But if, for example, you were passing XML data, then you can append spaces to make the data up to a 16 byte boundary and these will be decrypted as spaces by the Server, and then ignored by the parsing. You can add redundant padding bytes to a variety of file formats and the padding will be ignored, it all depends on the file format being processed.
Update
Given that the actual data is JSON and that I believe that JSON will ignore trailing spaces, the approach I would take is append spaces to the JSON data before encrypting it.
So convert the JSON string to bytes:
byte [] jsonBytes = jsonString.getBytes("UTF-8");
pad this if you need too:
if ( (jsonBytes.length % 16) != 0 ) {
// Now pad this with spaces
}
and you can encrypt the result with no worries about padding bytes.
I have an Android app that is the "server" in a client/server design. In the app, I need to compute an MD5 hash against a set of strings and return the result to the client in order to let the conversation between them to continue. My code to do this has been pieced together from numerous examples out there. The algorithm of computing the hash (not designed by me) goes like this:
Convert the string into an array of bytes
Use the MessageDigest class to generate a hash
Convert resulting hash back to a string
The hash seems to be correct for 99% of my customers. One of the customers seeing the wrong hash is running with a German locale, and it started to make me wonder if language could be factoring into the result I get. This is the code to make the byte array out of the string:
public static byte[] hexStringToByteArray(String s)
{
byte[] data = null;
if(s.length() % 2 != 0)
{
s = "0" + s;
}
int len = s.length();
data = new byte[len / 2];
for (int i = 0; i < len; i += 2)
{
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
And here's the current version of the hashing function:
public static String hashDataAsString(String dataToHash)
{
MessageDigest messageDigest;
try
{
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
byte[] data = hexStringToByteArray(dataToHash);
messageDigest.update(data);
final byte[] resultByte = messageDigest.digest();
return new String(Hex.encodeHex(resultByte));
}
catch(NoSuchAlgorithmException e)
{
throw new RuntimeException("Failed to hash data values", e);
}
}
I'm using the Hex.encodeHex function from Apache Commons.
I've tried switching my phone to a German locale, but my unit tests still produce the correct hash result. This customer is using stock Froyo, so that eliminates the risk that a custom ROM is at fault here. I've also found this alternative for converting from bytes to a string:
public static String MD5_Hash(String s) {
MessageDigest m = null;
try {
m = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
//m.update(s.getBytes(),0,s.length());
byte [] data = hexStringToByteArray(s);
m.update(data, 0, data.length);
String hash = new BigInteger(1, m.digest()).toString(16);
return hash;
}
In my unit tests, it results in the same answer. Could BigInteger be a safer alternative to use here?
In your hashDataAsString method, do you need to do hexStringToByteArray? Is the incoming data a hex string or just an arbitrary string? Could you not use String.getBytes()?
If you are doing string/byte conversions, do you know the encoding of the incoming data and the encoding assumptions of your data consumers? Do you need to use a consistent encoding at both ends (e.g. ASCII or UTF-8)?
Do you include non-ASCII data in your unit tests?