Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit e4131d3

Browse files
committed
Update gallery certificate info in ProcessSignature job (#497)
Progress on NuGet/NuGetGallery#6183
1 parent 94dbf50 commit e4131d3

7 files changed

Lines changed: 331 additions & 73 deletions

File tree

src/Validation.Common.Job/LoggerDiagnosticsSource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private static IDictionary<string, string> GetProperties(
9191

9292
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
9393
{
94-
_logger.Log<TState>(logLevel, eventId, state, exception, formatter);
94+
_logger.Log(logLevel, eventId, state, exception, formatter);
9595
}
9696

9797
public bool IsEnabled(LogLevel logLevel)
@@ -101,7 +101,7 @@ public bool IsEnabled(LogLevel logLevel)
101101

102102
public IDisposable BeginScope<TState>(TState state)
103103
{
104-
return _logger.BeginScope<TState>(state);
104+
return _logger.BeginScope(state);
105105
}
106106

107107
/// <summary>

src/Validation.Common.Job/Validation.Common.Job.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
<Version>2.27.0</Version>
116116
</PackageReference>
117117
<PackageReference Include="NuGetGallery.Core">
118-
<Version>4.4.5-dev-35743</Version>
118+
<Version>4.4.5-dev-36354</Version>
119119
</PackageReference>
120120
<PackageReference Include="Serilog">
121121
<Version>2.5.0</Version>

src/Validation.Common.Job/Validation.Common.Job.nuspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<dependency id="NuGet.Services.Storage" version="2.27.0" />
2424
<dependency id="NuGet.Services.Validation" version="2.27.0" />
2525
<dependency id="NuGet.Services.Validation.Issues" version="2.27.0" />
26-
<dependency id="NuGetGallery.Core" version="4.4.5-dev-35743" />
26+
<dependency id="NuGetGallery.Core" version="4.4.5-dev-36354" />
2727
<dependency id="Serilog" version="2.5.0" />
2828
<dependency id="System.Net.Http" version="4.3.3" />
2929
</dependencies>

src/Validation.PackageSigning.ProcessSignature/ProcessSignatureConfiguration.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,14 @@ public class ProcessSignatureConfiguration
2626
/// is in test mode and repository signed packages are not published.
2727
/// </summary>
2828
public bool CommitRepositorySignatures { get; set; }
29+
30+
/// <summary>
31+
/// The maximum length of a subject or issuer string to save to the gallery database. If a processed author
32+
/// certificate has an issuer or subject distinguished name or
33+
/// <see cref="System.Security.Cryptography.X509Certificates.X509NameType.SimpleName"/> that is longer than this
34+
/// value, null is stored in the database. We use 4000 since this is the maximum length for many package fields,
35+
/// such as description.
36+
/// </summary>
37+
public int MaxCertificateStringLength { get; set; } = 4000;
2938
}
3039
}

src/Validation.PackageSigning.ProcessSignature/SignaturePartsExtractor.cs

Lines changed: 91 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,36 @@
55
using System.Collections.Generic;
66
using System.Data.Entity;
77
using System.Linq;
8+
using System.Security.Cryptography.X509Certificates;
89
using System.Threading;
910
using System.Threading.Tasks;
1011
using Microsoft.Extensions.Logging;
1112
using Microsoft.Extensions.Options;
1213
using NuGet.Jobs.Validation.PackageSigning.Storage;
1314
using NuGet.Packaging.Signing;
1415
using NuGet.Services.Validation;
16+
using NuGetGallery;
1517

