1

I'm building a WebAPI for an external mobile company ( all I do is exposing services).

Right now , our DB uses non-encrypted values for columns like :

  • ObjectID
  • PolicyID
  • etc..

But now , when we expose it , and I need to encrypt values. ( only server can decrypt values , the mobile company doesn't care about the actual values).

I don't want to start digest every Stored Procedure response manually and replace values with encrypted ones. ( don't forget that our internal server does not uses encrypted values - it uses data regularly).

OK Here's a live example :

I have this controller code :

[HttpGet]
[ActionName("test2")]
public HttpResponseMessage test2(int id)
{
   var Data = GetDataFromSource_1();
            // or from GetDataFromSource_2(); 
   return Request.CreateResponse(HttpStatusCode.OK, Data);
}

Where GetDataFromSource_1 is via dynamic list (just to simulate a source)

public IEnumerable GetDataFromSource_1()
{
    List<dynamic> lst = new List<dynamic>();
    lst.Add(new
    {
        objId = 1,
        myOtherColumn = 5
    });
    lst.Add(new
    {
        objId = 2,
        myOtherColumn = 8
    });
    return lst;
}

And

GetDataFromSource_2 is via DataTable ( just to simulate another source)

public DataTable GetDataFromSource_2()
{
    DataTable dt = new DataTable("myTable");
    dt.Columns.Add("objId", typeof(int));
    dt.Columns.Add("myOtherColumn", typeof(int));
    DataRow row = dt.NewRow();
    row["objId"] = 1;
    row["myOtherColumn"] = 5;
    dt.Rows.Add(row);
    row = dt.NewRow();
    row["objId"] = 2;
    row["myOtherColumn"] = 8;
    dt.Rows.Add(row);
    return dt;
}

Both yields this json response:

{"Result":{"Success":true,"Message":""},"Data":[{"objId":1,"myOtherColumn":5},{"objId":2,"myOtherColumn":8}]}

Question

How (and where) can I scan the content of the response ( which is going to be sent) and replace for every column in ( and only for them) :

  • ObjectID
  • PolicyID
  • etc..

to an encrypted value ?

For example :

I Would like the output to be :

{
    "Result": {
        "Success": true,
        "Message": ""
    },
    "Data": [{
        "objId": "XXX_the_encrypted_valueXXX",
        "myOtherColumn": 5
    }, {
        "objId": "XXX_the_encrypted_valueXXX":  ,
        "myOtherColumn": 8
    }]
}

(where "XXX_the_encrypted_valueXXX" is an encrypted value of the old value.)

NB it's assumable that I have Utils.Encrypt(string st) method.

Also , we dont have entities so i can not decorate an entity . I need to plugin when the json is created

Royi Namir
  • 144,742
  • 138
  • 468
  • 792

3 Answers3

1

I think you should decorate the encrypted properties with an Attribute:

[JsonEncryptValue]
public Guid ObjectID {get;set;}

And then add a JsonConverter that will handle only properties that have a JsonEncryptValue attribute on them. You can re-write their value easily.

And then all you need to do is add your JsonConverter to the JsonSerializer in the WebApiConfig.cs file:

    JsonMediaTypeFormatter jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    JsonSerializerSettings jSettings = new Newtonsoft.Json.JsonSerializerSettings()
    {
        Formatting = Formatting.Indented,
        DateTimeZoneHandling = DateTimeZoneHandling.Utc
    };

    jSettings.Converters.Add(new EncryptionJsonConverter());
    jsonFormatter.SerializerSettings = jSettings;
Amir Popovich
  • 29,350
  • 9
  • 53
  • 99
  • I dont have properties . There is no Entities in our project so i have nothing to decorate – Royi Namir Nov 24 '14 at 10:46
  • Ohh... You can always create ugly workarounds like creating an httpmodule that does the replacement. I guess there is a better practice though. – Amir Popovich Nov 24 '14 at 10:53
