Validation.PackageSigning.Core
This library is the shared foundation for all NuGet package-signing validation workers. It owns the message contracts exchanged over Azure Service Bus, the certificate blob-store abstraction, and a small set of cryptographic helpers that are consumed across every signing-related validation job.The assembly and root namespace differ from the folder name. The project builds as
NuGet.Jobs.Validation.PackageSigning (assembly: NuGet.Jobs.Validation.PackageSigning), not
Validation.PackageSigning.Core. Keep this in mind when resolving namespace collisions.Overview
Message Contracts
Versioned Service Bus message types (
SignatureValidationMessage,
CertificateValidationMessage) and their IBrokeredMessageSerializer implementations that
bridge the queue wire-format to strongly-typed C# objects.Certificate Store
ICertificateStore / CertificateStore — a blob-backed store that persists X.509 certificates
keyed by their SHA-256 thumbprint under the path sha256/<thumbprint>.cer.Signature Extensions
PackageSignatureExtensions.IsPromotable() — decides whether a validated signature should be
promoted to Valid or held in InGracePeriod based on certificate status freshness relative
to the signing timestamp.Crypto Helpers
X509Certificate2Extensions.ComputeSHA256Thumbprint() and
CryptographicAttributeObjectCollectionExtensions.FirstOrDefault() extend BCL types in their
own namespaces so they feel like native APIs.Role in the NuGetGallery Ecosystem
Package signing validation in NuGetGallery is decomposed into several independently-deployed jobs that communicate through Azure Service Bus queues:- A signature validation job receives a
SignatureValidationMessageand verifies that the.nupkgat the given URI is properly signed. - A certificate validation job receives a
CertificateValidationMessageand checks revocation / validity of the certificate identified byCertificateKey. - Both jobs read and write X.509 DER blobs through
ICertificateStore.
Key Files and Classes
| File | Class / Type | Purpose |
|---|---|---|
Messages/SignatureValidationMessage.cs | SignatureValidationMessage | Immutable message carrying PackageId, PackageVersion, NupkgUri, ValidationId, and RequireRepositorySignature flag. |
Messages/SignatureValidationMessageSerializer.cs | SignatureValidationMessageSerializer | Serializes/deserializes SignatureValidationMessage via BrokeredMessageSerializer<SignatureValidationMessageData1> using schema name SignatureValidationMessageData v1. |
Messages/CertificateValidationMessage.cs | CertificateValidationMessage | Immutable message carrying CertificateKey, ValidationId, RevalidateRevokedCertificate, and SendCheckValidator. |
Messages/CertificateValidationMessageSerializer.cs | CertificateValidationMessageSerializer | Serializes/deserializes CertificateValidationMessage via schema name CertificateValidationMessageData v1. |
Storage/ICertificateStore.cs | ICertificateStore | Async interface: ExistsAsync, LoadAsync, SaveAsync — all keyed on SHA-256 thumbprint. |
Storage/CertificateStore.cs | CertificateStore | Blob-backed implementation. Blobs are stored at sha256/<thumbprint>.cer (lowercase). Verifies thumbprint on every load. |
Configuration/CertificateStoreConfiguration.cs | CertificateStoreConfiguration | POCO bound via IOptions<T>: DataStorageAccount and ContainerName. |
Extensions/PackageSignatureExtensions.cs | PackageSignatureExtensions | IsPromotable() — grace-period logic comparing EndCertificate.StatusUpdateTime against the latest trusted timestamp. |
Extensions/X509Certificate2Extensions.cs | X509Certificate2Extensions | ComputeSHA256Thumbprint() — SHA-256 hash of RawData encoded as a lowercase hex string without separators. |
Extensions/CryptographicAttributeObjectCollectionExtensions.cs | CryptographicAttributeObjectCollectionExtensions | FirstOrDefault(oid) — OID-based linear search over a CryptographicAttributeObjectCollection. |
Dependencies
Internal Project References
| Project | Purpose |
|---|---|
Validation.Common.Job | Provides IValidator, IProcessor, IValidatorStateService, SubscriptionProcessorJob, and DI bootstrapping shared by all validation jobs. Itself references NuGet.Services.ServiceBus and NuGet.Services.Storage. |
NuGet Package References
This project carries no direct NuGet
PackageReference entries in its .csproj. All
framework and NuGet package dependencies are inherited transitively through
Validation.Common.Job, which brings in Autofac, Azure.Storage.Blobs,
Microsoft.Extensions.*, NuGet.Packaging, NuGet.Services.ServiceBus, and
NuGet.Services.Storage.Package (via Validation.Common.Job) | Role |
|---|---|
NuGet.Services.ServiceBus | IBrokeredMessageSerializer<T>, BrokeredMessageSerializer<T>, [Schema] attribute, IReceivedBrokeredMessage |
NuGet.Services.Storage | IStorage, StreamStorageContent used by CertificateStore |
Azure.Storage.Blobs | Underlying blob transport |
Autofac / Autofac.Extensions.DependencyInjection | DI container used by hosting jobs |
Microsoft.Extensions.Logging | ILogger<T> consumed by CertificateStore |
NuGet.Packaging | NuGet package model types |
Notable Patterns and Implementation Details
SHA-256 thumbprints only. The BCL
X509Certificate2.Thumbprint property returns a SHA-1
hash. This library explicitly replaces it with ComputeSHA256Thumbprint(), which hashes
RawData with SHA-256. Every storage path, log message, and equality check in this library uses
the SHA-256 variant. Mixing the two will cause silent misses in the certificate store.Grace-period promotion logic.
PackageSignatureExtensions.IsPromotable() returns false
(keep in grace period) unless every TrustedTimestamp.EndCertificate.StatusUpdateTime and — when
not revoked — the signature’s own EndCertificate.StatusUpdateTime are all strictly after the
latest signing timestamp. A revoked signing certificate short-circuits this check and is
considered already out of the grace period, because revocation data may be purged by CAs over
time and cannot be reverified.Target Framework
This project targetsnet472 only. It is not a multi-targeted library, despite the downstream
Validation.Common.Job supporting both net472 and netstandard2.1. Consumers on .NET Core or
.NET 5+ should not reference this project directly until it is retargeted.