parsing signed and encrypted email - encryption

I am writing an application that must be able to read signed and encrypted emails and parse through their contents. I am able to get everything working fine for emails that are only encrypted, but do not know what to do when I get an email that is also signed. Once I decrypt this email instead of having an anticipated 4 parts in a Multipart object, I have only 1 part in a MimePart object with the file name smime.p7m. I do not know how to break this file up or verify the signature. I have found the documentation on verifying a signature (http://www.mimekit.net/docs/html/Working-With-SMime.htm#Verify), but I don't see how this does anything. Obviously there is something that I am just not understanding at this point.
Below is a sample of the code that I am using. Note that this will be refactored after I get everything figured out, but this code is thus far working fine for all emails that I have tested so far that are not signed (may or may not be encrypted).
public void decryptAndSendEmails()
{
List<EmailMessage> emails = getEmails();
foreach (var email in emails)
{
var decryptedEmailMessage = new EmailMessage(service);
MimeMessage message;
using (var stream = new MemoryStream(email.MimeContent.Content, false))
{
message = MimeMessage.Load(stream);
}
var pkcs7 = message.BodyParts.OfType<ApplicationPkcs7Mime>().FirstOrDefault();
if (pkcs7 != null)
{
//If the SecureMimeType has not been set as it should, set it to EnvelopedData
if (pkcs7.SecureMimeType == SecureMimeType.Unknown)
{
var content = new MemoryStream();
pkcs7.Content.DecodeTo(content);
content.Position = 0;
pkcs7 = new ApplicationPkcs7Mime(SecureMimeType.EnvelopedData, content);
}
using (var ctx = new TemporarySecureMimeContext())
{
using (var stream = File.OpenRead(ConfigurationManager.AppSettings["certLocation"]))
{
ctx.Import(stream, ConfigurationManager.AppSettings["certPassword"]);
}
var decrypted = pkcs7.Decrypt(ctx);
var decryptedParts = new List<MimePart>();
if (decrypted is Multipart)
{
decryptedParts = breakMultiPart((Multipart)decrypted);
}
else if (decrypted is MimePart)
{
decryptedParts.Add((MimePart)decrypted);
}
else
{
throw new InvalidOperationException("Unknown Mime part found");
}
var textParts = decryptedParts.Where(r => r is TextPart);
var htmlParts = textParts.Where(x => ((TextPart)x).IsHtml);
var textBodyParts = textParts.Where(x => !((TextPart)x).IsHtml);
var attachmentParts = decryptedParts.Where(r => !(r is TextPart));
if (htmlParts.Any())
{
if (htmlParts.Count() > 1)
{
throw new InvalidOperationException("multiple html body parts.");
}
var htmlPart = (TextPart)htmlParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.HTML, htmlPart.Text);
}
else
{
//Text body
if (textBodyParts.Count() > 1)
{
throw new InvalidOperationException("multiple text body parts.");
}
var textPart = (TextPart)textBodyParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.Text, textPart.Text);
}
foreach (var part in attachmentParts)
{
var content = new MemoryStream();
part.Content.DecodeTo(content);
content.Position = 0;
decryptedEmailMessage.Attachments.AddFileAttachment(part.FileName, content);
if (!part.IsAttachment)
{
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).IsInline = true;
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).ContentId = part.ContentId;
}
}
}
////do stuff with decrypted Email
}
else
{
//The email is not encrypted
decryptedEmailMessage = email;
//do stuff with decrypted Email
}
}
}

