GroupDocs.Signature for .NET 26.9 Release Notes

GroupDocs.Signature for .NET 26.9 is a security release. It adds .NET 10 support and stops loading the external resources a document links to unless you allow them. PDF digital signatures now use SHA-256 by default and honour the requested hash algorithm, verifying a PDF digital signature checks it cryptographically, and signing with an expired or not-yet-valid certificate is rejected unless you allow it. Word documents can also be signed with post-quantum ML-DSA certificates.

Full List of Changes in This Release

KeyCategorySummary
SIGNATURENET-5938Feature.NET 10 support and .NET Standard 2.1 discontinued
SIGNATURENET-5950EnhancementExternal resources are no longer loaded by default
SIGNATURENET-5976EnhancementPDF digital signatures use SHA-256 by default and honour HashAlgorithm
SIGNATURENET-5943EnhancementSignatureFont no longer exposes System.Drawing types on .NET 6 and later
SIGNATURENET-5995EnhancementSigning with an expired or not-yet-valid certificate is rejected by default
SIGNATURENET-5996EnhancementSign Word documents with post-quantum (ML-DSA) certificates
SIGNATURENET-5978BugVerify did not check PDF digital signatures cryptographically
SIGNATURENET-5997BugSubjectName and IssuerName were ignored when verifying PDF documents
SIGNATURENET-5998BugTime-stamp server credentials were sent only when both user name and password were set
SIGNATURENET-5999BugSaving presentations and Word documents as images failed on Linux and macOS
SIGNATURENET-6000BugLoadExternalResources was ignored for documents inside archives
SIGNATURENET-6001BugExternal images and style sheets of SVG images were always loaded
SIGNATURENET-6002BugSearch and verification of a spreadsheet with a linked picture threw ArgumentNullException
SIGNATURENET-6003BugCertificateVerifyOptions.Expired was wrong by the local UTC offset
SIGNATURENET-6004BugSignatureSettings.LogLevel had no effect
SIGNATURENET-6005BugA reused VerifyOptions reported signatures of documents verified earlier
SIGNATURENET-6006BugA reused DigitalSignOptions signed later presentations with its first certificate and comment
SIGNATURENET-5979EnhancementInternal improvements

Features

.NET 10 support and .NET Standard 2.1 discontinued

GroupDocs.Signature now ships a build for .NET 10. As announced in the 26.6 release notes, it no longer ships a build for .NET Standard 2.1.

PackageTarget framework
GroupDocs.Signaturenet462; net6.0; net8.0; net10.0 (all frameworks)
GroupDocs.Signature.Net462.NET Framework 4.6.2
GroupDocs.Signature.Net60.NET 6.0
GroupDocs.Signature.Net80.NET 8.0
GroupDocs.Signature.Net100.NET 10.0 (new)

The GroupDocs.Signature.NetStandard21 package is discontinued, and the GroupDocs.Signature package no longer has a netstandard2.1 target. Applications for .NET 6, .NET 8 and .NET 10 get the matching build automatically. A library that targets netstandard2.1 and references GroupDocs.Signature must target one of the frameworks above instead.


Public API Changes

MemberChange
LoadOptions.SkipExternalResourcesNew. bool, default true. When true, external resources are not loaded, except those that match WhitelistedResources.
LoadOptions.WhitelistedResourcesNew. List<string>, default empty. Parts of addresses that may be loaded while SkipExternalResources is true. An address is loaded when it contains one of them, ignoring case. Setting null clears the list.
LoadOptions.LoadExternalResourcesObsolete. Still works, with the opposite meaning of SkipExternalResources. Its default changed from true to false. Using it produces compiler warning CS0618.
SignatureFont implicit conversion from System.Drawing.FontRemoved from the .NET 6, .NET 8 and .NET 10 builds. Obsolete in the .NET Framework build. Assign the SignatureFont properties instead.
SignOptions.HashAlgorithmNo signature change. It now takes effect for PDF digital signatures; before, it was ignored.
DigitalVerifyOptions.SubjectName, DigitalVerifyOptions.IssuerNameNo signature change. They now take effect for PDF documents; before, they were ignored there.
SignatureSettings.LogLevelNo signature change. It now takes effect: only messages of the levels it contains reach the logger, and LogLevel.None logs nothing. Before, every message was logged whatever the value. The descriptions of the LogLevel values were corrected.
VerificationResult.Succeeded, VerificationResult.TotalSignaturesNo signature change. When one VerifyOptions object is used for several Verify calls, they now list only the signatures of the document just verified.
DigitalSignOptions.AllowExpiredNew. bool, default false. When false, signing with a certificate whose validity period has ended throws GroupDocsSignatureException. When true, the document is signed and a warning is logged.
DigitalSignOptions.AllowNotYetValidNew. bool, default false. The same, for a certificate whose validity period has not started yet.

No other members were added, removed or changed. Projects that treat warnings as errors and set LoadExternalResources must switch to SkipExternalResources. Code that signs with an expired or not-yet-valid certificate on purpose must set AllowExpired or AllowNotYetValid to true.

BeforeNow
LoadExternalResources = falsenothing to set, or SkipExternalResources = true
LoadExternalResources = trueSkipExternalResources = false
nothing set, so everything was loadedSkipExternalResources = false to keep that, or a whitelist

Enhancements

External resources are no longer loaded by default

A document can refer to resources stored outside it. Loading such a resource means requesting the address written in the document. On a server that processes documents from other people, that is a server-side request forgery risk, and on an offline installation the request fails or waits for a time-out. GroupDocs.Signature now skips external resources unless you allow them.

What changed:

  • LoadOptions.SkipExternalResources is true by default.
  • The setting applies to word processing documents (linked images, INCLUDEPICTURE fields), presentations (linked pictures), spreadsheets (pictures linked to a file or an address), SVG images (images and style sheet imports) and documents inside archives.
  • Skipped resources are not drawn in page previews or in documents saved as images, and image, barcode and QR-code search does not see them. Embedded images are not affected, and a signed Word document keeps its links.
  • Hyperlinks are never followed, whatever the setting.

Allow only the addresses you trust:

LoadOptions loadOptions = new LoadOptions
{
    WhitelistedResources = new List<string> { "https://cdn.example.com/images/" }
};
using (Signature signature = new Signature("sample.docx", loadOptions))
{
    // Only images from https://cdn.example.com/images/ are loaded.
}

Load everything, for trusted documents only:

LoadOptions loadOptions = new LoadOptions { SkipExternalResources = false };
using (Signature signature = new Signature("trusted.docx", loadOptions))
{
    // Every external resource is loaded, as in earlier versions.
}

For every situation in which GroupDocs.Signature can reach the network, see Network access and data privacy.

PDF digital signatures use SHA-256 by default and honour HashAlgorithm

New PDF digital signatures were created with SHA-1, and SignOptions.HashAlgorithm had no effect. Both are fixed.

What changed:

BeforeNow
Digest with the default HashAlgorithm.AutoSHA-1SHA-256
HashAlgorithm set to Sha256, Sha384 or Sha512ignored, SHA-1honoured
Signature format (SubFilter)adbe.pkcs7.sha1adbe.pkcs7.detached
  • This applies whether the certificate comes from a file, a stream or a DigitalSignature.Certificate object.
  • A time stamp now uses the same digest as the signature. With HashAlgorithm.Auto it stays SHA-256, as before.
  • A custom hash signing function (ICustomSignHash) now receives the digest algorithm that was actually used, instead of the requested value, which could be Auto.
  • HashAlgorithm.Sha1 is still available when you request it explicitly. It is not recommended.
  • When you sign a PDF document and set HashAlgorithm on a signature type other than a digital signature, the setting still has no effect, and a warning is now written to the logger configured in SignatureSettings.
  • Documents signed with earlier versions are not affected and still verify.
using (Signature signature = new Signature("sample.pdf"))
{
    DigitalSignOptions options = new DigitalSignOptions("certificate.pfx")
    {
        Password = "1234567890",
        HashAlgorithm = HashAlgorithm.Sha384 // honoured from this release; the default is SHA-256
    };
    signature.Sign("signed.pdf", options);
}

SignatureFont no longer exposes System.Drawing types on .NET 6 and later

The implicit conversion from System.Drawing.Font to SignatureFont was the only public member that required the Windows-only System.Drawing.Common package. It is removed from the .NET 6, .NET 8 and .NET 10 builds, and marked obsolete in the .NET Framework build. Assign the properties instead; unlike the conversion, this also carries Strikeout:

SignatureFont font = new SignatureFont
{
    FamilyName = "Arial",
    Size = 12,
    Bold = true,
    Strikeout = false
};

Signing with an expired or not-yet-valid certificate is rejected by default

Signing with a certificate that has expired, or whose validity period has not started yet, succeeded silently, and the problem only showed later, when a validator reported the signature as not valid. Now Sign throws GroupDocsSignatureException instead, and nothing is signed or saved. The message names the certificate by subject and thumbprint, gives the date it expired or becomes valid, and says which property allows it.

To sign anyway, for example to test with an old certificate, set DigitalSignOptions.AllowExpired or DigitalSignOptions.AllowNotYetValid to true. The document is then signed, and a warning is written to the logger configured in SignatureSettings:

SignatureSettings settings = new SignatureSettings(new ConsoleLogger());
using (Signature signature = new Signature("sample.pdf", settings))
{
    DigitalSignOptions options = new DigitalSignOptions("certificate.pfx")
    {
        Password = "1234567890",
        AllowExpired = true
    };
    signature.Sign("signed.pdf", options);
    // If certificate.pfx has expired, the document is signed and the console shows a warning such as:
    // The signing certificate expired on 2019-05-01 12:00 UTC (subject "CN=...", thumbprint ...).
}

What is checked:

  • a certificate given as a file, a stream or a DigitalSignature.Certificate object;
  • for spreadsheets, also the certificate of the first DigitalVBA extension, governed by the same two properties. With SignOnlyVBAProject the spreadsheet itself is not signed, so its own certificate is not checked;
  • digital signatures of images use no certificate and are not checked.

The certificate is compared with the current time in UTC, not with DigitalSignature.SignTime. The two properties are independent: AllowExpired does not allow a certificate that is not valid yet.

Sign Word documents with post-quantum (ML-DSA) certificates

Word documents (DOCX, DOC, ODT and the other Word formats) can now be signed with a PFX certificate that holds a post-quantum ML-DSA key (ML-DSA-44, ML-DSA-65 or ML-DSA-87) on every supported platform. Before, signing failed with “Digital certificate for X509Certificate2 has wrong format” unless the operating system itself could import ML-DSA keys (Windows with the post-quantum update, or Linux with .NET 10 and OpenSSL 3.5). The API does not change: pass the PFX as you would any other certificate.

using (Signature signature = new Signature("sample.docx"))
{
    DigitalSignOptions options = new DigitalSignOptions("ml-dsa-65.pfx")
    {
        Password = "1234567890"
    };
    signature.Sign("signed.docx", options);
}

Verifying the signed document with the same PFX, or with its public certificate, works the same way. Expired and not-yet-valid ML-DSA certificates are rejected by default, like any other certificate.

Limits:

  • Only Word documents. PDF documents, spreadsheets and presentations cannot be signed with ML-DSA certificates yet.
  • There is no standard XML-DSig identifier for ML-DSA yet, so other applications, such as Microsoft Word, may not be able to validate these signatures.
  • Where the operating system cannot import the key, the Certificate of the returned DigitalSignature is the public certificate, without its private key.

Bug Fixes

Verify checks PDF digital signatures cryptographically

Verify with DigitalVerifyOptions compared only the criteria you set (dates, reason, location, contact, certificate). With no criteria, any PDF that contained a signature field was reported as valid, even if the document had been changed after signing. Verify now also checks each PDF digital signature cryptographically, and reports a signature as valid only if that check passes. Search already did this check.

SubjectName and IssuerName are honoured for PDF documents

DigitalVerifyOptions.SubjectName and IssuerName were applied to Word documents but ignored for PDF documents. They now filter PDF signatures the same way: the certificate’s subject or issuer must contain the value, case-sensitive.

Time-stamp server credentials

The user name and password of a time-stamp server were sent only when both were set. They are now sent when either is set, for servers that need only one of them.

Saving presentations and Word documents as images on Linux and macOS

Signing a presentation or a Word document with ExportImageSaveOptions threw InvalidCastException on Linux and macOS. It now works. Nothing changes on Windows.

Documents inside archives follow the archive’s settings

Documents inside a ZIP, TAR, 7z, GZ or LZ archive were always opened with default options, so LoadExternalResources = false did not apply to them. They now inherit the external-resource settings passed for the archive.

SVG images

External images and style sheets referenced by an SVG image were loaded whatever the setting. While external resources are skipped, GroupDocs.Signature now removes the references it will not load before it reads the image. An SVG image that is not well-formed XML cannot be checked, so it is rejected with GroupDocsSignatureException, and so is one whose styles take too long to check; set SkipExternalResources = false to process it.

CertificateVerifyOptions.Expired and the local time zone

When a certificate file (.pfx) was verified with CertificateVerifyOptions, the Expired flag compared the certificate’s expiry date, which is in local time, with the current time in UTC. Near the expiry date the result was wrong by the local UTC offset: east of UTC an expired certificate was reported as not expired, and west of UTC a valid one as expired. Both times are now compared in UTC.

Spreadsheets with linked pictures

QR-code and barcode search and verification of a spreadsheet that contains a picture linked to an external file threw ArgumentNullException. Such pictures are now skipped; in verification they do not count as valid.

SignatureSettings.LogLevel takes effect

SignatureSettings.LogLevel had no effect: every error, warning and trace reached the logger whatever the value. It now decides which kinds of messages are logged. The values are flags that can be combined, and LogLevel.None logs nothing. The default is still LogLevel.All, so nothing changes unless you set it.

SignatureSettings settings = new SignatureSettings(new ConsoleLogger())
{
    // Errors and warnings, without the step-by-step traces.
    LogLevel = LogLevel.Error | LogLevel.Warning
};

The level only filters the log: exceptions are thrown as before. The level does not change which certificates are rejected; it only decides whether the warning for an allowed expired or not-yet-valid certificate is logged.

VerifyOptions used for several Verify calls

A VerifyOptions object used for several Verify calls kept the signatures found by the earlier calls. So VerificationResult.Succeeded and TotalSignatures, and the list returned by the Verify overloads that take a predicate, also contained signatures of documents verified before. Each Verify call now reports only the document it verifies. For an archive, the result still lists the signatures of every document in the archive.

DigitalSignOptions used for several presentations

A DigitalSignOptions object used to sign several presentations kept the digital signature it built for the first one, so a certificate or comment changed between calls was ignored for presentations. Each Sign call now uses the certificate and comment the options have at that time. Presentations are always signed with the time of signing: DigitalSignature.SignTime is not applied to them.