Windows Credential Management with the .net Framework 2.0(翻译)

原文链接

Windows Credential Management with the .net Framework 2.0

Kenny kerr

January 2006

Applies to:

Microsoft .NET Framework 2.0
NET Framework Security
Windows XP

概叙:这篇文章主要是介绍Credential Management API怎样处理界面和用户的凭证集合。同时也提供了一个能够做一些简单凭证管理的类库,可以被c#vb.net调用。

下载相关的例程:KerrCredentialsSample.exe

内容

介绍

请求凭证

托管代码的请求凭证

一个工具:请求凭证生成器

SecureString 基础

凭证集合

在托管代码中使用凭证集合

工具:凭证集合管理器

结论

 

 

介绍

管理用户凭证是一件非常困难的事情。理想上你的windows域凭证应该有足够的权限来访问你可能需要的资源。但是有这么简单吗?你可能不可避免的要去处理不同的安全权限,包括 windows域,Microsoft Passport和应用程序指定的验证策略。好像那些还不够挑战,凭证可能来自不同的form,包括smart cards, certificates 和密码。

 

自从2001windows xp发布,windows包含了credential management api去管理用户凭证。这些api 的设计是为了简化内部程序用户凭证管理,也为你自己的相关凭证管理提供了一致的安全的方法。她也可以被用来请求不需要被持久化的凭证,或者持久化你的应用程序的凭证,比如用数据保护apiData Protection API.

 

在这篇文章我会介绍windows credential management api 包括界面处理,凭证集合管理。介绍这些api的一个挑战是依赖你的背景知识和你怎么去用她,这些是一个c-style api,适用于visual c++或者任何一款支持windows的编译器。因为流行.net 框架的原因,一个理想的解决方案还应该包括.net 框架的支持托管代码的集合。所以也提供了一个功.net 框架调用的类库。

 

最后要说明的是: 当我解释windows credential management用到的不同的函数,结构,也包括列在表格中的相关的选项,标志和属性。虽然这些选项我的解释很多都是依赖与sdkmsndn类库,但是我不止是简单的从文档中copy说明,我是尽力想你说明她的一些你可能不能完全掌握的细节。这些都是基于我写credential management经验的积累。

 

请求凭证

如果你所需要得是你得域凭证,那么你不需要请求凭证。因为在你登陆会话时已经被验证,你可以访问你可能能访问的所有资源。明显的这是一个不现实的例子。所以你需要请求用户的凭证。在过去,不用的应用程序创建不用的用户界面来请求用户的凭证,但是在windows xp上,应用程序可以依靠内建的,简单的,安全的用户界面给用户输入。

Windows提供了CredUIPromptForCredentials函数显示一个可以配置的窗体去接收用户的凭证信息。

DWORD WINAPI CredUIPromptForCredentialsW(PCREDUI_INFOW info,
                                         PCWSTR targetName,
                                         PCtxtHandle reserved,
                                         DWORD errorCode,
                                         PWSTR userName,
                                         ULONG userNameBufferSize,
                                         PWSTR password,
                                           ULONG passwordBufferSize,
                                         PBOOL saveChecked,
DWORD flags);
因为这篇文章不是关于用c或者非托管的c++来管理用户凭证,所以我不对这个函数做过多细节的描叙。

 

第一个参数只向一个CREDUI_INFO结构的指针,它允许你指定它的父窗体和默认的图标,标题,信息。

 

targetName参数用来读取和写入凭证时的唯一定义,也被用来作为标题和信息的一部分,如果CREDUI_INFO没有被重写的话,晚些在我们讨论persisting凭证的时候你会对她认识的更深刻。
Errorcode 参数允许窗体提供相应的错误代码,但是sdk没有提供相应的链接,所以这个参数几乎没什么用。
username  userNameBufferSize参数用来指定初始化时的用户名。相应的passwordpasswordBufferSize用来指定初始化的用户密码。这些使得用c和非托管的c++编程相当困难,你必须非常小心的处理这些字符串,像你在下一章看到的,托管代码可以非常简单的写出非常优秀的代码。

 

