本文介绍了NET 4.5中sslStream的密码选择的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create a TLS1.1/TLS1.2 server using .Net's sslStream class. It appears that by default the only cipher suites that this stream accepts are:

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密码套件顺序或使用CNG功能:

I'd like to enable non-ECDHE versions of these (i.e. TLS_RSA_WITH_AES_128_CBC_SHA256). I googled around a bit and people talk about changing cipher suites by modifying the default SChannel settings -- either through the "SSL Cipher Suite Order" or using CNG functions: http://msdn.microsoft.com/en-us/library/windows/desktop/bb870930(v=vs.85).aspx

但是,我尝试了此方法,却无济于事。使用上述链接的C ++代码通过 BCryptEnumContextFunctions()列出已启用的密码套件,表明我想要的密码套件默认情况下处于启用状态。我什至添加了TLS_RSA_WITH_AES_128_CBC_SHA256作为最高优先级套件,并且sslStream仍然拒绝来自仅支持该密码的客户端的TLS连接(例外:客户端和服务器无法通信,因为它们不具有通用算法)任何想法都是继续在这里?

However, I tried this and I can't get anything to work. Using the above link's C++ code to list the enabled cipher suites with BCryptEnumContextFunctions() shows that the cipher suites I want are enabled by default. I even added TLS_RSA_WITH_AES_128_CBC_SHA256 as a top priority suite, and sslStream still refuses a TLS connection from a client that only supports that cipher (Exception: "The client and server cannot communicate, because they do not possess a common algorithm") Any idea what is going on here?

(顺便说一句,如果我的客户端支持一种ECDHE密码套件,那么一切都很好)

(By the way, if my client supports one of the ECDHE cipher suites, everything works great)

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

How are other folks implementing TLS in .Net 4.5? Should I be looking at opensource solutions? What about a wrapper for SChannel to use the CNG api more directly?

推荐答案

我联系了Microsoft的技术支持,并在使用了他们的专有跟踪之后事实证明,我在服务器上安装的证书没有将其私钥标记为交换密钥。显然,证书存储区中每个公钥的私钥对应物都有某些允许使用的用途。以我为例,在SSL / TLS握手期间,私钥只允许用于签名,而不允许用于加密对称密钥。这意味着我的服务器只能支持ECDHE密码套件。

I contacted Microsoft's technical support and after using their proprietary tracing ability, it turned out that the certificate I had installed on my server did not have it's private key marked as an "exchange key". Apparently the private key counterpart of every public key in the certificate store has certain uses for which it it is allowed. In my case, the private key was only allowed to be used for signatures and was not allowed to be used for encrypting a symmetric key during the SSL/TLS handshake. This meant that my server could only support ECDHE cipher suites.

事实证明,您无法在证书 MMC管理单元中检查私钥的启用状态。更糟糕的是,使用sslStream类,除了通用异常客户端和服务器无法通信,因为它们不具有通用算法之外,无法确定握手失败的任何信息。

It also turns out that you can't check the enabled uses of a private key in the Certificate MMC snap-in. Making matters worse, using the sslStream class, there is also no way of determining any information for a handshake failure beyond the generic exception "The client and server cannot communicate, because they do not possess a common algorithm".

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

The final thing to mention is how I managed to install a server certificate with a restricted private key in the first place. It turns out that I generated it that way. I was using the CertEnroll COM interface to programmatically generate a certificate signing request which I exported, had a certificate authority sign, and installed the certificate authority's response. The C# code that I used to generate the certificate signing request accidentally created a private key that was only enabled for signature use.

根据我的经验,CertEnroll界面难以使用,并且很难在线找到可用的示例。因此,为了供他人参考,我将包括我的C#代码,该代码生成用于SSL / TLS握手的base64编码的证书签名请求。在我的情况下,行 objPrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE; 丢失了。

From my experience, the CertEnroll interface is difficult to use and it's hard to find working examples online. So for others' reference I am including my C# code that generates a base64 encoded certificate signing request functional for SSL/TLS handshakes. In my case, the line objPrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE; was missing.

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;
}

这篇关于NET 4.5中sslStream的密码选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 00:53