I have finally figured this out using a combination of the comment from #jstedfast and the information I found in Unable to decrypt p7m using MimeKit. The following is the resulting code to fix this issue:
public void decryptAndSendEmails()
{
List<EmailMessage> emails = getEmails();
foreach (var email in emails)
{
var decryptedEmailMessage = new EmailMessage(service);
MimeMessage message;
using (var stream = new MemoryStream(email.MimeContent.Content, false))
{
message = MimeMessage.Load(stream);
}
var pkcs7 = message.BodyParts.OfType<ApplicationPkcs7Mime>().FirstOrDefault();
if (pkcs7 != null)
{
using (var ctx = new TemporarySecureMimeContext())
{
using (var stream = File.OpenRead(ConfigurationManager.AppSettings["certLocation"]))
{
ctx.Import(stream, ConfigurationManager.AppSettings["certPassword"]);
}
var decrypted = pkcs7.Decrypt(ctx);
if (decrypted != null && decrypted is MimePart && ((MimePart)decrypted).FileName == "smime.p7m")
{
//We need to verify the signature
var signedDecryptedEntity = decrypted as ApplicationPkcs7Mime;
signedDecryptedEntity.Verify(ctx, out decrypted); //the real decrypted data
}
var decryptedParts = new List<MimePart>();
if (decrypted is Multipart)
{
decryptedParts = breakMultiPart((Multipart)decrypted);
}
else if (decrypted is MimePart)
{
decryptedParts.Add((MimePart)decrypted);
}
else
{
throw new InvalidOperationException("Unknown Mime part found");
}
var textParts = decryptedParts.Where(r => r is TextPart);
var htmlParts = textParts.Where(x => ((TextPart)x).IsHtml);
var textBodyParts = textParts.Where(x => !((TextPart)x).IsHtml);
var attachmentParts = decryptedParts.Where(r => !(r is TextPart));
if (htmlParts.Any())
{
if (htmlParts.Count() > 1)
{
throw new InvalidOperationException("multiple html body parts.");
}
var htmlPart = (TextPart)htmlParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.HTML, htmlPart.Text);
}
else
{
//Text body
if (textBodyParts.Count() > 1)
{
throw new InvalidOperationException("multiple text body parts.");
}
var textPart = (TextPart)textBodyParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.Text, textPart.Text);
}
foreach (var part in attachmentParts)
{
var content = new MemoryStream();
part.Content.DecodeTo(content);
content.Position = 0;
decryptedEmailMessage.Attachments.AddFileAttachment(part.FileName, content);
if (!part.IsAttachment)
{
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).IsInline = true;
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).ContentId = part.ContentId;
}
}
}
//Do Something with email (decryptedEmailMessage)
}
else
{
//The email is not encrypted
decryptedEmailMessage = email;
//Do Something with email (decryptedEmailMessage)
}
}
}

Related

Xamarin Http Request Timeout Issue

