Is it possible to protect Azure connection strings that are referenced with CloudConfigurationManager? - asp.net-mvc-3

I've read the MSDN blog posts on protecting sensitive data in web.config by encrypting the contents and setting up a certificate on Azure so they can be read back.
However, there is top-secret data in my 'service configuration' .cscfg files in the Visual Studio Azure Deployment project. We store connection strings and other sensitive data here so that the test system, also on Azure, can be directed to equivalent test back-end services.
This data is accessed with CloudConfigurationManager (e.g. .GetSetting("AwsSecretKey")) rather than WebConfigurationManager as discussed in the blog post.
Is it possible to protect this data in a similar way? It's important that we have different AWS and SQL connection strings in test and production, and that the production keys are hidden from me and the rest of the dev staff.

YES, we do this with a x509 cert uploaded in the deployment configuration. However, the settings are only as secure as your policy/procedures for protecting the private key! Here is the code we use in an Azure Role to decrypt a value in the ServiceConfiguration:
/// <summary>Wrapper that will wrap all of our config based settings.</summary>
public static class GetSettings
{
private static object _locker = new object();
/// <summary>locked dictionary that caches our settings as we look them up. Read access is ok but write access should be limited to only within a lock</summary>
private static Dictionary<string, string> _settingValues = new Dictionary<string, string>();
/// <summary>look up a given setting, first from the locally cached values, then from the environment settings, then from app settings. This handles caching those values in a static dictionary.</summary>
/// <param name="settingsKey"></param>
/// <returns></returns>
public static string Lookup(string settingsKey, bool decrypt = false)
{
// have we loaded the setting value?
if (!_settingValues.ContainsKey(settingsKey))
{
// lock our locker, no one else can get a lock on this now
lock (_locker)
{
// now that we're alone, check again to see if someone else loaded the setting after we initially checked it
// if no one has loaded it yet, still, we know we're the only one thats goin to load it because we have a lock
// and they will check again before they load the value
if (!_settingValues.ContainsKey(settingsKey))
{
var lookedUpValue = "";
// lookedUpValue = RoleEnvironment.IsAvailable ? RoleEnvironment.GetConfigurationSettingValue(settingsKey) : ConfigurationManager.AppSettings[settingsKey];
// CloudConfigurationManager.GetSetting added in 1.7 - if in Role, get from ServiceConfig else get from web config.
lookedUpValue = CloudConfigurationManager.GetSetting(settingsKey);
if (decrypt)
lookedUpValue = Decrypt(lookedUpValue);
_settingValues[settingsKey] = lookedUpValue;
}
}
}
return _settingValues[settingsKey];
}
private static string Decrypt(string setting)
{
var thumb = Lookup("DTSettings.CertificateThumbprint");
X509Store store = null;
try
{
store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Cast<X509Certificate2>().Single(xc => xc.Thumbprint == thumb);
var rsaProvider = (RSACryptoServiceProvider)cert.PrivateKey;
return Encoding.ASCII.GetString(rsaProvider.Decrypt(Convert.FromBase64String(setting), false));
}
finally
{
if (store != null)
store.Close();
}
}
}
You then can leverage RoleEnvironment.IsAvailable to only decrypt values in the emulator or deployed environment, thereby running the web role in local IIS using an unencrypted App setting with key="MyConnectionString" for local debugging (without the emulator):
ContextConnectionString = GetSettings.Lookup("MyConnectionString", decrypt: RoleEnvironment.IsAvailable);
Then, to complete the example, we created a simple WinForsm App with the following code to encrypt/decrypt the value with the given cert. Our production team maintains access to the production cert and encrypts the necessary values using the WinForms App. They then provide the DEV team with the encrypted value. You can find a full working copy of the solution here. Here's the main code for the WinForms App:
private void btnEncrypt_Click(object sender, EventArgs e)
{
var thumb = tbThumbprint.Text.Trim();
var valueToEncrypt = Encoding.ASCII.GetBytes(tbValue.Text.Trim());
var store = new X509Store(StoreName.My, rbLocalmachine.Checked ? StoreLocation.LocalMachine : StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Cast<X509Certificate2>().Single(xc => xc.Thumbprint == thumb);
var rsaProvider = (RSACryptoServiceProvider)cert.PublicKey.Key;
var cypher = rsaProvider.Encrypt(valueToEncrypt, false);
tbEncryptedValue.Text = Convert.ToBase64String(cypher);
store.Close();
btnCopy.Enabled = true;
}
private void btnDecrypt_Click(object sender, EventArgs e)
{
var thumb = tbThumbprint.Text.Trim();
var valueToDecrypt = tbEncryptedValue.Text.Trim();
var store = new X509Store(StoreName.My, rbLocalmachine.Checked ? StoreLocation.LocalMachine : StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Cast<X509Certificate2>().Single(xc => xc.Thumbprint == thumb);
var rsaProvider = (RSACryptoServiceProvider)cert.PrivateKey;
tbDecryptedValue.Text = Encoding.ASCII.GetString(rsaProvider.Decrypt(Convert.FromBase64String(valueToDecrypt), false));
}
private void btnCopy_Click(object sender, EventArgs e)
{
Clipboard.SetText(tbEncryptedValue.Text);
}

Related

How to configure APP CENTER app key on android and iOS projects in Xamarin forms for different environment?

Currently, we proivde the App Secret key on the AppCenter.Configure method. This is same for all the different environments.
How can we configure the App Secret Key so that I can enter diffrent App Secret keys for different environments?
For e.g. If I need to have different App Secret Key for UAT and Production based on configuration.
You have to create two files, which contains your secrets to appcenter. Don't forget to set build action as Embeded Resource.
appsettings.debug.json
appsettings.release.json
These file will have this content:
{
"AppCenterKey": "super secret key"
}
Then you can load your file regarding to build configuration with this method:
private static void LoadAppSettings()
{
#if RELEASE
var appSettingsResourceStream = Assembly.GetAssembly(typeof(AppSettings)).GetManifestResourceStream("AppSettingsPoC.Configuration.appsettings.release.json");
#else
var appSettingsResourceStream = Assembly.GetAssembly(typeof(AppSettings)).GetManifestResourceStream("AppSettingsPoC.Configuration.appsettings.debug.json");
#endif
if(appSettingsResourceStream == null)
return;
using (var streamReader = new StreamReader(appSettingsResourceStream))
{
var jsonString = streamReader.ReadToEnd();
appSettings = JsonConvert.DeserializeObject<AppSettings>(jsonString);
}
}
Your AppSettings will store the key and it might look like this:
public class AppSettings
{
public string AppCenterKey { get; set; }
}
I did the same in my project following this article.

Outlook-Redemption - RDOFolder.Items ItemAdd Event not triggered regular with Exchange in Online-Mode

System-Environment:
Windows 10 Pro - Version: 1909 - OS System Build: 18363.752
Microsoft Outlook 2019 MSO - Version 1808 - 32-Bit
Microsoft Exchange 2016 15.1 Build (Build 1979.3)
-- Microsoft Exchange is installed on Microsoft Server 2016
Outlook Redemption COM-Library - Version 5.22.0.5498
Issue Summary:
The application sends emails via Outlook using the Outlook-Redemption COM-Library. The class "RedemptionHandler" is our Singleton-Class which interacts with the Outlook-Redemption COM-Library. During the construction of the RedemptionHandler we create a RDOSession with a static class named RedemptionLoader and call Logon() on the RDOSession. The RDOSession is used afterwards in Initialize() to retrieve the Folders for Drafts and mails which are sent.
public static class RedemptionLoader
{
public static RDOSession new_RDOSession()
{
return (RDOSession)NewRedemptionObject(new Guid("29AB7A12-B531-450E-8F7A-EA94C2F3C05F"));
}
}
public class RedemptionHandler
{
private static RedemptionHandler instance = null;
private static readonly object padlock = new object();
private RDOSession _rdoSession;
private RDOFolder _rdoSentFolder;
private RDOFolder _rdoDraftsFolder;
private RDOItems _sentItems = null;
public EventHandler<MailGesendetEventArgs> MailSuccessfullySent;
private RedemptionHandler()
{
_rdoSession = RedemptionLoader.new_RDOSession();
_rdoSession.Logon(null, null, false, null, null, null);
Initialize();
}
public static RedemptionHandler Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new RedemptionHandler();
}
return instance;
}
}
}
private void Initialize()
{
try
{
if (isInitialized) return;
_rdoSentFolder = _rdoSession.GetDefaultFolder(Redemption.rdoDefaultFolders.olFolderSentMail);
_sentItems = _rdoSentFolder.Items;
_sentItems.ItemAdd += MailSent;
_rdoDraftsFolder = _rdoSession.GetDefaultFolder(Redemption.rdoDefaultFolders.olFolderDrafts);
isInitialized = true;
}
catch
{
//TODO
isInitialized = false;
}
}
}
At this point, we have a working instance from our RedemptionHandler. The COM-Object RDOSession is created and referenced within just as the RDOFolder for Drafts and Sent. We have also registrered an event-listener for the Sent-Folder to recognize new Mails in this folder.
In the next steps we want to send an email and recognize this email if its stored in the sent-folder. We use the RDOMail.Fields - Property to store custom data within the RDOMail-Object.
public RDOMail CreateMail(string recipient, string subject, string body, Guid gdSender, string storagePath)
{
RDOMail newMail = _rdoDraftsFolder.Items.Add(Redemption.rdoItemType.olMailItem);
newMail.Recipients.Add(recipient);
newMail.Recipients.ResolveAll();
newMail.Subject = subject;
newMail.HTMLBody = body;
newMail.BodyFormat = (int)rdoBodyFormat.olFormatHTML;
// Here we want to store an identifier in the RDOMail.Fields
int id = newMail.GetIDsFromNames(PropertyGuid, PropertyGdItemId);
newMail.Fields[id] = Guid.NewGuid().ToString();
return newMail;
}
After the mail creation we want to display the mail to the user because we dont want to send data without letting the user know about it.
public void DisplayMail(RDOMail mail, bool modal = false)
{
mail.Display(modal, null);
}
The Outlook window now comes to front and the user checks the mail and clicks on send.
The Mail is now stored in the Sent-Folder.
The MailSent Event gets invoked by the RDOFolder.Items.Add Listener.
private void MailSent(RDOMail mail)
{
var test = mail.Fields[SenderId];
Console.WriteLine(test);
// test value is correct!
}
Difference between Exchange in Online-Mode and Cache-Mode:
If we use the Exchange with Cache-Mode, everything works fine. Everytime we send an email, the MailSent is triggered and we can read data from the RDOMail.Fields-Property. If we switch to Exchange without Cache, the MailSent Event is triggered only once, when the first mail is sent. All emails afterwars are sent but dont trigger the MailSent-Event. If we delete this line of code, everything works also fine without Cache-Mode.
var test = mail.Fields[SenderId];
This is because we think that reading data from the RDOMail.Fields - Property does something special if the cache-mode from exchange is deactivated.
We need to store custom data within the mails to check if new mails in the sent-folder are created by our application or not.
We highly appreciate help and hints.
I tried to fix this issue without success. I've set-up a new Project without any other code:
public partial class RedemptionTest : Form
{
static RDOSession _rdoSession;
static RDOFolder _rdoSentFolder;
static RDOFolder _rdoDraftsFolder;
static RDOItems _draftItems;
static RDOItems _sentItems;
public RedemptionTest()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_rdoSession = RedemptionLoader.new_RDOSession();
_rdoSession.Logon();
_rdoSentFolder = _rdoSession.GetDefaultFolder(rdoDefaultFolders.olFolderSentMail);
_rdoDraftsFolder = _rdoSession.GetDefaultFolder(rdoDefaultFolders.olFolderDrafts);
_sentItems = _rdoSentFolder.Items;
_draftItems = _rdoDraftsFolder.Items;
_draftItems.ItemAdd += DraftAdd;
_sentItems.ItemAdd += MailSent;
}
private void DraftAdd(RDOMail Item)
{
Console.WriteLine(Item.Subject);
}
private void MailSent(RDOMail Item)
{
Console.WriteLine(Item.Subject);
}
}
The Drafts-Folder Event is fired all the time, the MailSent Event is only fired the first time. I have stored all RDO-Objects in static variables to avoid them from being garbage collected.
The object raising the events (RDOItems) must be alive be able to fire the events. Your code is using multiple dot notation, which means the compiler creates an implicit variable to hold the RDOItems collection. As soon as that variable is released by the Garbage Collector, no events will be fired.
The line
_rdoSentFolder.Items.ItemAdd += MailSent;
must be changed to
RDOItems _sentItems; //global/class variable
..
_sentItems = _rdoSentFolder.Items;
_sentItems .ItemAdd += MailSent;
Have the same issue in Outlook VSTO add-in using Redemption. Happens for both Sent and Inbox folder. The same code works correctly in cached mode but fires events only once in Online mode.
Native Outlook object model Items.ItemAdd works correctly in Online mode for the same folder.
Currently, we were able to do a workaround for this by unsubscribing and resubscribing to event right in the event handler. Like this:
private void SentItems_ItemAdd(RDOMail rdoMail)
{
_sentItems.ItemAdd -= SentItems_ItemAdd;
_sentItems.ItemAdd += SentItems_ItemAdd;
Log.Debug("SentItems.ItemAdd");
SentMailItemAdd?.Invoke(rdoMail);
}

