我有一个Azure云服务,它使用自签名证书将SslStream保护到我在本地运行的应用程序。由于这些应用程序都不应该暴露在外面,并且出于其他原因,我对从机器商店“正确”加载证书不感兴趣;我只想要一个加密流和一种简单的身份验证方法。
当从嵌入式项目资源加载证书时,以下代码按预期工作(SslStream验证和加密),但是当从数据库中的字节加载证书时,它在调用AuthenticateAsServer()时失败。我已经验证证书是相同的(逐字节),无论它们是从嵌入文件还是从SQL加载,所以我不明白为什么当我使用SQL时我得到一个异常(“服务器模式ssl必须使用带有相关私钥的证书“):
NetStream = new SslStream(Client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateClientCertificate));
NetStream.AuthenticateAsServer(cert, true, System.Security.Authentication.SslProtocols.Default, false);
bool ValidateClientCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
try
{
byte[] clientCertBytes = certificate.GetRawCertData();
byte[] serverCertBytes = _serverCert.GetRawCertData();
if(clientCertBytes.Length != serverCertBytes.Length)
{
throw new Exception("Client/server certificates do not match");
}
for(int i = 0; i < clientCertBytes.Length; i++)
{
if(clientCertBytes[i] != serverCertBytes[i])
{
throw new Exception("Client/server certificates do not match");
}
}
}
catch(Exception ex)
{
SystemLogger.LogException(ex, "SslServerClient");
return false;
}
return true;
}
这是从嵌入式资源加载证书的代码:
Assembly assembly = Assembly.GetExecutingAssembly();
using(Stream stream = assembly.GetManifestResourceStream("Resources.localhost.pfx"))
{
using(MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
_serverCert = new X509Certificate2(memoryStream.ToArray(), "password");
}
}
这是从SQL加载证书的代码:
private static X509Certificate2 readCertificateFromSql(SqlParameterHelper sph)
{
X509Certificate2 result = null;
byte[] certBytes;
string certPassword;
using(IDataReader reader = sph.ExecuteReader())
{
if(reader.Read())
{
try
{
certBytes = (byte[])reader["Data"];
certPassword = (string)reader["Password"];
result = new X509Certificate2(certBytes, certPassword);
}
catch(Exception ex)
{
throw ex;
}
}
}
return result;
}
编辑:我发现两个X509Certificate2对象之间的区别似乎是从SQL加载的对象具有NULL的PrivateKey属性。我不明白为什么,因为从每个证书调用GetRawCertData()会返回完全相同的字节数组。
答案 0 :(得分:1)
我发现了问题。
我在SQL中存储的证书数据是通过在X509Certificate2对象上调用GetRawCertData()生成的。我需要做的是通过调用Export(X509ContentType.Pfx,&#34; password&#34;)来获取数据,这需要通过在构造函数中包含X509KeyStorageFlags.Exportable参数来实例化X509Certificate2。
这只是我,还是这个SSL证书业务看起来过于复杂?