Как получить X509Certificate, отправленный клиентом в веб-службе?

Видимо, я задал неправильный вопрос в своем предыдущем посте. У меня есть веб-служба, защищенная сертификатом X.509, работающая как безопасный веб-сайт (https://... ). Я хочу использовать сертификат клиентского компьютера (также 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...
    }
}

Мой вопрос заключается в следующем: как мне «получить» сертификат в коде веб-службы? В большинстве фрагментов кода, с которыми я сталкивался, рассказывается, как выполнить пользовательскую проверку, есть вызов GetCertificate(), очевидно, предполагая, что эта часть настолько проста, что все должны знать, как это сделать?

Мой основной класс наследуется от WebService, поэтому я могу использовать Context.Request.ClientCertificate для получения сертификата, но это HttpClientCertificate, а не X509Certificate2. HttpContext дает мне тот же результат. Все другие подходы используют код веб-конфигурации для вызова предопределенного кода проверки, не имея представления о том, как вызвать пользовательский метод C# для выполнения проверки.


person DaveN59    schedule 21.05.2009    source источник
comment
Если у вас нет HttpContext, см. ответ для stackoverflow.com/questions/7528455/   -  person Michael Freidgeim    schedule 21.06.2013


Ответы (2)


Я помню, как делал что-то подобное, это было давно, но пробовали ли вы это в своем веб-сервисе:

X509Certificate2 cert = new X509Certificate2(Context.Request.ClientCertificate.Certificate);
person DSO    schedule 21.05.2009
comment
Это работает! Теперь я, наконец, могу добраться до сути моей проблемы - выяснить, какое значение я могу использовать для сопоставления с сохраненным значением в базе данных... Спасибо!! - person DaveN59; 21.05.2009
comment
Отпечаток можно использовать, если вы рассматриваете свою базу данных как доверенный центр, я работал над приложениями, которые использовали этот подход. OTOH, если вам нужна модель распределенного доверия (с использованием центров сертификации), вам необходимо выполнить дополнительные проверки (например, проверку цепочки сертификатов). - person DSO; 21.05.2009
comment
Я думаю, что, возможно, я действовал слишком рано. Попытка получить доступ к Thumbprint вызывает исключение «System.Security.Cryptography.CryptographicException» — так что, несмотря на то, что HTTP-сертификат теперь имеет тип X509Certificate2, действительно ли это тот же сертификат, отправленный клиентом, или я просто создал новый зверь, который просто немного похож на него? - person DaveN59; 21.05.2009
comment
ОК, копая глубже, я обнаружил, что сообщение об исключении m_safeCertContext является недопустимым дескриптором. Проверяя это сообщение, оно, вероятно, вызвано попыткой запуска на тонком веб-сервере VS (режим отладки), а не в IIS. Вернуться к доске для рисования! Эй, если бы это было легко, они бы не платили нам за это, верно? - person DaveN59; 21.05.2009
comment
Я не думаю, что вы можете отлаживать SSL с помощью веб-сервера Visual Studio (если что-то не изменилось с тех пор, как я последний раз использовал его в VS 2005). Вы действительно должны настроить себя на отладку с помощью IIS. - person DSO; 22.05.2009

Что касается вопроса о том, как связать сертификат с пользователем, поэтому, если предположить, что личность пользователя, связанного с ключом, верна (поскольку сертификат был проверен обратно на доверенный корень и не был отозван), вам нужно связать личность, заявленная сертификатом пользователю. Вы можете просто использовать строковую форму LDAP для DN субъекта и посмотреть ее (cn=Username,ou=Department...), чтобы определить локальный идентификатор. Это устойчиво в случае, если пользователь повторно генерирует свой ключ/сертификат, скажем, из-за потери карты или естественного истечения срока действия сертификата. Это основано на том факте, что два ЦС не будут выдавать два сертификата с одним и тем же именем субъекта двум разным людям.

Если сертификат был выпущен MS CA, в нем может быть имя участника-пользователя, которое фактически является именем входа в домен.

В качестве альтернативы, если вы хотите связать личность пользователя с фактическим сертификатом, обычным методом является сохранение имени издателя и серийного номера сертификата. Центры сертификации должны выдавать уникальные серийные номера для каждого сертификата. Обратите внимание, что серийные номера могут быть большими в зависимости от ЦС. Однако использование этого метода не означает, что сведения о сертификате в базе данных должны обновляться каждый раз при повторном выпуске сертификата пользователя.

person Community    schedule 01.10.2009
comment
Отличная информация! Если бы я все еще работал над проектом, мне бы понадобилась именно такая информация, чтобы исправить проблемы, которые я создал в первой реализации. Однако, поскольку сейчас я перешел в другую компанию с другими обязанностями, я оставлю это как упражнение для кого-то другого... Спасибо за отзыв! - person DaveN59; 09.10.2009