Several changes in this release… so a new class diagram is advised:

Class diagram of the InfiniTec.Security 1.4 package (click to enlarge)
Breaking change is the renaming of the *SecurityContext* classes to *ImpersonationScope* classes. Additionally, a new base class has been introduced, the ImpersonationScope class, which essentially takes a username/password combination and does the impersonation.
Also new is the privilege handling: Privileges held by a WindowsIdentity can be enumerated, activated and permanently removed from the token.
Finally, the IdentityResolver can be used for two things:
- Translate a SID to an NT account name and vice versa on a remote machine.
- Identify the type of an account (User, group, computer, alias,…)
Change log
- The *SecurityContext* classes have been renamed to *ImpersonationScope*
- The SecurityContext class has been renamed to CallbackImpersonationScope
- New ImpersonationScope class, which accepts a username/password combination
- The AcquireCredentialCallbackEventHandler class has been removed in favor of the EventHandler<AcquireCredentialEventArgs> class
- Privilege Management: The Privileges and Privilege classes allows the manipulation of privileges held by a WindowsIdentity.
- New IdentityResolver. Basically, the same functionality as IdentityReference.Translate(). However, a remote computer can be used for the translation process. Additionally, the type of the account type (user, group, computer, etc) is returned
Downloads
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