-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[.NET 9.0 Regression] "Credentials supplied to the package were not recognized" when using certificates with wcf client #110067
Comments
this looks like dup of #109050. It seems like the something changed in certificate loading. |
I just solved my problem by removed the flag when i imported the certificate (i honestly don't know why i put these flags on the original code, but removing them seem make the job => code working in .net 8.0
=> code working in .net 9.0
but there is definitivly change in .net9 also in ServicePointManager This is my final code which works in .net9, without warning and with tlsoption and callback
|
For a while the |
Yeah i don't know exactly neither but there is definitively a strange behavior with the wcf client. anyway my problem is solved, and i get rid of call at the service point manager so everything is good on my side,, thanks for your help ! |
I reopend this issue because actually my solution not works in production. i tried the different workaround explain in other issues but nothing seem to work. do you guys have others ideas or workaround i could try ? Its kind of a disaster on my side, all my clients are already upgraded to .net 9.0, but without make this works i will have to make a regression to .net8.0. Any helps will be appreciated thanks ! |
There may be old key somewhere on the disk. I think @bartonjs had some instructions how to look in #109050. You can alto try this little trick: runtime/src/libraries/Common/tests/System/Net/Configuration.Certificates.Dynamic.cs Lines 163 to 168 in 0170d78
exporting and importing the cert may help. |
actually the certificate is not supposed to be installed on the machine but loaded dynamically with the content of the certificate and his password. Maybe its related at the user associated in the pool in the production server ? (don't have total admin rights) I will try investigate on the production server if i see this certificate is installed somewhere any others suggestion is welcoming |
OK guys, its definitively associated to the user who execute the app. In production, if i change the user associated with the pool in IIS for an administrator, it start working again. But its not an acceptable workaround, the user is not supposed to have admin rights in my app. Based on these new informations, i wait for your suggestions |
Private keys, when not loaded with EphemeralKeySet, are stored in either a machine central location (which you can force with MachineKeySet) or a user relative one (which you can force with UserKeySet). If neither User or Machine are specified, the contents of the PFX decide on a key-by-key basis. (EphemeralKeySet doesn't work with SslStream on Windows, because it doesn't work with the underlying Windows TLS provider). There are two different machine central locations (because why not) and two different user locations; one for the legacy Windows CAPI system, another for the "new" (2005) Windows CNG system. The only difference between .NET 8 and .NET 9 certificate loading when using If you can access the PFX that's failing, you can share metadata about it with something like A work in progress program to show metadata of a PFXusing System.CodeDom.Compiler;
using System.Formats.Asn1;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
namespace PfxInfo
{
internal class Program
{
private static IndentedTextWriter s_writer = new IndentedTextWriter(Console.Out, " ");
private static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("Input one or more files to read.");
return;
}
foreach (string path in args)
{
ShowPfx(path);
}
}
private static void ShowPfx(string path)
{
WriteLine(ConsoleColor.Yellow, $"{path}");
s_writer.Indent++;
string? pwd = null;
bool pwdSpecified = false;
try
{
byte[] bytes;
try
{
bytes = File.ReadAllBytes(path);
}
catch (Exception e)
{
WriteLine(ConsoleColor.Red, $"Could not read file: {e.GetType().Name}: {e.Message}");
return;
}
Pkcs12Info p12Info;
try
{
p12Info = Pkcs12Info.Decode(bytes, out int consumed, skipCopy: true);
if (consumed != bytes.Length)
{
WriteLine(ConsoleColor.DarkYellow, $"Warning: PKCS#12/PFX was only {consumed} of {bytes.Length} bytes.");
}
}
catch (Exception e)
{
WriteLine(ConsoleColor.Red, $"File did not parse as a PKCS#12/PFX: {e.GetType().Name}: {e.Message}");
return;
}
if (p12Info.IntegrityMode != Pkcs12IntegrityMode.Password)
{
s_writer.WriteLine($"Integrity mode: {p12Info.IntegrityMode}");
}
else
{
if (p12Info.VerifyMac(pwd))
{
s_writer.WriteLine("MAC verified with the NULL password");
}
else if (p12Info.VerifyMac(""))
{
s_writer.WriteLine("MAC verified with the empty password");
}
else
{
MaybePasswordPrompt(ref pwd, ref pwdSpecified);
if (p12Info.VerifyMac(pwd))
{
s_writer.WriteLine("MAC verified with the supplied password");
}
else if (!pwdSpecified)
{
s_writer.WriteLine("Skipping MAC verification");
}
else
{
WriteLine(ConsoleColor.DarkRed, "Password did not verify the MAC");
}
}
}
int contentsId = 0;
foreach (Pkcs12SafeContents contents in p12Info.AuthenticatedSafe)
{
WriteLine(ConsoleColor.DarkGreen, $"AuthSafe Contents {contentsId}");
contentsId++;
s_writer.Indent++;
try
{
s_writer.WriteLine($"Confidentiality Mode: {contents.ConfidentialityMode}");
if (contents.ConfidentialityMode == Pkcs12ConfidentialityMode.Password)
{
if (!pwdSpecified)
{
try
{
contents.Decrypt(pwd);
s_writer.WriteLine("Contents decrypted with the NULL password");
}
catch (Exception)
{
try
{
contents.Decrypt("");
s_writer.WriteLine("Contents decrypted with the NULL password");
}
catch (Exception)
{
MaybePasswordPrompt(ref pwd, ref pwdSpecified);
if (!pwdSpecified)
{
s_writer.WriteLine("Skipping decryption.");
continue;
}
}
}
}
if (pwdSpecified)
{
try
{
contents.Decrypt(pwd);
}
catch (Exception e)
{
WriteLine(ConsoleColor.DarkRed, $"Contents did not decrypt: {e.GetType().Name}: {e.Message}");
continue;
}
}
}
else if (contents.ConfidentialityMode != Pkcs12ConfidentialityMode.None)
{
s_writer.WriteLine("Skipping decryption.");
continue;
}
int bagId = 0;
foreach (Pkcs12SafeBag bag in contents.GetBags())
{
WriteLine(ConsoleColor.DarkGreen, $"SafeBag {bagId}: {bag.GetType().Name}");
bagId++;
s_writer.Indent++;
try
{
if (bag is Pkcs12CertBag certBag)
{
s_writer.WriteLine($"Certificate type: {certBag.GetCertificateType().Value}");
if (certBag.IsX509Certificate)
{
X509Certificate2? cert = null;
try
{
cert = certBag.GetCertificate();
s_writer.WriteLine("Certificate:");
s_writer.Indent++;
try
{
s_writer.WriteLine($"Issuer: {cert.Issuer}");
s_writer.WriteLine($"Serial Number (hex): {cert.SerialNumber}");
s_writer.WriteLine($"Subject: {cert.Subject}");
s_writer.WriteLine($"Not Before: {cert.NotBefore:yyyy-MM-dd HH:mm:ss}");
s_writer.WriteLine($"Not After: {cert.NotAfter:yyyy-MM-dd HH:mm:ss}");
}
finally
{
s_writer.Indent--;
}
}
catch (Exception)
{
WriteLine(ConsoleColor.DarkRed, $"Certificate did not load.");
}
finally
{
cert?.Dispose();
}
}
}
ShowAttributes(bag.Attributes);
}
finally
{
s_writer.Indent--;
}
}
}
finally
{
s_writer.Indent--;
}
}
}
finally
{
s_writer.Indent--;
}
static void ShowAttributes(CryptographicAttributeObjectCollection? attributes)
{
if (attributes?.Count > 0)
{
s_writer.WriteLine("Attributes:");
s_writer.Indent++;
try
{
int attrSetId = 0;
foreach (CryptographicAttributeObject attrSet in attributes)
{
s_writer.WriteLine($"Set {attrSetId}: {attrSet.Oid.Value} ({attrSet.Oid.FriendlyName})");
attrSetId++;
s_writer.Indent++;
try
{
int valueId = 0;
foreach (AsnEncodedData attr in attrSet.Values)
{
s_writer.WriteLine(valueId);
valueId++;
s_writer.Indent++;
try
{
byte[] encoded = attr.RawData;
if (encoded.Length < 12)
{
s_writer.WriteLine(Convert.ToHexString(encoded));
}
else
{
s_writer.WriteLine(
$"{encoded.Length} :: {Convert.ToHexString(encoded.AsSpan(0, 4))}...{Convert.ToHexString(encoded.AsSpan()[^4..])}");
}
if (encoded.Length > 0)
{
UniversalTagNumber kind =
(UniversalTagNumber)encoded[0];
switch (kind)
{
case UniversalTagNumber.BMPString:
case UniversalTagNumber.IA5String:
case UniversalTagNumber.NumericString:
case UniversalTagNumber.PrintableString:
case UniversalTagNumber.T61String:
case UniversalTagNumber.UTF8String:
{
try
{
string decoded =
AsnDecoder.
ReadCharacterString(
encoded,
AsnEncodingRules.BER,
kind,
out int consumed);
s_writer.WriteLine($"({kind}): {decoded}");
if (consumed != encoded.Length)
{
WriteLine(
ConsoleColor.DarkYellow,
$"Warning: String was only {consumed} of {encoded.Length} byte(s)");
}
}
catch (Exception e)
{
WriteLine(
ConsoleColor.DarkRed,
$"String did not decode as {kind}: {e.GetType().Name}: {e.Message}");
}
break;
}
}
}
s_writer.WriteLine();
}
finally
{
s_writer.Indent--;
}
}
}
finally
{
s_writer.Indent--;
}
}
}
finally
{
s_writer.Indent--;
}
}
else
{
s_writer.WriteLine("No attributes.");
}
}
static void MaybePasswordPrompt(ref string? pwd, ref bool specified)
{
if (!specified)
{
string tmp = PasswordPrompt(ref specified);
if (specified)
{
pwd = tmp;
}
}
}
static string PasswordPrompt(ref bool specified)
{
Console.WriteLine("Enter password: ");
Span<char> buf = stackalloc char[128];
string? pwd = null;
int i = 0;
ConsoleKeyInfo c;
while ((c = Console.ReadKey(true)).KeyChar is not ('\r' or '\n'))
{
if (i < buf.Length)
{
buf[i] = c.KeyChar;
}
else
{
if (pwd is null)
{
pwd = new string(buf);
}
pwd += c.KeyChar;
}
i++;
}
if (pwd is null)
{
if (i == 0)
{
return "";
}
specified = true;
return new string(buf.Slice(0, i));
}
specified = true;
return pwd;
}
}
private static void WriteLine(ConsoleColor foregroundColor, string message)
{
try
{
Console.ForegroundColor = foregroundColor;
s_writer.WriteLine(message);
}
finally
{
Console.ResetColor();
}
}
}
} If you can load the same PFX in both 8 and 9 and show some info like private static void LoadedPfxInfo(string file, string pwd, X509KeyStorageFlags flags)
{
using (X509Certificate2 cert = new X509Certificate2(file, pwd, flags))
{
LoadedPfxInfo(cert);
}
}
private static void FullLoadedPfxInfo(string file, string pwd, X509KeyStorageFlags flags)
{
X509Certificate2Collection coll = new X509Certificate2Collection();
coll.Import(file, pwd, flags);
foreach (X509Certificate2 cert in coll)
{
using (cert)
{
LoadedPfxInfo(cert);
}
}
}
private static void LoadedPfxInfo(X509Certificate2 cert)
{
Console.WriteLine();
Console.WriteLine(cert.SubjectName);
Console.WriteLine($" HasPrivateKey: {cert.HasPrivateKey}");
if (cert.HasPrivateKey)
{
CngKey cngKey = null;
using AsymmetricAlgorithm privateKey =
cert.GetRSAPublicKey() ??
cert.GetDSAPrivateKey() ??
(AsymmetricAlgorithm)cert.GetECDsaPrivateKey();
if (privateKey is RSACng rsaCng)
{
cngKey = rsaCng.Key;
}
else if (privateKey is DSACng dsaCng)
{
cngKey = dsaCng.Key;
}
else if (privateKey is ECDsaCng ecdsaCng)
{
cngKey = ecdsaCng.Key;
}
else if (privateKey is ICspAsymmetricAlgorithm capiKey)
{
CspKeyContainerInfo cspInfo = capiKey.CspKeyContainerInfo;
Console.WriteLine(" Private Key loaded via CAPI");
Console.WriteLine($" Machine Key: {cspInfo.MachineKeyStore}");
Console.WriteLine($" Provider Name: {cspInfo.ProviderName}");
}
if (cngKey is not null)
{
using (cngKey)
{
Console.WriteLine(" Private key loaded via CNG");
Console.WriteLine($" Machine Key: {cngKey.IsMachineKey}");
Console.WriteLine($" Provider Name: {cngKey.Provider?.Provider}");
}
}
}
} that would be useful in diagnosing things. |
I made some progress, i just discovered that when i put LoadUserProfile=True in my iis pool it start work again. Actually, there is no specific reason this parameter would be different than the default value (the defaut value is true) I think its because this pool is created by code (in c# with Microsoft.Web.Administration), and the default value of a booleean is false so lPool.ProcessModel.LoadUserProfile is init with false. Does this make sense for you, that LoadUserProfile at false produce this error in .net9 ? Anyway for me its an acceptable workaround, i will update my update procedure for correct the pool on my productions servers. I let a bit the issue open in case i discover other problems related |
Yep. Since you're no longer specifying MachineKeySet, the key wants to go to the user key store. The user key store requires that a user profile be loaded. (That's a Windows thing, not a .NET thing) |
OK i understand, but its strange the machineKeySet flag was working on .net 8 and not in .net9 (i have same error when i put this flag in production) |
Yeah, that part is surprising. My rough guess is that your PFX doesn't specify a key storage provider, so on .NET 8 it went into CAPI Machine Keys but on .NET 9 it went into CNG Machine Keys; but that'd require a reproing file to really investigate. (Or the info from the code snippets I shared) |
I will try to take a look at your code and execute with my certificatr to see a bit more whats happen |
I run your code with my certificat, it didn't give me so much informations
this is the result in .net9 give me extaly same results
How do you interpret these results ? |
@bartonjs @wfurt Just want to mention this issue also cause Reproduction StepsCalling RavenDB database by providing When create To load the certificate, the code I have save pfx in local disk then use After upgrade to .net 9
Expected behaviorHave the same behavior between .net8.0 and .net9.0 Actual behaviorthe code throw exception in .net9.0 Regression?yes, was working in .net8.0 Known WorkaroundsUse Configuration.net9, win-x64 Other informationIn .Net 9.0 host under live IIS with
|
|
I've got the same error with my code loading Apple Push Notification Service (APNS) certificate to communicate with APNS server. It worked fine in .NET 8, but started to fail right after switching my app to .NET 9. Like pointed in this post, it works when commenting the
|
Description
Hello guys, i have a piece of code in my app which call a soap service using wcf client and certificate authentification.
This code work fine in .net8.0, but after upgrading to .net9.0 it stopped working and finish with this exception "Credentials supplied to the package were not recognized".
I was wondering which change made in the framework can explain that.
this is the part where i configure the service
Interesting things : since .net 9.0, all call to ServicePointManager are flag obsolete. But i don't know it the error can come frome here. The "new X509Certificate2" is also obsolete but if i change for "X509CertificateLoader.LoadPkcs12" i still have same error.
This error is really annoying for me, it would be really a pain to come back in .net 8.0 as i already upgraded many client.
Unfortunately i can't give a repro project as there is very sensitives informations, especially the certificate used for connexion (but if its really necessary, i will check that).
Do you have some tips on how i can debug that or if you know which changes can explain that ?
This issue is high priority for me.
thanks !
Reproduction Steps
calling wcf service with client certificate authentication
Expected behavior
Have the same behavior wetween .net8.0 and .net9.0
Actual behavior
the code throw exception in .net9.0
Regression?
yes, was working in .net8.0
Known Workarounds
no workaround for now
Configuration
.net9, win-x64
Other information
No response
The text was updated successfully, but these errors were encountered: