Process byte arrays correctly Encoding Utf-8 [duplicate] - c#

This question already has an answer here:
FromBase64String/UTF Encoding
(1 answer)
Closed 2 years ago.
I am trying to download a PDF file and encode it in UTF-8 format and then transform it back into a Base64 to attach it to a MailMessage, however I get an exception when passing the string to Base64.
exception
The input is not a valid Base-64 string as it contains a non-base 64 character...
my code...
try
{
if (attachment.Uri.Contains("http") || attachment.Uri.Contains("https"))
{
byte[] contentByte = null;
using (var webClient = new WebClient())
{
webClient.UseDefaultCredentials = true;
Uri uri = new Uri(attachment.Uri);
contentByte = webClient.DownloadData(uri);
}
MemoryStream memStream = new MemoryStream(contentByte);
String fullString = Encoding.UTF8.GetString(memStream.ToArray());
String stringData = fullString.Split(',')[1];
byte[] byteData = System.Convert.FromBase64String(stringData);
MemoryStream streamData = new MemoryStream(byteData);
Attachment attachmentDoc = new Attachment(streamData, fileName);
mail.Attachments.Add(attachmentDoc);
}
}
catch (Exception ex)
{
notificationLog.Error.Write(String.Format("Sendmail error. Attachment not found: " + attachment.FileName));
}

You are going about this the wrong way.
You are trying to decode the PDF as UTF-8 (which it is not) to UTF-16, and then decode that UTF-16 as Base64 (which it is not) to a byte array, then attach those bytes to the email. That is the complete opposite of what you need.
Base64 operates on bytes, not strings/characters. There is no need to encode a PDF (a binary file) to UTF-8 before encoding it in Base64. Just encode the PDF bytes as-is straight to Base64, eg:
try
{
if (attachment.Uri.StartsWith("http:") || attachment.Uri.StartsWith("https:"))
{
byte[] contentByte = null;
using (var webClient = new WebClient())
{
webClient.UseDefaultCredentials = true;
Uri uri = new Uri(attachment.Uri);
contentByte = webClient.DownloadData(uri);
}
String stringData = System.Convert.ToBase64String(contentByte);
MemoryStream memStream = new MemoryStream(Encoding.ASCII.GetBytes(stringData));
Attachment attachmentDoc = new Attachment(memStream, fileName);
mail.Attachments.Add(attachmentDoc);
}
}
catch (Exception ex)
{
notificationLog.Error.Write(String.Format("Sendmail error. Attachment not found: " + attachment.FileName));
}
However, using this approach, the receiver won't be able to decode the PDF correctly, because it won't know the PDF bytes were encoded in Base64.
Fortunately, the Attachment class can handle the Base64 for you, and notify the receiver of the encoding. Just give the Attachment the raw PDF bytes as-is, and set its TransferEncoding property to Base64, eg:
try
{
if (attachment.Uri.StartsWith("http:") || attachment.Uri.StartsWith("https:"))
{
byte[] contentByte = null;
using (var webClient = new WebClient())
{
webClient.UseDefaultCredentials = true;
Uri uri = new Uri(attachment.Uri);
contentByte = webClient.DownloadData(uri);
}
MemoryStream memStream = new MemoryStream(contentByte);
Attachment attachmentDoc = new Attachment(memStream, fileName);
attachmentDoc.TransferEncoding = TransferEncoding.Base64;
mail.Attachments.Add(attachmentDoc);
}
}
catch (Exception ex)
{
notificationLog.Error.Write(String.Format("Sendmail error. Attachment not found: " + attachment.FileName));
}

Related

Error decrypting mime content - ASN1 bad tag value met

