I have an application that encrypts files for storage purposes and decrypts them for uploading to our server. For the most part it works fine and the decrypted files we receive are good files.
However, on a semi-regular basis (one or two files out of a few hundred every few days) the decrypted files are corrupt. It's possible that the original files were corrupt as well and the issue was not in the encrypt/decrypt process, but this has occurred to different users who presumably would have made sure that the files weren't corrupt to begin with, but I don't know how to verify this as I do not have direct contact with our users.
I want to make sure that my code is not the source of the problem, or if there is something I can do to check that the files were safely encrypted/decrypted in the application to make sure all the files we receive are good.
What is strange is that I have an ExceptionManager class that sends me any exceptions that occur, and I do not receive any exceptions when these corrupt files get processed and uploaded, so I have no way of knowing what went wrong.
This is my Encrypt code:
static string aesTransform = "AES/CBC/PKCS5Padding"
public static bool EncryptFile(Context context, string fileLocation, ref string encryptedFileLocation)
{
bool success;
try
{
Log logger = new Log(context);
logger.Debug("starting encryption");
if (File.Exists(fileLocation))
{
encryptedFileLocation = (encryptedFileLocation ?? fileLocation) + ".aes";
using (FileStream fInput = File.Open(fileLocation, FileMode.Open))
using (FileStream fOutput = File.Open(encryptedFileLocation, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
byte[] keyBytes = Encoding.UTF8.GetBytes(encryptKey);
SecretKeySpec key = new SecretKeySpec(keyBytes, 0, keyBytes.Length, "AES");
Cipher cipher = Cipher.GetInstance(aesTransform);
cipher.Init(CipherMode.EncryptMode, key, new IvParameterSpec(new byte[cipher.BlockSize]));
CipherOutputStream cos = new CipherOutputStream(fOutput, cipher);
byte[] buffer = new byte[1024];
int numBytesWritten = 0;
while ((numBytesWritten = fInput.Read(buffer, 0, buffer.Length)) > 0)
{
cos.Write(buffer, 0, numBytesWritten);
}
cos.Flush();
cos.Close();
}
logger.WriteLog(Log.LogType.Misc, fileLocation + " encrypted");
Delete(context, fileLocation);
}
success = true;
}
catch(Exception ex)
{
if (File.Exists(fileLocation) && File.Exists(encryptedFileLocation))
{
Delete(context, encryptedFileLocation);
}
if(encryptedFileLocation.EndsWith(".aes"))
{
encryptedFileLocation = encryptedFileLocation.Remove(encryptedFileLocation.ToUpper().LastIndexOf(".AES"));
}
ex.Data.Add("fileLocation", fileLocation);
ex.Data.Add("encryptedFileLocation", encryptedFileLocation);
ExceptionManager.ManageException(context, ex);
success = false;
}
return success;
}
And this is my Decrypt Code:
static string aesTransform = "AES/CBC/PKCS5Padding"
public static bool DecryptFile(Context context, string fileLocation, ref string decryptedFileLocation)
{
bool decrypted = false;
try
{
if (File.Exists(fileLocation))
{
if(fileLocation.ToUpper().EndsWith(".AES"))
{
decryptedFileLocation = fileLocation.Remove(fileLocation.ToUpper().LastIndexOf(".AES"));
}
else
{
decryptedFileLocation = fileLocation;
}
using (FileStream fInput = File.Open(fileLocation, FileMode.Open))
using (FileStream fOutput = File.Open(decryptedFileLocation, FileMode.OpenOrCreate))
{
FileInfo info = new FileInfo(fileLocation);
byte[] keyBytes = Encoding.UTF8.GetBytes(encryptKey);
SecretKeySpec key = new SecretKeySpec(keyBytes, 0, keyBytes.Length, "AES");
Cipher cipher = Cipher.GetInstance(aesTransform);
cipher.Init(CipherMode.DecryptMode, key, new IvParameterSpec(new byte[cipher.BlockSize]));
CipherInputStream cis = new CipherInputStream(fInput, cipher);
byte[] buffer = new byte[1024];
int numBytesWritten = 0;
while ((numBytesWritten = cis.Read(buffer, 0, buffer.Length)) > 0)
{
fOutput.Write(buffer, 0, numBytesWritten);
}
cis.Close();
decrypted = true;
Log logger = new Log(context);
logger.WriteLog(Log.LogType.Misc, fileLocation + " decrypted");
}
}
}
catch (Exception ex)
{
if (File.Exists(fileLocation) && File.Exists(decryptedFileLocation))
{
Delete(context, decryptedFileLocation);
}
decryptedFileLocation = fileLocation;
ex.Data.Add("fileLocation", fileLocation);
ExceptionManager.ManageException(context, ex);
decrypted = false;
}
return decrypted;
}
The encrypt key is identical for both Encrypt and Decrypt methods, both referencing a static string declared earlier in this class, so the keys match.
How can i calculate the CheckSum of my APK file in android? I want to calculate the APK checksum and compare it everytime my app. executes to see if some one has modified the binary code? How can i calculate check sum and achieve this?
Updated in 2020 - Google Play can now optimise, repackage and re-sign uploaded .apks (and add security meta data to the .apk) so it's unlikely this tamper check is still valid. Better to use the SafetyNet attestation API to verify the device and in turn your app - just ensure you're verifying the signature offline on your server.
Here's some code to checksum your APK. I wrote and article on adding tamper detections to your apps (which ironically didn't include apk checksum).
private static long getApkFileChecksum(Context context) {
String apkPath = context.getPackageCodePath();
Long chksum = null;
try {
// Open the file and build a CRC32 checksum.
FileInputStream fis = new FileInputStream(new File(apkPath));
CRC32 chk = new CRC32();
CheckedInputStream cis = new CheckedInputStream(fis, chk);
byte[] buff = new byte[80];
while (cis.read(buff) >= 0) ;
chksum = chk.getValue();
} catch (Exception e) {
e.printStackTrace();
}
return chksum;
}
You could also use this to can the sha-256 of your apk...
public static String getApkFileDigest(Context context) {
String apkPath = context.getPackageCodePath();
try {
byte[] hashed= getDigest(new FileInputStream(apkPath), "SHA-256");
return Base64.encodeToString(hashed, Base64.DEFAULT);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
public static final int BUFFER_SIZE = 2048;
public static byte[] getDigest(InputStream in, String algorithm) throws Throwable {
MessageDigest md = MessageDigest.getInstance(algorithm);
try {
DigestInputStream dis = new DigestInputStream(in, md);
byte[] buffer = new byte[BUFFER_SIZE];
while (dis.read(buffer) != -1) {
}
dis.close();
} finally {
in.close();
}
return md.digest();
}
Well its embarrassing to ask this question despite having lot of articles related to this in web, but I could not figure it out.I am struck with this one.
I am encrypting a text in Android using AES/CBC/PKCS5 algorithm but I could not decrypt it in windows 8 appstore application.Here is my encryption code
public static String encrypt(String plainText,String password) throws Exception {
// convert key to bytes
byte[] keyBytes = password.getBytes("UTF-8");
// Use the first 16 bytes (or even less if key is shorter)
byte[] keyBytes16 = new byte[16];
System.arraycopy(keyBytes, 0, keyBytes16, 0,
Math.min(keyBytes.length, 16));
// convert plain text to bytes
byte[] plainBytes = plainText.getBytes("UTF-8");
// setup cipher
SecretKeySpec skeySpec = new SecretKeySpec(keyBytes16, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[16]; // initialization vector with all 0
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));
// encrypt
byte[] encrypted = cipher.doFinal(plainBytes);
String encryptedString = Base64.encodeToString(
cipher.doFinal(plainBytes), Base64.NO_WRAP);
// encryptedString
return Base64.encodeToString(encrypted, Base64.NO_WRAP);
}
I am encrypting using the following code in windows 8 application
public string AES_Encrypt(string input, string pass)
{
SymmetricKeyAlgorithmProvider SAP = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
string encrypted = "";
try
{
byte[] test1 = System.Text.Encoding.UTF8.GetBytes(pass);
byte[] test2 = new byte[16];
for (int i = 0; i < test1.Length;i++ )
{
test2[i] = test1[i];
}
CryptographicKey key =
SAP.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(test2));
IBuffer Buffer = CryptographicBuffer.CreateFromByteArray(System.Text.Encoding.UTF8.GetBytes(input));
encrypted = CryptographicBuffer.EncodeToBase64String(CryptographicEngine.Encrypt(key, Buffer, null));
return encrypted;
}
catch (Exception ex)
{
return null;
}
}
following is the decryption algo
public string AES_Decrypt(string input, string pass)
{
SymmetricKeyAlgorithmProvider SAP = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
string decrypted = "";
try
{
byte[] test1 = System.Text.Encoding.UTF8.GetBytes(pass);
byte[] test2 = new byte[16];
for (int i = 0; i < test1.Length;i++ )
{
test2[i] = test1[i];
}
CryptographicKey key =
SAP.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(test2));
IBuffer Buffer = CryptographicBuffer.DecodeFromBase64String(input);
byte[] Decrypted;
CryptographicBuffer.CopyToByteArray(CryptographicEngine.Decrypt(key, Buffer, null), out Decrypted);
decrypted = System.Text.Encoding.UTF8.GetString(Decrypted, 0, Decrypted.Length);
return decrypted;
}
catch (Exception ex)
{
return null;
}
}
I know that there is something wrong with the IV being generated. If I give the Iv as null, the decryption algorithm generates some result(though it is wrong), If i give some values for IV, it throws an exception such as "Value does not fall within the expected range."
Any help is much appreciated.
I'm not sure about the transformation from Android to Windows but I noticed in the JAva you are using PKCS5 and in C# PKCS7.
public String generateKey(String title, String userName){
char[] hexDigits = "0123456789abcdef".toCharArray();
String source;
String MD5 = null;
byte[] digest = null;
source = title + "balh" + userName ;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
digest = md.digest(source.getBytes("UTF-16"));
StringBuilder sb = new StringBuilder(32);
for (byte b : digest)
{
sb.append(hexDigits[(b >> 4) & 0x0f]);
sb.append(hexDigits[b & 0x0f]);
}
System.out.println("Gened KEY ===="+sb.toString());
return sb.toString();
} catch (Exception e) {
}
return "";
}
I use the same code to generate key in android, and in Servlet. But I get different results. What am I doing wrong? Or if those are not compatible then how to make them.
I used the following method in both server and android client. It worked. But don't know what's the problem I had.
http://mobile.dzone.com/news/android-snippet-making-md5
I have a simple android client which needs to 'talk' to a simple C# HTTP listener. I want to provide a basic level of authentication by passing username/password in POST requests.
MD5 hashing is trivial in C# and provides enough security for my needs but I can't seem to find how to do this at the android end.
EDIT: Just to address the concerns raised about MD5 weakness - the C# server runs on the PCs of the users of my android client. In many cases, they'll be accessing the server using wi-fi on their own LANs but, at their own risk, they may choose to access it from the internet. Also the service on the server needs to use pass-through for the MD5 to a 3rd party application I have no control over.
Here is an implementation you can use (updated to use more up to date Java conventions - for:each loop, StringBuilder instead of StringBuffer):
public static String md5(final String s) {
final String MD5 = "MD5";
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest
.getInstance(MD5);
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuilder hexString = new StringBuilder();
for (byte aMessageDigest : messageDigest) {
String h = Integer.toHexString(0xFF & aMessageDigest);
while (h.length() < 2)
h = "0" + h;
hexString.append(h);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
Although it is not recommended for systems that involve even the basic level of security (MD5 is considered broken and can be easily exploited), it is sometimes enough for basic tasks.
The accepted answer didn't work for me in Android 2.2. I don't know why, but it was "eating" some of my zeros (0) .
Apache commons also didn't work on Android 2.2, because it uses methods that are supported only starting from Android 2.3.x. Also, if you want to just MD5 a string, Apache commons is too complex for that. Why one should keep a whole library to use just a small function from it...
Finally I found the following code snippet here which worked perfectly for me. I hope it will be useful for someone...
public String MD5(String md5) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(md5.getBytes("UTF-8"));
StringBuffer sb = new StringBuffer();
for (int i = 0; i < array.length; ++i) {
sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3));
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException e) {
} catch(UnsupportedEncodingException ex){
}
return null;
}
The androidsnippets.com code does not work reliably because 0's seem to be cut out of the resulting hash.
A better implementation is here.
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());
String hash = new BigInteger(1, m.digest()).toString(16);
return hash;
}
If using Apache Commons Codec is an option, then this would be a shorter implementation:
String md5Hex = new String(Hex.encodeHex(DigestUtils.md5(data)));
Or SHA:
String shaHex= new String(Hex.encodeHex(DigestUtils.sha("textToHash")));
Source for above.
Please follow the link and upvote his solution to award the correct person.
Maven repo link: https://mvnrepository.com/artifact/commons-codec/commons-codec
Current Maven dependency (as of 6 July 2016):
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
A solution above using DigestUtils didn't work for me. In my version of Apache commons (the latest one for 2013) there is no such class.
I found another solution here in one blog. It works perfect and doesn't need Apache commons. It looks a little shorter than the code in accepted answer above.
public static String getMd5Hash(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
BigInteger number = new BigInteger(1, messageDigest);
String md5 = number.toString(16);
while (md5.length() < 32)
md5 = "0" + md5;
return md5;
} catch (NoSuchAlgorithmException e) {
Log.e("MD5", e.getLocalizedMessage());
return null;
}
}
You will need these imports:
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
This is a slight variation of Andranik and Den Delimarsky answers above, but its a bit more concise and doesn't require any bitwise logic. Instead it uses the built-in String.format method to convert the bytes to two character hexadecimal strings (doesn't strip 0's). Normally I would just comment on their answers, but I don't have the reputation to do so.
public static String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
StringBuilder hexString = new StringBuilder();
for (byte digestByte : md.digest(input.getBytes()))
hexString.append(String.format("%02X", digestByte));
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
If you'd like to return a lower case string instead, then just change %02X to %02x.
Edit:
Using BigInteger like with wzbozon's answer, you can make the answer even more concise:
public static String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
BigInteger md5Data = new BigInteger(1, md.digest(input.getBytes()));
return String.Format("%032X", md5Data);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
Here is Kotlin version from #Andranik answer.
We need to change getBytes to toByteArray (don't need to add charset UTF-8 because the default charset of toByteArray is UTF-8) and cast array[i] to integer
fun String.md5(): String? {
try {
val md = MessageDigest.getInstance("MD5")
val array = md.digest(this.toByteArray())
val sb = StringBuffer()
for (i in array.indices) {
sb.append(Integer.toHexString(array[i].toInt() and 0xFF or 0x100).substring(1, 3))
}
return sb.toString()
} catch (e: java.security.NoSuchAlgorithmException) {
} catch (ex: UnsupportedEncodingException) {
}
return null
}
Hope it help
I have made a simple Library in Kotlin.
Add at Root build.gradle
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
at App build.gradle
implementation 'com.github.1AboveAll:Hasher:-SNAPSHOT'
Usage
In Kotlin
val ob = Hasher()
Then Use hash() method
ob.hash("String_You_Want_To_Encode",Hasher.MD5)
ob.hash("String_You_Want_To_Encode",Hasher.SHA_1)
It will return MD5 and SHA-1 Respectively.
More about the Library
https://github.com/ihimanshurawat/Hasher
Please use SHA-512, MD5 is insecure
public static String getSHA512SecurePassword(String passwordToHash) {
String generatedPassword = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update("everybreathyoutake".getBytes());
byte[] bytes = md.digest(passwordToHash.getBytes());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
generatedPassword = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return generatedPassword;
}
Useful Kotlin Extension Function Example
fun String.toMD5(): String {
val bytes = MessageDigest.getInstance("MD5").digest(this.toByteArray())
return bytes.toHex()
}
fun ByteArray.toHex(): String {
return joinToString("") { "%02x".format(it) }
}
In our MVC application we generate for long param
using System.Security.Cryptography;
using System.Text;
...
public static string getMD5(long id)
{
// convert
string result = (id ^ long.MaxValue).ToString("X") + "-ANY-TEXT";
using (MD5 md5Hash = MD5.Create())
{
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(result));
// Create a new Stringbuilder to collect the bytes and create a string.
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < data.Length; i++)
sBuilder.Append(data[i].ToString("x2"));
// Return the hexadecimal string.
result = sBuilder.ToString().ToUpper();
}
return result;
}
and same in Android application (thenk helps Andranik)
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
...
public String getIdHash(long id){
String hash = null;
long intId = id ^ Long.MAX_VALUE;
String md5 = String.format("%X-ANY-TEXT", intId);
try {
MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] arr = md.digest(md5.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < arr.length; ++i)
sb.append(Integer.toHexString((arr[i] & 0xFF) | 0x100).substring(1,3));
hash = sb.toString();
} catch (NoSuchAlgorithmException e) {
Log.e("MD5", e.getMessage());
}
return hash.toUpperCase();
}
i have used below method to give me md5 by passing string for which you want to get md5
public static String getMd5Key(String password) {
// String password = "12131123984335";
try {
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));
}
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);
}
System.out.println("Digest(in hex format):: " + hexString.toString());
return hexString.toString();
} catch (Exception e) {
// TODO: handle exception
}
return "";
}
MD5 is a bit old, SHA-1 is a better algorithm, there is a example here.
(Also as they note in that post, Java handles this on it's own, no Android specific code.)
Far too wasteful toHex() conversion prevails in other suggestions, really.
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String md5string(String s) {
return toHex(md5plain(s));
}
public static byte[] md5plain(String s) {
final String MD5 = "MD5";
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest.getInstance(MD5);
digest.update(s.getBytes());
return digest.digest();
} catch (NoSuchAlgorithmException e) {
// never happens
e.printStackTrace();
return null;
}
}
public static String toHex(byte[] buf) {
char[] hexChars = new char[buf.length * 2];
int v;
for (int i = 0; i < buf.length; i++) {
v = buf[i] & 0xFF;
hexChars[i * 2] = HEX_ARRAY[v >>> 4];
hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
this is working perfectly for me, I used this to get MD5 on LIST Array(then convert it to JSON object), but if you only need to apply it on your data. type format, replace JsonObject with yours.
Especially if you have a mismatch with python MD5 implementation use this!
private static String md5(List<AccelerationSensor> sensor) {
Gson gson= new Gson();
byte[] JsonObject = new byte[0];
try {
JsonObject = gson.toJson(sensor).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
MessageDigest m = null;
try {
m = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] thedigest = m.digest(JsonObject);
String hash = String.format("%032x", new BigInteger(1, thedigest));
return hash;
}
The provided solutions for the Scala language (a little shorter):
def getMd5(content: Array[Byte]) =
try {
val md = MessageDigest.getInstance("MD5")
val bytes = md.digest(content)
bytes.map(b => Integer.toHexString((b + 0x100) % 0x100)).mkString
} catch {
case ex: Throwable => null
}