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

Cms signature created with .Net Framework cannot be verified with .Net Core for an elliptic curve certificate #77377

Closed
jgustavs-tibco opened this issue Oct 24, 2022 · 6 comments · Fixed by #91183

Comments

@jgustavs-tibco
Copy link

Description

Link to a project to reproduce: https://github.com/jgustavs-tibco/cmssigner-incompatibility

If you use CmsSigner to create a signature in .Net Framework 4.8 with an ECC certificate then the signature cannot be verified using CmsSigner in .Net Core 6.0.

The signature is created as follows:

            using (var certificate = new X509Certificate2(CertificatePath, Password))
            {
                var cmsSigner = new CmsSigner(certificate);
                cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;
                var contentInfo = new ContentInfo(new Oid(PkcsObjectIdentifiersData), InputBytes);
                var signedCms = new SignedCms(SubjectIdentifierType.Unknown, contentInfo, true);
                signedCms.ComputeSignature(cmsSigner);
                File.WriteAllBytes(SignaturePath, signedCms.Encode());
            }

The signature is verified as follows.

                var contentInfo = new ContentInfo(InputBytes);
                var signedData = new SignedCms(contentInfo, true);
                signedData.Decode(signatureBytes);
                var signerInfos = signedData.SignerInfos;
                signedData.CheckSignature(true);

Note that it is possible to verify the signature in .Net Framework.

We have tried to generate the ECC certificate using .Net and using Java and it the verification fails in both cases. We have tried to reproduce the issue using RSA certificates. Then it works fine.

We noticed that the signature algorithm is SignedCms.SignerInfos is not the same if we try to generate the signature using .Net Framework and .Net Core which is not what we expected.

Reproduction Steps

Build the project in https://github.com/jgustavs-tibco/cmssigner-incompatibility and run test.bat.

This runs different combinations of .Net Framework and .Net Core and certificate key algorithms.

It gives the following output

Test create signature in .Net Framework. Verify in .Net Framework using elliptic curve.
 Verification ok
Test create signature in .Net Core. Verify in .Net Core using elliptic curve.
 Verification ok
Test create signature in .Net Framework. Verify in .Net Core using RSA.
 Verification ok
Test create signature in .Net Framework. Verify in .Net Core using elliptic curve.
 Verification failed
 Exception: SignerInfo digest algorithm '2.16.840.1.101.3.4.2.1' is not valid for signature algorithm ''.

Expected behavior

All combinations of .Net Framework and .Net Core and certificate key algorithms should work.

Actual behavior

The case where the signature is created in .Net Framework but verified in .Net Core does not work for ECC certificates.

Regression?

No response

Known Workarounds

No response

Configuration

We have tried .Net Framework 4.8 and .Net Core 6.0 on Windows.

Other information

No response

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

ghost commented Oct 24, 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

Link to a project to reproduce: https://github.com/jgustavs-tibco/cmssigner-incompatibility

If you use CmsSigner to create a signature in .Net Framework 4.8 with an ECC certificate then the signature cannot be verified using CmsSigner in .Net Core 6.0.

The signature is created as follows:

            using (var certificate = new X509Certificate2(CertificatePath, Password))
            {
                var cmsSigner = new CmsSigner(certificate);
                cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;
                var contentInfo = new ContentInfo(new Oid(PkcsObjectIdentifiersData), InputBytes);
                var signedCms = new SignedCms(SubjectIdentifierType.Unknown, contentInfo, true);
                signedCms.ComputeSignature(cmsSigner);
                File.WriteAllBytes(SignaturePath, signedCms.Encode());
            }

The signature is verified as follows.

                var contentInfo = new ContentInfo(InputBytes);
                var signedData = new SignedCms(contentInfo, true);
                signedData.Decode(signatureBytes);
                var signerInfos = signedData.SignerInfos;
                signedData.CheckSignature(true);

Note that it is possible to verify the signature in .Net Framework.

We have tried to generate the ECC certificate using .Net and using Java and it the verification fails in both cases. We have tried to reproduce the issue using RSA certificates. Then it works fine.

We noticed that the signature algorithm is SignedCms.SignerInfos is not the same if we try to generate the signature using .Net Framework and .Net Core which is not what we expected.

Reproduction Steps

Build the project in https://github.com/jgustavs-tibco/cmssigner-incompatibility and run test.bat.

This runs different combinations of .Net Framework and .Net Core and certificate key algorithms.

It gives the following output

Test create signature in .Net Framework. Verify in .Net Framework using elliptic curve.
 Verification ok
Test create signature in .Net Core. Verify in .Net Core using elliptic curve.
 Verification ok
Test create signature in .Net Framework. Verify in .Net Core using RSA.
 Verification ok
Test create signature in .Net Framework. Verify in .Net Core using elliptic curve.
 Verification failed
 Exception: SignerInfo digest algorithm '2.16.840.1.101.3.4.2.1' is not valid for signature algorithm ''.

Expected behavior

