Skip to content

Switch to dynamic cert gen for tests #39685

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

Merged
merged 7 commits into from
Mar 25, 2022
Merged

Switch to dynamic cert gen for tests #39685

merged 7 commits into from
Mar 25, 2022

Conversation

HaoK
Copy link
Member

@HaoK HaoK commented Jan 21, 2022

Fixes #32813 and should get us out of the business of expiring test certs

Will address the more complicated cert chain in a later PR (most likely next build ops) that's already tracked via #39669

@ghost ghost added the area-auth Includes: Authn, Authz, OAuth, OIDC, Bearer label Jan 21, 2022
@HaoK
Copy link
Member Author

HaoK commented Jan 26, 2022

Hey @bartonjs I finally got around to trying the snippet you suggested for us to dynamically generate certs in: #32806 (comment)

Everything seems mostly fine except for one case where Ubuntu is seeming to allow an expired self signed cert: https://dev.azure.com/dnceng/public/_build/results?buildId=1566042&view=ms.vss-test-web.build-test-results-tab&runId=44083042&resultId=106439&paneView=debug

This seems weird as its not failing on Mac or Windows, are there any known issues with these apis on linux?

I'm also not sure if the old cert matches exactly what the new cert does, does passing in no/null eku end up creating a cert with both client/server eku?

I guess we have always had issues on ubuntu, since there's another test that was already disabled:

[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/32813")]

Existing selfSignedNoEkuExpired cert details:
image

@HaoK
Copy link
Member Author

HaoK commented Jan 27, 2022

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@bartonjs
Copy link
Member

I'm also not sure if the old cert matches exactly what the new cert does, does passing in no/null eku end up creating a cert with both client/server eku?

Except for specific specs that say otherwise (such as RFC 3161 trusted timestamping), a cert that has no EKU extension is treated as supporting all usages. Since TLS doesn't have such a disclaimer, yes, a cert with no EKU extension at all is valid as both a client cert and a server cert

Everything seems mostly fine except for one case where Ubuntu is seeming to allow an expired self signed cert:

Weird. We certainly have a lot of expiration related tests in X509Chain, and I can't think of anywhere that we'd have a behavior like that.

Comment on lines 979 to 981
SignedClient = MakeCert(
"CN=Valid Signed Client,OU=dev,DC=idunno-dev,DC=org",
ClientEku,
now);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the name, and usage, I'd expect that SignedClient is supposed to be issued by SelfSignedPrimaryRoot (or maybe SelfSignedSecondaryRoot?). But it's just another self-signed cert.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right:

image

So would it be correct for me to just slap on a "issuer CN=Valid Signed Secondary Root" to match the old cert?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. You'll have to, for this cert, replace return request.CreateSelfSigned(notBefore, notAfter);

Span<byte> serial = stackalloc byte[19];
RandomNumberGenerator.Fill(serial);

using (X509Certificate2 pubOnly = request.Create(SelfSignedSecondaryRoot, notBefore, notAfter, serial))
{
    return pubOnly.CopyWithPrivateKey(key);
}

So, something like

        private static X509Certificate2 MakeCert(
            string subjectName,
            string eku,
            DateTimeOffset notBefore,
            DateTimeOffset notAfter,
            X509Certificate2 issuerCert = null)
        {
            using (var key = RSA.Create(2048))
            {
                CertificateRequest request = new CertificateRequest(
                    subjectName,
                    key,
                    HashAlgorithmName.SHA256,
                    RSASignaturePadding.Pkcs1);

                if (eku != null)
                {
                    request.CertificateExtensions.Add(
                        new X509EnhancedKeyUsageExtension(
                            new OidCollection { new Oid(eku, null) }, false));
                }

                if (issuerCert == null)
                {
                    return request.CreateSelfSigned(notBefore, notAfter);
                }
                else
                {
                    Span<byte> serial = stackalloc byte[19];
                    RandomNumberGenerator.Fill(serial);

                    using (X509Certificate2 pubOnly = request.Create(SelfSignedSecondaryRoot, notBefore, notAfter, serial))
                    {
                        return pubOnly.CopyWithPrivateKey(key);
                    }
               }
        }

But you'll have to also create SelfSignedSecondaryRoot as a valid CA cert. (An X509BasicConstraintsExtension where isCa is true, and the key usages needs to include cert signing)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I will give that a shot, but this leads me to ask, are we missing some tests to exercise these kinds of subtle cert differences, or it doesn't really matter to us since at our layer we are just passing the certs. Basically if no tests fail without the issuer in the old cert, maybe we should either add tests to verify this, or don't bother specifying it... cc @blowdart for his thoughts as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean obviously switch away from my old cert names :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well... none of your certs here have valid CN/SAN entries to be used as a TLS server, so there is clearly some custom validation going on wherever these certs are used. I don't see where it's happening, so it's hard to say what's what.

public async Task VerifyValidClientCertWithAdditionalCertificatesAuthenticates()
{
using var host = await CreateHost(
new CertificateAuthenticationOptions
{
Events = successfulValidationEvents,
ChainTrustValidationMode = X509ChainTrustMode.CustomRootTrust,
CustomTrustStore = new X509Certificate2Collection() { Certificates.SelfSignedPrimaryRoot, },
AdditionalChainCertificates = new X509Certificate2Collection() { Certificates.SignedSecondaryRoot },
RevocationMode = X509RevocationMode.NoCheck
}, Certificates.SignedClient);
using var server = host.GetTestServer();
var response = await server.CreateClient().GetAsync("https://example.com/");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

certainly looks like it should have failed in your current configuration (and the current configuration with checked-in certs... unless SelfSignedSecondaryRoot is actually a child issuing authority of SelfSignedPrimaryRoot?). It feels like it should only pass when the server cert chains up to SelfSignedPrimaryRoot.

Of course, that test is disabled, but should presumably have had the Skip removed in this PR since it's addressing the issue of expired certs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, for some reason only VerifyExpiredSelfSignedFails and VerifyNotYetValidSelfSignedFails fail but only on Ubuntu right now

@HaoK HaoK marked this pull request as ready for review March 24, 2022 20:08
@HaoK HaoK requested review from Tratcher and Pilchie as code owners March 24, 2022 20:08
@HaoK HaoK requested review from sebastienros and wtgodbe March 24, 2022 20:09
@HaoK
Copy link
Member Author

HaoK commented Mar 24, 2022

This gets us most of the way there by deleting the certs, there's still some tests skipped on ubuntu and mac, but we don't have any specific platform code that we need to worry about (and the tests are already skipped today) so this is still an improvement

@HaoK HaoK requested a review from BrennanConroy as a code owner March 24, 2022 21:30
@HaoK HaoK merged commit 5e60992 into main Mar 25, 2022
@HaoK HaoK deleted the haok/certup branch March 25, 2022 16:09
@ghost ghost added this to the 7.0-preview3 milestone Mar 25, 2022
@dougbu dougbu modified the milestones: 7.0-preview3, 7.0-preview4 Apr 5, 2022
halter73 pushed a commit that referenced this pull request May 20, 2024
wtgodbe pushed a commit that referenced this pull request Jun 4, 2024
* [release/6.0] Switch to dynamic cert gen for tests

- Cherry-picked from #39685

* Remove non-existing project from AspNetCore.sln

* Log ClientCertificateAuthenticationTests

---------

Co-authored-by: Hao Kung <HaoK@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-auth Includes: Authn, Authz, OAuth, OIDC, Bearer
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update cert auth tests to generate certs dynamically instead
5 participants