这个窗体可以显示一个check box提供给用户来选择是否保存凭证。saveChecked 参数就是用来初始化这个check box 的状态用的。这个check box也可以在用户关闭窗体的时候来判断用户的喜好。注意的是这个参数被忽略,当凭证不是generic凭证的时候。

 

最好,falgs参数允许你指定一个联合的标识。这里的问题是错误的混合不同的标识也会导致程序的错误。

 

相比探索这个函数的更多细节,我将会提供一些为简单任务的解决方案给你,用托管代码写的。在那之前,我们先来看一看一个用C++写的例子。这个例子调用CredUIPromptForCredentials

 

CREDUI_INFO info = { sizeof (CREDUI_INFO) };
info.hwndParent = GetSafeHwnd();
 
info.pszCaptionText = L"Title";
info.pszMessageText = L"Message";
 
CBitmap bitmap;
VERIFY(bitmap.LoadBitmap(IDB_PROMPT_BITMAP));
info.hbmBanner = bitmap;
 
CString target;
const DWORD errorCode = 0;
 
wchar_t userName[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
 
wcscpy_s(userName,
         _countof(userName),
         L"initial user name");
 
wchar_t password[CREDUI_MAX_PASSWORD_LENGTH + 1] = { 0 };
 
BOOL saveChecked = false;
 
Const DWORD flags = CREDUI_FLAGS_DO_NOT_PERSIST | 
                    CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX ;
 
DWORD result = ::CredUIPromptForCredentials(&info,
                                            target,
                                            0, // reserved
                                            errorCode,
                                            userName,
                                            _countof(userName)
                                            password,
                                            _countof(password)
                                            &saveChecked,
                                            flags);
 
switch (result)
{
    case NO_ERROR:
    {
        // User chose OK...
        break;
    }
    case ERROR_CANCELLED:
    {
        // User chose Cancel...
        break;
    }
    default:
    {
        // Handle all other errors...
    }
}
 
::SecureZeroMemory(password,
                   sizeof (password));

这段代码的结果是显示一下的窗体:

Windows Credential Management with the .net Framework 2.0(翻译)_第1张图片

正像你所看到的,有一大段代码是为了调用这个函数不会出错,首先,你要熟悉结构CREDUI_INFO去控制这个窗体的不同元素。接下来,你要为接收的用户名合密码准备缓冲区。和显示初始值。CREDUI_MAX_USERNAME_LENGTH常量表示用户名的最大长度。相同的CREDUI_MAX_PASSWORD_LENGTH表示密码的可能最大长度。在用户名和密码长度后面加1是为了加上介绍字符串NULL, 调用wcscpy_s函数是为了显示用户名和密码的初始值。最后,我指定了两个标识,意思是,我不想persist 这个凭证但是我想让保存密码的check box 显示。saveChecked将会包括用户的选项。

显然在这里你很容易出错。

 

请求凭证,用托管代码

因为CredUIPromptForCredentials

[C++]

ref class MainWindow : Windows::Forms::Form
{
public:

 

    void Prompt()
    {
        PromptForCredentials dialog;

 

        dialog.Title = "Title";
        dialog.Message = "Message";
        dialog.Banner = Images::Banner;
        dialog.UserName = "initial user name";

 

        dialog.DoNotPersist = true;
        dialog.ShowSaveCheckBox = true;

 

        if (Windows::Forms::DialogResult::OK == dialog.ShowDialog(this))
        {
            // Use credentials wisely...
        }
    }
};

[C#]

class MainWindow : Form
{
    public void Prompt()
    {
        using (PromptForCredentials dialog = new PromptForCredentials())
        {
            dialog.Title = "Title";
            dialog.Message = "Message";
            dialog.Banner = Images.Banner;
            dialog.UserName = "initial user name";

 

            dialog.DoNotPersist = true;
            dialog.ShowSaveCheckBox = true;

 

            if (DialogResult.OK == dialog.ShowDialog(this))
            {
                // Use credentials wisely...
            }
        }
    }
}


你可能感兴趣的:([随笔分类]c++)