Skip to content
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

Why the D value of RSA is different #102418

Open
Varorbc opened this issue May 19, 2024 · 13 comments
Open

Why the D value of RSA is different #102418

Varorbc opened this issue May 19, 2024 · 13 comments
Labels
area-System.Security question Answer questions and provide assistance, not an issue with source code or documentation. untriaged New issue has not been triaged by the area owner

Comments

@Varorbc
Copy link
Contributor

Varorbc commented May 19, 2024

var privatekeyBytes = Convert.FromBase64String("");

var rsa = RSA.Create(); rsa.ImportPkcs8PrivateKey(privatekeyBytes, out _);

var privateParameters = rsa.ExportParameters(true);
var d = Convert.ToBase64String(privateParameters.D!);

Why do RSA objects with the same key have different D values, I got the same D value on my local Windows computer as on another Linux computer, But I got a different D value on another Windows

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label May 19, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

@teo-tsirpanis teo-tsirpanis added the question Answer questions and provide assistance, not an issue with source code or documentation. label May 19, 2024
@bartonjs
Copy link
Member

There are two main ways to calculate D. One is based on the Carmichael's Totient of N, λ(N), called D-Lambda; the other (older) is based on the Euler's Totient of N, φ(N), called D-Phi. Both function, so they're equivalent (as far as the correctness of RSA is concerned).

When CNG imports a private key, it does the sanity tests that it needs, then it saves the parts it cares about. Modern RSA implementations use the Chinese Remainder Theorem (CRT) to speed up the computations, and the CRT doesn't ever use the D value (it uses P/Q/DP/DQ/InverseQ); so CNG doesn't actually remember what the D value is.

When exporting the key, CNG does the equivalent of "oh, crud, I need to know the D value to write it down in export. Well, I have P, Q, and E, and from those I can calculate D... so let's just calculate it again".

CNG uses the D-Lambda calculation; so if your original key used D-Phi then the D value will change.

If you're seeing a D-Phi be preserved on Windows then possibly you're using .NET Framework, where RSA.Create() returns an instance of RSACryptoServiceProvider. That class uses a different system cryptography library, which might preserve D.

We noticed this a long time ago, had the chat with Windows, and in the end decided that there was nothing sensible we could do about it. So our import/export tests tolerate D changing.

internal static void AssertKeyEquals(in RSAParameters expected, in RSAParameters actual)
{
Assert.Equal(expected.Modulus, actual.Modulus);
Assert.Equal(expected.Exponent, actual.Exponent);
Assert.Equal(expected.P, actual.P);
Assert.Equal(expected.DP, actual.DP);
Assert.Equal(expected.Q, actual.Q);
Assert.Equal(expected.DQ, actual.DQ);
Assert.Equal(expected.InverseQ, actual.InverseQ);
if (expected.D == null)
{
Assert.Null(actual.D);
}
else
{
Assert.NotNull(actual.D);
// If the value matched expected, take that as valid and shortcut the math.
// If it didn't, we'll test that the value is at least legal.
if (!expected.D.SequenceEqual(actual.D))
{
VerifyDValue(actual);
}
}
}

@Varorbc
Copy link
Contributor Author

Varorbc commented May 20, 2024

Thank you very much for your explanation, let me have a deeper understanding. I use the net6.0 target framework, but it has different values. I want to know more about it. Besides D-Phi, are there any other reasons that cause its values to change?Because the current situation is that the same code, the same key, is only the difference of the running environment, but the D value of two Windows is inconsistent, and the D value of one Windows is consistent with that of the other Linux.

@bartonjs
Copy link
Member

bartonjs commented May 20, 2024

What it really boils down to is "we send the values to the underlying cryptography libraries, and they send us back whatever they want". D-Phi vs D-Lambda is the usual reason, but there may be others.

Since it's not .NET that's actually changing the values, I can't really offer any more of a "why" than I already have.

@Varorbc
Copy link
Contributor Author

Varorbc commented May 21, 2024

The two Windows I mentioned before, one is Windows Server 2019 and the other is Windows 11, so they must have used different encryption underlying libraries.Do you know to whom I should consult this question further?

@bartonjs
Copy link
Member

I don't know of anyone that can answer that question with public contact, no.

Looking through history I see that we added the "tolerate it changing" code in 2018 f0d034c, and in 2019 I have an email thread asking "Hey, did you start being D-preserving all of a sudden?" to which they said basically "Yeah, because too many people complained. You know, like you did?"

If you're seeing a change between Windows Server 2019 and Win 10-current, then it probably changed in 19H1 or 19H2.

@Varorbc
Copy link
Contributor Author

Varorbc commented May 21, 2024

This is really a painful thing. Can you ask your leaders to investigate the reason with the Windows team?

If you're seeing a change between Windows Server 2019 and Win 10-current, then it probably changed in 19H1 or 19H2.

@danmoseley
Copy link
Member

@Varorbc Can you describe why it's painful in your scenarios?

@Varorbc
Copy link
Contributor Author

Varorbc commented May 21, 2024

@danmoseley My current scenario is that I need to generate PuttyKeyFile via RSA, and PuttyKeyFile uses the value D, my program is distributed, there are multiple servers, Windows and Linux, now they behave differently, and none of us know what caused it, we do not know what to do How to solve it.

@SteveSyfuhs
Copy link

Why is this differing behavior a problem though? The fact that it's different does not explain why that makes it painful. Why do you need to solve it? Why does a changing D value cause you problems?

@Varorbc
Copy link
Contributor Author

Varorbc commented May 21, 2024

@danmoseley My current scenario is that I need to generate PuttyKeyFile via RSA, and PuttyKeyFile uses the value D, my program is distributed, there are multiple servers, Windows and Linux, now they behave differently, and none of us know what caused it, we do not know what to do How to solve it.

How can I solve this problem? The point is that you don't even know why.

@bartonjs
Copy link
Member

The D value being different doesn't matter to the RSA algorithm, they both should produce an equivalent result.

If something's using a hash of the key file, then the different bytes would be a problem, sure. In that case, it seems like upgrading to a newer version of Windows will solve the problem (based on the 2019 email that I forgot about, suggesting Windows changed to D-Preserving then). Or you can try using RSACryptoServiceProvider and see if you get more amenable results.

@Varorbc
Copy link
Contributor Author

Varorbc commented May 22, 2024

In that case, it seems like upgrading to a newer version of Windows will solve the problem (based on the 2019 email that I forgot about, suggesting Windows changed to D-Preserving then).

The newer version is more new? Can you tell me the specific version?

Or you can try using RSACryptoServiceProvider and see if you get more amenable results.

I tried it, but the results were still different

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Security question Answer questions and provide assistance, not an issue with source code or documentation. untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

5 participants