【问题标题】:Cipher selection for sslStream in .NET 4.5.NET 4.5 中 sslStream 的密码选择
【发布时间】:2014-04-03 00:34:22
【问题描述】:

我正在尝试使用 .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 密码套件顺序”或使用 CNG 功能: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 连接(例外:“客户端和服务器无法通信,因为它们不具备通用算法”)知道什么是在这里进行吗?

(顺便说一句,如果我的客户支持其中一种 ECDHE 密码套件,那么一切正常)

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

【问题讨论】:

    标签: c# .net ssl sslstream


    【解决方案1】:

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

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

    最后要提到的是,我首先是如何设法安装带有受限私钥的服务器证书。事实证明,我是这样生成的。我正在使用 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;
    }
    

    【讨论】:

    • 你说它没有在MMC中列出,你不能在key usage下检查“数据加密”吗?
    • 你可以检查一下,但是如果私钥最初不是用objPrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;创建的,那么这个值对SChannel实际上并不重要我有一个密钥用法设置为“密钥加密”的签名证书握手仍然无效。
    猜你喜欢
    • 2014-08-03
    • 1970-01-01
    • 2013-04-02
    • 2020-04-15
    • 2013-04-21
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多