sessionKey using nonce (client) and nonce(server) based on KDF2 - asp.net

I am working on a project. Client have common authentication system. I have to generate a nonce from my web server, encrypt using public key of authentication system, and sing using my webserver private key and post to the authentication system (XML base64 encoded). Authentication system decrypt using their private key, generate a nonce encrypt using my public key and and send back to my webserver in response. my webserver decrypt the response using my private key.
Now next step is to construct a sessionKey using nonce (client) and nonce(server) based on KDF2 algorithm. I am using asp.net 4.0 .. not able to understand and find any kind of help on "sessionKey using nonce (client) and nonce(server) based on KDF2 " in asp.net.
protected void ButtonLogin_Click()
{
string datatopost = CreatEncryptandSignXML();
PostxmltoCommongateway(datatopost);
}
public string CreatEncryptandSignXML()
{
string ClientNonce = null;
ClientNonce = Guid.NewGuid().ToString("N");
Session["ClientNonce"] = ClientNonce;
byte[] bytesToEncode = Encoding.UTF8.GetBytes(ClientNonce);
string encodednonce = Convert.ToBase64String(bytesToEncode);
string xml = "<?xml version=\"1.0\"?><root><LoginRequest>Nonce=" + encodednonce + "</LoginRequest></root>";
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml(xml);
doc.PreserveWhitespace = true;
// Sing XML using private key ------------------------------------------------------------
System.Security.Cryptography.X509Certificates.X509Certificate2 commonauthpublickey =
new System.Security.Cryptography.X509Certificates.X509Certificate2
(#"C:\commonauthserver\publickey\sample.cer");
System.Xml.XmlElement elementToEncrypt = doc.GetElementsByTagName("LoginRequest")[0] as System.Xml.XmlElement;
System.Security.Cryptography.Xml.EncryptedXml encXML = new System.Security.Cryptography.Xml.EncryptedXml();
System.Security.Cryptography.Xml.EncryptedData data = encXML.Encrypt(elementToEncrypt, commonauthpublickey);
System.Security.Cryptography.Xml.EncryptedXml.ReplaceElement(elementToEncrypt, data, false);
// Sign XML using Private Key ---------------------------------------------------------------
System.Security.Cryptography.X509Certificates.X509Certificate2 mywerbserverprivatekey =
new System.Security.Cryptography.X509Certificates.X509Certificate2
(#"C:\mywebserver\privatekey\mywebserver.pfx","samplepasword");
System.Security.Cryptography.Xml.SignedXml sign = new System.Security.Cryptography.Xml.SignedXml(doc);
System.Security.Cryptography.Xml.KeyInfo keyInfo = new System.Security.Cryptography.Xml.KeyInfo();
sign.SigningKey = mywerbserverprivatekey.PrivateKey;
System.Security.Cryptography.Xml.KeyInfoX509Data keyInfoData = new System.Security.Cryptography.Xml.KeyInfoX509Data();
keyInfoData.AddIssuerSerial(mywerbserverprivatekey.Issuer, mywerbserverprivatekey.GetSerialNumberString());
keyInfo.AddClause(keyInfoData);
sign.KeyInfo = keyInfo;
System.Security.Cryptography.Xml.Reference reference = new System.Security.Cryptography.Xml.Reference();
reference.Uri = "";
System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform env = new
System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
sign.AddReference(reference);
sign.ComputeSignature();
System.Xml.XmlElement signedElement = sign.GetXml();
signedElement.Prefix = "ds";
doc.DocumentElement.AppendChild(signedElement);
return doc.InnerXml;
}
void PostxmltoCommongateway(string encData)
{
string URLAuth = "http://commonauth.com/getway/commomauth.do";
byte[] bytesToEncode = Encoding.UTF8.GetBytes(encData);
string encodedText = Convert.ToBase64String(bytesToEncode);
string encodedXML = HttpUtility.UrlEncode(encodedText);
string postString = string.Format("encryptedData={0}", encodedXML);
const string contentType = "application/x-www-form-urlencoded";
System.Net.ServicePointManager.Expect100Continue = false;
CookieContainer cookies = new CookieContainer();
HttpWebRequest webRequest = WebRequest.Create(URLAuth) as HttpWebRequest;
webRequest.Method = "POST";
webRequest.ContentType = contentType;
webRequest.CookieContainer = cookies;
webRequest.ContentLength = postString.Length;
webRequest.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1";
webRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
StreamWriter requestWriter = new StreamWriter(webRequest.GetRequestStream());
requestWriter.Write(postString);
requestWriter.Close();
try
{
WebResponse response = webRequest.GetResponse();
StreamReader responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
string responseData = responseReader.ReadToEnd();
responseReader.Close();
webRequest.GetResponse().Close();
string servernonce = DecryptResponse(responseData);
string clientnonce = Session["ClientNonce"].ToString();
/// here i have to generate a sessionKey using nonce (client) and nonce(server) based on KDF2
// CreateSessionkeybasedonKDF2(servernonce,clientnonce)
}
catch (Exception ex)
{
LabelMessage.Text = ex.Message;
}
}
public string DecryptResponse(string response)
{
System.Security.Cryptography.X509Certificates.X509Certificate2 mywerbserverprivatekey =
new System.Security.Cryptography.X509Certificates.X509Certificate2
(#"C:\mywebserver\privatekey\mywebserver.pfx", "samplepasword");
RSACryptoServiceProvider.UseMachineKeyStore = false;
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)mywerbserverprivatekey.PrivateKey;
byte[] decrypted = rsa.Decrypt(Convert.FromBase64String(response), false);
return ASCIIEncoding.UTF8.GetString(decrypted);
}
Thank you for your response in advance.

KDF2 is the key seed (any length, should represent a key with enough entropy) postfixed by a 4 byte counter value in BigEndian notation, starting with value 1. So that would be KEY_SEED|00000001 for the first key. After the concatenation the value is hashed using a secure hash algorithm like SHA-1 (the hash to be used is configurable). Then you take the X leftmost bits or bytes you require from the resulting hash value.
I can only tell you how to implement KDF2 as it is not often included in cryptographic API's, I'm afraid you have to do the implementation and testing yourself (that's "too localized" anyway).

Related

Ruby OpenSSL::Cipher::CipherError (key not set)

I am trying to port an existing .net encryption code to ruby. But stuck with the key not set error.
Bellow is the .net code to encrypt a string.
private static string Encrypt(string strToEncrypt, string saltValue, string password)
{
using (var csp = new AesCryptoServiceProvider())
{
ICryptoTransform e = GetCryptoTransform(csp, true, saltValue, password);
byte[] inputBuffer = Encoding.UTF8.GetBytes(strToEncrypt);
byte[] output = e.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
string encrypted = Convert.ToBase64String(output);
return encrypted;
}
}
private static ICryptoTransform GetCryptoTransform(AesCryptoServiceProvider csp, bool encrypting, string saltValue, string password)
{
csp.Mode = CipherMode.CBC;
csp.Padding = PaddingMode.PKCS7;
var passWord = password;
var salt = saltValue;
//a random Init. Vector. just for testing
String iv = "e675f725e675f123";
var spec = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(passWord), Encoding.UTF8.GetBytes(salt), 1000);
byte[] key = spec.GetBytes(16);
csp.IV = Encoding.UTF8.GetBytes(iv);
csp.Key = key;
if (encrypting)
{
return csp.CreateEncryptor();
}
return csp.CreateDecryptor();
}
I have used Ruby's OpenSSL::PKCS5 library to generate key and OpenSSL::Cipher to encrypt using AES algorithm like bellow.
def aes_encrypt(input_string)
cipher = OpenSSL::Cipher.new('AES-128-CBC')
cipher.encrypt
key = encryption_key
iv = cipher.random_iv
cipher.update(input_string) + cipher.final
end
def encryption_key
OpenSSL::PKCS5.pbkdf2_hmac_sha1(PASSWORD, SALT, 1000, 16)
end
Can anyone let know where I am missing? (Padding ?)

Tableau Unexpired Trusted Ticket - including ClientIP

I have an ASP.NET web application in which I'm rendering different tableau dashboards from a site based on the menu clicked by the user. I have multiple menus and each menu was tied to a tableau URL.
Tableau Trusted Authentication has been implemented to get the trusted ticket from the tableau server. Once the ticket has been retrieved, I am appending the ticket to the dashboard URL along with the server name for each menu.
The trusted ticketing module is working fine and the visualizations are getting rendered in my web application. However, frequently I am getting a message of "Could not locate unexpired ticket" error.
On checking with this error, this is due to the ticket calls getting duplicated.
I reached out to the support regarding this and got a response that I can add client_ip during my trusted ticketing.
Tableau Trusted Ticket
I am not able to find any code article related to adding client_ip in trusted ticketing.
Below is my trusted ticket code.
public class TableauTicket
{
public string getTableauTicket(string tabserver, string sUsername)
{
try
{
ASCIIEncoding enc = new ASCIIEncoding();
string postData = string.Empty;
string resString = string.Empty;
postData = "username=" + sUsername + "";
// FEATURE 816 END - Custom Visualization - KV
if (postData != string.Empty)
{
byte[] data = enc.GetBytes(postData);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(tabserver + "/trusted");
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
req.ContentLength = data.Length;
Stream outStream = req.GetRequestStream();
outStream.Write(data, 0, data.Length);
outStream.Close();
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
StreamReader inStream = new StreamReader(stream: res.GetResponseStream(), encoding: enc);
resString = inStream.ReadToEnd();
inStream.Close();
return resString;
}
else
{
resString = "User not authorised";
return resString;
}
}
catch (Exception ex)
{
string resString = "User not authorised";
return resString;
string strTrailDesc = "Exception in tableau ticket - " + ex.Message;
}
}
public int Double(int i)
{
return i * 2;
}
}
Can anyone please let me know how the client_ip can be passed in trusted ticketing code?
Also, the client IP will get changed for each user and how this will be handled in the trusted ticketing?
UPDATE
I have solved the issue using the source code provided by tableau on how to embed the view in SharePoint.
Below is the code which may help users having the same issue.
string GetTableauTicket(string tabserver, string tabuser, ref string errMsg)
{
ASCIIEncoding enc = new ASCIIEncoding();
// the client_ip parameter isn't necessary to send in the POST unless you have
// wgserver.extended_trusted_ip_checking enabled (it's disabled by default)
string postData = "username=" + tabuser + "&client_ip=" + Page.Request.UserHostAddress;
byte[] data = enc.GetBytes(postData);
try
{
string http = _tabssl ? "https://" : "http://";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(http + tabserver + "/trusted");
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = data.Length;
// Write the request
Stream outStream = req.GetRequestStream();
outStream.Write(data, 0, data.Length);
outStream.Close();
// Do the request to get the response
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
StreamReader inStream = new StreamReader(res.GetResponseStream(), enc);
string resString = inStream.ReadToEnd();
inStream.Close();
return resString;
}
// if anything bad happens, copy the error string out and return a "-1" to indicate failure
catch (Exception ex)
{
errMsg = ex.ToString();
return "-1";
}
}
Assuming your code is working, (I have done this part in Java and not really an expert in asp.net) all you have to do is to add something like:
postData = postData +"&client_ip=" +<variable for client IP>;
The way it is handled on tableau server is :
you turn on wgserver.extended_trusted_ip_checking on Tableau server. see details here
Tableau will match the client IP you passed in the POST request 'client_ip=XXX.XXX.XXX.XXX' while obtaining the token, with the actual IP of the the machine where the browser is trying to access tableau server.

files not uploading to FTP Server [duplicate]

I try upload a file to an FTP-server with C#. The file is uploaded but with zero bytes.
private void button2_Click(object sender, EventArgs e)
{
var dirPath = #"C:/Documents and Settings/sander.GD/Bureaublad/test/";
ftp ftpClient = new ftp("ftp://example.com/", "username", "password");
string[] files = Directory.GetFiles(dirPath,"*.*");
var uploadPath = "/httpdocs/album";
foreach (string file in files)
{
ftpClient.createDirectory("/test");
ftpClient.upload(uploadPath + "/" + Path.GetFileName(file), file);
}
if (string.IsNullOrEmpty(txtnaam.Text))
{
MessageBox.Show("Gelieve uw naam in te geven !");
}
}
The existing answers are valid, but why re-invent the wheel and bother with lower level WebRequest types while WebClient already implements FTP uploading neatly:
using (var client = new WebClient())
{
client.Credentials = new NetworkCredential(ftpUsername, ftpPassword);
client.UploadFile("ftp://host/path.zip", WebRequestMethods.Ftp.UploadFile, localFile);
}
Easiest way
The most trivial way to upload a file to an FTP server using .NET framework is using WebClient.UploadFile method:
WebClient client = new WebClient();
client.Credentials = new NetworkCredential("username", "password");
var url = "ftp://ftp.example.com/remote/path/file.zip";
client.UploadFile(url, #"C:\local\path\file.zip");
Advanced options
If you need a greater control, that WebClient does not offer (like TLS/SSL encryption, ascii/text transfer mode, active mode, transfer resuming, progress monitoring, etc), use FtpWebRequest. Easy way is to just copy a FileStream to an FTP stream using Stream.CopyTo:
var url = "ftp://ftp.example.com/remote/path/file.zip";
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.Credentials = new NetworkCredential("username", "password");
request.Method = WebRequestMethods.Ftp.UploadFile;
using (Stream fileStream = File.OpenRead(#"C:\local\path\file.zip"))
using (Stream ftpStream = request.GetRequestStream())
{
fileStream.CopyTo(ftpStream);
}
Progress monitoring
If you need to monitor an upload progress, you have to copy the contents by chunks yourself:
var url = "ftp://ftp.example.com/remote/path/file.zip";
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.Credentials = new NetworkCredential("username", "password");
request.Method = WebRequestMethods.Ftp.UploadFile;
using (Stream fileStream = File.OpenRead(#"C:\local\path\file.zip"))
using (Stream ftpStream = request.GetRequestStream())
{
byte[] buffer = new byte[10240];
int read;
while ((read = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
ftpStream.Write(buffer, 0, read);
Console.WriteLine("Uploaded {0} bytes", fileStream.Position);
}
}
For GUI progress (WinForms ProgressBar), see C# example at:
How can we show progress bar for upload with FtpWebRequest
Uploading folder
If you want to upload all files from a folder, see
Upload directory of files to FTP server using WebClient.
For a recursive upload, see
Recursive upload to FTP server in C#
.NET 5 Guide
async Task<FtpStatusCode> FtpFileUploadAsync(string ftpUrl, string userName, string password, string filePath)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUrl);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(userName, password);
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (Stream requestStream = request.GetRequestStream())
{
await fileStream.CopyToAsync(requestStream);
}
using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
{
return response.StatusCode;
}
}
.NET Framework
public void UploadFtpFile(string folderName, string fileName)
{
FtpWebRequest request;
string folderName;
string fileName;
string absoluteFileName = Path.GetFileName(fileName);
request = WebRequest.Create(new Uri(string.Format(#"ftp://{0}/{1}/{2}", "127.0.0.1", folderName, absoluteFileName))) as FtpWebRequest;
request.Method = WebRequestMethods.Ftp.UploadFile;
request.UseBinary = 1;
request.UsePassive = 1;
request.KeepAlive = 1;
request.Credentials = new NetworkCredential(user, pass);
request.ConnectionGroupName = "group";
using (FileStream fs = File.OpenRead(fileName))
{
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
Stream requestStream = request.GetRequestStream();
requestStream.Write(buffer, 0, buffer.Length);
requestStream.Flush();
requestStream.Close();
}
}
How to use
UploadFtpFile("testFolder", "E:\\filesToUpload\\test.img");
use this in your foreach
and you only need to create folder one time
to create a folder
request = WebRequest.Create(new Uri(string.Format(#"ftp://{0}/{1}/", "127.0.0.1", "testFolder"))) as FtpWebRequest;
request.Method = WebRequestMethods.Ftp.MakeDirectory;
FtpWebResponse ftpResponse = (FtpWebResponse)request.GetResponse();
The following works for me:
public virtual void Send(string fileName, byte[] file)
{
ByteArrayToFile(fileName, file);
var request = (FtpWebRequest) WebRequest.Create(new Uri(ServerUrl + fileName));
request.Method = WebRequestMethods.Ftp.UploadFile;
request.UsePassive = false;
request.Credentials = new NetworkCredential(UserName, Password);
request.ContentLength = file.Length;
var requestStream = request.GetRequestStream();
requestStream.Write(file, 0, file.Length);
requestStream.Close();
var response = (FtpWebResponse) request.GetResponse();
if (response != null)
response.Close();
}
You can't read send the file parameter in your code as it is only the filename.
Use the following:
byte[] bytes = File.ReadAllBytes(dir + file);
To get the file so you can pass it to the Send method.
public static void UploadFileToFtp(string url, string filePath, string username, string password)
{
var fileName = Path.GetFileName(filePath);
var request = (FtpWebRequest)WebRequest.Create(url + fileName);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(username, password);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false;
using (var fileStream = File.OpenRead(filePath))
{
using (var requestStream = request.GetRequestStream())
{
fileStream.CopyTo(requestStream);
requestStream.Close();
}
}
var response = (FtpWebResponse)request.GetResponse();
Console.WriteLine("Upload done: {0}", response.StatusDescription);
response.Close();
}
In the first example must change those to:
requestStream.Flush();
requestStream.Close();
First flush and after that close.
This works for me,this method will SFTP a file to a location within your network.
It uses SSH.NET.2013.4.7 library.One can just download it for free.
//Secure FTP
public void SecureFTPUploadFile(string destinationHost,int port,string username,string password,string source,string destination)
{
ConnectionInfo ConnNfo = new ConnectionInfo(destinationHost, port, username, new PasswordAuthenticationMethod(username, password));
var temp = destination.Split('/');
string destinationFileName = temp[temp.Count() - 1];
string parentDirectory = destination.Remove(destination.Length - (destinationFileName.Length + 1), destinationFileName.Length + 1);
using (var sshclient = new SshClient(ConnNfo))
{
sshclient.Connect();
using (var cmd = sshclient.CreateCommand("mkdir -p " + parentDirectory + " && chmod +rw " + parentDirectory))
{
cmd.Execute();
}
sshclient.Disconnect();
}
using (var sftp = new SftpClient(ConnNfo))
{
sftp.Connect();
sftp.ChangeDirectory(parentDirectory);
using (var uplfileStream = System.IO.File.OpenRead(source))
{
sftp.UploadFile(uplfileStream, destinationFileName, true);
}
sftp.Disconnect();
}
}
publish date: 06/26/2018
https://learn.microsoft.com/en-us/dotnet/framework/network-programming/how-to-upload-files-with-ftp
using System;
using System.IO;
using System.Net;
using System.Text;
namespace Examples.System.Net
{
public class WebRequestGetExample
{
public static void Main ()
{
// Get the object used to communicate with the server.
FtpWebRequest request =
(FtpWebRequest)WebRequest.Create("ftp://www.contoso.com/test.htm");
request.Method = WebRequestMethods.Ftp.UploadFile;
// This example assumes the FTP site uses anonymous logon.
request.Credentials = new NetworkCredential("anonymous",
"janeDoe#contoso.com");
// Copy the contents of the file to the request stream.
byte[] fileContents;
using (StreamReader sourceStream = new StreamReader("testfile.txt"))
{
fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
}
request.ContentLength = fileContents.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(fileContents, 0, fileContents.Length);
}
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
Console.WriteLine($"Upload File Complete, status
{response.StatusDescription}");
}
}
}
}
Best way I've found is FluentFtp
You can find the repo here:
https://github.com/robinrodricks/FluentFTP
and the quickstart example here:
https://github.com/robinrodricks/FluentFTP/wiki/Quick-Start-Example.
And actually the WebRequest class recommended by a few people here, is not recommended by Microsoft anymore, check out this page:
https://learn.microsoft.com/en-us/dotnet/api/system.net.webrequest?view=net-5.0
// create an FTP client and specify the host, username and password
// (delete the credentials to use the "anonymous" account)
FtpClient client = new FtpClient("123.123.123.123", "david", "pass123");
// connect to the server and automatically detect working FTP settings
client.AutoConnect();
// upload a file and retry 3 times before giving up
client.RetryAttempts = 3;
client.UploadFile(#"C:\MyVideo.mp4", "/htdocs/big.txt",
FtpRemoteExists.Overwrite, false, FtpVerify.Retry);
// disconnect! good bye!
client.Disconnect();
I have observed that -
FtpwebRequest is missing.
As the target is FTP, so the NetworkCredential required.
I have prepared a method that works like this, you can replace the value of the variable ftpurl with the parameter TargetDestinationPath. I had tested this method on winforms application :
private void UploadProfileImage(string TargetFileName, string TargetDestinationPath, string FiletoUpload)
{
//Get the Image Destination path
string imageName = TargetFileName; //you can comment this
string imgPath = TargetDestinationPath;
string ftpurl = "ftp://downloads.abc.com/downloads.abc.com/MobileApps/SystemImages/ProfileImages/" + imgPath;
string ftpusername = krayknot_DAL.clsGlobal.FTPUsername;
string ftppassword = krayknot_DAL.clsGlobal.FTPPassword;
string fileurl = FiletoUpload;
FtpWebRequest ftpClient = (FtpWebRequest)FtpWebRequest.Create(ftpurl);
ftpClient.Credentials = new System.Net.NetworkCredential(ftpusername, ftppassword);
ftpClient.Method = System.Net.WebRequestMethods.Ftp.UploadFile;
ftpClient.UseBinary = true;
ftpClient.KeepAlive = true;
System.IO.FileInfo fi = new System.IO.FileInfo(fileurl);
ftpClient.ContentLength = fi.Length;
byte[] buffer = new byte[4097];
int bytes = 0;
int total_bytes = (int)fi.Length;
System.IO.FileStream fs = fi.OpenRead();
System.IO.Stream rs = ftpClient.GetRequestStream();
while (total_bytes > 0)
{
bytes = fs.Read(buffer, 0, buffer.Length);
rs.Write(buffer, 0, bytes);
total_bytes = total_bytes - bytes;
}
//fs.Flush();
fs.Close();
rs.Close();
FtpWebResponse uploadResponse = (FtpWebResponse)ftpClient.GetResponse();
string value = uploadResponse.StatusDescription;
uploadResponse.Close();
}
Let me know in case of any issue, or here is one more link that can help you:
https://msdn.microsoft.com/en-us/library/ms229715(v=vs.110).aspx

Encrypting email in Microsoft.Exchange.WebServices.Data

I am trying to send an encrypted email using Microsoft.Exchange.WebServices.Data.
public void SendMessage(FacadeModel.EmailMessage message)
{
var item = new EWS.EmailMessage(_mailService);
var handler = new SecureMimeMessageHandler();
byte[] con = handler.encry("test", "me#mail.com.au");
item.MimeContent = new EWS.MimeContent(Encoding.ASCII.HeaderName, con);
item.ToRecipients.Add("me#mail.com.au");
item.From = new EWS.EmailAddress("", "me#mail.com.au");
item.Body = "test";
item.Send();
}
public byte[] encry(string body, string to)
{
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
X509Certificate2Collection certs = store.Certificates;
X509Certificate2 cert1 = GetMatchingCertificate(certs[1], "me#mail.com.au", X509KeyUsageFlags.KeyEncipherment);
StringBuilder msg = new StringBuilder();
msg.AppendLine(string.Format("Content-Type: application/pkcs7-mime; smime-type=signed-data;name=smime.p7m"));
msg.AppendLine("Content-Transfer-Encoding: 7bit");
msg.AppendLine();
msg.AppendLine(body);
EnvelopedCms envelope = new EnvelopedCms(new ContentInfo(Encoding.UTF8.GetBytes(msg.ToString())));
CmsRecipient recipient = new CmsRecipient(SubjectIdentifierType.IssuerAndSerialNumber, cert1);
envelope.Encrypt(recipient);
return envelope.Encode();
}
But still i am getting a plain email with no encryption in outlook. where have i gone wrong?
I posted a suggestion on the MSDN forum. Try setting the ItemClass to "IPM.Note.SMIME".

Compression and encryption SOAP - ASP.NET web service

I need advice. I zip and crypt SOAP message on web service and client side.
Client is winforms app.
If I only crypt SOAP message, it works good.
If I only zip SOAP message it also works good.
I use SOAP extension on crypt and zip SOAP.
I use AES - Advanced Encryption Standard - Rijndael and on compresion I use SharpZipLib from http://sourceforge.net/projects/sharpdevelop/.
The problem is I send dataset on client.
Firstly I zip and secondly encrypt SOAP on web service side.
Send on client.
On client side I load XML from stream. But it finish with this error :
Data at the root level is invalid. Line 1, position 2234.
Here is the code, where I load XML from stream:
var doc = new XmlDocument();
using (var reader = new XmlTextReader(inputStream))
{
doc.Load(reader);
}
Any advice ? Thank you...
Here are methods on web service side which zip and crypt SOAP :
//encrypt string
private static string EncryptString(string #string, string initialVector, string salt, string password,
string hashAlgorithm, int keySize, int passwordIterations)
{
byte[] initialVectorBytes = Encoding.ASCII.GetBytes(initialVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(salt);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(#string);
var derivedPassword = new PasswordDeriveBytes(password, saltValueBytes, hashAlgorithm, passwordIterations);
byte[] keyBytes = derivedPassword.GetBytes(keySize / 8);
var symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initialVectorBytes);
using (var memStream = new MemoryStream())
{
var cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
var serializer = new XmlSerializer(typeof(byte[]));
var sb = new StringBuilder();
TextWriter writer = new StringWriter(sb);
serializer.Serialize(writer, memStream.ToArray());
writer.Flush();
var doc = new XmlDocument();
doc.LoadXml(sb.ToString());
if (doc.DocumentElement != null) return doc.DocumentElement.InnerXml;
}
return "";
}
//zip string
private static byte[] ZipArray(string stringToZip)
{
byte[] inputByteArray = Encoding.UTF8.GetBytes(stringToZip);
var ms = new MemoryStream();
// SharpZipLib.Zip,
var zipOut = new ZipOutputStream(ms);
var zipEntry = new ZipEntry("ZippedFile");
zipOut.PutNextEntry(zipEntry);
zipOut.SetLevel(7);
zipOut.Write(inputByteArray, 0, inputByteArray.Length);
zipOut.Finish();
zipOut.Close();
return ms.ToArray();
}
//zip and encrypt SOAP
public virtual Stream OutSoap(string[] soapElement, Stream inputStream)
{
#region Load XML from SOAP
var doc = new XmlDocument();
using (XmlReader reader = XmlReader.Create(inputStream))
{
doc.Load(reader);
}
var nsMan = new XmlNamespaceManager(doc.NameTable);
nsMan.AddNamespace("soap",
"http://schemas.xmlsoap.org/soap/envelope/");
#endregion Load XML from SOAP
#region Zip SOAP
XmlNode bodyNode = doc.SelectSingleNode(#"//soap:Body", nsMan);
bodyNode = bodyNode.FirstChild.FirstChild;
while (bodyNode != null)
{
if (bodyNode.InnerXml.Length > 0)
{
// Zip
byte[] outData = ZipArray(bodyNode.InnerXml);
bodyNode.InnerXml = Convert.ToBase64String(outData);
}
bodyNode = bodyNode.NextSibling;
}
#endregion Zip SOAP
#region Crypt SOAP
foreach (string xPathQuery in soapElement)
{
XmlNodeList nodesToEncrypt = doc.SelectNodes(xPathQuery, nsMan);
if (nodesToEncrypt != null)
foreach (XmlNode nodeToEncrypt in nodesToEncrypt)
{
//Encrypt
nodeToEncrypt.InnerXml = EncryptString(nodeToEncrypt.InnerXml,
user.IV, user.Salt, user.Password, user.HashType,
user.KeySize, user.PasswordIterations);
}
}
#endregion Crypt SOAP
inputStream.Position = 0;
var settings = new XmlWriterSettings { Encoding = Encoding.UTF8 };
using (XmlWriter writer = XmlWriter.Create(inputStream, settings))
{
doc.WriteTo(writer);
return inputStream;
}
}
Here is a code on client side which decrypt and uzip SOAP :
//decrypt string
private static string DecryptString(string #string, string initialVector, string salt, string password,
string hashAlgorithm, int keySize, int passwordIterations)
{
byte[] initialVectorBytes = Encoding.ASCII.GetBytes(initialVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(salt);
byte[] cipherTextBytes = Convert.FromBase64String(#string);
var derivedPassword = new PasswordDeriveBytes(password, saltValueBytes, hashAlgorithm, passwordIterations);
byte[] keyBytes = derivedPassword.GetBytes(keySize / 8);
var symmetricKey = new RijndaelManaged { Mode = CipherMode.CBC };
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initialVectorBytes);
using (var memStream = new MemoryStream(cipherTextBytes))
{
var cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Read);
var plainTextBytes = new byte[cipherTextBytes.Length];
int byteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, byteCount);
}
}
//unzip string
private static byte[] UnzipArray(string stringToUnzip)
{
byte[] inputByteArray = Convert.FromBase64String(stringToUnzip);
var ms = new MemoryStream(inputByteArray);
var ret = new MemoryStream();
// SharpZipLib.Zip
var zipIn = new ZipInputStream(ms);
var theEntry = zipIn.GetNextEntry();
var buffer = new Byte[2048];
int size = 2048;
while (true)
{
size = zipIn.Read(buffer, 0, buffer.Length);
if (size > 0)
{
ret.Write(buffer, 0, size);
}
else
{
break;
}
}
return ret.ToArray();
}
public virtual Stream InSoap(Stream inputStream, string[] soapElement)
{
#region Load XML from SOAP
var doc = new XmlDocument();
using (var reader = new XmlTextReader(inputStream))
{
doc.Load(reader);
}
var nsMan = new XmlNamespaceManager(doc.NameTable);
nsMan.AddNamespace("soap",
"http://schemas.xmlsoap.org/soap/envelope/");
#endregion Load XML from SOAP
#region Decrypt SOAP
foreach (string xPathQuery in soapElement)
{
XmlNodeList nodesToEncrypt = doc.SelectNodes(xPathQuery, nsMan);
if (nodesToEncrypt != null)
foreach (XmlNode nodeToEncrypt in nodesToEncrypt)
{
nodeToEncrypt.InnerXml = DecryptString(nodeToEncrypt.InnerXml, saltPhrase, passwordPhrase, initialVector,
hashAlgorithm, passwordIterations, keySize);
}
}
#endregion Decrypt SOAP
#region UnZip SOAP
XmlNode node = doc.SelectSingleNode("//soap:Body", nsMan);
node = node.FirstChild.FirstChild;
while (node != null)
{
if (node.InnerXml.Length > 0)
{
byte[] outData = UnzipArray(node.InnerXml);
string sTmp = Encoding.UTF8.GetString(outData);
node.InnerXml = sTmp;
}
node = node.NextSibling;
}
#endregion UnZip SOAP
var retStream = new MemoryStream();
doc.Save(retStream);
return retStream;
}
strong text
I'm not sure why your unencrypted xml won't parse, but I think you're first step should be to dump the decrypted data to the terminal to see exactly what text you're getting back. Perhaps the process corrupts your data somehow, or you have an encoding issue.
Alternatively, you could configure your server to use https and gzip compression to achieve the same goal. You won't loose any security with this approach and this is by far the more standard way to do things. You can also have a look at MS's support for the WS-Security standard

Resources