I wrote a function to create a mime message and encrypt the content with users public cert that's stored in the cert store while the private keys are stored on my smart card. My function simply saves the pkcs7mime content to a file.
I have another test function, which simply reads the file and tries to decrypt the content. However, when I try to decrypt, I get the error: ASN1 bad tag value met.
My encryption function code:
private static void EncryptMime()
{
MimeMessage mm = new MimeMessage();
var ctx = new WindowsSecureMimeContext();
mm.From.Add(new MailboxAddress("Bob", "Bob#diamondsg.onmicrosoft.com"));
mm.To.Add(new MailboxAddress("Alice", "Alice#diamondsg.onmicrosoft.com"));
mm.Subject = "Smime Test";
mm.Body = new TextPart("plain")
{
Text = #"This is a test."
};
var signer = mm.From.Mailboxes.FirstOrDefault();
var recipients = mm.To.Mailboxes.AsEnumerable();
ApplicationPkcs7Mime p7m = ApplicationPkcs7Mime.SignAndEncrypt(ctx, signer, DigestAlgorithm.Sha1, recipients, mm.Body);
MemoryStream ms = new MemoryStream();
p7m.WriteTo(ms, true);
byte[] bytes = ms.GetBuffer();
File.WriteAllBytes("smime.p7m", bytes);
}
Here is my decrypt function:
private static void DecryptMime()
{
CryptographyContext.Register(typeof(WindowsSecureMimeContext));
string messagetext = "";
// Read the p7m file
byte[] bytes =
File.ReadAllBytes("smime.p7m");
MemoryStream ms = new MemoryStream(bytes);
ApplicationPkcs7Mime p7m = new ApplicationPkcs7Mime(SecureMimeType.EnvelopedData, ms);
// WindowsSecureMimeContext ctx = new WindowsSecureMimeContext(StoreLocation.CurrentUser);
if (p7m != null && p7m.SecureMimeType == SecureMimeType.EnvelopedData)
{
Console.WriteLine("Decrypting message...");
try
{
p7m = p7m.Decrypt() as ApplicationPkcs7Mime;
}
catch (Exception ex)
{
Console.WriteLine("Error decrypting: " + ex.Message);
}
}
if (p7m != null && p7m.SecureMimeType == SecureMimeType.CompressedData)
{
Console.WriteLine("Decompressing message...");
p7m = p7m.Decompress() as ApplicationPkcs7Mime;
}
if (p7m != null && p7m.SecureMimeType == SecureMimeType.SignedData)
{
Console.WriteLine("Verifying message...");
p7m.Verify(out MimeEntity entity);
MimeMessage mm = new MimeMessage(entity);
messagetext = mm.GetTextBody(MimeKit.Text.TextFormat.Text);
Console.WriteLine("Decrypted Message: " + messagetext);
}
}
Edit:
I tried isolating the issue by using the following code:
MemoryStream ms = new MemoryStream();
p7m.WriteTo(ms);
ApplicationPkcs7Mime new_p7m = new ApplicationPkcs7Mime(SecureMimeType.EnvelopedData, ms);
if (new_p7m != null && new_p7m.SecureMimeType == SecureMimeType.EnvelopedData)
{
Console.WriteLine("Decrypting message...");
try
{
new_p7m = new_p7m.Decrypt() as ApplicationPkcs7Mime;
}
catch (Exception ex)
{
Console.WriteLine("Error decrypting: " + ex.Message);
}
}
So the problem seems to start as early as the WriteTo() function...
Update #2:
Ok, so I did a toString on both the p7m and the new_p7m object. Apparently, the content is different...
p7m: Content-Type: application/pkcs7-mime; smime-type=enveloped-data;
name=smime.p7m
Content-Disposition: attachment; filename=smime.p7m
Content-Transfer-Encoding: base64
MIIOOwYJKoZIhvcNAQcDoIIOLDCCDigCAQIxggEwMIIBLAIBAoAU2NQqDvYHJuMeC27IpyiV
....
New p7m: Content-Type: application/pkcs7-mime; smime-type=enveloped-data;
name=smime.p7m
Content-Disposition: attachment; filename=smime.p7m
Content-Transfer-Encoding: base64
Q29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9wa2NzNy1taW1lOyBzbWltZS10eXBlPWVudmVs
....
To, somehow the actual content is changing... very strange...
I'm betting that your code to save the content to disk is writing a corrupt file:
MemoryStream ms = new MemoryStream();
p7m.WriteTo(ms, true);
byte[] bytes = ms.GetBuffer();
File.WriteAllBytes("smime.p7m", bytes);
The problem with the above code is that GetBuffer() returns more bytes than are actually used by the memory stream in most cases. The above logic will also save the content in base64 which is probably not what you want. The following code snippet will decode the base64 content into the smime.p7m file:
using (var stream = File.Create ("smime.p7m"))
p7m.Content.DecodeTo (stream);
Then, to reconstruct the MIME part back for decrypting, do this:
var bytes = File.ReadAllBytes ("smime.p7m");
var ms = new MemoryStream (bytes);
var p7m = new ApplicationPkcs7Mime (SecureMimeType.EnvelopedData, ms);
The problem you were facing is that the content was base64 encoded before and it should not have been.
Of course, unless there's really a need to save just the raw content and then re-construct the application/pkcs7-mime part the hard way, why not just save the entire MIME part like this?
using (var stream = File.Create ("smime.p7m"))
p7m.WriteTo (stream);
And then re-load it like this:
using (var stream = File.OpenRead ("smime.p7m"))
p7m = (ApplicationPkcs7Mime) MimeEntity.Load (stream);
Then you don't have to worry about getting things wrong.