All combinations of .Net Framework and .Net Core and certificate key algorithms should work.

Actual behavior

The case where the signature is created in .Net Framework but verified in .Net Core does not work for ECC certificates.

Regression?

No response

Known Workarounds

No response

Configuration

We have tried .Net Framework 4.8 and .Net Core 6.0 on Windows.

Other information

No response

Author: jgustavs-tibco
Assignees: -
Labels:

area-System.Security, untriaged

Milestone: -

@vcsjones
Copy link
Member

vcsjones commented Oct 24, 2022

Okay, I think I see what is going on here. In .NET Framework, the signature algorithm is 1.2.840.10045.2.1 (EcPublicKey).

When we lookup the signature info here:

lookup.Add(Oids.EcPublicKey, new ECDsaCmsSignature(null, default));

We use a default hash algorithm name. When we compare the digest with the default digest of what we are expecting the comparison fails here:

I think the right fix here for maintaining compat with .NET Framework is to skip the hash algorithm check if we are using .NET Framework's signature algorithm identifier. Basically change the check to:

if (_expectedDigest != default(HashAlgorithmName) && _expectedDigest != digestAlgorithmName)

(or make _expectedDigest nullable, etc).

@bartonjs does this sound reasonable?

@bartonjs
Copy link
Member

if (_expectedDigest != default(HashAlgorithmName) && _expectedDigest != digestAlgorithmName)

seems like the right fix to me.

@vcsjones vcsjones self-assigned this Oct 24, 2022
@vcsjones
Copy link
Member

As a matter of an update to this, even once we get past the hash algorithm check, the digest that gets signed and verified differs between .NET Framework and .NET 6+. So this will take a little more time for me to understand exactly what .NET Framework is doing differently.

@vcsjones vcsjones removed their assignment Jun 6, 2023
@jeffhandley
Copy link
Member

jeffhandley commented Jun 28, 2023

I'm moving this to Future as we won't be able to further investigate it during .NET 8. I'm also going to label this as help wanted [up-for-grabs] Good issue for external contributors since more investigation is needed to get to a root cause and proposed fix for this issue.

@jeffhandley jeffhandley added the help wanted [up-for-grabs] Good issue for external contributors label Jun 28, 2023
@jeffhandley jeffhandley added this to the Future milestone Jun 28, 2023
@jeffhandley jeffhandley removed the untriaged New issue has not been triaged by the area owner label Jun 28, 2023
jborean93 added a commit to jborean93/runtime that referenced this issue Aug 27, 2023
Supports SignedCms signatured with an ECDSA key created by the Windows
API or .NET Framework. These signatures store an EC public key OID
rather than a hash specific ECDSA OID used in newer versions of dotnet.

Fixes dotnet#77377
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Aug 27, 2023
@jborean93
Copy link
Contributor

@vcsjones I tested out skipping the check if the EcPublicKey OID was in the signature and I found it worked just fine #91183.

As a matter of an update to this, even once we get past the hash algorithm check, the digest that gets signed and verified differs between .NET Framework and .NET 6+

Do you remember how you verified this? From testing I can generate a signature from both .NET and the Win32 API directly with an ECDSA key and the code is able to check the digest just fine. Even the test the PR adds is fine with the new changes.

I do have a user report with a problematic signature signed by signtool.exe where these changes do fail with the below stacktrace:

System.Security.Cryptography.CryptographicException: Invalid signature.
   at System.Security.Cryptography.Pkcs.SignerInfo.Verify(X509Certificate2Collection extraStore, X509Certificate2 certificate, Boolean verifySignatureOnly)
   at System.Security.Cryptography.Pkcs.SignerInfo.CheckSignature(X509Certificate2Collection extraStore, Boolean verifySignatureOnly)
   at System.Security.Cryptography.Pkcs.SignerInfo.CheckSignature(Boolean verifySignatureOnly)
...

I don't think they are related as even Windows fails to verify the signature but I've yet to figure out why

Untitled

@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Aug 28, 2023
github-actions bot pushed a commit that referenced this issue Aug 28, 2023
Supports SignedCms signatured with an ECDSA key created by the Windows
API or .NET Framework. These signatures store an EC public key OID
rather than a hash specific ECDSA OID used in newer versions of dotnet.

Fixes #77377
@vcsjones vcsjones removed the help wanted [up-for-grabs] Good issue for external contributors label Aug 29, 2023
@vcsjones vcsjones modified the milestones: Future, 9.0.0 Aug 29, 2023
carlossanlop pushed a commit that referenced this issue Aug 31, 2023
Supports SignedCms signatured with an ECDSA key created by the Windows
API or .NET Framework. These signatures store an EC public key OID
rather than a hash specific ECDSA OID used in newer versions of dotnet.

Fixes #77377

Co-authored-by: Jordan Borean <jborean93@gmail.com>
@vcsjones vcsjones modified the milestones: 9.0.0, 8.0.0 Aug 31, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Oct 1, 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.

5 participants