未将安装在服务结构上的证书标记为可导出,这会破坏密钥库身份验证

时间:2019-01-24 23:27:29

标签: .net azure-service-fabric azure-keyvault .net-4.7

我有一个针对.Net 4.7的新结构服务。在4.7中,进行了加密更改,破坏了某些身份验证方案,包括密钥库。更改了身份验证代码以处理4.5和4.7。一切在我的本地群集上都可以正常运行,但是在实际群集上会失败,但以下情况除外:

消息:由于未处理的异常导致主机进程崩溃,RunAsync失败:System.Security.Cryptography.CryptographicException:密钥在指定状态下无效。

在System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)    在System.Security.Cryptography.Utils._ExportKey(SafeKeyHandle hKey,Int32 blobType,对象cspObject)    在System.Security.Cryptography.RSACryptoServiceProvider.ExportParameters(布尔值includePrivateParameters)    在 System.Security.Cryptography.RSA.ToXmlString(布尔值includePrivateParameters)

证书已安装并通过代码找到。证书的私钥也具有适当的权限,因此可以由运行服务的NetworkService读取它们。看来,此故障是由于以下事实造成的:部署到服务结构的证书未标记为可导出。下面是所使用的Sign方法的代码,因此我们的4.5和4.7应用程序混合使用能够在同一库中使用keyvault。我们的其他云服务都很好,只是服务结构失败了。

我认为可能的解决方案是编写4.7特定的Sign方法,但是我还没有找到一个4.7特定的示例,该示例未使用已经使用的相同方法。我还没有找到一种方法来获取部署模板以将证书标记为可导出。

        /// <summary>
        /// Sign a message with the private key of the certificate provided
        /// The signature is expected use the SHA256 algorithm or the assertion will not be valid.
        /// </summary>
        /// <param name="message">The message to sign.</param>
        /// <returns>Array of signed bytes.</returns>
        public byte[] Sign(string message)
        {
            byte[] messageBytes = Encoding.UTF8.GetBytes(message);

            X509AsymmetricSecurityKey x509Key = new X509AsymmetricSecurityKey(this.certificate);

            using (RSA rsa = x509Key.GetAsymmetricAlgorithm(SecurityAlgorithms.RsaSha256Signature, true) as RSA)
            {
                RSACryptoServiceProvider newRsa = null;

                try
                {
                    if (rsa is RSACryptoServiceProvider)
                    {
                        // For .NET 4.6 and below we get the old RSACryptoServiceProvider implementation as the default.
                        // Try and get an instance of RSACryptoServiceProvider which supports SHA256
                        newRsa = GetCryptoProviderForSha256((RSACryptoServiceProvider)rsa);
                    }
                    else
                    {
                        // For .NET Framework 4.7 and onwards the RSACng implementation is the default.
                        // Since we're targeting .NET Framework 4.5, we cannot actually use this type as it was
                        // only introduced with .NET Framework 4.6.
                        // Instead we try and create an RSACryptoServiceProvider based on the private key from the
                        // certificate.
                        newRsa = GetCryptoProviderForSha256(this.certificate);
                    }

                    using (SHA256Cng sha = new SHA256Cng())
                    {
                        return newRsa.SignData(messageBytes, sha);
                    }
                }
                finally
                {
                    // We only want to dispose of the 'newRsa' instance if it is a *different instance*
                    // from the original one that was used to create it.
                    if (newRsa != null && !ReferenceEquals(rsa, newRsa))
                    {
                        newRsa.Dispose();
                    }
                }
            }
        }

        /// <summary>
        /// Create a <see cref="RSACryptoServiceProvider"/> using the private key from the given <see cref="X509Certificate2"/>.
        /// </summary>
        /// <param name="certificate">Certificate including private key with which to initialize the <see cref="RSACryptoServiceProvider"/> with</param>
        /// <returns><see cref="RSACryptoServiceProvider"/> initialized with private key from <paramref name="certificate"/></returns>
        private static RSACryptoServiceProvider GetCryptoProviderForSha256(X509Certificate2 certificate)
        {
            string privateKeyXmlParams = certificate.PrivateKey.ToXmlString(true);
            RSACryptoServiceProvider rsa = null;

            try
            {
                rsa = new RSACryptoServiceProvider();
                rsa.FromXmlString(privateKeyXmlParams);
                return rsa;
            }
            catch (Exception)
            {
                if (rsa != null)
                {
                    rsa.Dispose();
                }

                throw;
            }
        }

更新

结果证明,纯4.7的解决方案很简单

                    else if (rsa is RSACng)
                    {
                        return rsa.SignData(messageBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                    }

但是,对于旧版.Net版本中的项目可以引用的库,效果不是很好,例如我从中借用此代码的库。在这种情况下,较新的RSACng类型将不可用。我想可以用一堆#if NETXXX来处理它,但这需要在每个.Net版本中进行更新。

0 个答案:

没有答案