Programmatically access TFS annotations to determine owner

I'm working on a project team and our application is in TFS. I'm attempting to determine how many lines of code each team member is responsible. In TFS, I'm aware of the Annotate feature in the Visual Studio interface which allows you to see who last modified each line of code so I know TFS has this information.
I've written a small console app which accesses my TFS project and all its files, but I now need to programmatically access annotations so I can see who the owner of each line is. Here is my existing code:
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
public class Program
{
static void Main(string[] args)
{
var credentials = new NetworkCredential(username, password, domain);
var server = new TfsTeamProjectCollection(new Uri(serverUrl), credentials);
var version = server.GetService(typeof(VersionControlServer)) as VersionControlServer;
var items = version.GetItems(projectPath, RecursionType.Full);
var fileItems = items.Items.Where(x => x.ItemType == ItemType.File);
foreach (var fileItem in fileItems)
{
var serverItem = fileItem.ServerItem;
//TODO: retrieve and parse annotations
}
}
}
I can't seem to figure out how to retrieve annotations once I have the TFS item. This link explains how to do it by calling TFPT, but after implementing it (tfpt annotate /noprompt <filename>), you are only give the last changeset and code per line, not the owner.
I also found a Microsoft.TeamFoundation.VersionControl.Server namespace that has an Annotation class. I installed TFS on my machine to have access to that DLL, but it doesn't seem like it is of any help to this problem.
How can you programmatically access TFS annotations to determine the owner of a line of code for a file?
You may have to query the branch when a Item's change type is Branch.
For a simple example, there is a scenario
$/Project
/Main`
/a.txt
/Develop
/a.txt (branched from main)
When you query the history of $/project/Develop/a.txt, you can also get the history of $/project/Main/a.txt using following code
void GetAllHistory(string serverItem)
{
var changesets=vcs.QueryHistory(serverItem,
Microsoft.TeamFoundation.VersionControl.Client.VersionSpec.Latest,
0,
Microsoft.TeamFoundation.VersionControl.Client.RecursionType.None,
null,
new Microsoft.TeamFoundation.VersionControl.Client.ChangesetVersionSpec(1),
Microsoft.TeamFoundation.VersionControl.Client.VersionSpec.Latest,
int.MaxValue,
true,
false);
foreach (var obj in changesets)
{
Microsoft.TeamFoundation.VersionControl.Client.Changeset cs = obj as Microsoft.TeamFoundation.VersionControl.Client.Changeset;
if (cs == null)
{
return;
}
foreach (var change in cs.Changes)
{
if (change.Item.ServerItem != serverItem)
{
return;
}
Console.WriteLine(string.Format("ChangeSetID:{0}\tFile:{1}\tChangeType:{2}", cs.ChangesetId,change.Item.ServerItem, change.ChangeType));
if ((change.ChangeType & Microsoft.TeamFoundation.VersionControl.Client.ChangeType.Branch) == Microsoft.TeamFoundation.VersionControl.Client.ChangeType.Branch)
{
var items=vcs.GetBranchHistory(new Microsoft.TeamFoundation.VersionControl.Client.ItemSpec[]{new Microsoft.TeamFoundation.VersionControl.Client.ItemSpec(serverItem, Microsoft.TeamFoundation.VersionControl.Client.RecursionType.None)},
Microsoft.TeamFoundation.VersionControl.Client.VersionSpec.Latest);
GetAllHistory(items[0][0].Relative.BranchToItem.ServerItem);
}
}
}
}

Where to use SAS Token in Xamarin.Android

I'm making an Android app that will connect to an Azure Storage Account to save information in a table. When I run the app in the simulator, and press the button that opens the page that connects to the database I get an exception stating "Shared Key is not supported using the PCL. Please use a SAS token."
So I followed the steps to generate a SAS token but I'm not sure what to do with the string. Can anyone suggest where I should place the string?
namespace UndergroundSports
{
[Activity]
public class austinBowlingSignUpPage : Activity
{
protected override async void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.austinBowlingSignUpPage);
EditText austinBowlingFullNameEntry = FindViewById<EditText> (Resource.Id.austinBowlingFullNameEntry);
EditText austinBowlingEmailEntry = FindViewById<EditText> (Resource.Id.austinBowlingEmailEntry);
Button austinBowlingSubmitButton = FindViewById<Button> (Resource.Id.austinBowlingSignUpButton);
string sas = "https://undergroundathletes.blob.core.windows.net/underground-container?sv=2015-04-05&sr=c&sig=Gcgc28K%2B\nc6uQk9pkHRAotshR7zEU%3D&se=2016-04-20T18%3A13%3A31Z&sp=rwdl";
string connectionString =
"DefaultEndpointsProtocol=http;" +
"AccountName=My_Account_Name;" +
"AccountKey=My_Account_Key";
CloudStorageAccount storageaccount = CloudStorageAccount.Parse (connectionString);
CloudTableClient tableClient = storageaccount.CreateCloudTableClient ();
CloudTable austinBowlingAthletes = tableClient.GetTableReference ("austinBowlingAthletesTable");
await austinBowlingAthletes.CreateIfNotExistsAsync();
austinBowlingSubmitButton.Click += async (sender, e) => {
austinBowlingAthlete austinBowlingAthlete1 = new austinBowlingAthlete();
austinBowlingAthlete1.fullname = austinBowlingFullNameEntry.ToString();
austinBowlingAthlete1.email = austinBowlingEmailEntry.ToString();
TableOperation insertOperation = TableOperation.Insert(austinBowlingAthlete1);
await austinBowlingAthletes.ExecuteAsync(insertOperation);
};
}
}
}
You need to create StorageCredentials based on the SAS token, use the credentials to create CloudStorageAccount and call CreateCloudTableClient on the storage account to get CloudTableClient:
StorageCredentials creds = new StorageCredentials(sas);
CloudStorageAccount storageAccount = new CloudStorageAccount(creds, null, null, tableStorageUri, null);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable austinBowlingAthletes = tableClient.GetTableReference("austinBowlingAthletesTable");
Everything else stays the same.
Check this example: Teal (Azure Storage sample for Xamarin)

Getting Web Site Name from Web Project Setup

I'm creating a setup project for WCF net-tcp service. One thing I came across is that I need to change "Web Site->Manage Application->Advanced settings->Enabled Protocols". It can be also done using command line:
%windir%\system32\inetsrv\appcmd.exe set app "[Web Site Name]/[Applicaiton Name]" /enabledProtocols:http,net.tcp
The problem is in custom action I can get [TARGETSITE] but it's value is "/LM/W3SVC/2" (I have [TARGETVDIR] too). The question is how can I get Web Site Name or how can I use [TARGETSITE] to set application enabled protocols?
The solution I ended with involves converting metabasePath to site name and then using appcmd:
private static string GetSiteName(string metabasePath)
{
var siteIdString = metabasePath.Substring(metabasePath.LastIndexOf("/") + 1);
long siteId;
long.TryParse(siteIdString, out siteId);
if (siteId != 0)
{
var iisManager = new ServerManager();
var config = iisManager.GetApplicationHostConfiguration();
var sites = config.GetSection("system.applicationHost/sites").GetCollection();
ConfigurationElement selectedSite = null;
foreach (var site in sites)
{
if ((long)site.GetAttribute("id").Value == siteId)
selectedSite = site;
}
if (selectedSite != null)
{
return selectedSite.GetAttribute("name").Value as string;
}
}
return null;
}
To use this you will have to reference:
C:\Windows\System32\inetsrv\Microsoft.Web.Administration.dll
C:\Windows\System32\inetsrv\Microsoft.Web.Management.dll

Resources