You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将C#控制台应用部署到启用FIPS的RHEL主机并解决SQL连接问题

问题描述

我有一个名为SqlTest的C#控制台应用,仅用于连接SQL Server并读取一列数据,已简化代码。该应用在本地运行正常,但部署到启用FIPS的RHEL(8.9 Ootpa)主机后,在尝试连接SQL Server时失败。应用能正常启动并输出“Query data example:”,说明已开始运行。

我对将C#应用部署到Linux不太熟悉,希望只是遗漏了几个步骤,或者需要针对启用FIPS的Linux主机添加配置,以让应用能连接SQL Server。

部署步骤

在Visual Studio Community 2022(版本17.7.4)中打开终端:

View -> Terminal

在终端中运行命令:

dotnet publish -c release -r rhel.8-x64 --self-contained

命令执行完成后,会在release文件夹中生成publish文件夹,将该文件夹复制到RHEL主机。

在RHEL主机中,进入publish文件夹,运行SqlTest应用:

./SqlTest

Program.cs代码

using Microsoft.Data.SqlClient;

namespace sqltest
{
    class Program
    {
        public partial class MySqlData
        {
            public decimal my_id { get; set; }
        }
        static void Main(string[] args)
        {
            try
            {
                SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
                builder.UserID = "my_user";
                builder.Password = "Super_Secret_Pass123";
                builder.DataSource = "myhost.com\\myhost_dev";
                builder.InitialCatalog = "DEV";
                builder.TrustServerCertificate = true;
                builder.Encrypt = true;
                builder.MultiSubnetFailover = true;
                builder.IntegratedSecurity = false;
                String sqlQuery = "SELECT my_id FROM my_table";

                using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
                {
                    Console.WriteLine("\nQuery data example:");
                    Console.WriteLine("=========================================\n");

                    connection.Open();

                    using (SqlCommand command = new SqlCommand(sqlQuery, connection))
                    {
                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                MySqlData my_data = new MySqlData();
                                my_data.my_id = reader.GetDecimal(0);
                                Console.WriteLine("{0}", my_data.my_id);
                            }
                        }
                    }
                }
            }
            catch (SqlException e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
} 

运行报错信息

Microsoft.Data.SqlClient.SqlException (0x80131904): A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: TCP Provider, error: 35 - An internal exception was caught) ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL. ---> Interop+Crypto+OpenSslCryptographicException: error:141A3066:SSL routines:tls_process_ske_dhe:bad dh value --- End of inner exception stack trace --- at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan1 input, Byte[]& sendBuf, Int32& sendCount) at System.Net.Security.SslStreamPal.HandshakeInternal(SafeFreeCredentials credential, SafeDeleteSslContext& context, ReadOnlySpan1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) --- End of inner exception stack trace --- at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm) at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions) at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation) at Microsoft.Data.SqlClient.SNI.SNITCPHandle.EnableSsl(UInt32 options)
at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, SqlCommand command, Boolean callerHasConnectionLock, Boolean asyncClose) at Microsoft.Data.SqlClient.TdsParser.EnableSsl(UInt32 info, SqlConnectionEncryptOption encrypt, Boolean integratedSecurity, String serverCertificateFilename) at Microsoft.Data.SqlClient.TdsParser.ConsumePreLoginHandshake(SqlConnectionEncryptOption encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean& marsCapable, Boolean& fedAuthRequired, Boolean tlsFirst, String serverCert) at Microsoft.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, TimeoutTimer timeout, SqlConnectionString connectionOptions, Boolean withFailover) at Microsoft.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, TimeoutTimer timeout, Boolean withFailover) at Microsoft.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout) at Microsoft.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance) at Microsoft.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken, DbConnectionPool pool, Func3 accessTokenCallback) at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) at Microsoft.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions) at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection) at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection) at Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions) at Microsoft.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions) at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides) at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides) at sqltest.Program.Main(String[] args) in C:\Users\my_user\source\repos\SqlTest\SqlTest\Program.cs:line 35
ClientConnectionId:568dade4-7a67-4126-bddf-95897f9f61a1 Error Number:-2146893019,State:0,Class:20

解决方法

从报错信息里的error:141A3066:SSL routines:tls_process_ske_dhe:bad dh value可以看出,问题出在FIPS模式下,SQL Server使用的DH密钥不符合FIPS标准要求。以下是几种可行的解决方式:

1. 强制使用TLS 1.2+并禁用DH密钥交换

在代码中修改连接字符串构建器,指定使用TLS 1.2或更高版本:

builder.SslProtocol = System.Security.Authentication.SslProtocols.Tls12;

或者直接在连接字符串中追加参数:;SslProtocol=Tls12

同时在RHEL主机上配置OpenSSL优先使用符合FIPS的加密套件:

  • 编辑/etc/crypto-policies/back-ends/opensslcnf.config,排除DH相关套件,仅保留FIPS批准的加密算法
  • 执行update-crypto-policies --set FIPS使配置生效

2. 配置SQL Server使用符合FIPS的加密算法

如果有权限修改SQL Server配置,调整其TLS加密套件为FIPS 140-2/140-3批准的类型:

  • 禁用DH密钥交换,改用ECDHE等符合标准的密钥交换算法
  • 通过SQL Server配置管理器(Windows)或配置文件(Linux版SQL Server)完成设置

3. 调整.NET应用的FIPS兼容设置

在应用的runtimeconfig.json中添加FIPS兼容配置:

{
  "runtimeOptions": {
    "configProperties": {
      "System.Net.Security.UseSystemDefaultTlsSettings": true,
      "System.Security.Cryptography.UseFipsAlgorithms": true
    }
  }
}

或者在RHEL主机上设置环境变量后再运行应用:

export DOTNET_SYSTEM_CRYPTOGRAPHY_USEFIPSALGORITHMS=1
./SqlTest

4. 升级Microsoft.Data.SqlClient版本

旧版本的Microsoft.Data.SqlClient在FIPS环境下可能存在兼容性问题,升级到最新稳定版(如5.x及以上):

  • 修改项目的csproj文件中的包版本:
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.5" />
  • 重新执行发布命令并部署到RHEL主机

内容的提问来源于stack exchange,提问作者RyanWorks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 18:20:58