1

What you can do is create a custom DelegatingHandler by deriving it and supplying your own implementation and register it your config.MessageHandlers.

So we need a handler and a recursive method which iterates the entire JSON. We'll use the answer provided in Searching for a specific JToken by name in a JObject hierarchy:

private static void FindTokens(JToken containerToken, string name, List<JToken> matches)
{
    if (containerToken.Type == JTokenType.Object)
    {
        foreach (JProperty child in containerToken.Children<JProperty>())
        {
            if (child.Name == name)
            {
                matches.Add(child.Value);
            }
            FindTokens(child.Value, name, matches);
        }
    }
    else if (containerToken.Type == JTokenType.Array)
    {
        foreach (JToken child in containerToken.Children())
        {
            FindTokens(child, name, matches);
        }
    }
}

And the complete handler will look as following:

public class JsonEncrypterHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);
        var returnedJson = await response.Content.ReadAsStringAsync();

        JObject jObj = JObject.Parse(returnedJson);

        List<JToken> objIdTokens = new List<JToken>();
        List<JToken> policyIdTokens = new List<JToken>();

        FindTokens(jObj, "objid", objIdTokens);
        FindTokens(jObj, "policyid", policyIdTokens);

        foreach (JValue objId in objIdTokens)
        {
            objId.Value = Utils.Encrypt(objIdValue);
        }

        foreach (JValue policyId in policyIdTokens)
        {
            policyId.Value = Utils.Encrypt(policyIdTokens);
        }

        response.Content = JsonConvert.SerializeObject(jObj);
        return response;
    }
}
Community
  • 1
  • 1
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • will it do it recursivly ? – Royi Namir Nov 24 '14 at 11:41
  • `Data` can contain anything. as my question states : _How can I scan the content of the response ( which is going to be sent) and replace **for every column in ( and only for them)**_ – Royi Namir Nov 24 '14 at 11:43
  • That's why i asked if the properties are constant. Meaning, you want to change `objId` and `policyId` only. I'll ask again. Do you know which properties you want to encrypt? – Yuval Itzchakov Nov 24 '14 at 11:45
  • And I've answered :-) , **only** those 2 columns should be changed , but they can appear in a different hierarchy. it's just like you open this json in notepad , and press "search and replace" and modify all values for "objId" and "policyId" columns – Royi Namir Nov 24 '14 at 11:46
  • Right, well, we're iterating the entire `Data` property, which is a `JArray`. So it'll iterate the whole array. Is it going to appear in places other than your `Data` array? If the entire hierarchy can have them, i'll need to create a more general algorithm that looks for those properties. – Yuval Itzchakov Nov 24 '14 at 11:47
  • No it's not. but `data ` is not always an array . it can be object. consider this demo json http://i.stack.imgur.com/FJoSJ.jpg , objId and policyId can appear anywhere in this file – Royi Namir Nov 24 '14 at 11:51
  • Can you add a full complex JSON to your question? – Yuval Itzchakov Nov 24 '14 at 11:58
  • I need something real to work with. Make sure it contains the `objId` field and the others you want. – Yuval Itzchakov Nov 24 '14 at 12:00
  • here it is : http://i.stack.imgur.com/Q0bWL.jpg those 1,2,3 values should be changed. here is the json : http://jsbin.com/yuniqi/2/edit?html – Royi Namir Nov 24 '14 at 12:04
  • @RoyiNamir See my modified answer. – Yuval Itzchakov Nov 24 '14 at 12:22
0

I think you have to create customer attribute. Decorate all your action or controller with that attribute.

In that on you have to read result and deserialize back to Json object or .net object.

Following link will help you on that.

http://damienbod.wordpress.com/2014/01/04/web-api-2-using-actionfilterattribute-overrideactionfiltersattribute-and-ioc-injection/

dotnetstep
  • 17,065
  • 5
  • 54
  • 72