C# Send Email with embedded images using CDOSYS

I'm trying to send an e-mail with embedded images. Here's what I'm doing...
I have a tinymce text editor where a user can select an image with accompanying text. The e-mail is sent successfully (using CDOSYS - email provider is zohomail) except none of the images are displayed in the e-mail.
Anyone able to tell me what I need to do to display images in an e-mail?
The key function I'm using to convert the image tags to a linked resources is below (uses the agility pack - Install-Package HtmlAgilityPack -Version 1.6.5):
public string ConvertToHtmlDocument(string originalText)
{
HtmlDocument document = new HtmlDocument();
document.LoadHtml(originalText);
document.DocumentNode.Descendants("img").ToList();
foreach(var d in document.DocumentNode.Descendants("img").ToList())
{
string base64String = "";
string currentSrcValue = d.GetAttributeValue("src", null);
string mimeType = MimeMapping.GetMimeMapping(Server.MapPath(currentSrcValue));
using (Image image = Image.FromFile(Server.MapPath(currentSrcValue)))
{
using (MemoryStream m = new MemoryStream())
{
image.Save(m, image.RawFormat);
byte[] imageBytes = m.ToArray();
// Convert byte[] to Base64 String
base64String = Convert.ToBase64String(imageBytes);
}
}
//currentSrcValue = currentSrcValue.Split(',')[1];//Base64 part of string
byte[] imageData = Convert.FromBase64String(base64String);
string contentId = Guid.NewGuid().ToString();
LinkedResource inline = new LinkedResource(new MemoryStream(imageData), mimeType);
inline.ContentId = contentId;
inline.TransferEncoding = TransferEncoding.Base64;
d.SetAttributeValue("src", "cid:" + inline.ContentId);
}
return document.DocumentNode.OuterHtml;
}
[
[

Saving an image from bytes

I am trying to create an API which will save an image at a given location .
Below is my Code for that
Image image = Image.FromFile(#"C:\Users\abc\Desktop\img-logo.jpg");
byte[] bytes = (byte[])(new ImageConverter()).ConvertTo(image, typeof(byte[]));
string str = Convert.ToBase64String(bytes);
Save_Application_Image("12004", str);
and the method in API
public void Save_Application_Image(string staffCode , string bytearray)
{
try
{
byte[] bytes = Convert.FromBase64String(bytearray);
file_path = "~/uploads/" + file_name;
FileStream file = File.Create(HttpContext.Current.Server.MapPath(file_path));
file.Write(bytes, 0, bytes.Length);
file.Close();
}
catch(Exception ex)
{
logger.LogError(ex);
}
finally
{
}
}
This api has to be called from Android Application so I will receive two string parameter.
It is saving the file perfectly but file is not readable. No preview for image file.
What is the right approach for this ?
I'm unsure of how the ImageConverter works, but I've used this before to convert Images to byte[]:
Image image = Image.FromFile(#"C:\Users\abc\Desktop\img-logo.jpg");
using (var stream = new MemoryStream())
{
image.Save(stream);
string savedImage = Convert.ToBase64String(stream.ToArray());
Save_Application_Image("12004", str);
}

Converting file into Base64String and back again

The title says it all:
I read in a tar.gz archive like so
break the file into an array of bytes
Convert those bytes into a Base64 string
Convert that Base64 string back into an array of bytes
Write those bytes back into a new tar.gz file
I can confirm that both files are the same size (the below method returns true) but I can no longer extract the copy version.
Am I missing something?
Boolean MyMethod(){
using (StreamReader sr = new StreamReader("C:\...\file.tar.gz")) {
String AsString = sr.ReadToEnd();
byte[] AsBytes = new byte[AsString.Length];
Buffer.BlockCopy(AsString.ToCharArray(), 0, AsBytes, 0, AsBytes.Length);
String AsBase64String = Convert.ToBase64String(AsBytes);
byte[] tempBytes = Convert.FromBase64String(AsBase64String);
File.WriteAllBytes(#"C:\...\file_copy.tar.gz", tempBytes);
}
FileInfo orig = new FileInfo("C:\...\file.tar.gz");
FileInfo copy = new FileInfo("C:\...\file_copy.tar.gz");
// Confirm that both original and copy file have the same number of bytes
return (orig.Length) == (copy.Length);
}
EDIT: The working example is much simpler (Thanks to #T.S.):
Boolean MyMethod(){
byte[] AsBytes = File.ReadAllBytes(#"C:\...\file.tar.gz");
String AsBase64String = Convert.ToBase64String(AsBytes);
byte[] tempBytes = Convert.FromBase64String(AsBase64String);
File.WriteAllBytes(#"C:\...\file_copy.tar.gz", tempBytes);
FileInfo orig = new FileInfo(#"C:\...\file.tar.gz");
FileInfo copy = new FileInfo(#"C:\...\file_copy.tar.gz");
// Confirm that both original and copy file have the same number of bytes
return (orig.Length) == (copy.Length);
}
Thanks!
If you want for some reason to convert your file to base-64 string. Like if you want to pass it via internet, etc... you can do this
Byte[] bytes = File.ReadAllBytes("path");
String file = Convert.ToBase64String(bytes);
And correspondingly, read back to file:
Byte[] bytes = Convert.FromBase64String(b64Str);
File.WriteAllBytes(path, bytes);
Another working example in VB.NET:
Public Function base64Encode(ByVal myDataToEncode As String) As String
Try
Dim myEncodeData_byte As Byte() = New Byte(myDataToEncode.Length - 1) {}
myEncodeData_byte = System.Text.Encoding.UTF8.GetBytes(myDataToEncode)
Dim myEncodedData As String = Convert.ToBase64String(myEncodeData_byte)
Return myEncodedData
Catch ex As Exception
Throw (New Exception("Error in base64Encode" & ex.Message))
End Try
'
End Function
private String encodeFileToBase64Binary(File file){
String encodedfile = null;
try {
FileInputStream fileInputStreamReader = new FileInputStream(file);
byte[] bytes = new byte[(int)file.length()];
fileInputStreamReader.read(bytes);
encodedfile = Base64.encodeBase64(bytes).toString();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return encodedfile;
}
For Java, consider using Apache Commons FileUtils:
/**
* Convert a file to base64 string representation
*/
public String fileToBase64(File file) throws IOException {
final byte[] bytes = FileUtils.readFileToByteArray(file);
return Base64.getEncoder().encodeToString(bytes);
}
/**
* Convert base64 string representation to a file
*/
public void base64ToFile(String base64String, String filePath) throws IOException {
byte[] bytes = Base64.getDecoder().decode(base64String);
FileUtils.writeByteArrayToFile(new File(filePath), bytes);
}

Convert a base64string back to byte[]

I want to use C# to convert a byteArray to base64string by using Convert.ToBase64String(), then I want to send this string to save using MySQL by sending post data to a PHP page. My problem is I can't convert this string back to a byteArray using this method as after this string is retrieved from the PHP page (after it got data from MySQL), it tells me that argument on method Convert.FromBase64String() was wrong.
I don't where is problem occurs, how can I solve it?
My code:
public static string BitmapToString(BitmapImage img)
{
try
{
WriteableBitmap bmp = new WriteableBitmap(img);
byte[] byteArray = null;
string str = null;
MemoryStream stream = new MemoryStream();
bmp.SaveJpeg(stream, bmp.PixelWidth, bmp.PixelHeight, 0, 100);
byteArray = stream.ToArray();
str = Convert.ToBase64String(byteArray);
return str;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
return null;
}
public static BitmapImage StringToBitmap(string str)
{
try
{
byte[] byteArray = Convert.FromBase64String(str);
Stream memStream = new MemoryStream(byteArray);
BitmapImage img = null;
MemoryStream stream = new MemoryStream(byteArray);
stream.Seek(0, SeekOrigin.Begin);
img = new BitmapImage();
img.SetSource(stream);
return img;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
return null;
}
Convert.FromBase64String() will work for Convert.ToBase64String() results but there can be inputs in your aplication which not converted to Base64String, those cases might fail.
This is not be a issue with those two methods. Check Convert.ToBase64String() result and what you get when you read it from database.

Categories

Resources