Использование сертификата клиента не в хранилище сертификатов

Я пытаюсь выполнить аутентификацию себя с помощью WebService с помощью моего клиентского сертификата, но по некоторым причинам (я объясню), я не хочу загружать сертификат из хранилища, а читать его с диска.

Следующее:

// gw is teh WebService client
X509Certificate cert = new X509Certificate(PathToCertificate);
_gw.ClientCertificates.Add(ClientCertificate());
ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true;
_gw.DoSomeCall();

возвращает всегда 403 - Служба не разрешает мне. Но, когда я сохраняю этот сертификат в CertStore, он работает. (Как указано в MSDN.)

Можно ли использовать сертификат не в магазине?

(причина в том, что я получил услугу Windows (клиент), иногда вызывающую webservice (сервер), а после неопределенного времени служба "забывает" мои сертификаты и не разрешает сервер без видимых причин)

Ответ 1

Какой тип файла PathToCertificate? Если это только файл .cer, он не будет содержать закрытый ключ для сертификата, и попытка использования этого сертификата для SSL/TLS завершится с ошибкой.

Однако, если у вас есть файл PKCS7 или PKCS12, который содержит открытый и закрытый ключ для сертификата, ваш код будет работать (возможно, вам придется использовать перегрузку, которая берет пароль, если у него есть закрытый ключ).

Чтобы проверить это, я пошел в http://www.mono-project.com/UsingClientCertificatesWithXSP и создал файл client.p12 в соответствии с этими инструкциями. Я также создал простой сервер HTTPS, используя HttpListener для тестирования.

Затем я скомпилировал следующую программу в "client.exe" и запустил как:

 client.exe https://<MYSSLSERVER>/ client.p12 password

где client.p12 - это файл PKCS12, сгенерированный ранее, и "пароль" - это пароль, который я установил для закрытого ключа сертификата.

using System;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;

public class HttpWebRequestClientCertificateTest : ICertificatePolicy {

    public bool CheckValidationResult (ServicePoint sp, X509Certificate certificate,
            WebRequest request, int error)
    {
            return true; // server certificate CA is not known to windows.
    }

    static void Main (string[] args)
    {
            string host = "https://localhost:1234/";
            if (args.Length > 0)
                    host = args[0];

            X509Certificate2 certificate = null;
            if (args.Length > 1) {
                    string password = null;
                    if (args.Length > 2)
                            password = args [2];
                    certificate = new X509Certificate2 (args[1], password);
            }

            ServicePointManager.CertificatePolicy = new HttpWebRequestClientCertificateTest ();

            HttpWebRequest req = (HttpWebRequest) WebRequest.Create (host);
            if (certificate != null)
                    req.ClientCertificates.Add (certificate);

            WebResponse resp = req.GetResponse ();
            Stream stream = resp.GetResponseStream ();
            StreamReader sr = new StreamReader (stream, Encoding.UTF8);
            Console.WriteLine (sr.ReadToEnd ());
    }
}

Сообщите мне, хотите ли вы загрузить код сервера и сертификаты, используемые с обеих сторон теста.

Ответ 2

У вас есть потенциал, по крайней мере, для двух проблем...

Первый...

Файл сертификата клиента не может содержать закрытый ключ, если только он не имеет доступа к паролю. Вы должны использовать сертификат PKCS # 12 (*.pfx) с паролем, чтобы ваш клиент имел доступ к закрытому ключу. Клиентский код должен будет предоставить пароль при открытии сертификата, как уже опубликовали другие. Существует несколько способов создать это, проще всего использовать следующую командную строку, чтобы сначала сгенерировать сертификат, а затем использовать диспетчер сертификатов MMC для экспорта закрытого ключа сертификатов:

Process p = Process.Start(
    "makecert.exe",
    String.Join(" ", new string[] {
        "-r",//                     Create a self signed certificate
        "-pe",//                    Mark generated private key as exportable
        "-n", "CN=" + myHostName,// Certificate subject X500 name (eg: CN=Fred Dews)
        "-b", "01/01/2000",//       Start of the validity period; default to now.
        "-e", "01/01/2036",//       End of validity period; defaults to 2039
        "-eku",//                   Comma separated enhanced key usage OIDs
        "1.3.6.1.5.5.7.3.1," +//    Server Authentication (1.3.6.1.5.5.7.3.1)
        "1.3.6.1.5.5.7.3.2", //     Client Authentication (1.3.6.1.5.5.7.3.2)
        "-ss", "my",//              Subject certificate store name that stores the output certificate
        "-sr", "LocalMachine",//    Subject certificate store location.
        "-sky", "exchange",//       Subject key type <signature|exchange|<integer>>.
        "-sp",//                    Subject CryptoAPI provider name
        "Microsoft RSA SChannel Cryptographic Provider",
        "-sy", "12",//              Subject CryptoAPI provider type
        myHostName + ".cer"//       [outputCertificateFile]
    })
);

Во-вторых...

Ваша следующая проблема будет серверной. Сервер должен разрешить этот сертификат. У вас есть правильная логика, но на неправильной стороне провода переместите эту строку на веб-сервер, обрабатывающий запрос. Если вы не можете, вы должны взять файл ".cer", сохраненный выше, на сервер и добавить его в список доверия к серверу:

ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true;

Ответ 3

Потенциальной проблемой может быть кеширование сеансов SSL (кэша Schannel). Только первый запрос согласовывает SSL-квитирование. Последующие запросы будут использовать один и тот же идентификатор сеанса и надеются, что сервер примет его. Если сервер очищает SessionId, запросы будут сбой с ошибкой 403. Чтобы отключить локальное кэширование ssl-сеанса (и принудительное согласование SSL для каждого запроса), вы должны открыть папку реестра Windows:

[HKEY_LOCAL_MACHINE] [System] [CurrentControlSet] [управления] [SecurityProviders] [SCHANNEL]

и добавьте ключ с именем ClientCacheTime (DWORD) со значением 0.

Эта проблема рассматривается здесь:

http://support.microsoft.com/?id=247658

Ответ 4

Вам нужен пароль для сертификата? Если это так, в конструкторе есть поле.

X509Certificate cert = new X509Certificate(PathToCertificate,YourPassword);