.NET 4.5中sslStream的密码选择

我正在尝试使用.Net的sslStream类创建TLS1.1 / TLS1.2服务器。 默认情况下,此流接受的唯一密码套件似乎是:

TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 

我想启用这些的非ECDHE版本(即TLS_RSA_WITH_AES_128_CBC_SHA256)。 我搜索了一下,人们谈论通过修改默认的SChannel设置来更改密码套件 – 通过“SSL密码套件订单”或使用CNGfunction: http : //msdn.microsoft.com/en-us/library/窗户/桌面/ bb870930(v = vs.85)的.aspx

但是,我试过这个,我无法得到任何工作。 使用上面链接的C ++代码列出带有BCryptEnumContextFunctions()的启用密码套件,表明默认情况下启用了我想要的密码套件。 我甚至添加了TLS_RSA_WITH_AES_128_CBC_SHA256作为最高优先级套件,并且sslStream仍然拒绝来自仅支持该密码的客户端的TLS连接(例外:“客户端和服务器无法通信,因为它们没有通用算法”)任何想法是什么在这里?

(顺便说一句,如果我的客户支持其中一个ECDHE密码套件,一切都很好)

其他人如何在.Net 4.5中实现TLS? 我应该关注开源解决方案吗? SChannel的包装器如何更直接地使用CNG api?

我联系了微软的技术支持,在使用他们的专有跟踪function之后,我发现我服务器上安装的证书没有将其私钥标记为“交换密钥”。 显然,证书库中每个公钥的私钥对应物具有允许它的某些用途。 在我的例子中,私钥只允许用于签名,并且不允许在SSL / TLS握手期间用于加密对称密钥。 这意味着我的服务器只能支持ECDHE密码套件。

事实certificate,您无法在证书MMC管理单元中检查已启用的私钥用途。 更糟糕的是,使用sslStream类,除了通用exception“客户端和服务器无法通信,因为它们没有通用算法”之外,也无法确定握手失败的任何信息。

最后要提到的是我是如何设法安装带有受限私钥的服务器证书的。 事实certificate我是这样生成的。 我使用CertEnroll COM接口以编程方式生成我导出的证书签名请求,具有证书颁发机构标志,并安装了证书颁发机构的响应。 我用于生成证书签名请求的C#代码意外创建了一个仅允许签名使用的私钥。

根据我的经验,CertEnroll界面很难使用,很难在网上找到工作示例。 因此,对于其他人的参考,我将包含我的C#代码,该代码生成用于SSL / TLS握手的base64编码证书签名请求。 在我的例子中,行objPrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE; 失踪。

 using CERTENROLLLib; using CERTCLILib; public string GenerateRequest(string Subject, StoreLocation Location) { //code originally came from: http://blogs.msdn.com/b/alejacma/archive/2008/09/05/how-to-create-a-certificate-request-with-certenroll-and-net-c.aspx //modified version of it is here: http://stackoverflow.com/questions/16755634/issue-generating-a-csr-in-windows-vista-cx509certificaterequestpkcs10 //here is the standard for certificates: http://www.ietf.org/rfc/rfc3280.txt //the PKCS#10 certificate request (http://msdn.microsoft.com/en-us/library/windows/desktop/aa377505.aspx) CX509CertificateRequestPkcs10 objPkcs10 = new CX509CertificateRequestPkcs10(); //assymetric private key that can be used for encryption (http://msdn.microsoft.com/en-us/library/windows/desktop/aa378921.aspx) CX509PrivateKey objPrivateKey = new CX509PrivateKey(); //access to the general information about a cryptographic provider (http://msdn.microsoft.com/en-us/library/windows/desktop/aa375967.aspx) CCspInformation objCSP = new CCspInformation(); //collection on cryptographic providers available: http://msdn.microsoft.com/en-us/library/windows/desktop/aa375967(v=vs.85).aspx CCspInformations objCSPs = new CCspInformations(); CX500DistinguishedName objDN = new CX500DistinguishedName(); //top level object that enables installing a certificate response http://msdn.microsoft.com/en-us/library/windows/desktop/aa377809.aspx CX509Enrollment objEnroll = new CX509Enrollment(); CObjectIds objObjectIds = new CObjectIds(); CObjectId objObjectId = new CObjectId(); CObjectId objObjectId2 = new CObjectId(); CX509ExtensionKeyUsage objExtensionKeyUsage = new CX509ExtensionKeyUsage(); CX509ExtensionEnhancedKeyUsage objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage(); string csr_pem = null; // Initialize the csp object using the desired Cryptograhic Service Provider (CSP) objCSPs.AddAvailableCsps(); //Provide key container name, key length and key spec to the private key object objPrivateKey.ProviderName = providerName; objPrivateKey.Length = KeyLength; objPrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE; //Must flag as XCN_AT_KEYEXCHANGE to use this certificate for exchanging symmetric keys (needed for most SSL cipher suites) objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES; if (Location == StoreLocation.LocalMachine) objPrivateKey.MachineContext = true; else objPrivateKey.MachineContext = false; //must set this to true if installing to the local machine certificate store objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG; //must set this if we want to be able to export it later. objPrivateKey.CspInformations = objCSPs; // Create the actual key pair objPrivateKey.Create(); // Initialize the PKCS#10 certificate request object based on the private key. // Using the context, indicate that this is a user certificate request and don't // provide a template name if (Location == StoreLocation.LocalMachine) objPkcs10.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, objPrivateKey, ""); else objPkcs10.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, objPrivateKey, ""); //Set hash to sha256 CObjectId hashobj = new CObjectId(); hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "SHA256"); objPkcs10.HashAlgorithm = hashobj; // Key Usage Extension -- we only need digital signature and key encipherment for TLS: // NOTE: in openSSL, I didn't used to request any specific extensions. Instead, I let the CA add them objExtensionKeyUsage.InitializeEncode( CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE | CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE ); objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage); // Enhanced Key Usage Extension objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.1"); // OID for Server Authentication usage (see this: http://stackoverflow.com/questions/17477279/client-authentication-1-3-6-1-5-5-7-3-2-oid-in-server-certificates) objObjectId2.InitializeFromValue("1.3.6.1.5.5.7.3.2"); // OID for Client Authentication usage (see this: http://stackoverflow.com/questions/17477279/client-authentication-1-3-6-1-5-5-7-3-2-oid-in-server-certificates) objObjectIds.Add(objObjectId); objObjectIds.Add(objObjectId2); objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds); objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage); // Encode the name in using the Distinguished Name object // see here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379394(v=vs.85).aspx objDN.Encode( Subject, X500NameFlags.XCN_CERT_NAME_STR_SEMICOLON_FLAG ); // Assign the subject name by using the Distinguished Name object initialized above objPkcs10.Subject = objDN; //suppress extra attributes: objPkcs10.SuppressDefaults = true; // Create enrollment request objEnroll.InitializeFromRequest(objPkcs10); csr_pem = objEnroll.CreateRequest( EncodingType.XCN_CRYPT_STRING_BASE64 ); csr_pem = "-----BEGIN CERTIFICATE REQUEST-----\r\n" + csr_pem + "-----END CERTIFICATE REQUEST-----"; return csr_pem; }