Xamarin.Android: Decrypted Files Randomly Being Corrupted - android

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.

Related

CryptoKit-ios and Android encrypting and decrypting

I am trying to encrypt in android and decrypt in ios.I am using AES.GCM encryption however when i try to decrypt the package in ios i get Aunthetication faliure message.Below is the code for ios decryption
func decryptData(decryptToData:Data,key:SymmetricKey)->String {
let combinedData = decryptToData // Previous sealed bo
let sealedBoxToOpen = try! AES.GCM.SealedBox(combined: decryptToData)
if let decryptedData = try? AES.GCM.open(sealedBoxToOpen, using: key) {
decryptedString = String(data: decryptedData, encoding: .utf8)!
print(decryptedString ?? "Failed")
} else {
print(CryptoKitError) // Ouch, doSomething() threw an error.
}
}
This is similar to iOS CryptoKit in Java but i am doing the other way around.
This is the android encryption code
public synchronized Map<String, String> encrypt(byte[] rawKey, byte[] rawData, #Nullable byte[] associatedData) throws StashDataEncryptionException {
byte[] rawEncryptionKey = null;
if (rawKey == null) {
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[KEY_LENGTH_BYTE];
secureRandom.nextBytes(key);
rawEncryptionKey = key;
} else {
rawEncryptionKey = rawKey;
}
byte[] iv = null;
byte[] encrypted = null;
try {
iv = new byte[IV_LENGTH_BYTE];
secureRandom.nextBytes(iv);
final Cipher cipherEnc = getCipher();
cipherEnc.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(rawEncryptionKey, "AES"), new GCMParameterSpec(TAG_LENGTH_BIT, iv));
if (associatedData != null) {
cipherEnc.updateAAD(associatedData);
}
encrypted = cipherEnc.doFinal(rawData);
String base64Key = Base64.encodeToString(rawEncryptionKey, Base64.DEFAULT);
//concat all of it to a single message
ByteBuffer byteBuffer = ByteBuffer.allocate(1 + iv.length + encrypted.length);
byteBuffer.put((byte) iv.length);
byteBuffer.put(iv);
byteBuffer.put(encrypted);
byte[] cipherMessage = byteBuffer.array();
Map map = new HashMap<String, String>();
map.put(MAP_KEY, base64Key);
map.put(MAP_Byte_CONTENT, cipherMessage);
return map;
} catch (Exception e) {
throw new StashDataEncryptionException("could not encrypt data", e);
}
}
i checked the key,iv and tag lenght.Its same on the ios side as android
Hey there I played very long to day with this and I came up with a working Xcode playground demoing CryptoKit AES-GCM 256 encryption & decryption. I also had this error very often today but could solve it. You can clone my playgrounds repo and try it out, play with it:
https://github.com/Blackjacx/Playgrounds/blob/master/playgrounds/CryptoKit.playground/Contents.swift
Implementations will differ, enough to invalidate authenticated encryption.
Use an enterprise grade, cross platform compatible library like libsodium.

Download files via FTP on Android

