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.
adbe.pkcs7.detached format instead of SHA-1. Verify reports a PDF digital signature as valid only if it passes a cryptographic check. Signing with an expired or not-yet-valid certificate throws GroupDocsSignatureException unless DigitalSignOptions.AllowExpired or AllowNotYetValid is set. SignatureSettings.LogLevel now takes effect: only the chosen kinds of messages are logged, and LogLevel.None logs nothing. .NET Standard 2.1 is no longer shipped: use the .NET 6, .NET 8, .NET 10 or .NET Framework 4.6.2 build. See the sections below for details.Full List of Changes in This Release
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.
| Package | Target framework |
|---|---|
GroupDocs.Signature | net462; 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
| Member | Change |
|---|---|
LoadOptions.SkipExternalResources | New. bool, default true. When true, external resources are not loaded, except those that match WhitelistedResources. |
LoadOptions.WhitelistedResources | New. 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.LoadExternalResources | Obsolete. 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.Font | Removed from the .NET 6, .NET 8 and .NET 10 builds. Obsolete in the .NET Framework build. Assign the SignatureFont properties instead. |
SignOptions.HashAlgorithm | No signature change. It now takes effect for PDF digital signatures; before, it was ignored. |
DigitalVerifyOptions.SubjectName, DigitalVerifyOptions.IssuerName | No signature change. They now take effect for PDF documents; before, they were ignored there. |
SignatureSettings.LogLevel | No 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.TotalSignatures | No signature change. When one VerifyOptions object is used for several Verify calls, they now list only the signatures of the document just verified. |
DigitalSignOptions.AllowExpired | New. 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.AllowNotYetValid | New. 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.
| Before | Now |
|---|---|
LoadExternalResources = false | nothing to set, or SkipExternalResources = true |
LoadExternalResources = true | SkipExternalResources = false |
| nothing set, so everything was loaded | SkipExternalResources = 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.SkipExternalResourcesistrueby 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:
| Before | Now | |
|---|---|---|
Digest with the default HashAlgorithm.Auto | SHA-1 | SHA-256 |
HashAlgorithm set to Sha256, Sha384 or Sha512 | ignored, SHA-1 | honoured |
| Signature format (SubFilter) | adbe.pkcs7.sha1 | adbe.pkcs7.detached |
- This applies whether the certificate comes from a file, a stream or a
DigitalSignature.Certificateobject. - A time stamp now uses the same digest as the signature. With
HashAlgorithm.Autoit 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 beAuto. HashAlgorithm.Sha1is still available when you request it explicitly. It is not recommended.- When you sign a PDF document and set
HashAlgorithmon a signature type other than a digital signature, the setting still has no effect, and a warning is now written to the logger configured inSignatureSettings. - 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.Certificateobject; - for spreadsheets, also the certificate of the first
DigitalVBAextension, governed by the same two properties. WithSignOnlyVBAProjectthe 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
Certificateof the returnedDigitalSignatureis 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.