如何在 Web 服务中获取客户端发送的 X509Certificate?

发布于 2024-07-20 05:11:00 字数 4329 浏览 11 评论 0原文

显然我在之前的帖子中问了错误的问题。 我有一个使用 X.509 证书保护的 Web 服务,作为安全网站运行 (https://..。 )。 我想使用公司根 CA 颁发的客户端计算机证书(也是 X.509)向服务器验证客户端计算机是否有权使用该服务。 为了做到这一点,我需要检查证书并寻找一些识别特征并将其与数据库中存储的值(可能是指纹?)进行匹配。

这是我用来从本地证书存储区获取证书的代码(直接从 http://msdn.microsoft.com/en-us/magazine/cc163454.aspx):

 public static class SecurityCertificate
{
    private static X509Certificate2 _certificate = null;

    public static X509Certificate2 Certificate
    {
        get { return _certificate; }
    }

    public static bool LoadCertificate()
    {
        // get thumbprint from app.config
        string thumbPrint = Properties.Settings.Default.Thumbprint;
        if ( string.IsNullOrEmpty( thumbPrint ) )
        {
            // if no thumbprint on file, user must select certificate to use
            _certificate = PickCertificate( StoreLocation.LocalMachine, StoreName.My );
            if ( null != _certificate )
            {
                // show certificate details dialog
                X509Certificate2UI.DisplayCertificate( _certificate );
                Properties.Settings.Default.Thumbprint = _certificate.Thumbprint;
                Properties.Settings.Default.Save();
            }
        }
        else
        {
            _certificate = FindCertificate( StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, thumbPrint );
        }

        if ( null == _certificate )
        {
            MessageBox.Show( "You must have a valid machine certificate to use STS." );
            return false;
        }

        return true;
    }

    private static X509Certificate2 PickCertificate( StoreLocation location, StoreName name )
    {
        X509Store store = new X509Store( name, location );
        try
        {
            // create and open store for read-only access
            store.Open( OpenFlags.ReadOnly );

            X509Certificate2Collection coll = store.Certificates.Find( X509FindType.FindByIssuerName, STSClientConstants.NBCCA, true );
            if ( 0 == coll.Count )
            {
                MessageBox.Show( "No valid machine certificate found - please contact tech support." );
                return null;
            }

            // pick a certificate from the store
            coll = null;
            while ( null == coll || 0 == coll.Count )
            {
                coll = X509Certificate2UI.SelectFromCollection(
                        store.Certificates, "Local Machine Certificates",
                        "Select one", X509SelectionFlag.SingleSelection );
            }

            // return first certificate found
            return coll[ 0 ];
        }
        // always close the store
        finally { store.Close(); }
    }

    private static X509Certificate2 FindCertificate( StoreLocation location, StoreName name, X509FindType findType, string findValue )
    {
        X509Store store = new X509Store( name, location );
        try
        {
            // create and open store for read-only access
            store.Open( OpenFlags.ReadOnly );

            // search store
            X509Certificate2Collection col = store.Certificates.Find( findType, findValue, true );

            // return first certificate found
            return col[ 0 ];
        }
        // always close the store
        finally { store.Close(); }
    }

然后,我将证书附加到出站流:

public static class ServiceDataAccess
{    
    private static STSWebService _stsWebService = new STSWebService();

    public static DataSet GetData(Dictionary<string,string> DictParam, string action)
    {
        // add the machine certificate here, the first web service call made by the program (only called once)
        _stsWebService.ClientCertificates.Add( SecurityCertificate.Certificate );
        // rest of web service call here...
    }
}

我的问题是 - 我如何“获取” Web 服务代码中的证书? 我遇到的大多数示例代码片段都涉及如何进行自定义验证,其中都有一个 GetCertificate() 调用,显然假设该部分非常简单,每个人都应该知道如何做到这一点?

我的主类继承自WebService,因此我可以使用Context.Request.ClientCertificate来获取证书,但这是一个HttpClientCertificate,而不是X509Certificate2。 HttpContext 给了我同样的结果。 其他方法都是使用Web配置代码来调用预定义的验证代码,不知道如何调用自定义的C#方法来进行验证。

Apparently I was asking the wrong question in my earlier post. I have a web service secured with a X.509 certificate, running as a secure web site (https://...). I want to use the client's machine certificate (also X.509) issued by the company's root CA to verify to the server that the client machine is authorized to use the service. In order to do this, I need to inspect the certificate and look for some identifying feature and match that to a value stored in a database (maybe the Thumbprint?).

Here is the code I use to get the certificate from the local certificate store (lifted straight from http://msdn.microsoft.com/en-us/magazine/cc163454.aspx):

 public static class SecurityCertificate
{
    private static X509Certificate2 _certificate = null;

    public static X509Certificate2 Certificate
    {
        get { return _certificate; }
    }

    public static bool LoadCertificate()
    {
        // get thumbprint from app.config
        string thumbPrint = Properties.Settings.Default.Thumbprint;
        if ( string.IsNullOrEmpty( thumbPrint ) )
        {
            // if no thumbprint on file, user must select certificate to use
            _certificate = PickCertificate( StoreLocation.LocalMachine, StoreName.My );
            if ( null != _certificate )
            {
                // show certificate details dialog
                X509Certificate2UI.DisplayCertificate( _certificate );
                Properties.Settings.Default.Thumbprint = _certificate.Thumbprint;
                Properties.Settings.Default.Save();
            }
        }
        else
        {
            _certificate = FindCertificate( StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, thumbPrint );
        }

        if ( null == _certificate )
        {
            MessageBox.Show( "You must have a valid machine certificate to use STS." );
            return false;
        }

        return true;
    }

    private static X509Certificate2 PickCertificate( StoreLocation location, StoreName name )
    {
        X509Store store = new X509Store( name, location );
        try
        {
            // create and open store for read-only access
            store.Open( OpenFlags.ReadOnly );

            X509Certificate2Collection coll = store.Certificates.Find( X509FindType.FindByIssuerName, STSClientConstants.NBCCA, true );
            if ( 0 == coll.Count )
            {
                MessageBox.Show( "No valid machine certificate found - please contact tech support." );
                return null;
            }

            // pick a certificate from the store
            coll = null;
            while ( null == coll || 0 == coll.Count )
            {
                coll = X509Certificate2UI.SelectFromCollection(
                        store.Certificates, "Local Machine Certificates",
                        "Select one", X509SelectionFlag.SingleSelection );
            }

            // return first certificate found
            return coll[ 0 ];
        }
        // always close the store
        finally { store.Close(); }
    }

    private static X509Certificate2 FindCertificate( StoreLocation location, StoreName name, X509FindType findType, string findValue )
    {
        X509Store store = new X509Store( name, location );
        try
        {
            // create and open store for read-only access
            store.Open( OpenFlags.ReadOnly );

            // search store
            X509Certificate2Collection col = store.Certificates.Find( findType, findValue, true );

            // return first certificate found
            return col[ 0 ];
        }
        // always close the store
        finally { store.Close(); }
    }

Then, I attach the certificate to the outbound stream thusly:

public static class ServiceDataAccess
{    
    private static STSWebService _stsWebService = new STSWebService();

    public static DataSet GetData(Dictionary<string,string> DictParam, string action)
    {
        // add the machine certificate here, the first web service call made by the program (only called once)
        _stsWebService.ClientCertificates.Add( SecurityCertificate.Certificate );
        // rest of web service call here...
    }
}

My question is this -- how do I "get" the certificate in the web service code? Most sample code snippets I have come across that cover how to do custom validation have a GetCertificate() call in there, apparently assuming that part is so easy everyone should know how to do it?

My main class inherits from WebService, so I can use Context.Request.ClientCertificate to get a certificate, but that's an HttpClientCertificate, not an X509Certificate2. HttpContext gives me the same result. Other approaches all use web configuration code to call pre-defined verification code, with no clue as to how to call a custom C# method to do the verification.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

想你只要分分秒秒 2024-07-27 05:11:00

我记得做过类似的事情,已经有一段时间了,但是,您是否在网络服务中尝试过此操作:

X509Certificate2 cert = new X509Certificate2(Context.Request.ClientCertificate.Certificate);

I recall doing something similar, its been awhile but, have you tried this in your web service:

X509Certificate2 cert = new X509Certificate2(Context.Request.ClientCertificate.Certificate);
乖乖 2024-07-27 05:11:00

关于如何将证书绑定回用户,因此假设与密钥关联的用户身份良好(因为证书已验证回受信任的根并且尚未被撤销),那么您需要绑定证书向用户声明的身份。 您可以仅使用主题 DN 的 LDAP 字符串形式并查找 (cn=用户名,ou=部门...) 来确定本地 ID。 在用户重新生成密钥/证书(例如由于卡丢失或证书自然过期)的情况下,这是有弹性的。 这依赖于这样一个事实:两个 CA 不会向两个不同的人颁发具有相同主题名称的两个证书。

如果证书是由 MS CA 颁发的,则其中可能包含一个实际上是域登录名的 UPN。

或者,如果您想将用户的身份与实际证书联系起来,通常的方法是存储颁发者名称和证书序列号。 CA 必须为每个证书颁发唯一的序列号。 请注意,序列号可能会很大,具体取决于 CA。 然而,使用此方法并不意味着每次重新颁发用户证书时都必须更新数据库中的证书详细信息。

On the subject of how to tie the certificate back to a user, so assuming the identity of the user associated with the key is good (as the certificate has been verified back to a trusted root and has not been revoked) then you need to tie the identity claimed by the cert to a user. You could just use the LDAP string form of the subject DN and look that up (cn=Username,ou=Department...) to determine the local ID. This is resiliant in the case the user re-generates their key/certificate say because of a card loss or natural expiry of the certificate. This relies on the fact that two CAs won't issue two certs with the same subject name to two different people.

If the certificate was issued by a MS CA it might have a UPN in it that is effectively a domain logon name.

Alternatively if you want to tie the user's identity to an actual certificate the usual method is to store the issuer name and certificate serial number. CAs must issue unique serial numbers for each certificate. Note serial numbers can be large depending on the CA. Not however that using this method then means the cert details in the database must be updated every time the user cert is re-issued.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文