Использование сертификата клиента не в хранилище сертификатов
Я пытаюсь выполнить аутентификацию себя с помощью 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);