1618
namespace NuGet.Jobs.Validation.PackageSigning.ProcessSignature
1719
{
1820
public class SignaturePartsExtractor : ISignaturePartsExtractor
1921
{
2022
private readonly ICertificateStore _certificateStore;
21-
private readonly IValidationEntitiesContext _entitiesContext;
23+
private readonly IValidationEntitiesContext _validationEntitiesContext;
24+
private readonly IEntitiesContext _galleryEntitiesContext;
2225
private readonly IOptionsSnapshot<ProcessSignatureConfiguration> _configuration;
2326
private readonly ILogger<SignaturePartsExtractor> _logger;
2427

2528
public SignaturePartsExtractor(
2629
ICertificateStore certificateStore,
27-
IValidationEntitiesContext entitiesContext,
30+
IValidationEntitiesContext validationEntitiesContext,
31+
IEntitiesContext galleryEntitiesContext,
2832
IOptionsSnapshot<ProcessSignatureConfiguration> configuration,
2933
ILogger<SignaturePartsExtractor> logger)
3034
{
3135
_certificateStore = certificateStore ?? throw new ArgumentNullException(nameof(certificateStore));
32-
_entitiesContext = entitiesContext ?? throw new ArgumentNullException(nameof(entitiesContext));
36+
_validationEntitiesContext = validationEntitiesContext ?? throw new ArgumentNullException(nameof(validationEntitiesContext));
37+
_galleryEntitiesContext = galleryEntitiesContext ?? throw new ArgumentNullException(nameof(galleryEntitiesContext));
3338
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
3439
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
3540
}
@@ -53,8 +58,80 @@ public async Task ExtractAsync(int packageKey, PrimarySignature primarySignature
5358
await SaveCertificatesToStoreAsync(context);
5459

5560
// Commit the database changes.
56-
await _entitiesContext.SaveChangesAsync();
61+
await _validationEntitiesContext.SaveChangesAsync();
62+
63+
// Update certificate information in the gallery.
64+
await UpdateCertificateInformationAsync(context);
65+
}
66+
}
67+
68+
private async Task UpdateCertificateInformationAsync(Context context)
69+
{
70+
// Only operate on author signatures. Packages that only have a repository signature are not interesting
71+
// for this purpose.
72+
if (context.PrimarySignature.Type != SignatureType.Author)
73+
{
74+
return;
5775
}
76+
77+
var hashedCertificate = context
78+
.Author
79+
.Certificates
80+
.SignatureEndCertificate;
81+
var certificate = hashedCertificate.Certificate;
82+
var thumbprint = hashedCertificate.Thumbprint;
83+
84+
// Fetch the certificate record from the gallery database using the SHA-256 thumbprint.
85+
var certificateRecord = await _galleryEntitiesContext
86+
.Certificates
87+
.Where(c => c.Thumbprint == thumbprint)
88+
.FirstOrDefaultAsync();
89+
if (certificateRecord == null)
90+
{
91+
_logger.LogWarning(
92+
"No certificate record was found in the gallery database for thumbprint {Thumbprint}.",
93+
thumbprint);
94+
return;
95+
}
96+
97+
// Do nothing if the certificate details are already populated.
98+
var expiration = certificate.NotAfter.ToUniversalTime();
99+
var subject = NoLongerThanOrNull(certificate.Subject);
100+
var issuer = NoLongerThanOrNull(certificate.Issuer);
101+
var shortSubject = NoLongerThanOrNull(certificate.GetNameInfo(X509NameType.SimpleName, forIssuer: false));
102+
var shortIssuer = NoLongerThanOrNull(certificate.GetNameInfo(X509NameType.SimpleName, forIssuer: true));
103+
if (certificateRecord.Expiration == expiration
104+
&& certificateRecord.Subject == subject
105+
&& certificateRecord.Issuer == issuer
106+
&& certificateRecord.ShortSubject == shortSubject
107+
&& certificateRecord.ShortIssuer == shortIssuer)
108+
{
109+
return;
110+
}
111+
112+
// Save the certificate details to the gallery record.
113+
certificateRecord.Expiration = expiration;
114+
certificateRecord.Subject = subject;
115+
certificateRecord.Issuer = issuer;
116+
certificateRecord.ShortSubject = shortSubject;
117+
certificateRecord.ShortIssuer = shortIssuer;
118+
119+
await _galleryEntitiesContext.SaveChangesAsync();
120+
121+
_logger.LogInformation(
122+
"Gallery certificate information for certificate with thumbprint {Thumbprint} has been populated.",
123+
thumbprint);
124+
}
125+
126+
private string NoLongerThanOrNull(string input)
127+
{
128+
if (string.IsNullOrWhiteSpace(input) ||
129+
input.Length > _configuration.Value.MaxCertificateStringLength)
130+
{
131+
return null;
132+
}
133+
134+
return input;
58135
}
59136

60137
private static void ExtractSignaturesAndCertificates(Context context)
@@ -260,7 +337,7 @@ private async Task<PackageSignature> InitializePackageSignatureAsync(
260337
IReadOnlyDictionary<string, EndCertificate> thumbprintToEndCertificate,
261338
bool replacePackageSignature)
262339
{
263-
var packageSignatures = await _entitiesContext
340+
var packageSignatures = await _validationEntitiesContext
264341
.PackageSignatures
265342
.Include(x => x.TrustedTimestamps)
266343
.Include(x => x.EndCertificate)
@@ -309,10 +386,10 @@ private async Task<PackageSignature> InitializePackageSignatureAsync(
309386
// explicit and to facilitate unit testing, we explicitly remove them.
310387
foreach (var trustedTimestamp in packageSignature.TrustedTimestamps)
311388
{
312-
_entitiesContext.TrustedTimestamps.Remove(trustedTimestamp);
389+
_validationEntitiesContext.TrustedTimestamps.Remove(trustedTimestamp);
313390
}
314391

315-
_entitiesContext.PackageSignatures.Remove(packageSignature);
392+
_validationEntitiesContext.PackageSignatures.Remove(packageSignature);
316393

317394
packageSignature = InitializePackageSignature(
318395
packageKey,
@@ -356,7 +433,7 @@ private PackageSignature InitializePackageSignature(
356433
};
357434

358435
packageSignature.EndCertificateKey = packageSignature.EndCertificate.Key;
359-
_entitiesContext.PackageSignatures.Add(packageSignature);
436+
_validationEntitiesContext.PackageSignatures.Add(packageSignature);
360437

361438
return packageSignature;
362439
}
@@ -394,7 +471,7 @@ private void InitializeTrustedTimestamp(
394471
};
395472
trustedTimestamp.EndCertificateKey = trustedTimestamp.EndCertificate.Key;
396473
packageSignature.TrustedTimestamps.Add(trustedTimestamp);
397-
_entitiesContext.TrustedTimestamps.Add(trustedTimestamp);
474+
_validationEntitiesContext.TrustedTimestamps.Add(trustedTimestamp);
398475
}
399476
else
400477
{
@@ -454,7 +531,7 @@ private void ConnectCertificates(
454531
EndCertificate = endCertificateEntity,
455532
ParentCertificate = parentCertificateEntity,
456533
};
457-
_entitiesContext.CertificateChainLinks.Add(link);
534+
_validationEntitiesContext.CertificateChainLinks.Add(link);
458535
endCertificateEntity.CertificateChainLinks.Add(link);
459536
parentCertificateEntity.CertificateChainLinks.Add(link);
460537

@@ -476,7 +553,7 @@ private async Task<IReadOnlyDictionary<string, EndCertificate>> InitializeEndCer
476553

477554
// Find all of the end certificate entities that intersect with the set of certificates found in the
478555
// package that is currently being processed.
479-
var existingEntities = await _entitiesContext
556+
var existingEntities = await _validationEntitiesContext
480557
.EndCertificates
481558
.Include(x => x.CertificateChainLinks)
482559
.Where(x => thumbprints.Contains(x.Thumbprint))
@@ -495,7 +572,7 @@ private async Task<IReadOnlyDictionary<string, EndCertificate>> InitializeEndCer
495572
Thumbprint = certificateAndUse.Certificate.Thumbprint,
496573
CertificateChainLinks = new List<CertificateChainLink>(),
497574
};
498-
_entitiesContext.EndCertificates.Add(entity);
575+
_validationEntitiesContext.EndCertificates.Add(entity);
499576

500577
thumbprintToEntity[certificateAndUse.Certificate.Thumbprint] = entity;
501578
}
@@ -524,7 +601,7 @@ private async Task<IReadOnlyDictionary<string, ParentCertificate>> InitializePar
524601

525602
// Find all of the parent certificate entities that intersect with the set of certificates found in the
526603
// package that is currently being processed.
527-
var existingEntities = await _entitiesContext
604+
var existingEntities = await _validationEntitiesContext
528605
.ParentCertificates
529606
.Include(x => x.CertificateChainLinks)
530607
.Where(x => thumbprints.Contains(x.Thumbprint))
@@ -541,7 +618,7 @@ private async Task<IReadOnlyDictionary<string, ParentCertificate>> InitializePar
541618
Thumbprint = certificate.Thumbprint,
542619
CertificateChainLinks = new List<CertificateChainLink>(),
543620
};
544-
_entitiesContext.ParentCertificates.Add(entity);
621+
_validationEntitiesContext.ParentCertificates.Add(entity);
545622

546623
thumbprintToEntity[certificate.Thumbprint] = entity;
547624
}

0 commit comments

Comments
 (0)