Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.Net MVC & WebAPI encryption

I want to utilise some form of "simple" encryption that is reasonably secure but very low friction in terms of impact on development process.

Supposing I own both sides of the conversation in a client <> web service situation. My application is a windows phone/win8/silverlight/desktop app and the server is ASP.Net MVC or WebAPI.

In my mind, I want something as simple as:-

<security encryption="off|sometype|someothertype">
    <privatekey>12345MyKey54321</privatekey>
</security>

as some form of configuration parameter on both the client and server. Additionally an authentication routine will return and store some form of public key.

Doing so will enable the 'encryption mode' and result in any http requests being encrypted & hashed in the selected manner using the provided keys. The end result being anything sniffed on the local, proxy or remote machines would not be able to view the data without the key and decryption method. On the server, data is decrypted using the same key before hitting controller actions.

Other than swapping out HttpRequest/WebClient calls for something like EncryptedHttpRequest and adding the appropriate hook on the MVC/WebAPI side of things, all other client code and controller actions would be ignorant to the fact the data was encrypted.

Am I missing something or could setup not be this simple? As far as I have searched there is nothing that offers this level of simplicity so I figure I'm missing some gaping flaw in my logic?

like image 584
DannyT Avatar asked Aug 15 '12 19:08

DannyT


2 Answers

All you are looking for can be achieved by simply using HTTPS. Just buy a certificate (or use a self-signed certificate) and there is your encryption.

Do not re-invent the wheel.

like image 148
Aliostad Avatar answered Oct 19 '22 22:10

Aliostad


I've done this successfully. It isn't too difficult and works well. I use it for activating a license for a product. The most important thing is that your truly control the client and server - no one can extract your private key from your code on the client.

Step 1: Create an MVC controller action method that takes no arguments:

[HttpPost]        public ActionResult Activate()        { ... }

Step 2: In the controller just use the HttpRequest.InputStream to get ahold of the bytes sent from the client.

var stream = this.HttpContext.Request.InputStream;

Step 3: Create a CryptoStream to deserialize.

I've included creating both encryption and decryption examples here. The sharedSecret is a byte[] of sufficient length (512 bytes) of random bytes - this is what you protect!

public CryptoStream CreateEncryptionStream(Stream writeStream)
{            
    TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider();               
    PasswordDeriveBytes derivedBytes = new PasswordDeriveBytes(this._sharedSecret, null);                
    CryptoStream cryptoStream = new CryptoStream(writeStream, cryptoProvider.CreateEncryptor(derivedBytes.GetBytes(16), derivedBytes.GetBytes(16)), CryptoStreamMode.Write);            
    return cryptoStream;        
}        

public CryptoStream CreateDecryptionStream(Stream readStream)        
{            
    TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider();            
    PasswordDeriveBytes derivedBytes = new PasswordDeriveBytes(this._sharedSecret, null);                
    CryptoStream cryptoStream = new CryptoStream(readStream, cryptoProvider.CreateDecryptor(derivedBytes.GetBytes(16), derivedBytes.GetBytes(16)), CryptoStreamMode.Read);            
    return cryptoStream;        
}

Step 4: Use your CryptoStream another stream reader to decrypt.

I use an XmlReader so that all my existing serialization code can work either in the clear (when reading/writing to disk or database on the server) or encrypted (when transmitting).

using (var reader = XmlReader.Create(decryptionStream, settings))                { ... }

Step 5: Formulate a secure response in your controller.

This is doing the reverse of Steps 1-4 to encrypt your response object. Then you just write your encrypted response to a memory stream and return it as a File result. Below, I've shown how I do this for my license response object.

var responseBytes = GetLicenseResponseBytes(licenseResponse);
return File(responseBytes, "application/octet-stream");

private byte[] GetLicenseResponseBytes(LicenseResponse licenseResponse)        
{            
    if (licenseResponse != null)            
    {                
        using (MemoryStream memoryStream = new MemoryStream())                
        {                    
            this._licenseResponseSerializer.Write(memoryStream, licenseResponse);

            return memoryStream.ToArray();                
        }            
    }           
    return null;        
}

Step 6: Implement your client request response.

You can use HttpWebRequest or the WebClient classes to formulate the request. Here's a couple of examples from the code I use.

byte[] postBytes = GetLicenseRequestBytes(licenseRequest);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(licenseServerUrl);
request.Method = "POST";
request.ContentType = "application/octet-stream";
request.Proxy = WebRequest.DefaultWebProxy;
using (Stream requestStream = request.GetRequestStream())
{                
    requestStream.Write(postBytes, 0, postBytes.Length);
}            
return request;

private LicenseResponse ProcessHttpResponse(HttpWebResponse response)
{
    if ((response.StatusCode == HttpStatusCode.OK) && response.ContentType.Contains("application/octet-stream"))
    {
        var stream = response.GetResponseStream();
        if (stream != null)
        {
            var licenseResponse = this._licenseResponseSerializer.Read(stream);
            return licenseResponse;
        }
    }
    return new LicenseResponse(LicensingResult.Error);
}

Summary and Tips

  • Use the streams in the request/responses on the client and server to communicate binary octet-stream data
  • Use CryptoStream along with an encryption algorithm (consider using the strongest encryption possilbe) and a good private key to encrypt data when you serialize/deserialize it.
  • Make sure to check the size and format all incoming data to the client and server (avoid buffer overruns and throw exceptions early)
  • Protect your private key on your client using obfuscation if possible (take a look at the DeepSea obfustactor)
like image 23
Geoff Cox Avatar answered Oct 20 '22 00:10

Geoff Cox