I need to make a connection with a local FTP protocol between a computer (server) and my Android device (client). This should download files (images, OBJ,...) to be used in the Android Unity app scene. I've used WWW class to create this connection and it works fine in the Unity player run in another computer as client. Once I've exported the same scene as Android apk it didn't work (I'm sure that FTP connection is stable and it works because I'm able to access to the files from the browser). Does anybody know if there is another way or there are problems in my code to use the FTP protocol on Android Unity app? (the client doesn't need any authorisation and the authentication is anonymous) Here is the code I use to download one image inside the scene and render it as a sprite.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.IO;
public class ClientFTP : MonoBehaviour
{
public UnityEngine.UI.Image label;
IEnumerator Start ()
{
// Create the connection and whait until it is established
string url = ("ftp://192.168.10.11/prova.png");
WWW ftpconnection = new WWW (url);
yield return ftpconnection;
// Download the image and render it as a texture
Texture2D tex = new Texture2D (250, 192);
ftpconnection.LoadImageIntoTexture (tex);
// Assign the texture to a new sprite
Sprite s = Sprite.Create (tex, new Rect (0, 0, 250f, 192f), new Vector2 (0.5f, 0.5f), 300);
label.preserveAspect = true;
label.sprite = s;
}
}
Why use FTP if you don't need Credential to access the files? You can just place the files in your server then access them with the WWW or UnityWebRequest API.
To answer your FTP question, WWW is not meant to be used with the FTP protocol. This is what the FtpWebRequest API is used for.
Below is a sample of FtpWebRequest.
private byte[] downloadWithFTP(string ftpUrl, string savePath = "", string userName = "", string password = "")
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(ftpUrl));
//request.Proxy = null;
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = true;
//If username or password is NOT null then use Credential
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
request.Credentials = new NetworkCredential(userName, password);
}
request.Method = WebRequestMethods.Ftp.DownloadFile;
//If savePath is NOT null, we want to save the file to path
//If path is null, we just want to return the file as array
if (!string.IsNullOrEmpty(savePath))
{
downloadAndSave(request.GetResponse(), savePath);
return null;
}
else
{
return downloadAsbyteArray(request.GetResponse());
}
}
byte[] downloadAsbyteArray(WebResponse request)
{
using (Stream input = request.GetResponseStream())
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while (input.CanRead && (read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
}
void downloadAndSave(WebResponse request, string savePath)
{
Stream reader = request.GetResponseStream();
//Create Directory if it does not exist
if (!Directory.Exists(Path.GetDirectoryName(savePath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
}
FileStream fileStream = new FileStream(savePath, FileMode.Create);
int bytesRead = 0;
byte[] buffer = new byte[2048];
while (true)
{
bytesRead = reader.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
break;
fileStream.Write(buffer, 0, bytesRead);
}
fileStream.Close();
}
Usage:
Download and save(No Credential):
string path = Path.Combine(Application.persistentDataPath, "FTP Files");
path = Path.Combine(path, "data.png");
downloadWithFTP("ftp://yourUrl.com/yourFile", path);
Download and save(With Credential):
string path = Path.Combine(Application.persistentDataPath, "FTP Files");
path = Path.Combine(path, "data.png");
downloadWithFTP("ftp://yourUrl.com/yourFile", path, "UserName", "Password");
Download Only (No Credential):
string path = Path.Combine(Application.persistentDataPath, "FTP Files");
path = Path.Combine(path, "data.png");
byte[] yourImage = downloadWithFTP("ftp://yourUrl.com/yourFile", "");
//Convert to Sprite
Texture2D tex = new Texture2D(250, 192);
tex.LoadImage(yourImage);
Sprite s = Sprite.Create(tex, new Rect(0, 0, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f));
Download Only (With Credential):
string path = Path.Combine(Application.persistentDataPath, "FTP Files");
path = Path.Combine(path, "data.png");
byte[] yourImage = downloadWithFTP("ftp://yourUrl.com/yourFile", "", "UserName", "Password");
//Convert to Sprite
Texture2D tex = new Texture2D(250, 192);
tex.LoadImage(yourImage);
Sprite s = Sprite.Create(tex, new Rect(0, 0, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f));
public boolean ftpDownloadVideo() {
String desFilePath = (Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS)).toString(); ///xet trong moi truong SD card////
String srcFilePath = "/detect_disease/rice_detect_disease_2019_09_24__10_07_08.avi";
boolean status = false;
Log.d(TAG, "2");
try {
Log.e("downloadFTP login : ", "Success");
OutputStream desFileStream = new BufferedOutputStream( new FileOutputStream(desFilePath));
Log.d(TAG,"FileOutputStream");
status = mFTPClient.retrieveFile(srcFilePath, desFileStream);
Log.d(TAG, "4");
desFileStream.close();
Log.e("downloadFTP status : ", "" + status);
Log.d(TAG, "5");
return status;
} catch (Exception e) {
Log.d(TAG, "download failed");
}
return status;
}

Android upload to network drive(samba share) performance issue

i have a problem when I upload a picture of 100kb to samba share with JCIFS from my tablet, it takes about 10-20 minutes (before I changed my buffer from 1024 to 20971520 it took almost 6 hours) but it does not give any effect anymore to increase it
it is not the connection issue as i had tested it with ES File where it Uploaded my picture immediately
private class MyCopy extends AsyncTask<String, String, String> {
String z = "";
String username = "", password = "", servername = "", filestocopy = "";
#Override
protected void onPreExecute() {
username = edtusername.getText().toString();
password = edtpassword.getText().toString();
servername = "smb://" + edtservername.getText().toString();
filestocopy = editdir.getText().toString();
}
protected String doInBackground(String... params) {
// String buffer;
// buffer = setingPreferences.getString("buffer", "");
File file = new File(filestocopy);
String filename = file.getName();
NtlmPasswordAuthentication auth1 = new NtlmPasswordAuthentication(
servername, username, password);
try {
SmbFile sfile = new SmbFile(servername + "/" + filename, auth1);
if (!sfile.exists())
sfile.createNewFile();
sfile.connect();
InputStream in = new FileInputStream(file);
SmbFileOutputStream sfos = new SmbFileOutputStream(sfile);
byte[] buf = new byte[20971520]; //(parseInt(buffer))
int len;
while ((len = in.read(buf)) > 0){
sfos.write(buf, 0, len);
}
in.close();
sfos.close();
z = "File copied successfully";
} catch (Exception ex) {
z = z + " " + ex.getMessage().toString();
}
return z;
}
}
The buffer size shouldn't make a noticeable difference, but it definitely shouldn't be 20M. Use something like 4k instead.
Are you sure it's the actual file transfer that is taking so long? There's no reason 100k should take more than a few seconds at the most. Have you tried putting log statements between each step, including before and after the authentication calls, createNewFile(), and connect() to check if those are the bottleneck?
Also, I believe you should be copying bytes while the read length is >= 0 instead of strictly > 0, since -1 signals the end of the stream, not 0.
Did you try
new SmbFile("username:password#server/")
instead of using NTLM? It can also be a DNS issue, so do try
jcifs.Config.setProperty("resolveOrder", "DNS");
If neither works, you might want to try BufferedOutputStream with your SmbFileOutputStream.

APK checksum- Binary Code Protection

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();
}

AES CBC PKCS5 Decryption in WIndows 8 Appstore application

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.

Categories

Resources