I have a mobile application based on Xamarin and a Web API based on .Net Core. Mobile app consumes methods of Web API via HttpClient. The code below is my base method to call any Web API method and the point is I want to set a timeout but could not achieved to set the exact timeout value whatever I have implemented. Tried Timespan.FromSeconds() or TimeSpan.FromMilliseconds() etc. When client makes a request to Web API, a loader is displayed to lock UI and removed after API response. Some clients gave me a feedback that the loader is displayed forever and request never ends. Maybe, the server is unreachable in this particular time or internet connection is broken for client etc. All I want to set a timeout and break the request and display an alert message to client. Of course, I googled and tried too much as mentioned but no result. If anyone can help me, will be appreciated.
public async Task<BaseResponseModel> Post(BasePostModel postModel)
{
var responseModel = new BaseResponseModel();
var json = postModel.ToString();
var jsonParam = new StringContent(json, Encoding.UTF8, "application/json");
var isPosted = true;
var clientHandler = new HttpClientHandler()
{
AllowAutoRedirect = true,
};
var url = GetURL(postModel.UrlKey);
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
ContractResolver = new DefaultContractResolver(),
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
var client = new HttpClient(clientHandler);
//client.Timeout = TimeSpan.FromSeconds(10);
//var cancellationTokenSource = new CancellationTokenSource();
//cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("X-Env", "MOBILE_API");
AttachToken(ref client, responseModel.Id);
try
{
if (Preferences.ContainsKey("UserJwtExprieDate"))
{
var expiryDate = Preferences.Get("UserJwtExprieDate", null);
if (DateTime.Now > DateTime.Parse(expiryDate))
{
Preferences.Remove("UserJwtExprieDate");
Preferences.Remove("HomePageInformation");
int index = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.Count - 1;
Page currPage = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack[index];
if (currPage as SigninForFactorOne != null)
{}
else
{
App.LogoutUser();
}
}
else
{
var response = await client.PostAsync(url, jsonParam);
if (response.IsSuccessStatusCode)
{
string result = response.Content.ReadAsStringAsync().Result;
var resultModel = JsonConvert.DeserializeObject<BaseResponseModel>(result, settings);
if (resultModel.ErrorType == APIErrorTypes.NULL)
{
if (resultModel.IsSucceed)
{
responseModel.Data = resultModel.Data;
}
else
{
responseModel.Error = resultModel.Error;
}
responseModel.Message = resultModel.Message;
}
else
{
responseModel.Error = "Token Expried Date.";
Preferences.Remove("UserJwtExprieDate");
Preferences.Remove("HomePageInformation");
App.LogoutUser();
}
}
else
{
new AppException(new Exception("HTTP Client response is not succeed!"), responseModel.Id);
isPosted = false;
}
}
}
else
{
var response = await client.PostAsync(url, jsonParam);
if (response.IsSuccessStatusCode)
{
string result = response.Content.ReadAsStringAsync().Result;
var resultModel = JsonConvert.DeserializeObject<BaseResponseModel>(result, settings);
if (resultModel.ErrorType == APIErrorTypes.NULL)
{
if (resultModel.IsSucceed)
{
responseModel.Data = resultModel.Data;
}
else
{
responseModel.Error = resultModel.Error;
}
responseModel.Message = resultModel.Message;
}
else
{
responseModel.Error = "Token Expried Date.";
Preferences.Remove("UserJwtExprieDate");
Preferences.Remove("HomePageInformation");
App.LogoutUser();
}
}
else
{
new AppException(new Exception("HTTP Client response is not succeed!"), responseModel.Id);
isPosted = false;
}
}
}
catch (Exception ex)
{
new AppException(ex, responseModel.Id, 500, "anonymous.user", "Unable to post data to API!");
isPosted = false;
}
finally
{
if (!isPosted)
{
responseModel.Error = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage);
responseModel.Message = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage);
}
}
return responseModel;
}
I've used the solution below to manually set a time-out which works fine.
internal class TimeOutHandler : DelegatingHandler
{
private readonly TimeSpan TimeOut;
public TimeOutHandler(TimeSpan timeOut) => TimeOut = timeOut;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage req, CancellationToken ct)
{
using (var ctTimeOut = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
ctTimeOut.CancelAfter(TimeOut);
try
{
return await base.SendAsync(req, ctTimeOut.Token);
}
catch (OperationCanceledException) when (!ct.IsCancellationRequested)
{
throw new TimeoutException();
}
}
}
}
How to use
var interval = TimeSpan.FromSeconds(10);
var handler = new TimeOutHandler(interval)
{
InnerHandler = new HttpClientHandler()
};
var client = new HttpClient(handler);
For more information, check out: https://thomaslevesque.com/2018/02/25/better-timeout-handling-with-httpclient/

Passing images from one page to another in Xamarin Forms

In my app I need to pass images from one page to another page image view to display. I am taking a photo from camera and do some stuffs, then I want to send that images to the second page.
if (await isCamAvailable())
{
MediaFile photo1 = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions() { Directory = "NewBusiness", Name = "image1.jpg", PhotoSize = PhotoSize.MaxWidthHeight, MaxWidthHeight = 1024, CompressionQuality = 95 });
if (photo1 != null)
{
PhotoImage1.Source = ImageSource.FromStream(() => { return photo1.GetStream(); });
countList.Remove("a");
countList.Add("a");
}
}
Then I am added it to a string array by doing
private List<string> sendImgList = new List<string>();
sendImgList.Add(createImgByteString(photo1.GetStream()));
private string createImgByteString(Stream data)
{
var bytes = new byte[data.Length];
return Convert.ToBase64String(bytes);
}
Then from second page (for testing i just added only one image)
foreach (string ss in imgList) {
byte[] Base64Stream = Convert.FromBase64String(ss);
imgView.Source = ImageSource.FromStream(() => new MemoryStream(Base64Stream));
}
I followed this example. But image not showing.
https://forums.xamarin.com/discussion/139360/how-to-transfer-images-from-one-page-to-another
Also getting this in logcat..
[0:] ImageLoaderSourceHandler: Image data was invalid: Xamarin.Forms.StreamImageSource05-29 14:22:43.758 W/monodroid-assembly( 8737): typemap: unable to find mapping to a Java type from managed type 'System.Byte, mscorlib'
It seems that you used the Media.Plugin . Why don't you pass the ImageSource directly?
If you do want to convert it to byte array , check the following code
public byte[] GetImageStreamAsBytes(Stream input)
{
var buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
var imgDate = GetImageStreamAsBytes(photo1.GetStream());
It would be better to pass the byte array directly .
The best mode to pass parameter in pages is with Prism.
https://prismlibrary.com/docs/xamarin-forms/navigation/passing-parameters.html
>
_navigationService.NavigateAsync(new Uri("MainPage", new NavigationParameters
{
{ "key_parameter", image }
})));
And on other page:
>
public override void OnNavigatedTo(INavigationParameters parameters)
{
image = (Image)parameters["key_parameter"];
}

