Skip to content

Commit e6bbaec

Browse files
authored
Test: Replace BouncyCastle with System.Formats.Asn1 (#10368)
Resolve NuGet/Engineering#5812.
1 parent 388e253 commit e6bbaec

15 files changed

Lines changed: 332 additions & 243 deletions

Directory.Packages.props

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="8.0.0" />
8282
<PackageVersion Include="Microsoft.Identity.Client" Version="4.65.0" />
8383
<PackageVersion Include="Microsoft.Identity.Web" Version="3.6.2" />
84+
<PackageVersion Include="Microsoft.Internal.NuGet.Testing.SignedPackages" Version="6.13.2-rc.1" />
8485
<PackageVersion Include="Microsoft.Net.Http" Version="2.2.29" />
8586
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
8687
<PackageVersion Include="Microsoft.Owin.Host.SystemWeb" Version="4.2.2" />
@@ -136,16 +137,17 @@
136137
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
137138
<PackageVersion Include="System.Diagnostics.Debug" Version="4.3.0" />
138139
<PackageVersion Include="System.Drawing.Common" Version="9.0.0" />
139-
<PackageVersion Include="System.Formats.Asn1" Version="8.0.1" />
140+
<PackageVersion Include="System.Formats.Asn1" Version="8.0.2" />
140141
<PackageVersion Include="System.Linq.Expressions" Version="4.3.0" />
141142
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
142143
<PackageVersion Include="System.Reflection.Metadata" Version="1.7.0-preview1-26717-04" />
143144
<PackageVersion Include="System.Runtime" Version="4.3.1" />
145+
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="8.0.1" />
146+
<PackageVersion Include="System.Security.Cryptography.X509Certificates" Version="4.3.2" />
144147
<PackageVersion Include="System.Text.Encodings.Web" Version="8.0.0" />
145148
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
146149
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
147150
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
148-
<PackageVersion Include="Test.Utility" Version="6.4.2-rc.25" />
149151
<PackageVersion Include="UAParser" Version="3.1.44" />
150152
<PackageVersion Include="WebActivatorEx" Version="2.0.6" />
151153
<PackageVersion Include="WebGrease" Version="1.6.0" />

NuGet.config

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<configuration>
33
<packageSources>
44
<clear />
5+
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
56
<add key="NuGet.org" value="https://api.nuget.org/v3/index.json" />
67
<add key="nuget-build" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/nuget-build/nuget/v3/index.json" />
78
</packageSources>
@@ -10,6 +11,9 @@
1011
</disabledPackageSources>
1112
<packageSourceMapping>
1213
<clear />
14+
<packageSource key="dotnet-tools">
15+
<package pattern="Microsoft.Internal.NuGet.Testing.SignedPackages" />
16+
</packageSource>
1317
<packageSource key="NuGet.org">
1418
<package pattern="Antlr" />
1519
<package pattern="AngleSharp.*" />
@@ -78,7 +82,6 @@
7882
<package pattern="NuGet.Services.*" />
7983
<package pattern="NuGet.StrongName.*" />
8084
<package pattern="Strathweb.CacheOutput.WebApi2.StrongName" />
81-
<package pattern="Test.Utility" />
8285
</packageSource>
8386
<packageSource key="nuget-server-upstreams">
8487
<package pattern="Microsoft.Internal.*" />

tests/Validation.PackageSigning.Core.Tests/Support/CertificateIntegrationTestFixture.cs

Lines changed: 52 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,13 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Security.Cryptography;
56
using System.Security.Cryptography.X509Certificates;
67
using System.Threading.Tasks;
8+
using Microsoft.Internal.NuGet.Testing.SignedPackages;
79
using NuGet.Common;
8-
using Org.BouncyCastle.Asn1.X509;
9-
using Org.BouncyCastle.Crypto.Parameters;
10-
using Org.BouncyCastle.Security;
11-
using Org.BouncyCastle.X509;
12-
using Org.BouncyCastle.X509.Extension;
13-
using Test.Utility.Signing;
1410
using TestUtil;
1511
using Xunit;
16-
using BCCertificate = Org.BouncyCastle.X509.X509Certificate;
1712

1813
namespace Validation.PackageSigning.Core.Tests.Support
1914
{
@@ -38,7 +33,7 @@ public CertificateIntegrationTestFixture()
3833
{
3934
Assert.True(
4035
UserHelper.IsAdministrator(),
41-
$"This test must be executing with administrator privileges since it installs a trusted root. Add {UserHelper.EnableSkipVariableName} environment variable to skip this test.");
36+
$"This test must be executing with administrator privileges since it installs a trusted root. Add {UserHelper.EnableSkipVariableName} environment variable to skip this test.");
4237
_testServer = new AsyncLazy<SigningTestServer>(SigningTestServer.CreateAsync);
4338
_rootCertificateAuthority = new AsyncLazy<CertificateAuthority>(CreateDefaultTrustedRootCertificateAuthorityAsync);
4439
_certificateAuthority = new AsyncLazy<CertificateAuthority>(CreateDefaultTrustedCertificateAuthorityAsync);
@@ -88,7 +83,7 @@ private async Task<CertificateAuthority> CreateDefaultTrustedRootCertificateAuth
8883
{
8984
var testServer = await GetTestServerAsync();
9085
var rootCa = CertificateAuthority.Create(testServer.Url);
91-
var rootCertificate = rootCa.Certificate.ToX509Certificate2();
86+
var rootCertificate = new X509Certificate2(rootCa.Certificate);
9287

9388
_trustedRoot = new TrustedTestCert<X509Certificate2>(
9489
rootCertificate,
@@ -138,10 +133,10 @@ private async Task<X509Certificate2> CreateDefaultTrustedSigningCertificateAsync
138133

139134
public X509Certificate2 CreateSigningCertificate(CertificateAuthority ca)
140135
{
141-
void CustomizeAsSigningCertificate(X509V3CertificateGenerator generator)
136+
void CustomizeAsSigningCertificate(CertificateRequest certificateRequest)
142137
{
143-
generator.AddSigningEku();
144-
generator.AddAuthorityInfoAccess(ca, addOcsp: true, addCAIssuers: true);
138+
certificateRequest.AddSigningEku();
139+
certificateRequest.AddAuthorityInfoAccess(ca, addOcsp: true, addCAIssuers: true);
145140
}
146141

147142
return IssueCertificate(ca, "Signing", CustomizeAsSigningCertificate).certificate;
@@ -151,49 +146,39 @@ public async Task<X509Certificate2> CreateUntrustedRootSigningCertificateAsync()
151146
{
152147
var options = IssueCertificateOptions.CreateDefaultForRootCertificateAuthority();
153148

154-
options.CustomizeCertificate = (X509V3CertificateGenerator generator) =>
149+
options.CustomizeCertificate = (CertificateRequest certificateRequest) =>
155150
{
156-
generator.AddExtension(
157-
X509Extensions.SubjectKeyIdentifier,
158-
critical: false,
159-
extensionValue: new SubjectKeyIdentifierStructure(options.KeyPair.Public));
160-
generator.AddExtension(
161-
X509Extensions.BasicConstraints,
162-
critical: true,
163-
extensionValue: new BasicConstraints(cA: true));
164-
generator.AddExtension(
165-
X509Extensions.KeyUsage,
166-
critical: true,
167-
extensionValue: new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.KeyCertSign | KeyUsage.CrlSign));
168-
generator.AddSigningEku();
151+
certificateRequest.CertificateExtensions.Add(
152+
new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, critical: false));
153+
certificateRequest.CertificateExtensions.Add(
154+
new X509BasicConstraintsExtension(certificateAuthority: true, hasPathLengthConstraint: false, pathLengthConstraint: 0, critical: true));
155+
certificateRequest.CertificateExtensions.Add(
156+
new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, critical: true));
157+
certificateRequest.AddSigningEku();
169158
};
170159

171160
var testServer = await GetTestServerAsync();
172161
var rootCa = CertificateAuthority.Create(testServer.Url, options);
173162

174-
var certificate = rootCa.Certificate.ToX509Certificate2();
175-
176-
certificate.PrivateKey = DotNetUtilities.ToRSA(options.KeyPair.Private as RsaPrivateCrtKeyParameters);
177-
178-
return certificate;
163+
return rootCa.Certificate;
179164
}
180165

181166
public async Task<RevokableCertificate> CreateRevokableSigningCertificateAsync()
182167
{
183168
var ca = await GetCertificateAuthorityAsync();
184169

185-
void CustomizeAsSigningCertificate(X509V3CertificateGenerator generator)
170+
void CustomizeAsSigningCertificate(CertificateRequest certificateRequest)
186171
{
187-
generator.AddSigningEku();
188-
generator.AddAuthorityInfoAccess(ca, addOcsp: true, addCAIssuers: true);
172+
certificateRequest.AddSigningEku();
173+
certificateRequest.AddAuthorityInfoAccess(ca, addOcsp: true, addCAIssuers: true);
189174
}
190175

191176
var issued = IssueCertificate(ca, "Revoked Signing", CustomizeAsSigningCertificate);
192177
var revocationDate = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromHours(1));
193178

194179
void Revoke()
195180
{
196-
ca.Revoke(issued.publicCertificate, RevocationReason.Unspecified, revocationDate);
181+
ca.Revoke(issued.publicCertificate, X509RevocationReason.Unspecified, revocationDate);
197182
}
198183

199184
Task WaitForResponseExpirationAsync()
@@ -211,7 +196,7 @@ public async Task<UntrustedSigningCertificate> CreateUntrustedSigningCertificate
211196
{
212197
var testServer = await GetTestServerAsync();
213198
var untrustedRootCa = CertificateAuthority.Create(testServer.Url);
214-
var untrustedRootCertificate = untrustedRootCa.Certificate.ToX509Certificate2();
199+
var untrustedRootCertificate = new X509Certificate2(untrustedRootCa.Certificate);
215200
var responders = testServer.RegisterRespondersForEntireChain(untrustedRootCa);
216201

217202
var certificate = CreateSigningCertificate(untrustedRootCa);
@@ -225,16 +210,16 @@ public async Task<X509Certificate2> CreateExpiringSigningCertificateAsync()
225210
{
226211
var ca = await GetCertificateAuthorityAsync();
227212

228-
void CustomizeExpiringSigningCertificate(X509V3CertificateGenerator generator)
213+
void CustomizeExpiringSigningCertificate(CertificateRequest certificateRequest)
229214
{
230-
generator.AddSigningEku();
231-
generator.AddAuthorityInfoAccess(ca, addOcsp: true, addCAIssuers: true);
232-
233-
generator.SetNotBefore(DateTime.UtcNow.AddSeconds(-2));
234-
generator.SetNotAfter(DateTime.UtcNow.AddSeconds(10));
215+
certificateRequest.AddSigningEku();
216+
certificateRequest.AddAuthorityInfoAccess(ca, addOcsp: true, addCAIssuers: true);
235217
}
236218

237-
var (@public, certificate) = IssueCertificate(ca, "Expired Signing", CustomizeExpiringSigningCertificate);
219+
DateTimeOffset notBefore = DateTimeOffset.UtcNow.AddSeconds(-2);
220+
DateTimeOffset notAfter = DateTimeOffset.UtcNow.AddSeconds(10);
221+
222+
var (@public, certificate) = IssueCertificate(ca, "Expired Signing", CustomizeExpiringSigningCertificate, notBefore, notAfter);
238223

239224
return certificate;
240225
}
@@ -255,7 +240,7 @@ public async Task<UntrustedTimestampService> CreateUntrustedTimestampServiceAsyn
255240
{
256241
var testServer = await GetTestServerAsync();
257242
var untrustedRootCa = CertificateAuthority.Create(testServer.Url);
258-
var untrustedRootCertificate = untrustedRootCa.Certificate.ToX509Certificate2();
243+
var untrustedRootCertificate = new X509Certificate2(untrustedRootCa.Certificate);
259244
var timestampService = TimestampService.Create(untrustedRootCa);
260245
var responders = testServer.RegisterDefaultResponders(timestampService);
261246

@@ -276,7 +261,7 @@ public async Task<RevokableTimestampService> CreateRevokableTimestampServiceAsyn
276261

277262
void Revoke()
278263
{
279-
rootCa.Revoke(timestampService.Certificate, RevocationReason.Unspecified, revocationDate);
264+
rootCa.Revoke(timestampService.Certificate, X509RevocationReason.Unspecified, revocationDate);
280265
}
281266

282267
Task WaitForResponseExpirationAsync()
@@ -304,10 +289,10 @@ IDisposable AddOcspResponder()
304289
return testServer.RegisterResponder(intermediateCa.OcspResponder);
305290
}
306291

307-
void CustomizeAsSigningCertificate(X509V3CertificateGenerator generator)
292+
void CustomizeAsSigningCertificate(CertificateRequest certificateRequest)
308293
{
309-
generator.AddSigningEku();
310-
generator.AddAuthorityInfoAccess(intermediateCa, addOcsp: true, addCAIssuers: true);
294+
certificateRequest.AddSigningEku();
295+
certificateRequest.AddAuthorityInfoAccess(intermediateCa, addOcsp: true, addCAIssuers: true);
311296
}
312297

313298
var issued = IssueCertificate(intermediateCa, "Signing Certificate With Unavailable Revocation", CustomizeAsSigningCertificate);
@@ -328,7 +313,7 @@ public async Task<TimestampServiceWithUnavailableRevocation> CreateTimestampServ
328313
{
329314
var testServer = await GetTestServerAsync();
330315
var rootCa = CertificateAuthority.Create(testServer.Url);
331-
var rootCertificate = rootCa.Certificate.ToX509Certificate2();
316+
var rootCertificate = new X509Certificate2(rootCa.Certificate);
332317

333318
var trust = new TrustedTestCert<X509Certificate2>(
334319
rootCertificate,
@@ -356,27 +341,31 @@ Task WaitForResponseExpirationAsync()
356341
disposable);
357342
}
358343

359-
protected (BCCertificate publicCertificate, X509Certificate2 certificate) IssueCertificate(
344+
protected (X509Certificate2 publicCertificate, X509Certificate2 certificate) IssueCertificate(
360345
CertificateAuthority ca,
361346
string name,
362-
Action<X509V3CertificateGenerator> customizeCertificate)
347+
Action<CertificateRequest> customizeCertificate,
348+
DateTimeOffset? notBefore = null,
349+
DateTimeOffset? notAfter = null)
363350
{
364-
var keyPair = SigningTestUtility.GenerateKeyPair(publicKeyLength: 2048);
365-
366-
var publicCertificate = ca.IssueCertificate(new IssueCertificateOptions
351+
using (RSA keyPair = SigningTestUtility.GenerateKeyPair(publicKeyLength: 2048))
367352
{
368-
CustomizeCertificate = customizeCertificate,
369-
NotAfter = DateTime.UtcNow.AddMinutes(10),
370-
NotBefore = DateTime.UtcNow.AddSeconds(-10),
371-
KeyPair = keyPair,
353+
notBefore ??= DateTimeOffset.UtcNow.AddSeconds(-10);
354+
notAfter ??= DateTimeOffset.UtcNow.AddMinutes(10);
372355

373-
SubjectName = new X509Name($"C=US,ST=WA,L=Redmond,O=NuGet,CN=NuGet Test ${name} Certificate ({Guid.NewGuid()})")
374-
});
356+
X509Certificate2 certificate = ca.IssueCertificate(new IssueCertificateOptions
357+
{
358+
CustomizeCertificate = customizeCertificate,
359+
NotAfter = notAfter.Value,
360+
NotBefore = notBefore.Value,
361+
KeyPair = keyPair,
362+
SubjectName = new X500DistinguishedName($"C=US,ST=WA,L=Redmond,O=NuGet,CN=NuGet Test ${name} Certificate ({Guid.NewGuid()})")
363+
});
375364

376-
var certificate = publicCertificate.ToX509Certificate2();
377-
certificate.PrivateKey = DotNetUtilities.ToRSA(keyPair.Private as RsaPrivateCrtKeyParameters);
365+
X509Certificate2 publicCertificate = new(certificate.RawData);
378366

379-
return (publicCertificate, certificate);
367+
return (publicCertificate, certificate);
368+
}
380369
}
381370

382371
private async Task<string> GetDefaultTrustedSigningCertificateThumbprintAsync()

tests/Validation.PackageSigning.Core.Tests/Support/ExtensionMethods.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,12 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Security.Cryptography.X509Certificates;
6-
using Test.Utility.Signing;
7-
using BCCertificate = Org.BouncyCastle.X509.X509Certificate;
5+
using Microsoft.Internal.NuGet.Testing.SignedPackages;
86

97
namespace Validation.PackageSigning.Core.Tests.Support
108
{
119
public static class ExtensionMethods
1210
{
13-
public static X509Certificate2 ToX509Certificate2(this BCCertificate certificate)
14-
{
15-
return new X509Certificate2(certificate.GetEncoded());
16-
}
17-
1811
public static DisposableList<IDisposable> RegisterResponders(
1912
this ISigningTestServer testServer,
2013
CertificateAuthority ca,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
#nullable enable
5+
6+
using System;
7+
using System.Formats.Asn1;
8+
using System.Security.Cryptography.X509Certificates;
9+
10+
namespace Microsoft.Internal.NuGet.Testing.SignedPackages
11+
{
12+
internal sealed class X509AuthorityInformationAccessExtension : X509Extension
13+
{
14+
private static readonly string AuthorityInfoAccess = "1.3.6.1.5.5.7.1.1";
15+
private static readonly string Ocsp = "1.3.6.1.5.5.7.48.1";
16+
private static readonly string CaIssuers = "1.3.6.1.5.5.7.48.2";
17+
18+
internal X509AuthorityInformationAccessExtension(Uri? ocspResponderUrl, Uri? caIssuersUrl)
19+
: base(AuthorityInfoAccess, Encode(ocspResponderUrl, caIssuersUrl), critical: false)
20+
{
21+
}
22+
23+
private static byte[] Encode(Uri? ocspResponderUrl, Uri? caIssuersUrl)
24+
{
25+
AsnWriter writer = new(AsnEncodingRules.DER);
26+
27+
using (writer.PushSequence())
28+
{
29+
if (ocspResponderUrl is not null)
30+
{
31+
using (writer.PushSequence())
32+
{
33+
writer.WriteObjectIdentifier(Ocsp);
34+
writer.WriteCharacterString(
35+
UniversalTagNumber.IA5String,
36+
ocspResponderUrl.OriginalString,
37+
new Asn1Tag(TagClass.ContextSpecific, tagValue: 6));
38+
}
39+
}
40+
41+
if (caIssuersUrl is not null)
42+
{
43+
using (writer.PushSequence())
44+
{
45+
writer.WriteObjectIdentifier(CaIssuers);
46+
writer.WriteCharacterString(
47+
UniversalTagNumber.IA5String,
48+
caIssuersUrl.OriginalString,
49+
new Asn1Tag(TagClass.ContextSpecific, tagValue: 6));
50+
}
51+
}
52+
}
53+
54+
return writer.Encode();
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)