diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index d89ce63d496fb3..ca8d53c618407f 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -15,6 +16,7 @@ using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using System.Text; using Microsoft.Win32.SafeHandles; internal static partial class Interop @@ -28,6 +30,7 @@ internal static partial class OpenSsl private const int DefaultTlsCacheSizeClient = 500; // since we keep only one TLS Session per hostname, 500 should be enough to cover most scenarios private const int DefaultTlsCacheSizeServer = -1; // use implementation default private const SslProtocols FakeAlpnSslProtocol = (SslProtocols)1; // used to distinguish server sessions with ALPN + private static readonly Lazy s_defaultSigAlgs = new(GetDefaultSignatureAlgorithms); private sealed class SafeSslContextCache : SafeHandleCache { } @@ -414,6 +417,11 @@ internal static SafeSslHandle AllocateSslHandle(SslAuthenticationOptions sslAuth sslHandle.SslContextHandle = sslCtxHandle; } + if (!sslAuthenticationOptions.AllowRsaPssPadding || !sslAuthenticationOptions.AllowRsaPkcs1Padding) + { + ConfigureSignatureAlgorithms(sslHandle, sslAuthenticationOptions.AllowRsaPssPadding, sslAuthenticationOptions.AllowRsaPkcs1Padding); + } + if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0) { if (sslAuthenticationOptions.IsServer) @@ -516,6 +524,141 @@ internal static SafeSslHandle AllocateSslHandle(SslAuthenticationOptions sslAuth return sslHandle; } + internal static string[] GetDefaultSignatureAlgorithms() + { + ushort[] rawAlgs = Interop.Ssl.GetDefaultSignatureAlgorithms(); + + // The mapping below is taken from STRINT_PAIR signature_tls13_scheme_list and other + // data structures in OpenSSL source code (apps/lib/s_cb.c file). + static string ConvertAlg(ushort rawAlg) => rawAlg switch + { + 0x0201 => "rsa_pkcs1_sha1", + 0x0203 => "ecdsa_sha1", + 0x0401 => "rsa_pkcs1_sha256", + 0x0403 => "ecdsa_secp256r1_sha256", + 0x0501 => "rsa_pkcs1_sha384", + 0x0503 => "ecdsa_secp384r1_sha384", + 0x0601 => "rsa_pkcs1_sha512", + 0x0603 => "ecdsa_secp521r1_sha512", + 0x0804 => "rsa_pss_rsae_sha256", + 0x0805 => "rsa_pss_rsae_sha384", + 0x0806 => "rsa_pss_rsae_sha512", + 0x0807 => "ed25519", + 0x0808 => "ed448", + 0x0809 => "rsa_pss_pss_sha256", + 0x080a => "rsa_pss_pss_sha384", + 0x080b => "rsa_pss_pss_sha512", + 0x081a => "ecdsa_brainpoolP256r1_sha256", + 0x081b => "ecdsa_brainpoolP384r1_sha384", + 0x081c => "ecdsa_brainpoolP512r1_sha512", + 0x0904 => "mldsa44", + 0x0905 => "mldsa65", + 0x0906 => "mldsa87", + _ => + Tls12HashName((byte)(rawAlg >> 8)) is string hashName && + Tls12SignatureName((byte)rawAlg) is string sigName + ? $"{sigName}+{hashName}" + : $"0x{rawAlg:x4}" // this will cause the setter to fail, but at least we get a string representation in the log. + }; + + static string? Tls12HashName(byte raw) => raw switch + { + 0x00 => "none", + 0x01 => "MD5", + 0x02 => "SHA1", + 0x03 => "SHA224", + 0x04 => "SHA256", + 0x05 => "SHA384", + 0x06 => "SHA512", + _ => null + }; + + static string? Tls12SignatureName(byte raw) => raw switch + { + 0x00 => "anonymous", + 0x01 => "RSA", + 0x02 => "DSA", + 0x03 => "ECDSA", + _ => null + }; + + string[] result = Array.ConvertAll(rawAlgs, ConvertAlg); + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(null, $"Default signature algorithms: {string.Join(":", result)}"); + } + + return result; + } + + internal static unsafe void ConfigureSignatureAlgorithms(SafeSslHandle sslHandle, bool enablePss, bool enablePkcs1) + { + byte[] buffer = ArrayPool.Shared.Rent(512); + try + { + int index = 0; + + foreach (string alg in s_defaultSigAlgs.Value) + { + // includes both rsa_pss_pss_* and rsa_pss_rsae_* + if (alg.StartsWith("rsa_pss_", StringComparison.Ordinal) && !enablePss) + { + continue; + } + + if (alg.StartsWith("rsa_pkcs1_", StringComparison.Ordinal) && !enablePkcs1) + { + continue; + } + + // Ensure we have enough space for the algorithm name, separator and null terminator. + EnsureSize(ref buffer, index + alg.Length + 2); + + if (index > 0) + { + buffer[index++] = (byte)':'; + } + + index += Encoding.UTF8.GetBytes(alg, buffer.AsSpan(index)); + } + buffer[index] = 0; // null terminator + + int ret; + fixed (byte* pBuffer = buffer) + { + ret = Interop.Ssl.SslSetSigalgs(sslHandle, pBuffer); + if (ret != 1) + { + throw CreateSslException(SR.Format(SR.net_ssl_set_sigalgs_failed, "server")); + } + + ret = Interop.Ssl.SslSetClientSigalgs(sslHandle, pBuffer); + if (ret != 1) + { + throw CreateSslException(SR.Format(SR.net_ssl_set_sigalgs_failed, "client")); + } + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + static void EnsureSize(ref byte[] buffer, int size) + { + if (buffer.Length < size) + { + // there are a few dozen algorithms total in existence, so we don't expect the buffer to grow too large. + Debug.Assert(size < 10 * 1024, "The buffer should not grow too large."); + + byte[] oldBuffer = buffer; + buffer = ArrayPool.Shared.Rent(buffer.Length * 2); + oldBuffer.AsSpan().CopyTo(buffer); + ArrayPool.Shared.Return(oldBuffer); + } + } + } + internal static SecurityStatusPal SslRenegotiate(SafeSslHandle sslContext, out byte[]? outputBuffer) { int ret = Interop.Ssl.SslRenegotiate(sslContext, out Ssl.SslErrorCode errorCode); diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs index 65c0d37e3d5a30..cdac694697f017 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs @@ -83,6 +83,24 @@ internal static unsafe ReadOnlySpan SslGetAlpnSelected(SafeSslHandle ssl) [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslRead", SetLastError = true)] internal static partial int SslRead(SafeSslHandle ssl, ref byte buf, int num, out SslErrorCode error); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetDefaultSignatureAlgorithms")] + private static unsafe partial int GetDefaultSignatureAlgorithms(Span algorithms, ref int algorithmCount); + + internal static ushort[] GetDefaultSignatureAlgorithms() + { + // 256 algorithms should be more than enough for any use case. + Span algorithms = stackalloc ushort[256]; + int algorithmCount = algorithms.Length; + int res = GetDefaultSignatureAlgorithms(algorithms, ref algorithmCount); + + if (res != 0 || algorithmCount > algorithms.Length) + { + throw Interop.OpenSsl.CreateSslException(SR.net_ssl_get_default_sigalgs_failed); + } + + return algorithms.Slice(0, algorithmCount).ToArray(); + } + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslRenegotiate")] internal static partial int SslRenegotiate(SafeSslHandle ssl, out SslErrorCode error); @@ -186,6 +204,12 @@ internal static SafeSharedX509StackHandle SslGetPeerCertChain(SafeSslHandle ssl) [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslSetPostHandshakeAuth")] internal static partial void SslSetPostHandshakeAuth(SafeSslHandle ssl, int value); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslSetSigalgs")] + internal static unsafe partial int SslSetSigalgs(SafeSslHandle ssl, byte* str); + + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslSetClientSigalgs")] + internal static unsafe partial int SslSetClientSigalgs(SafeSslHandle ssl, byte* str); + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_Tls13Supported")] private static partial int Tls13SupportedImpl(); diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs index cbff2b2798efed..ffb51e5dd0d969 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs @@ -248,7 +248,7 @@ public enum Flags SCH_CRED_IGNORE_REVOCATION_OFFLINE = 0x1000, SCH_CRED_CACHE_ONLY_URL_RETRIEVAL_ON_CREATE = 0x2000, SCH_SEND_ROOT_CERT = 0x40000, - SCH_SEND_AUX_RECORD = 0x00200000, + SCH_SEND_AUX_RECORD = 0x00200000, SCH_USE_STRONG_CRYPTO = 0x00400000, SCH_USE_PRESHAREDKEY_ONLY = 0x800000, SCH_ALLOW_NULL_ENCRYPTION = 0x02000000, @@ -259,7 +259,7 @@ public enum Flags internal unsafe struct TLS_PARAMETERS { public int cAlpnIds; // Valid for server applications only. Must be zero otherwise. Number of ALPN IDs in rgstrAlpnIds; set to 0 if applies to all. - public IntPtr rgstrAlpnIds; // Valid for server applications only. Must be NULL otherwise. Array of ALPN IDs that the following settings apply to; set to NULL if applies to all. + public UNICODE_STRING* rgstrAlpnIds; // Valid for server applications only. Must be NULL otherwise. Array of ALPN IDs that the following settings apply to; set to NULL if applies to all. public uint grbitDisabledProtocols; // List protocols you DO NOT want negotiated. public int cDisabledCrypto; // Number of CRYPTO_SETTINGS structures; set to 0 if there are none. public CRYPTO_SETTINGS* pDisabledCrypto; // Array of CRYPTO_SETTINGS structures; set to NULL if there are none; @@ -278,7 +278,7 @@ public enum Flags internal unsafe struct CRYPTO_SETTINGS { public TlsAlgorithmUsage eAlgorithmUsage; // How this algorithm is being used. - public UNICODE_STRING* strCngAlgId; // CNG algorithm identifier. + public UNICODE_STRING strCngAlgId; // CNG algorithm identifier. public int cChainingModes; // Set to 0 if CNG algorithm does not have a chaining mode. public UNICODE_STRING* rgstrChainingModes; // Set to NULL if CNG algorithm does not have a chaining mode. public int dwMinBitLength; // Minimum bit length for the specified CNG algorithm. Set to 0 if not defined or CNG algorithm implies bit length. @@ -374,7 +374,7 @@ internal static unsafe partial int VerifySignature( ref CredHandle contextHandle, in SecBufferDesc input, uint sequenceNumber, - uint *qualityOfProtection); + uint* qualityOfProtection); [LibraryImport(Interop.Libraries.SspiCli, SetLastError = true)] internal static partial int QuerySecurityContextToken( diff --git a/src/libraries/Common/src/System/Net/Security/SslClientAuthenticationOptionsExtensions.cs b/src/libraries/Common/src/System/Net/Security/SslClientAuthenticationOptionsExtensions.cs index d1ad29435f9be0..a8a599d6d3d84d 100644 --- a/src/libraries/Common/src/System/Net/Security/SslClientAuthenticationOptionsExtensions.cs +++ b/src/libraries/Common/src/System/Net/Security/SslClientAuthenticationOptionsExtensions.cs @@ -29,14 +29,18 @@ public static SslClientAuthenticationOptions ShallowClone(this SslClientAuthenti EncryptionPolicy = options.EncryptionPolicy, LocalCertificateSelectionCallback = options.LocalCertificateSelectionCallback, RemoteCertificateValidationCallback = options.RemoteCertificateValidationCallback, - TargetHost = options.TargetHost + TargetHost = options.TargetHost, +#pragma warning disable CA1416 // Ignore SupportedOSPlatform checks, the value will be validated at runtime inside SslStream + AllowRsaPssPadding = options.AllowRsaPssPadding, + AllowRsaPkcs1Padding = options.AllowRsaPkcs1Padding +#pragma warning restore CA1416 }; #if DEBUG // Try to detect if a property gets added that we're not copying correctly. // The property count is guard for new properties that also needs to be added above. PropertyInfo[] properties = typeof(SslClientAuthenticationOptions).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)!; - Debug.Assert(properties.Length == 13); + Debug.Assert(properties.Length == 15); foreach (PropertyInfo pi in properties) { object? origValue = pi.GetValue(options); diff --git a/src/libraries/System.Net.Security/ref/System.Net.Security.cs b/src/libraries/System.Net.Security/ref/System.Net.Security.cs index ca8aa52a2dbf62..08aaf8df3ed2cc 100644 --- a/src/libraries/System.Net.Security/ref/System.Net.Security.cs +++ b/src/libraries/System.Net.Security/ref/System.Net.Security.cs @@ -31,9 +31,9 @@ public CipherSuitesPolicy(System.Collections.Generic.IEnumerable message, System.Buffers.IBufferWriter signatureWriter) { } public void Dispose() { } public byte[]? GetOutgoingBlob(System.ReadOnlySpan incomingBlob, out System.Net.Security.NegotiateAuthenticationStatusCode statusCode) { throw null; } public string? GetOutgoingBlob(string? incomingBlob, out System.Net.Security.NegotiateAuthenticationStatusCode statusCode) { throw null; } public System.Net.Security.NegotiateAuthenticationStatusCode Unwrap(System.ReadOnlySpan input, System.Buffers.IBufferWriter outputWriter, out bool wasEncrypted) { throw null; } public System.Net.Security.NegotiateAuthenticationStatusCode UnwrapInPlace(System.Span input, out int unwrappedOffset, out int unwrappedLength, out bool wasEncrypted) { throw null; } - public System.Net.Security.NegotiateAuthenticationStatusCode Wrap(System.ReadOnlySpan input, System.Buffers.IBufferWriter outputWriter, bool requestEncryption, out bool isEncrypted) { throw null; } - public void ComputeIntegrityCheck(System.ReadOnlySpan message, System.Buffers.IBufferWriter signatureWriter) { } public bool VerifyIntegrityCheck(System.ReadOnlySpan message, System.ReadOnlySpan signature) { throw null; } + public System.Net.Security.NegotiateAuthenticationStatusCode Wrap(System.ReadOnlySpan input, System.Buffers.IBufferWriter outputWriter, bool requestEncryption, out bool isEncrypted) { throw null; } } public partial class NegotiateAuthenticationClientOptions { @@ -102,8 +102,8 @@ public enum NegotiateAuthenticationStatusCode } public partial class NegotiateStream : System.Net.Security.AuthenticatedStream { - public NegotiateStream(System.IO.Stream innerStream) : base(default(System.IO.Stream), default(bool)) { } - public NegotiateStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen) : base(default(System.IO.Stream), default(bool)) { } + public NegotiateStream(System.IO.Stream innerStream) : base (default(System.IO.Stream), default(bool)) { } + public NegotiateStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen) : base (default(System.IO.Stream), default(bool)) { } public override bool CanRead { get { throw null; } } public override bool CanSeek { get { throw null; } } public override bool CanTimeout { get { throw null; } } @@ -201,6 +201,8 @@ public partial class SslClientAuthenticationOptions { public SslClientAuthenticationOptions() { } public bool AllowRenegotiation { get { throw null; } set { } } + public bool AllowRsaPkcs1Padding { get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("linux"), System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } + public bool AllowRsaPssPadding { get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("linux"), System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } public bool AllowTlsResume { get { throw null; } set { } } public System.Collections.Generic.List? ApplicationProtocols { get { throw null; } set { } } public System.Security.Cryptography.X509Certificates.X509ChainPolicy? CertificateChainPolicy { get { throw null; } set { } } @@ -226,6 +228,8 @@ public partial class SslServerAuthenticationOptions { public SslServerAuthenticationOptions() { } public bool AllowRenegotiation { get { throw null; } set { } } + public bool AllowRsaPkcs1Padding { get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("linux"), System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } + public bool AllowRsaPssPadding { get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("linux"), System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } public bool AllowTlsResume { get { throw null; } set { } } public System.Collections.Generic.List? ApplicationProtocols { get { throw null; } set { } } public System.Security.Cryptography.X509Certificates.X509ChainPolicy? CertificateChainPolicy { get { throw null; } set { } } @@ -241,32 +245,32 @@ public SslServerAuthenticationOptions() { } } public partial class SslStream : System.Net.Security.AuthenticatedStream { - public SslStream(System.IO.Stream innerStream) : base(default(System.IO.Stream), default(bool)) { } - public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen) : base(default(System.IO.Stream), default(bool)) { } - public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen, System.Net.Security.RemoteCertificateValidationCallback? userCertificateValidationCallback) : base(default(System.IO.Stream), default(bool)) { } - public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen, System.Net.Security.RemoteCertificateValidationCallback? userCertificateValidationCallback, System.Net.Security.LocalCertificateSelectionCallback? userCertificateSelectionCallback) : base(default(System.IO.Stream), default(bool)) { } - public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen, System.Net.Security.RemoteCertificateValidationCallback? userCertificateValidationCallback, System.Net.Security.LocalCertificateSelectionCallback? userCertificateSelectionCallback, System.Net.Security.EncryptionPolicy encryptionPolicy) : base(default(System.IO.Stream), default(bool)) { } + public SslStream(System.IO.Stream innerStream) : base (default(System.IO.Stream), default(bool)) { } + public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen) : base (default(System.IO.Stream), default(bool)) { } + public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen, System.Net.Security.RemoteCertificateValidationCallback? userCertificateValidationCallback) : base (default(System.IO.Stream), default(bool)) { } + public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen, System.Net.Security.RemoteCertificateValidationCallback? userCertificateValidationCallback, System.Net.Security.LocalCertificateSelectionCallback? userCertificateSelectionCallback) : base (default(System.IO.Stream), default(bool)) { } + public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen, System.Net.Security.RemoteCertificateValidationCallback? userCertificateValidationCallback, System.Net.Security.LocalCertificateSelectionCallback? userCertificateSelectionCallback, System.Net.Security.EncryptionPolicy encryptionPolicy) : base (default(System.IO.Stream), default(bool)) { } public override bool CanRead { get { throw null; } } public override bool CanSeek { get { throw null; } } public override bool CanTimeout { get { throw null; } } public override bool CanWrite { get { throw null; } } public virtual bool CheckCertRevocationStatus { get { throw null; } } - [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ObsoleteAttribute("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId="SYSLIB0058", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public virtual System.Security.Authentication.CipherAlgorithmType CipherAlgorithm { get { throw null; } } - [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ObsoleteAttribute("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId="SYSLIB0058", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public virtual int CipherStrength { get { throw null; } } - [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ObsoleteAttribute("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId="SYSLIB0058", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public virtual System.Security.Authentication.HashAlgorithmType HashAlgorithm { get { throw null; } } - [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ObsoleteAttribute("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId="SYSLIB0058", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public virtual int HashStrength { get { throw null; } } public override bool IsAuthenticated { get { throw null; } } public override bool IsEncrypted { get { throw null; } } public override bool IsMutuallyAuthenticated { get { throw null; } } public override bool IsServer { get { throw null; } } public override bool IsSigned { get { throw null; } } - [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ObsoleteAttribute("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId="SYSLIB0058", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public virtual System.Security.Authentication.ExchangeAlgorithmType KeyExchangeAlgorithm { get { throw null; } } - [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ObsoleteAttribute("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId="SYSLIB0058", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public virtual int KeyExchangeStrength { get { throw null; } } public override long Length { get { throw null; } } public virtual System.Security.Cryptography.X509Certificates.X509Certificate? LocalCertificate { get { throw null; } } @@ -319,6 +323,7 @@ public override void Flush() { } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public virtual System.Threading.Tasks.Task NegotiateClientCertificateAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override int Read(byte[] buffer, int offset, int count) { throw null; } + public override int Read(System.Span buffer) { throw null; } public override System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override int ReadByte() { throw null; } @@ -327,8 +332,10 @@ public override void SetLength(long value) { } public virtual System.Threading.Tasks.Task ShutdownAsync() { throw null; } public void Write(byte[] buffer) { } public override void Write(byte[] buffer, int offset, int count) { } + public override void Write(System.ReadOnlySpan buffer) { } public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override void WriteByte(byte value) { } } public partial class SslStreamCertificateContext { @@ -687,7 +694,7 @@ public partial class AuthenticationException : System.SystemException { public AuthenticationException() { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId="SYSLIB0051", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] protected AuthenticationException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) { } public AuthenticationException(string? message) { } public AuthenticationException(string? message, System.Exception? innerException) { } @@ -696,7 +703,7 @@ public partial class InvalidCredentialException : System.Security.Authentication { public InvalidCredentialException() { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId="SYSLIB0051", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] protected InvalidCredentialException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) { } public InvalidCredentialException(string? message) { } public InvalidCredentialException(string? message, System.Exception? innerException) { } @@ -707,7 +714,7 @@ namespace System.Security.Authentication.ExtendedProtection public partial class ExtendedProtectionPolicy : System.Runtime.Serialization.ISerializable { [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId="SYSLIB0051", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] protected ExtendedProtectionPolicy(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public ExtendedProtectionPolicy(System.Security.Authentication.ExtendedProtection.PolicyEnforcement policyEnforcement) { } public ExtendedProtectionPolicy(System.Security.Authentication.ExtendedProtection.PolicyEnforcement policyEnforcement, System.Security.Authentication.ExtendedProtection.ChannelBinding customChannelBinding) { } diff --git a/src/libraries/System.Net.Security/src/Resources/Strings.resx b/src/libraries/System.Net.Security/src/Resources/Strings.resx index cf00f35a9a0044..5b842ae977aae3 100644 --- a/src/libraries/System.Net.Security/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Security/src/Resources/Strings.resx @@ -314,6 +314,12 @@ Failed to allocate SSL/TLS context, OpenSSL error - {0}. + + Failed to get default signature algorithms. + + + Failed to set {0} signature algorithms. + Operation failed with error - {0}. @@ -410,6 +416,9 @@ CipherSuitesPolicy is not supported on this platform. + + Disabling AllowRsaRssPadding or AllowRsaPkcs1Padding is not supported on this platform. + System.Net.Security is not supported on this platform. diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index e7c1c325b7a75e..134419623e0c81 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -186,6 +186,8 @@ Link="Common\Interop\Windows\Interop.Libraries.cs" /> + public X509ChainPolicy? CertificateChainPolicy { get; set; } + + private bool _allowRsaPssPadding = true; + /// + /// Gets or sets a value that indicates whether the the rsa_pss_* family of TLS signature algorithms is enabled for use in the TLS handshake. + /// + public bool AllowRsaPssPadding + { + get => _allowRsaPssPadding; + + [SupportedOSPlatform("windows")] + [SupportedOSPlatform("linux")] + set { _allowRsaPssPadding = value; } + } + + private bool _allowRsaPkcs1Padding = true; + /// + /// Gets or sets a value that indicates whether the the rsa_pkcs1_* family of TLS signature algorithms is enabled for use in the TLS handshake. + /// + public bool AllowRsaPkcs1Padding + { + get => _allowRsaPkcs1Padding; + + [SupportedOSPlatform("windows")] + [SupportedOSPlatform("linux")] + set { _allowRsaPkcs1Padding = value; } + } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs index cc082eaa04f7e6..6ca64677652d50 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.Versioning; using System.Collections.Generic; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; @@ -92,5 +93,31 @@ public EncryptionPolicy EncryptionPolicy /// are ignored. /// public X509ChainPolicy? CertificateChainPolicy { get; set; } + + private bool _allowRsaPssPadding = true; + /// + /// Gets or sets a value that indicates whether the the rsa_pss_* family of TLS signature algorithms is enabled for use in the TLS handshake. + /// + public bool AllowRsaPssPadding + { + get => _allowRsaPssPadding; + + [SupportedOSPlatform("windows")] + [SupportedOSPlatform("linux")] + set { _allowRsaPssPadding = value; } + } + + private bool _allowRsaPkcs1Padding = true; + /// + /// Gets or sets a value that indicates whether the the rsa_pkcs1_* family of TLS signature algorithms is enabled for use in the TLS handshake. + /// + public bool AllowRsaPkcs1Padding + { + get => _allowRsaPkcs1Padding; + + [SupportedOSPlatform("windows")] + [SupportedOSPlatform("linux")] + set { _allowRsaPkcs1Padding = value; } + } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslSessionsCache.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslSessionsCache.cs index 4c74bcb2af0005..d69e86b5501d1f 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslSessionsCache.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslSessionsCache.cs @@ -29,6 +29,8 @@ internal static class SslSessionsCache private readonly bool _sendTrustList; private readonly bool _checkRevocation; private readonly bool _allowTlsResume; + private readonly bool _allowRsaPssPadding; + private readonly bool _allowRsaPkcs1Padding; // // SECURITY: X509Certificate.GetCertHash() is virtual hence before going here, @@ -42,7 +44,9 @@ internal SslCredKey( EncryptionPolicy encryptionPolicy, bool sendTrustList, bool checkRevocation, - bool allowTlsResume) + bool allowTlsResume, + bool allowRsaPssPadding, + bool allowRsaPkcs1Padding) { _thumbPrint = thumbPrint ?? Array.Empty(); _allowedProtocols = allowedProtocols; @@ -51,23 +55,24 @@ internal SslCredKey( _checkRevocation = checkRevocation; _sendTrustList = sendTrustList; _allowTlsResume = allowTlsResume; + _allowRsaPssPadding = allowRsaPssPadding; + _allowRsaPkcs1Padding = allowRsaPkcs1Padding; } public override int GetHashCode() { - int hashCode = 0; - if (_thumbPrint.Length > 3) - { - hashCode ^= _thumbPrint[0] | (_thumbPrint[1] << 8) | (_thumbPrint[2] << 16) | (_thumbPrint[3] << 24); - } - - return HashCode.Combine(_allowedProtocols, - (int)_encryptionPolicy, - _isServerMode, - _sendTrustList, - _checkRevocation, - _allowedProtocols, - hashCode); + HashCode hash = default; + hash.AddBytes(_thumbPrint); + hash.Add(_allowedProtocols); + hash.Add((int)_encryptionPolicy); + hash.Add(_isServerMode); + hash.Add(_sendTrustList); + hash.Add(_checkRevocation); + hash.Add(_allowTlsResume); + hash.Add(_allowRsaPssPadding); + hash.Add(_allowRsaPkcs1Padding); + + return hash.ToHashCode(); } public override bool Equals([NotNullWhen(true)] object? obj) => @@ -86,6 +91,8 @@ public bool Equals(SslCredKey other) _sendTrustList == other._sendTrustList && _checkRevocation == other._checkRevocation && _allowTlsResume == other._allowTlsResume && + _allowRsaPssPadding == other._allowRsaPssPadding && + _allowRsaPkcs1Padding == other._allowRsaPkcs1Padding && thumbPrint.AsSpan().SequenceEqual(otherThumbPrint); } } @@ -103,7 +110,9 @@ public bool Equals(SslCredKey other) EncryptionPolicy encryptionPolicy, bool checkRevocation, bool allowTlsResume, - bool sendTrustList) + bool sendTrustList, + bool allowRsaPssPadding, + bool allowRsaPkcs1Padding) { if (s_cachedCreds.IsEmpty) { @@ -111,7 +120,7 @@ public bool Equals(SslCredKey other) return null; } - var key = new SslCredKey(thumbPrint, (int)sslProtocols, isServer, encryptionPolicy, sendTrustList, checkRevocation, allowTlsResume); + var key = new SslCredKey(thumbPrint, (int)sslProtocols, isServer, encryptionPolicy, sendTrustList, checkRevocation, allowTlsResume, allowRsaPssPadding, allowRsaPkcs1Padding); //SafeCredentialReference? cached; SafeFreeCredentials? credentials = GetCachedCredential(key); @@ -144,7 +153,9 @@ internal static void CacheCredential( EncryptionPolicy encryptionPolicy, bool checkRevocation, bool allowTlsResume, - bool sendTrustList) + bool sendTrustList, + bool allowRsaPssPadding, + bool allowRsaPkcs1Padding) { Debug.Assert(creds != null, "creds == null"); @@ -154,7 +165,7 @@ internal static void CacheCredential( return; } - SslCredKey key = new SslCredKey(thumbPrint, (int)sslProtocols, isServer, encryptionPolicy, sendTrustList, checkRevocation, allowTlsResume); + SslCredKey key = new SslCredKey(thumbPrint, (int)sslProtocols, isServer, encryptionPolicy, sendTrustList, checkRevocation, allowTlsResume, allowRsaPssPadding, allowRsaPkcs1Padding); SafeFreeCredentials? credentials = GetCachedCredential(key); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 1e0a8ed0e2a756..4e9f4327ee9e22 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -604,7 +604,9 @@ private bool AcquireClientCredentials(ref byte[]? thumbPrint, bool newCredential _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck, _sslAuthenticationOptions.AllowTlsResume, - sendTrustList: false); + sendTrustList: false, + _sslAuthenticationOptions.AllowRsaPssPadding, + _sslAuthenticationOptions.AllowRsaPkcs1Padding); // We can probably do some optimization here. If the selectedCert is returned by the delegate // we can always go ahead and use the certificate to create our credential @@ -742,15 +744,16 @@ private bool AcquireServerCredentials(ref byte[]? thumbPrint) // // Note selectedCert is a safe ref possibly cloned from the user passed Cert object // - byte[] guessedThumbPrint = selectedCert.GetCertHash(HashAlgorithmName.SHA512); - bool sendTrustedList = _sslAuthenticationOptions.CertificateContext!.Trust?._sendTrustInHandshake ?? false; + byte[] guessedThumbPrint = selectedCert.GetCertHash(HashAlgorithmName.SHA512); bool sendTrustedList = _sslAuthenticationOptions.CertificateContext!.Trust?._sendTrustInHandshake ?? false; SafeFreeCredentials? cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint, _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.IsServer, _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck, _sslAuthenticationOptions.AllowTlsResume, - sendTrustedList); + sendTrustedList, + _sslAuthenticationOptions.AllowRsaPssPadding, + _sslAuthenticationOptions.AllowRsaPkcs1Padding); if (cachedCredentialHandle != null) { _credentialsHandle = cachedCredentialHandle; @@ -952,7 +955,9 @@ private ProtocolToken GenerateToken(ReadOnlySpan inputBuffer, out int cons _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck, _sslAuthenticationOptions.AllowTlsResume, - sendTrustList); + sendTrustList, + _sslAuthenticationOptions.AllowRsaPssPadding, + _sslAuthenticationOptions.AllowRsaPkcs1Padding); } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs index 4c9c1c8aea9952..61bc2fd2e71c04 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs @@ -445,18 +445,63 @@ public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchCredentials( if (NetEventSource.Log.IsEnabled()) NetEventSource.Info($"flags=({flags}), ProtocolFlags=({protocolFlags}), EncryptionPolicy={policy}"); - Interop.SspiCli.TLS_PARAMETERS tlsParameters; + Interop.SspiCli.TLS_PARAMETERS tlsParameters = default; + credential.cTlsParameters = 1; + credential.pTlsParameters = &tlsParameters; + if (protocolFlags != 0) { - // If we were asked to do specific protocol we need to fill TLS_PARAMETERS. - tlsParameters = default; tlsParameters.grbitDisabledProtocols = (uint)protocolFlags ^ uint.MaxValue; - - credential.cTlsParameters = 1; - credential.pTlsParameters = &tlsParameters; } - return AcquireCredentialsHandle(direction, &credential); + Span cryptoSettings = stackalloc Interop.SspiCli.CRYPTO_SETTINGS[2]; + + // init to null ptrs to prevent freeing uninitialized memory in finally block + Span algIdPtrs = stackalloc IntPtr[2] { IntPtr.Zero, IntPtr.Zero }; + int cryptoSettingsCount = 0; + + try + { + if (!authOptions.AllowRsaPkcs1Padding) + { + algIdPtrs[cryptoSettingsCount] = Marshal.StringToHGlobalUni("SCH_RSA_PKCS_PAD"); + + cryptoSettings[cryptoSettingsCount] = new() + { + eAlgorithmUsage = Interop.SspiCli.CRYPTO_SETTINGS.TlsAlgorithmUsage.TlsParametersCngAlgUsageCertSig + }; + + Interop.NtDll.RtlInitUnicodeString(out cryptoSettings[cryptoSettingsCount].strCngAlgId, algIdPtrs[cryptoSettingsCount]); + cryptoSettingsCount++; + } + + if (!authOptions.AllowRsaPssPadding) + { + algIdPtrs[cryptoSettingsCount] = Marshal.StringToHGlobalUni("SCH_RSA_PSS_PAD"); + + cryptoSettings[cryptoSettingsCount] = new() + { + eAlgorithmUsage = Interop.SspiCli.CRYPTO_SETTINGS.TlsAlgorithmUsage.TlsParametersCngAlgUsageCertSig + }; + Interop.NtDll.RtlInitUnicodeString(out cryptoSettings[cryptoSettingsCount].strCngAlgId, algIdPtrs[cryptoSettingsCount]); + cryptoSettingsCount++; + } + + tlsParameters.pDisabledCrypto = (Interop.SspiCli.CRYPTO_SETTINGS*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(cryptoSettings)); + tlsParameters.cDisabledCrypto = cryptoSettingsCount; + + return AcquireCredentialsHandle(direction, &credential); + } + finally + { + foreach (IntPtr algIdPtr in algIdPtrs.Slice(0, cryptoSettingsCount)) + { + if (algIdPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(algIdPtr); + } + } + } } public static unsafe ProtocolToken EncryptMessage(SafeDeleteSslContext securityContext, ReadOnlyMemory input, int headerSize, int trailerSize) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index 7852a4c671f427..d5486ef526aa1a 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -938,7 +938,7 @@ public async Task SslStream_ClientCertificateContext_SendsChain(bool useTrust) TargetHost = "localhost", }; clientOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; - clientOptions.ClientCertificateContext = SslStreamCertificateContext.Create(clientCertificate, useTrust ? null : clientChain, offline:true, trust); + clientOptions.ClientCertificateContext = SslStreamCertificateContext.Create(clientCertificate, useTrust ? null : clientChain, offline: true, trust); await SslStream_ClientSendsChain_Core(clientOptions, clientChain); @@ -1137,6 +1137,112 @@ public async Task SslStream_UnifiedHello_Ok(bool useOptionCallback) await Assert.ThrowsAnyAsync(() => serverTask); } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.SupportsTls13))] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)] + public async Task DisableUnusedRsaPadding_Connects(bool clientDisable, bool serverDisable) + { + (Stream client, Stream server) = TestHelper.GetConnectedTcpStreams(); + + using SslStream clientSslStream = new SslStream(client); + using SslStream serverSslStream = new SslStream(server); + + using X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate(); + using X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate(); + + // the test certificates use PSS padding, so we disable PKCS1 padding + Task t1 = clientSslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions() + { + ClientCertificates = new X509CertificateCollection(new X509Certificate2[] { clientCertificate }), + RemoteCertificateValidationCallback = delegate { return true; }, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck, + AllowRsaPkcs1Padding = !clientDisable + }, CancellationToken.None); + Task t2 = serverSslStream.AuthenticateAsServerAsync(new SslServerAuthenticationOptions() + { + ServerCertificate = serverCertificate, + RemoteCertificateValidationCallback = delegate { return true; }, + ClientCertificateRequired = true, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck, + AllowRsaPkcs1Padding = !serverDisable + }, CancellationToken.None); + + await t1.WaitAsync(TestConfiguration.PassingTestTimeout); + await t2.WaitAsync(TestConfiguration.PassingTestTimeout); + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.SupportsTls13))] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)] + public async Task DisableUsedRsaPadding_Throws(bool clientDisable, bool serverDisable) + { + (Stream client, Stream server) = TestHelper.GetConnectedTcpStreams(); + + using SslStream clientSslStream = new SslStream(client); + using SslStream serverSslStream = new SslStream(server); + + using X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate(); + using X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate(); + + // the test certificates use PSS padding + Task t1 = clientSslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions() + { + ClientCertificates = new X509CertificateCollection(new X509Certificate2[] { clientCertificate }), + RemoteCertificateValidationCallback = delegate { return true; }, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck, + AllowRsaPssPadding = !clientDisable, + }, CancellationToken.None); + Task t2 = serverSslStream.AuthenticateAsServerAsync(new SslServerAuthenticationOptions() + { + ServerCertificate = serverCertificate, + RemoteCertificateValidationCallback = delegate { return true; }, + ClientCertificateRequired = true, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck, + AllowRsaPssPadding = !serverDisable, + }, CancellationToken.None); + + await Assert.ThrowsAsync(() => t1.WaitAsync(TestConfiguration.PassingTestTimeout)); + await Assert.ThrowsAsync(() => t2.WaitAsync(TestConfiguration.PassingTestTimeout)); + } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [PlatformSpecific(~(TestPlatforms.Windows | TestPlatforms.Linux))] + public void DisallowPkcsOrPss_UnsupportedPlatforms_Throws(bool disablePkcs1Padding, bool disablePssPadding) + { + using X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate(); + (Stream stream1, Stream stream2) = TestHelper.GetConnectedStreams(); + using SslStream client = new SslStream(stream1); + using SslStream server = new SslStream(stream2); + + Assert.Throws(() => + { + server.AuthenticateAsServer(new SslServerAuthenticationOptions() + { + ServerCertificate = serverCertificate, + RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true, + AllowRsaPkcs1Padding = !disablePkcs1Padding, + AllowRsaPssPadding = !disablePssPadding + }); + }); + + Assert.Throws(() => + { + client.AuthenticateAsClient(new SslClientAuthenticationOptions() + { + RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true, + AllowRsaPkcs1Padding = !disablePkcs1Padding, + AllowRsaPssPadding = !disablePssPadding + }); + }); + } + private static bool ValidateServerCertificate( object sender, X509Certificate retrievedServerPublicCertificate, diff --git a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c index 09fd43b529bd3e..d69d74111f1a1c 100644 --- a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c @@ -397,6 +397,8 @@ static const Entry s_cryptoNative[] = DllImportEntry(CryptoNative_SslSetSession) DllImportEntry(CryptoNative_SslSetTlsExtHostName) DllImportEntry(CryptoNative_SslSetVerifyPeer) + DllImportEntry(CryptoNative_SslSetSigalgs) + DllImportEntry(CryptoNative_SslSetClientSigalgs) DllImportEntry(CryptoNative_SslShutdown) DllImportEntry(CryptoNative_SslStapleOcsp) DllImportEntry(CryptoNative_SslUseCertificate) @@ -407,6 +409,7 @@ static const Entry s_cryptoNative[] = DllImportEntry(CryptoNative_X509DecodeOcspToExpiration) DllImportEntry(CryptoNative_X509Duplicate) DllImportEntry(CryptoNative_SslGet0AlpnSelected) + DllImportEntry(CryptoNative_GetDefaultSignatureAlgorithms) }; EXTERN_C const void* CryptoResolveDllImport(const char* name); diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h index 82023bab1b7af2..a5d23b6a3796d7 100644 --- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h +++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h @@ -718,6 +718,7 @@ extern bool g_libSslUses32BitTime; REQUIRED_FUNCTION(SSL_do_handshake) \ REQUIRED_FUNCTION(SSL_free) \ REQUIRED_FUNCTION(SSL_get_ciphers) \ + REQUIRED_FUNCTION(SSL_get_sigalgs) \ REQUIRED_FUNCTION(SSL_get_client_CA_list) \ REQUIRED_FUNCTION(SSL_get_current_cipher) \ REQUIRED_FUNCTION(SSL_get_error) \ @@ -1293,6 +1294,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define SSL_do_handshake SSL_do_handshake_ptr #define SSL_free SSL_free_ptr #define SSL_get_ciphers SSL_get_ciphers_ptr +#define SSL_get_sigalgs SSL_get_sigalgs_ptr #define SSL_get_client_CA_list SSL_get_client_CA_list_ptr #define SSL_get_certificate SSL_get_certificate_ptr #define SSL_get_current_cipher SSL_get_current_cipher_ptr diff --git a/src/native/libs/System.Security.Cryptography.Native/osslcompat_111.h b/src/native/libs/System.Security.Cryptography.Native/osslcompat_111.h index a56becf4f85578..56d00cdbd9e360 100644 --- a/src/native/libs/System.Security.Cryptography.Native/osslcompat_111.h +++ b/src/native/libs/System.Security.Cryptography.Native/osslcompat_111.h @@ -71,6 +71,7 @@ void SSL_CTX_set_security_level(SSL_CTX* ctx, int32_t level); int32_t SSL_is_init_finished(SSL* ssl); unsigned long SSL_set_options(SSL* ctx, unsigned long options); void SSL_set_post_handshake_auth(SSL *s, int val); +int32_t SSL_set_post_handshake_auth(SSL *s, int val); int SSL_session_reused(SSL* ssl); int SSL_verify_client_post_handshake(SSL *s); const SSL_METHOD* TLS_method(void); diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c index 93d352702748f6..49d4e5aead38ec 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c @@ -1117,6 +1117,19 @@ int32_t CryptoNative_SslSetTlsExtHostName(SSL* ssl, uint8_t* name) return (int32_t)SSL_set_tlsext_host_name(ssl, name); } +int32_t CryptoNative_SslSetSigalgs(SSL* ssl, uint8_t* str) +{ + ERR_clear_error(); + return (int32_t) SSL_ctrl(ssl, SSL_CTRL_SET_SIGALGS_LIST, 0, (void*)str); +} + +int32_t CryptoNative_SslSetClientSigalgs(SSL* ssl, uint8_t* str) +{ + if (ssl == NULL || str == NULL) + return 0; + return (int32_t) SSL_ctrl(ssl, SSL_CTRL_SET_CLIENT_SIGALGS_LIST, 0, (void*)str); +} + int32_t CryptoNative_SslGetCurrentCipherId(SSL* ssl, int32_t* cipherId) { // No error queue impact. @@ -1188,6 +1201,91 @@ static int MakeSelfSignedCertificate(X509* cert, EVP_PKEY* evp) return ret; } +int32_t CryptoNative_GetDefaultSignatureAlgorithms(uint16_t* buffer, int32_t* count) +{ + int ret = 0; + + SSL_CTX* clientCtx = CryptoNative_SslCtxCreate(TLS_method()); + SSL_CTX* serverCtx = CryptoNative_SslCtxCreate(TLS_method()); + + BIO *bio1 = BIO_new(BIO_s_mem()); + BIO *bio2 = BIO_new(BIO_s_mem()); + + SSL* client = NULL; + SSL* server = NULL; + + if (clientCtx != NULL && serverCtx != NULL && bio1 != NULL && bio2 != NULL) + { + SSL_CTX_set_verify(clientCtx, SSL_VERIFY_NONE, NULL); + SSL_CTX_set_verify(serverCtx, SSL_VERIFY_NONE, NULL); + + server = CryptoNative_SslCreate(serverCtx); + SSL_set_accept_state(server); + + client = CryptoNative_SslCreate(clientCtx); + SSL_set_connect_state(client); + + // set BIOs in opposite + SSL_set_bio(client, bio1, bio2); + SSL_set_bio(server, bio2, bio1); + + // SSL_set_bio takes ownership so we need to up reference since same BIO is shared. + BIO_up_ref(bio1); + BIO_up_ref(bio2); + bio1 = NULL; + bio2 = NULL; + + // send/receive the client hello + ret = SSL_do_handshake(client); + ret = SSL_do_handshake(server); + + int c = SSL_get_sigalgs(server, 0, NULL, NULL, NULL, NULL, NULL); + if (c > 0) + { + for (int i = 0; i < c; i++) + { + if (i >= *count) + { + // this should not happen, but just in case + ret = -1; + break; + } + + unsigned char sig, hash; + SSL_get_sigalgs(server, i, NULL, NULL, NULL, &sig, &hash); + buffer[i] = (uint16_t)(hash << 8 | sig); + } + + *count = c; + ret = 0; + } + } + + if (bio1) + { + BIO_free(bio1); + } + + if (bio2) + { + BIO_free(bio2); + } + + if (client != NULL) + { + SSL_free(client); + } + + if (server != NULL) + { + SSL_free(server); + } + + ERR_clear_error(); + + return ret; +} + int32_t CryptoNative_OpenSslGetProtocolSupport(SslProtocols protocol) { // Many of these helpers already clear the error queue, and we unconditionally diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h index 8566c7b8ff9b35..66457e17710771 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h @@ -522,6 +522,16 @@ Shims the SSL_set_tlsext_host_name method. */ PALEXPORT int32_t CryptoNative_SslSetTlsExtHostName(SSL* ssl, uint8_t* name); +/* +Shims the SSL_set1_sigalgs_list method. +*/ +PALEXPORT int32_t CryptoNative_SslSetSigalgs(SSL* ssl, uint8_t* str); + +/* +Shim for SSL_set_client_sigalgs +*/ +PALEXPORT int32_t CryptoNative_SslSetClientSigalgs(SSL* ssl, uint8_t* str); + /* Shims the SSL_get_current_cipher and SSL_CIPHER_get_id. */ @@ -533,6 +543,11 @@ and emits a value indicating if the cipher belongs to the SSL2-TLS1.2 list, or t */ PALEXPORT const char* CryptoNative_GetOpenSslCipherSuiteName(SSL* ssl, int32_t cipherSuite, int32_t* isTls12OrLower); +/* +Returns the signature algorithms enabled by default +*/ +PALEXPORT int32_t CryptoNative_GetDefaultSignatureAlgorithms(uint16_t* buffer, int32_t* count); + /* Checks if given protocol version is supported. */