ASP.NET Cloudinary getting response

I have little problem with using Cloudinary, I can upload the images it works fine but I guess i cant get any response from Cloudinary. Suggestion? about required parameters
Handler
public async Task<Photo> Handle(Command request, CancellationToken cancellationToken)
{
var photoUploadResult = _photoAccessor.AddPhoto(request.File);
var photo = new Photo
{
Url = photoUploadResult.Url,
Id = photoUploadResult.PublicId
};
var success = await _context.SaveChangesAsync() > 0;
if (success) return photo;
throw new Exception("Problem saving changes");
}
Accessor
public PhotoUploadResult AddPhoto(IFormFile file)
{
var uploadResult = new ImageUploadResult();
if (file.Length > 0)
{
using (var stream = file.OpenReadStream())
{
var uploadParams = new ImageUploadParams
{
File = new FileDescription(file.FileName, stream)
};
uploadResult = _cloudinary.Upload(uploadParams);
}
}
if (uploadResult.Error != null)
throw new Exception(uploadResult.Error.Message);
return new PhotoUploadResult
{
PublicId = uploadResult.PublicId,
Url = uploadResult.SecureUri.AbsoluteUri
};
}
What do you get in response? Can you try:
string cloud_name = "<Cloud Name>";
string ApiKey = "<Api-Key>";
string ApiSecret = "<Api-Secret>";
Account account = new Account(cloud_name,ApiKey,ApiSecret);
Cloudinary cloudinary = new Cloudinary(account);
cloudinary.Api.Timeout = int.MaxValue;
var ImguploadParams = new ImageUploadParams()
{
File = new FileDescription(#"http://res.cloudinary.com/demo/image/upload/couple.jpg"),
PublicId = "sample",
Invalidate = true,
Overwrite = true
};
var ImguploadResult = cloudinary.Upload(ImguploadParams);
Console.WriteLine(ImguploadResult.SecureUri);

GWT read mime type client side

I'm trying to read the mime type in GWT client side in order to validate a file before upload it. To do this I use JSNI to read the file header using HTML5 filereader API. However my problem is that GWT does not wait for the result of the reading and continue the code execution. The side effect is that my boolean is not set yet and my condition goes wrong. Is there any mechanism like promise implemented in GWT?
Any help on this would be much appreciated!
UploadImageButtonWidget.java
private boolean isMimeTypeValid = false;
private String mimeType = null;
public native boolean isValid(Element element)/*-{
var widget = this;
var files = element.files;
var reader = new FileReader();
var CountdownLatch = function (limit){
this.limit = limit;
this.count = 0;
this.waitBlock = function (){};
};
CountdownLatch.prototype.countDown = function (){
this.count = this.count + 1;
if(this.limit <= this.count){
return this.waitBlock();
}
};
CountdownLatch.prototype.await = function(callback){
this.waitBlock = callback;
};
var barrier = new CountdownLatch(1);
reader.readAsArrayBuffer(files[0]);
reader.onloadend = function(e) {
var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
var header = "";
for (var i = 0; i < arr.length; i++) {
header += arr[i].toString(16);
}
widget.#com.portal.client.widgets.base.UploadImageButtonWidget::setMimeType(Ljava/lang/String;)(header);
barrier.countDown();
}
return barrier.await(function(){
return widget.#com.portal.client.widgets.base.UploadImageButtonWidget::isMimeTypeValid();
});
}-*/
public void setMimeType(String headerString) {
boolean mimeValid = true;
if (headerString.equalsIgnoreCase(PNG_HEADER)) {
mimeType = PNG_MIMETYPE;
} else if (headerString.equalsIgnoreCase(GIF_HEADER)) {
mimeType = GIF_MIMETYPE;
} else if (headerString.equalsIgnoreCase(JPG_HEADER1) || headerString.equalsIgnoreCase(JPG_HEADER2) || headerString.equalsIgnoreCase(JPG_HEADER3)) {
mimeType = JPG_MIMETYPE;
} else {
mimeValid = false;
setValidationError(i18n.uploadErrorNotImageBasedOnMimeType());
fileChooser.getElement().setPropertyJSO("files", null);
setErrorStatus();
}
setMimeTypeValid(mimeValid);
}
public boolean isMimeTypeValid() {
GWT.log("mimeType" + mimeType);
GWT.log("isMimetypeValid" + String.valueOf(isMimeTypeValid));
return mimeType != null;
}
in the activity:
public void validateAndUpload() {
UploadImageButtonWidget uploadImageButtonWidget = view.getUpload();
if (uploadImageButtonWidget.isValid()) {
GWT.log("mime ok: will be uploaded");
uploadImage();
} else {
GWT.log("mime not ok: will not be uploaded");
}
}

How display a json error message?

I cannot display the Json message, Im trying prevente that user upload files with same name this is my controller code:
//POST: /Quote/Create Save the Uploaded file
public ActionResult SaveUploadedFile(int? chunk, string name)
{
bool exists;
var fileUpload = Request.Files[0];
var uploadPath = "C:\\Files";
chunk = chunk ?? 0;
if (System.IO.File.Exists(Path.Combine(uploadPath, name)))
{
exists = true;
}
else {
exists = false;
}
if (!exists)
{
using (var fs = new FileStream(Path.Combine(uploadPath, name), chunk == 0 ? FileMode.Create : FileMode.Append))
{
var buffer = new byte[fileUpload.InputStream.Length];
fileUpload.InputStream.Read(buffer, 0, buffer.Length);
fs.Write(buffer, 0, buffer.Length);
}
return Json(new { success = true }, JsonRequestBehavior.AllowGet);
}
else {
return Json(new { success = false, Message = "The file" + name +"already exists" }, JsonRequestBehavior.AllowGet);
}
}
This is my view code, if files success is false, then display the Json message:
UploadComplete: function (up, files) {
if (!files.success) {
alert(files.Message);
console.log(up);
} else {
var j = 0;
if (count > 0) {
j = count;
} else {
j = #i + '';
}
$.each(files, function (i, file) {
var extension = file.name.split(".");
$('.files').append('<input type=\"hidden\" name=\"Files[' + j + '].Name\" value=\"' + file.name + '\" />');
$('.files').append('<input type=\"hidden\" name=\"Files[' + j + '].Date\" value=\"' + "#DateTime.Now" + '\" />');
j++;
});
}
}
Thanks in advance !!
It seems that you need to be returning a string result rather than an ActionResult, since all you really want is if it passed or not. Also shortened your code a little to reflect the changes.
If you did want to return an object (meaning you wanted more than one property), I would create a model (class and then object), then return JsonResult rather than ActionResult.
Good documentation on how to return JsonResult object
C#
public string SaveUploadedFile(int? chunk, string name)
{
bool exists = false;
var fileUpload = Request.Files[0];
var uploadPath = "C:\\Files";
chunk = chunk ?? 0;
exists = System.IO.File.Exists(Path.Combine(uploadPath, name));
if (!exists)
{
using (var fs = new FileStream(Path.Combine(uploadPath, name), chunk == 0 ? FileMode.Create : FileMode.Append))
{
var buffer = new byte[fileUpload.InputStream.Length];
fileUpload.InputStream.Read(buffer, 0, buffer.Length);
fs.Write(buffer, 0, buffer.Length);
}
}
return message = !exists ? string.Empty
: "The file" + name + " already exists";
}
Javascript
if (files.message != '') { // meaning "exists" is true
console.log(up);
} else {
......
......
}

Resources