Description
To grant an account access to all mailboxes on a mailbox store, the easiest way it to grant the "Receive as" and "Send as" permissions on the mailbox store. To do this, navigate to the mailbox store in the Exchange Systems manager:
The mailbox store in the Exchange Systems Manager Next, select "Properties" on the context menu of the mailbox store and select the "Security tab".
If you want to grant the access to all mailboxes to a non-administrative account, you can simply add that account to the list and grant the "Send As" and "Receive As" permission.
To grant those permissions to an administrative account, you must perform a few additional steps, since members of the Domain Administrators have a deny on these permission
The security properties of the mailbox store This denial is placed at the organizational level. This can be examined with ADSIEdit (adsiedit.msc):
ADSI edit displaying the security settings for the Exchange Organization You can see, that the Domain Administrators have both, an allow as well as a deny permission. This is propagated down to the mailbox store, and prevents administrative accounts from accessing other users mailboxes. Now, you can remove these deny permission, but this is not advisable - they are there for a reason. You would end up with far too many people having these rights.
A better approach is to give only one specific account these rights. If this account is also in the Domain Administrators group and already displayed in the security tab, click "Advanced" and then "Add". Select the account the grant the "Receive As" and "Send as" permissions.
The account will be granted the permissions, because an explicit allow on a lower level overrides an inherited deny.
cdb844eb-7c3b-424f-b01b-cb89eac2a87b|3|4.7
Tags:
permissions, send, receive, as, domain, administrators, mailbox, store
Technorati:
permissions, send, receive, as, domain, administrators, mailbox, store
Update
- 2008-04-11
- Added information for EventSinks on Windows 2000
- 2007-09-13
- Revised the last section and clarified necessary permissions to run the event sink
- Added links to related articles
More Information
On MSDN , there is an article describing describes the necessary steps to create an event sink with managed code (Building Managed Event Sink DLLs), and another one (Managing Event Registrations) explains how to register the eventsink on the target server. Unfortunately, that article does not explain how to deploy the event sink into production. The part where the COM+ application is created is simply missing. Additionally, the example misses one attribute that should be added to the assemblyinfo.cs (or assemblyinfo.vb).
The necessary attributes
Apart from the AssemblyKeyName or AssemblyKeyFileAttribute, you should add these attributes to the assemblyinfo.* file:
1 [assembly: ApplicationActivation(ActivationOption.Server)]
2 [assembly: ApplicationName("Title of COM+ Application")]
3 [assembly: ApplicationAccessControl(false ,AccessChecksLevel = AccessChecksLevelOption.Application)]
Be sure to insert the correct name of the COM+ application in line 2.
Installing the COM+ application on a production server
Once you've copied all the assemblies into a folder on the destination server, you can use the Regvcs tool to register the eventsink assembly and create the COM+ application (Note: The above link points to the .NET 2.0 documentation. But the tool is also available for .NET 1.x).
Just call
1 regsvcs -fc managedeventsink.dll
The only thing left to do is to set the process identity for the newly created COM+ application. To do this, open the Component Services MMC (Under administrative tools) and navigate to your COM+ application. Open the properties of the application and select the identity tab. This looks like this:
Here you should select "This user" and enter a user account which has access to the folder to which the event sink will be bound. If you are writing a global event sink which is bound to a mailbox store, and want to access items outside of the scope from the thread on which the OnSyncSave method is called, that account must have permissions to access all mailboxes on that store. See How to grant access to all mailboxes on a mailbox store to a special account for more information on how to do this.
The event sink can now be registered on the Exchange server as described in the second article mentioned at the beginning of this article (See this article if you want to install the sink on Exchange 2007 64bit)
If you want to use an application settings file, you shoud read this article.
Considerations for Windows 2000
If you need to register your event sink on Windows 2000 you will run in a few problems because the COM+ version available. There are two main issues on this platform:
- Loading of dependent assemblies - Windows 2000 will look for dependent assemblies of the eventsink in the working directory - which is %SystemRoot%\system32. To get around this issue, install all assemblies in the Global Assembly Cache.
- Accessing configuration data - On Windows 2003 you can access an application.config by creating a manifest file and setting some COM+ options (See How to use applications settings from an event sink). Unfortunately, the required options are not available on Windows 2000. You'll have to put your configuration settings into the file %SystemRoot%\System32\dllhost.exe.config. The file will most likely not exists, so you'll have to create it.
54455ebc-e580-4e48-9d8f-3bc0d613d9a5|1|4.0
Description [Updated]
Updates
- 2008-07-23: Corrected intra-site links.
One question that came up lately in the newsgroups is: How can I find messages containing certain keywords in the mailboxes of all users in my organization?
This scenario is not directly supported by Exchange. All one can do is to search each mailbox individually.
You must follow these steps to do the search over all mailboxes:
- Enumerate the users which have a mailbox. Essentially, these are the users appearing on the global address list. See How to get the Global Address List programatically for more information on how to do this.
- Build the mailbox url which can be used to access the mailbox via WebDAV or ExOleDB. See Get the WebDAV url for an Exchange 2000/2003 mailbox on how to do this. If you are using the ExOleDB provider or want to use the administrative virtual root instead, see the remarks section for more information.
- Once you have the url for the mailbox you can start accessing it. If you must support different languages, see Getting Well-Known Mailbox Folder URLs on MSDN to get the url of the default folders.
- If you are using WebDAV and have Form-based-authentication enabled on your server, you must do a manual logon to the mailbox. See Access the Exchange store via WebDAV with Form-Based-Authentication turned on.
Remarks
Permissions
Depending on how you want to access the mailboxes, you need different permissions:
- If you are using the normal urls (e.g. http://myserver/exchange/username), you need access permissions to all mailboxes on the MAPI level. See HOWTO: Grant access to all mailboxes on a mailbox store to a special account on how to do this. If you have more than one mailbox store, you should grant the necessary permissions on each mailbox store. To simplify this process, you could also grant the "Send as" and "Receive as" permission on the Administrative Groups container via ADSIEdit.msc instead of each mailbox store.
- You can also use the administrative virtual root. This method is used by the Exchange Systems manager, and it is available via WebDAV and ExOleDB. The normal MAPI permissions are completely ignored when using this method, but an administrative account is required to use this method (See Working with Store Permissions in Microsoft Exchange 2000 and 2003 on Technet for more information on this topic).
Mailbox urls
- If you are using WebDAV to access the store, you can simply build the mailbox url based on the article Get the WebDAV url for an Exchange 2000/2003 mailbox. To use the administrative root instead, modify the url from http://myserver/exchange/mailboxname to http://myserver/exadmin/admin/<dsndomainname>/mbx/<mailboxname>. You must replace the <dnsdomainname> with the primary smtp domain name of your organization.
- If you are using ExOleDB, you must modify the address from http://myserver/exchange/mailboxname to file://./backofficestorage/<dnsdomainname>/mbx/<mailboxname>. To use the administrative virtual root, change this url to file://./backofficestorage/admin/<dnsdomainname>/mbx/<mailboxname>.
a966542f-ad7f-49c4-89a7-b39ed5799c48|2|5.0
Tags:
exchange, adsi, gal, exoledb, webdav, mailbox, permissions
Technorati:
exchange, adsi, gal, exoledb, webdav, mailbox, permissions
Description
Outlook refuses to open certain file types received in an email. There are two level: Level 1 files cannot be accessed by the user, and level 2 files must be saved to disk before they can be opened.
InfoPath has a similar restriction: File with specific extensions cannot be attached to a formular. In fact, the InfoPath model stacks on top of the Outlook restriction list. To unblock an extension for InfoPath formulars, it must be unblocked both, in Outlook and InfoPath.
Solution
To unblock an extension in InfoPath, follow these steps:
- Unblock the extension in Outlook (see Regain access to blocked atachments in Outlook on how to do this).
- Open the registry editor
- If you are using InfoPath 2003, navigate to the key HKEY_CURRENT_USER\Sofware\Microsoft\Office\11.0\InfoPath\Security. If you are using InfoPat 2007, navigate to the key HKEY_CURRENT_USER\Sofware\Microsoft\Office\12.0\InfoPath\Security.
- If it does not exist, create a string value named UnsafeFileTypesRemove.
- Open the values and enter a semicolon delimited list of extensions (e.g. chm;msi;exe)
- Close the registry editor.
The new filetypes can be added to a form the next time InfoPath is restarted.
References
66449871-05dc-43b5-ab34-9bb9b3598996|1|2.0
Technorati:
Description
In his blog Michael Entin talks about using C# iterators to simplify writing of asynchronous code. Be sure to read his article before proceeding!
If you want to do a web request using the WebRequest class, you can either do this synchronously, like this one:
1 publicstring DoRequest(string uri)
2 {
3 WebRequest request = WebRequest.Create(uri);
4
5 WebResponse response = request.GetResponse();
6
7 using (Stream stream = response.GetResponseStream())
8 {
9 byte[] buffer = newbyte[4096];
10 StringBuilder result = newStringBuilder();
11 do
12 {
13 stream.Read(buffer, 0, buffer.Length);
14
15 if (bytesRead > 0)
16 {
17 result.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
18 }
19 } while (bytesRead != 0);
20
21 return result.ToString();
22 }
23 }
Very nice and elegant. But a waste of resources. The operation blocks the caller (and with it probably the UI). The solution to this is to perform the entire request asynchronously: Use BeginGetRequestStream, EndGetRequestStream, and the other BeginXXX and EndXXX methods. You will end up with many callback methods for each of them. At the end you'll have a very complicated set of methods, which will be hard to debug.
Michael (and other) had the ingenious idea to use C# iterators to merge these two concepts, and take the best of both worlds together. The AsyncOperation class in this library implements his approach, at least the basic principles.
Creative misuse…
The basic idea is to implement the operation (e.g. downloading a website) as an iterator method:
1 privateIEnumerable<OperationAction> DownloadUrlInternal(string url)
2 {
3 WebRequest request = WebRequest.Create(Url);
4
5 IAsyncResult ar = request.BeginGetResponse(null, null);
6
7 ProgressUpdate<Status> update = newProgressUpdate<Status>(Status.ReceivingResponse, ar.AsyncWaitHandle);
8 yieldreturn update;
9
10 WebResponse response = request.EndGetResponse(ar);
11 }
This method implements an iterator and returns an operation action of some sort. After a call to BeginGetResponse, the method creates a ProgressChangedEventStateBag which contains the status of the current request, as well as the Waithandle returned by the BeginGetResponse. Using the 'yield return' statement, this information is passed to the caller.
The method is not resumed until the Waithandle is signaled.
The DowndloadUrl method is not called directly by an application. Instead, a wrapper class is used, called AsyncOperation. This class ensures that the DownloadUrl method is called properly and translates those OperationActions into events like ProgressChanged, Completed, Error and Timedout.
The wrapper is written this way:
1 publicAsyncOperation<Status, string> DownloadUrl(string url)
2 {
3 returnnewAsyncOperation<Status, string>(DownloadUrlInternal(url, 3000);
4 }
This call initializes a new AsyncOperation class with a timeout of 3 seconds. The calling application can use this code to run the code:
1 helper = reader.DownloadUrl(tbUrl.Text);
2
3 helper.ProgressChanged += helper_ProgressChanged;
4 helper.Finished += helper_Finished;
5 helper.Error += helper_Error;
6
7 helper.Run();
The entire source code, as well as a sample application can be downloaded from the bottom of this page.
The AsyncOperation are intensively used in my InfiniTec.Exchange project.
More Information
The AsyncOperation classes
The main classes are the AsyncOperation and those derived from AsyncOperation. The most important members are displayed in this diagram:
(click to enlarge)
Most of the logic is implemented in the AsyncOperation class. However, only the AsyncOperation<StatusType> and the AsyncOperation<StatusType, ResultType> classes can be used. The first one is best suited for operation which do not yield a result. For example copying an input stream to an output stream. An example for this is the AsyncOperations.Copy
operation, which does exactly this. During progress updates, it returns the current operation (reading from the input stream or writing to the output stream), as well as the current progress, if the source stream supports seeking.
The second operation type reports status updates and returns a result. A good example for this would be the download of a file using the HttpWebRequest operation.
The operation actions
An asynchronous operation can return several different operation actions. These are displayed below:
(click to enlarge)
- The ProgrssUpdate<StatusType> action is used to report progress changes to the caller. This is also the action that drives the operation. Every BeginXXX method should be followed by a ProgressUpdate action containing the IAsyncResult.AsyncWaitHandle.
- The abstract ProgressUpdate action is only used internally.
- The OperationResult<ResultType> is used to report the result of the operation.
- The NestedOperation can be used to run an asynchronous operation from another asynchronous operation. The current operation is not resumed until the nested operation has completed. The calling operation can specifiy wether progress updates are propagated to the root operation. Addionally, the calling operation can specify, wehter it wants to handle a possible timeout of the nested operation and certain exception. Unhandled exceptions and timeouts are propagated up the call chain until a handler is found or until the root operation is reached.
- The NestedOperation<StatusType> is a combination of the NestedOperation action and the ProgressUpdate<StatusType> action. This type will first report the progress update and then execute the nested operation.
Progress updates of nested operations
The main problem of propagating the progress of nested operations to the root operation is the possible incompatible types of the status types. This is solved by marking properties of the status classes of the parent operation as a nested state. The NestedState attribute is used for this purpose. During a nested progress update, the status class of the parent operation is searched for a matching property which is marked with the NestedState attribute.
ChangeLog
Version 2.0 (05/15/2006)
This version is a major update… somewhere in the middle I lost track of the changes…. Some of the changes are:
- New class AsyncOperations. Contains a basic Stream-to-Stream copy operation
- Changed the name of the AsyncOperationBase class to AsyncOperation
- Added an Update method to the ProgressUpdate class
- The type of the Timeout property has been changed from int to TimeSpan
- The StatusBag classes have benn renamed to OperationActions.
- Nested operation are now better supported
- Progress updates can now be reported from a nested operation to the root operation.
- New Class AsyncOperation<StatusType> for operations which don't return a result
- WaitForComletion operation added to the AsyncOperation class. Blocks the calling thread until the operation is finished.
Version 1.1 (03/31/2006)
- Added a WaitHandle that is signaled, when the status of the operation changes. This allows better nesting of asynchronous operations.
- Added Status, StatusInformation, Result and Exception properties. Now the current statatus can be read from the AsyncOperation class.
- Added Abort method to the AsyncOperation class.
- OnFinished in OnCompleted umbenannt
- Added NDoc documentation
Version 1.0 (03/30/2006)
Downloads
4745ba40-8fd4-4037-82f8-78f92b5f2f49|0|.0
Technorati:
Zusammenfassung
Am 09.01.2006 habe ich einen Vortrag in der .NET Usergroup Paderborn gehalten. Vorgestellt wurden einige ASP.NET Features und einige der neuen Security Features in .NET 2.0
Inhalt
Die folgenden Themen kommen in dem Vortrag vor:
- ASP.NET Features
- URL Rewriting
- Eigene SitemapProvider
- Caching
- ObjectDatasource
- XML Transformationen mit Extension Objekten
- Mehrsprachigkeit
- Security
- SecureString
- Sichere Passworteingabe
- Passwörter speichern (ProtectedData, ProtectedMemory)
Downloads
5460f951-f8be-416f-aed3-019df24bca4b|0|.0
Technorati:
Symptome
Wird ein Sentinel Dongle über ein Netzwerk benutzt (insbesonders Dial-Up oder VPN Verbindungen), so kann es vorkommen das der Dongle von der Client Applikation nicht nich gefunden wird, obwohl der Dongle auf dem Server eingesteckt ist.
Ursache
Der Sentinel Protection Server bindet sich nicht auf die allgemeine IP Adresse (0.0.0.0/0), sondern explizit auf jede verfügbare IP Adresse und wartet auf UDP Port 6001 auf eingehende Verbindungen.
Ändert sich eine IP Adresse oder wird eine neue Verbindung aufgebaut (z.B. eine VPN Verbindung), bekommt der Sentinel Protection Server dies nicht mit.
Lösung
Bei jeder Änderung von IP Adressen muss der Dienst "Sentinel Protection Server" (SentinelProtectionServer) neu gestartet werden:
1 net stop SentinelProtectionServer
2 net start SentinelProtectionServer
Alternativ kann die angehängte Software benutzt werden. Es handelt sich dabei um einen Windows Dienst, der bei Änderungen an der Netzwerkkonfiguration (neue IP-Adresse oder Aufbau einer DFÜ Verbindung) den in der Konfigurations-Datei angegebenen Dienst neustartet. Um den Sentinel Protection Server neustarten zu lassen, tragen Sie den Dienstnamen "SentinelProtectionServer" in die Konfigurationsdatei NetworkAddressMonitorService.exe.config:
1 <?xmlversion="1.0"encoding="utf-8" ?>
2 <configuration>
3 <configSections>
4 <sectionGroupname="applicationSettings"type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
5 <sectionname="InfiniTec.NetworkAddressMonitorService.Properties.Settings"type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"requirePermission="false" />
6 </sectionGroup>
7 </configSections>
8 <applicationSettings>
9 <InfiniTec.NetworkAddressMonitorService.Properties.Settings>
10 <settingname="ServiceToRestart"serializeAs="String">
11 <value>SentinelProtectionServer</value>
12 </setting>
13 </InfiniTec.NetworkAddressMonitorService.Properties.Settings>
14 </applicationSettings>
15 </configuration>
Downloads
f78891cf-f0f8-453b-8796-6f1cda34bc38|0|.0
The url rewriting...
"Any problem in computer science can be solved with another layer of indirection."
David Wheeler (chief programmer for the EDSAC project in the early 1950s)
URL rewriting is a handy technique to produce much nicer URLs. Instead of http://www.infinitec.de/default.aspx?site=1&subpage=3 a user could use an address like http://www.infinitec.de/exchange/howtos/webdavevents.aspx. When the webserver processes the request, it will rewrite the URL to the webpage that processes the requests and displays the correct content.
In .NET 2.0, this is accomplished by implementing a IHttpModule and registering it in the web.config file.
Here is an example in C#:
1 using System.Web;
2
3 public class UrlRewriter : IHttpModule
4 {
5 #region IHttpModule Members
6
7 public void Dispose() {}
8
9 public void Init(HttpApplication context)
10 {
11 context.BeginRequest += context_BeginRequest;
12 }
13
14 void context_BeginRequest(object sender, EventArgs e)
15 {
16 HttpApplication app;
17
18 app = (HttpApplication)sender;
19
20 app.Context.RewritePath("test.aspx");
21 }
22
23 #endregion
24 }
The Init method attaches the context_BeginRequest method with the BeginRequest event of the HttpApplication. This implementation redirects each request to the test.aspx in the same virtual directory of the server. In a real world application, this method must extract the necessary information from the original Url and redirect the execution to the correct page for further processing.
To get the sample working, the new module must be registered in the web.config file:
1 <configuration>
2 <system.web>
3 <httpModules>
4 <add name="UrlRewriter" type="UrlRewriter" />
5 </httpModules>
6 </system.web>
7 </configuration>
You can now open a webbrowser and open any page on the website, regardless if it exists or not. The only restriction is that the extension of the requested page must be registered for the ASP.NET ISAPI filter. Regardless, which page you open, the request is now always processed by the test.aspx file.
When you take a look at the source code of a webpage from your browser, you will notice that the action attribute of the form tag now points back to the test.aspx file, instead of the original URL. A postback would thus reveal the true structure of your website and it might even break the functionality, because the test.aspx file might not know which content to display when called directly.
To solve this problem, the original request path must be restored before the test.aspx file is rendered:
1 using System.Web.UI;
2
3 public partial class test : Page
4 {
5 protected void Page_Load(object sender, EventArgs e)
6 {
7 Context.RewritePath("default.aspx");
8 }
9 }
For the sake of simplicity, the url is always rewritten to the default.aspx file.
...the caching...
Cashing is a great way to scale your application. Especially when your pages require intensive calculations. Caching is activated on a per-page base by using a page-directive at the top of each page:
1 <%@ OutputCache Duration="600" VaryByCustom="sitemap" VaryByParam="none" %>
The duration attribute specifies that this page should be cached for 10 minutes (600 seconds). The VaryByParam parameter is set to "none", so any parameters (either POST or GET parameters) are ignored by the caching framework. Finally, the VaryByCustom parameter specifies, that a special method should be called by the the caching framework: The GetVaryByCustomString method, which must be defined in the global.asax file:
1 <%@ Application Language="C#" %>
2
3 <script runat="server">
4
5 public override string GetVaryByCustomString(HttpContext context, string custom)
6 {
7
8 }
9 </script>
...and the problems
Unfortunately, when the caching is used together with URL rewriting, the caching will be unreliable at best...
This is caused by the fact that the caching framework will now cache the output for the wrong url, since it has been rewritten to the test.aspx file. To correct this, the GetVaryByCustomString can be used:
1 <%@ Application Language="C#" %>
2
3 <script runat="server">
4 public override string GetVaryByCustomString(HttpContext context, string custom)
5 {
6 System.Globalization.CultureInfo ci;
7 string lang;
8
9 if (!custom.Equals("sitemap", StringComparison.CurrentCultureIgnoreCase))
10 return base.GetVaryByCustomString(context, custom);
11
12 ci = context.Items["CurrentUICulture"] as System.Globalization.CultureInfo;
13
14 lang = ci.DisplayName;
15
16 if (context.Session != null && context.Session["ChosenLanguage"] != null) lang = ((System.Globalization.CultureInfo)context.Session["ChosenLanguage"]).DisplayName;
17 return (string)HttpContext.Current.Items["OriginalPath"] + ci.DisplayName;
18 }
19 </script>
The above implementation returns the original request and the name of the selected language. Thus, a request is served from the ASP.NET cache if the requested path AND the language matches.
This implementation solves the problem that the wrong content ist displayed.
958303a7-9ba6-4c1a-84b2-70b8fe5bbe9d|4|3.5
The QueryCredentialDialog
If you connect to a network resource, windows uses a common dialog to query for a username / password, as shown hiere:
(click to enlarge)This dialog can also be used from custom applications, by calling the CredUIPromptForCredentials. The QueryCredentialDialog is essentially a wrapper around this function. It is derived from the CommonDialog class, and as such, can be dropped on any Windows form. The entered password is returned securely in a SecureString object. As such, the password is encrypted most of the time.
Note that the dialog does not check whether the entered credential represents a correct username and password. This makes it possible, to use this dialog even if you are requesting arbitrary credentials.
The CredentialManager
Windows also has a credential manager, which is tightly integrated with the CredUIPromptForCredentials method. If you choose to save the credential you enter into the dialog, this credential is stored in the Windows credential manager. Unfortunately, this is true for an unlimited amount of time, so the entered credential do not expire over time.
The CredentialManager implemented by this class saves the provided credentials in a secure manner (using a SecureString for the passwords). The credentials do expire over time, using two different expiration schemes:
- Absolute expiration: Regardless how often a credential is used in a given timespan, the credential will expire after this timespan.
- Sliding expiration: If a credential is not used for a specified timespan, the credential will expire.
The two expiration schemes can be used in together.
The SecurityContext
If you want to execute a region of code in the context of another user, you can use this class. The base class must be provided with a callback that provides the logon credential used for the impersonation. The main advantage, however, is the possibility to use the using construct, as shown in this example:
1 using (SecurityContext context = new SecurityContext("target", AcquireCallback, true))
2 {
3 DoSomething();
4 }
The AcquireCallback method could be implemented like this example:
1 privatevoid AcquireCredential(object source, AcquireCredentialCallbackArgs e)
2 {
3 CmdLinePromptForCredential prompt;
4
5
6 prompt = new CmdLinePromptForCredential(e.TargetName,
7 string.IsNullOrEmpty(e.Username) ? _Username : e.Username,
8 QueryCredentialOptions.ExcludeCertificates | QueryCredentialOptions.DoNotPersist |
9 QueryCredentialOptions.ValidateUsername);
10
11 e.Cancel = !prompt.Prompt();
12 e.Username = prompt.Username;
13 e.Password = prompt.Password;
14 }
If do not want to impersonate immediately, you can set the last parameter in the SecurityContext constructor to false. You can either use the context.Impersonate() method later, or use the StartProcess method to run a process under the usercontext of the SecurityContext.
Two derived classes are implemented, which simplyfies the usage of the SecurityContext object: The DialogSecurityContext, and the CmdLineSecurityContext. The first one is designed for use in a Windows Forms application, as it utilizes the QueryCredentialDialog to get a valid username/password combination. The latter one uses the CmdLinePromptForCredential class, which is essentially a console version of the QueryCredentialDialog.
The credential acquired by the impersonation context is cached in the Credential manager for future use. Unless the credential expires, the user will not be asked again to enter a username/password.
The SecureStringDecryptor
The SecureString is a very usefull class when handling sensitive data, because its contents are encryped most of the time. Unfortunately, if you want to use such a string to encrypt using the Rijndael algorithm, you are stuck, because it does not accept a SecureString as a key. It requires an array of bytes. So, the right thing to do is to use one of the DeriveBytes classes: PasswordDeriveBytes or the Rfc2898DeriveBytes class. Because the PasswordDeriveBytes.GetBytes method is marked obsolete, you should stick to the latter one.
Next disappointment: Neither one of the DeriveBytes implementations accept a SecureString as a password. Only a string or a byte array. At this point, you can use the SecureStringDecryptor: It decrypts the SecureString into a pinned byte array, that can then be used to initialize one of the DeriveBytes classes. Once you are finished, just call the Dispose method of the SecureStringDecryptor, and the password array is zeroed out. Here is an example in C#:
1 privatevoid EncryptData(byte[] dataToProtect)
2 {
3 SecureString secret = GetSecret();
4 byte[] salt = GetSalt();
5 DeriveBytes db;
6 Rijndael rijndael;
7 ICryptoTransform transform;
8
9 using (SecureStringDecryptor helper = newSecureStringDecryptor(secret)) {
10 db = newRfc2898DeriveBytes(helper.Password, salt, 1024);
11 }
12 rijndael = Rijndael.Create();
13 transform = rijndael.CreateEncryptor(db.GetBytes(16), rijndael.GenerateIV())
14
15 return transform.TransformFinalBlock(dataToProtect, 0, dataToProtect.Length);
16 }
ChangeLog
Version 1.3
Version 1.2
- Added a Confirm and Invalidate method the the SecurityContext. This is useful when generic credentials are queried from the user instead of Windows credentials (for example to authenticate to a remote webserver). In this case you can manually confirm or invalidate the credential.
- Added a Username and Password property to the SecurityContext class. Again this is important when working with generic credentials.
- Added a new class: SecureStringDecryptor. This class allows you to get a byte array containing the decrypted content of a SecureString instance. But be careful when using this class. Do not convert the byte array to a string instance. Do not make a copy the byte array. You should use this class as described in this article only. Otherwise, most of the security advantages of the SecureString class are gone. You have been warned!
Version 1.0
Initial Release
Downloads
6b35d9b4-d995-456e-a554-ab4c62c23069|0|.0
Technorati:
When working with Exchange, it is often necessary to fetch some informations from Active Directory. This helper library has this functionality:
- Find a user in a multi-domain forest based on his domain\username.
- Construct the URL for mailbox of a given user, based on his domain\username. This URL can then be used for WebDAV queries.
- Get a DirectorySearcher for the Global Address list and all the other defined address lists
Full source included, and, believe it or not, documentation :-)
Downloads
85528b42-37b2-45c1-9c6e-6ec463f8dbd6|0|.0
Technorati: