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

Issue in ECDsaCertificateExtensions.CopyWithPrivateKey with TPM #75971

Closed
Fassino opened this issue Sep 21, 2022 · 4 comments · Fixed by #80558
Closed

Issue in ECDsaCertificateExtensions.CopyWithPrivateKey with TPM #75971

Fassino opened this issue Sep 21, 2022 · 4 comments · Fixed by #80558

Comments

@Fassino
Copy link

Fassino commented Sep 21, 2022

Description

When creating a Cng ECDSA private key in the TPM Cng and using CertificateRequest to generate a self signed certificate.

The implementation calls ECDsaCertificateExtensions.CopyWithPrivateKey which call Helpers.AreSamePublicECParameters(publicKey.ExportParameters(false), privateKey.ExportParameters(false))).
Unfortunately privateKey.ExportParameters returns a structure where the ECParameters.Curve.Oid.Value is not set.
That breaks AreSamePublicECParameters which returns false and raise an exception in CopyWithPrivateKey.

System.ArgumentException: The provided key does not match the public key for this certificate. (Parameter 'privateKey')
at System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions.CopyWithPrivateKey(X509Certificate2 certificate, ECDsa privateKey)
at System.Security.Cryptography.X509Certificates.CertificateRequest.CreateSelfSigned(DateTimeOffset notBefore, DateTimeOffset notAfter)

Reproduction Steps

        CngKeyCreationParameters keyParams = new()
        {
            Provider = new CngProvider("Microsoft Platform Crypto Provider"),
        };
        CngKey key = CngKey.Create(CngAlgorithm.ECDsaP384, "KeyInTPM", keyParams);
        ECDsaCng cngkey = new ECDsaCng(key);
        CertificateRequest request = new(
                subjectName,
                cngkey ,
                HashAlgorithmName.SHA256);
        X509Certificate2 selfSigned = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow);

Expected behavior

No exception.

Actual behavior

Exception

Regression?

No response

Known Workarounds

No know workaround

Configuration

Bug occurs on:
Windows 10 Pro wich Visual Studio 2022 and .net 5.0.
Windows 10 Pro wich Visual Studio 2022 and .net 6.0.

Other information

No response

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Sep 21, 2022
@ghost
Copy link

ghost commented Sep 21, 2022

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

Issue Details

Description

When creating a Cng ECDSA private key in the TPM Cng and using CertificateRequest to generate a self signed certificate.

The implementation calls ECDsaCertificateExtensions.CopyWithPrivateKey which call Helpers.AreSamePublicECParameters(publicKey.ExportParameters(false), privateKey.ExportParameters(false))).
Unfortunately privateKey.ExportParameters returns a structure where the ECParameters.Curve.Oid.Value is not set.
That breaks AreSamePublicECParameters which returns false and raise an exception in CopyWithPrivateKey.

System.ArgumentException: The provided key does not match the public key for this certificate. (Parameter 'privateKey')
at System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions.CopyWithPrivateKey(X509Certificate2 certificate, ECDsa privateKey)
at System.Security.Cryptography.X509Certificates.CertificateRequest.CreateSelfSigned(DateTimeOffset notBefore, DateTimeOffset notAfter)

Reproduction Steps

        CngKeyCreationParameters keyParams = new()
        {
            Provider = new CngProvider("Microsoft Platform Crypto Provider"),
        };
        CngKey key = CngKey.Create(CngAlgorithm.ECDsaP384, "KeyInTPM", keyParams);
        ECDsaCng cngkey = new ECDsaCng(key);
        CertificateRequest request = new(
                subjectName,
                cngkey ,
                HashAlgorithmName.SHA256);
        X509Certificate2 selfSigned = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow);

Expected behavior

No exception.

Actual behavior

Exception

Regression?

No response

Known Workarounds

No know workaround

Configuration

Bug occurs on:
Windows 10 Pro wich Visual Studio 2022 and .net 5.0.
Windows 10 Pro wich Visual Studio 2022 and .net 6.0.

Other information

No response

Author: Fassino
Assignees: -
Labels:

area-System.Security

Milestone: -

@vcsjones
Copy link
Member

I can reproduce it. Taking a look.

@vcsjones
Copy link
Member

This problem can be reduced to this:

#pragma warning disable CA1416
using System;
using System.Security.Cryptography;

CngKeyCreationParameters keyParams = new()
{
    Provider = new CngProvider("Microsoft Platform Crypto Provider"),
    KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey,
};

CngKey key = CngKey.Create(CngAlgorithm.ECDsaP384, "KeyInTPM-TestKey", keyParams);
ECDsaCng tpmEcdsa = new ECDsaCng(key);
ECDsaCng ephemeralEcdsa = new ECDsaCng(ECCurve.NamedCurves.nistP384);

var parameters = tpmEcdsa.ExportParameters(false);
var parameters2 = ephemeralEcdsa.ExportParameters(false);

Console.WriteLine(parameters.Curve.Oid.Value ?? "<null>");
Console.WriteLine(parameters2.Curve.Oid.Value ?? "<null>");

For the Software key, the OID has a Value. For the TPM key, the OID does not have a Value, it only has a FriendlyName. This becomes a problem here:

return (aCurve.Oid.Value == bCurve.Oid.Value && aCurve.Oid.FriendlyName == bCurve.Oid.FriendlyName);

Which is called from here:

using (ECDsa? publicKey = GetECDsaPublicKey(certificate))
{
if (publicKey == null)
throw new ArgumentException(SR.Cryptography_PrivateKey_WrongAlgorithm);
if (!Helpers.AreSamePublicECParameters(publicKey.ExportParameters(false), privateKey.ExportParameters(false)))

When we grab the ECDsa public key off of the certificate, there is a Value for the OID. But the TPM key does not have an OID Value, so the comparison fails.

@bartonjs I think we can relax this check a bit for Windows. I'll open a pull request for discussion.

@vcsjones
Copy link
Member

Actually, fixing that just uncovers another problem. ECDsa.KeySize reports 0 for TPM keys. This is a problem when we try to calculate the signature size.

ExportParameters(false);
fieldSizeBits = KeySize;
// This implementation of ECDsa doesn't set KeySize, we can't
if (fieldSizeBits == 0)
{
throw new NotSupportedException(SR.Cryptography_InvalidKeySize);

That can be reproduced with:

CngKey key = CngKey.Create(CngAlgorithm.ECDsaP384, "KeyInTPM", keyParams);
ECDsaCng cngkey = new ECDsaCng(key);
cngkey.SignData(new byte[48], HashAlgorithmName.SHA384, DSASignatureFormat.Rfc3279DerSequence);

Okay, but maybe we can fix that by using the ECCurve to derive the field size for known named curves. nistP256 is 256-bit, nistP384 is 384-bit, etc.

Then we get an error from CNG in the form of a CryptographicException saying "TPM 2.0: Structure is wrong size".

It seems like a number of things need to change for this to work.

@bartonjs bartonjs added this to the Future milestone Sep 28, 2022
@jeffschwMSFT jeffschwMSFT removed the untriaged New issue has not been triaged by the area owner label Oct 27, 2022
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jan 12, 2023
@vcsjones vcsjones modified the milestones: Future, 8.0.0 Jan 14, 2023
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jan 16, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Feb 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants