我正在使用C#/ WCF。我有一个Web服务,应由客户端调用。 这是服务定义:
<service behaviorConfiguration="WCFInterface.CommonBehavior" name="WCFInterface.Content">
<endpoint address="" binding="ws2007HttpBinding" bindingConfiguration="wsHttpUserName"
contract="ABB.fTunes.WCFInterface.IContent">
<identity>
<dns value="fTunesTestServer" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
这就是约束力:
<ws2007HttpBinding>
<binding name="wsHttpUserName">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</ws2007HttpBinding>
如果我理解正确,从服务器发送到客户端的消息将使用证书加密。目前我仍在使用开发人员证书。 我在服务器上创建了根证书,证书撤销列表和密钥。
我正在使用Windows Installer安装客户端,我有一个自定义安装操作来安装证书。
以下代码显示了如何将证书添加到商店
Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClientCertificates.MyRoot.cer");
byte[] buffer = new byte[((int)(manifestResourceStream.Length - 1L)) + 1];
manifestResourceStream.Read(buffer, 0, (int)manifestResourceStream.Length);
manifestResourceStream.Close();
var cert = new X509Certificate2(buffer);
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
/*
// The CRL is also needed, no idea why
manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClientCertificates.MyRoot.crl");
buffer = new byte[((int)(manifestResourceStream.Length - 1L)) + 1];
manifestResourceStream.Read(buffer, 0, (int)manifestResourceStream.Length);
manifestResourceStream.Close();
cert = new X509Certificate2(buffer);
store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
*/
// This is the key
manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClientCertificates.MyTestServer.cer");
buffer = new byte[((int)(manifestResourceStream.Length - 1L)) + 1];
manifestResourceStream.Read(buffer, 0, (int)manifestResourceStream.Length);
manifestResourceStream.Close();
cert = new X509Certificate2(buffer);
store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
我现在有两种行为:
安装证书是有效的,但是当我调用Web服务时,我得到SecurityNegotiationException
。
当我手动添加证书吊销列表时,与服务器的通信工作。
当我尝试以编程方式(请参阅上面的代码)时,它不起作用。我收到“无法找到请求的对象”异常。
我尝试使用不同的商店,但没有成功。
我有两个问题: a)为什么我需要在客户端上使用CRL? b)如果我需要它,我该如何以编程方式安装它?上面的错误在哪里?
感谢您的帮助, 凯
答案 0 :(得分:4)
通常,CRL必须在线可用,并且可以从服务器证书中指定的吊销URL下载。我不知道是否有一个带外机制来获取它,但即使有,它也有点失败的目的(允许客户发现服务器证书已被泄露/撤销)。也就是说,CRL对于自签名证书来说真的有点过分,除非您使用该证书进行真正的相互身份验证,并且您担心密钥被泄露(在这种情况下,购买商业证书并让他们处理它) 。
如果您在没有吊销URL的情况下无法获得证书,我建议您完全禁用CRL的客户端检查,除非您真的需要它。您可以通过将以下内容添加到Web服务客户端的app.config:
来完成此操作 <system.net>
<settings>
<servicePointManager checkCertificateRevocationList="false"/>
</settings>
</system.net>
如果您正在使用WCF,则可能需要使用clientCredentials endpointBehavior,在serviceCertificate-&gt; revocationMode:NoCheck下进行连接。
答案 1 :(得分:2)
可以在此处找到自动安装证书吊销列表的方法,包括通过C#代码:Programatically install Certificate Revocation List (CRL)
答案 2 :(得分:0)
我们必须仅使用 Win32 API 来执行此操作。没有一流的 C# 系统 API 可以做到这一点。
public class CRLHandler
{
private const int CERT_STORE_PROV_SYSTEM = 10;
private const int CERT_SYSTEM_STORE_LOCAL_MACHINE = (2 << 16);
public const int X509_ASN_ENCODING = 0x00000001;
public const int PKCS_7_ASN_ENCODING = 0x00010000;
public const int CERT_STORE_ADD_REPLACE_EXISTING = 3;
[DllImport("CRYPT32.DLL", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CertOpenStore(
int storeProvider,
int encodingType,
IntPtr hcryptProv,
int flags,
string pvPara);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CertCreateCRLContext(
int dwCertEncodingType,
byte[] pbCrlEncoded,
int cbCrlEncoded);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool CertAddCRLContextToStore(
IntPtr hCertStore,
IntPtr pCertContext,
uint dwAddDisposition,
IntPtr ppStoreContext);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool CertFreeCRLContext(
IntPtr pCrlContext);
public void AddOrUpdateCRLToStore(string crlString)
{
IntPtr crlContext = IntPtr.Zero;
try
{
byte[] rawData = Convert.FromBase64String(crlString);
IntPtr hLocalCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
IntPtr.Zero,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
"CA");
crlContext = CertCreateCRLContext(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
rawData,
rawData.Length);
if (crlContext == IntPtr.Zero)
{
string error = "AddOrUpdateCRLToStore - CertCreateCRLContext error #" + Marshal.GetLastWin32Error();
throw new Exception(error);
}
bool crlAddResult = CertAddCRLContextToStore(
hLocalCertStore, crlContext, CERT_STORE_ADD_REPLACE_EXISTING, IntPtr.Zero);
if (!crlAddResult)
{
string error = "AddOrUpdateCRLToStore - CertAddCRLContextToStore #" + Marshal.GetLastWin32Error();
throw new Exception(error);
}
}
finally
{
if(crlContext != IntPtr.Zero)
{
CertFreeCRLContext(crlContext);
}
}
}
}
AddOrUpdateCRLToStore 方法采用 base64 编码的 crl 字符串(不带头和尾),然后将 crl 导入到本地计算机的 CA 存储中。
要修改此 API 中的本地机器、证书存储等值refer this