💡 Полный рабочий пример доступен на GitHub:
sign-word-with-ml-dsa-certificates-dotnet

Старый способ был планом проекта

Спросите, что нужно, чтобы сделать подпись документов постквантовой, и получите дорожную карту: оценить алгоритмы, выбрать библиотеку, написать слой абстракции над кодом подписи, спланировать период двойной подписи, заложить бюджет на квартал.

Большая часть этого всё ещё актуальна для организационной части — закупка сертификатов, политика, поддержка валидаторов. Кодовая часть оказалась меньше, чем предполагает дорожная карта, и это стоит знать, прежде чем кто‑то будет выделять на неё квартал бюджета.

ML-DSA подпись — это возможность GroupDocs.Signature для .NET, позволяющая подписывать Word‑документы сертификатами, основанными на FIPS 204, постквантовом стандарте подписи NIST. Она появилась в версии 26.9, и с точки зрения вызывающего кода это другой PFX‑файл.

Есть лучший способ

Вот весь изменённый код:

using var signature = new Signature(sourcePath);

var options = new DigitalSignOptions(pfxPath)
{
    Password = certificatePassword
};

SignResult result = signature.Sign(outputPath, options);

Это тот же вызов, что используется для RSA‑сертификата. Алгоритм является свойством сертификата, поэтому отдельный параметр не выбирается, слой абстракции не нужен, и второй путь кода не появляется на переходный период. Укажите DigitalSignOptions на ML‑DSA PFX, и результатом будет подпись ML‑DSA.

Считать сертификат обратно из результата имеет смысл, пока используется несколько сертификатов:

var created = result.Succeeded.OfType<DigitalSignature>().FirstOrDefault();
return created?.Certificate?.Subject ?? "(no certificate returned)";

Выбор уровня с числами вместо мнений

ML‑DSA поставляется в трёх наборах параметров, соответствующих категориям безопасности NIST 2, 3 и 5. Чем выше уровень, тем больше — и ключ, и подпись — и разумный способ решить, какой уровень нужен, — подписать свой документ три раза и посмотреть:

var levels = new Dictionary<string, string>
{
    ["ML-DSA-44"] = MlDsa44Pfx,
    ["ML-DSA-65"] = MlDsa65Pfx,
    ["ML-DSA-87"] = MlDsa87Pfx
};

Пример записывает одну подписанную копию для каждого уровня и фиксирует её размер, так что компромисс измеряется, а не берётся из таблицы спецификации. Для одного контракта разница незаметна; для архива из нескольких миллионов подписанных документов это вопрос ёмкости, который стоит задать до стандартизации на высшем уровне.

ML‑DSA‑65 — разумный вариант по умолчанию, если политика не предписывает иной. Профили вроде CNSA 2.0 явно указывают ML‑DSA‑87, а ML‑DSA‑44 имеет смысл только тогда, когда размер важнее запаса прочности.

Для проверки нужен только публичный сертификат

Сценарий распространения не изменился по сравнению с RSA, что является второй хорошей новостью:

var options = new DigitalVerifyOptions(certificatePath);
if (password != null)
{
    options.Password = password;
}

VerificationResult result = signature.Verify(options);

Получателю нужен только .cer подписанта и ничего больше. Результат считается валидным лишь тогда, когда подпись совпадает с содержимым, а сертификат совпадает по серийному номеру и отпечатку; поэтому документ, подписанный другим лицом, не проходит проверку — что демонстрирует пример, запуская проверку дважды: один раз с правильным сертификатом, один раз с чужим.

Сравнение: Ожидаемое vs. Фактическое

Что предполагает план миграции Что на самом деле требует 26.9
Code change слой абстракции над подписью другой путь к PFX
API surface новые постквантовые методы DigitalSignOptions, без изменений
Level selection конфигурация библиотеки какой сертификат загружать
Verification новые инструменты для получателей публичный .cer подписанта
Platform work обработка ключей per‑OS нет — библиотека решает это внутри
Format coverage все форматы только форматы Word, пока

Последняя строка ограничивает планирование и приводит к честной части этой статьи.

Что пока не работает

Два ограничения, о которых стоит знать заранее.

Покрытие форматов ограничено Word в версии 26.9 — DOCX, DOC, ODT и остальные форматы семейства Word. PDF, таблицы и презентации нельзя подписать ML‑DSA. Для конвейера, ориентированного на PDF, этот релиз подходит лишь для прототипирования и измерений, а не для миграции.

Поддержка валидаторов — это второе ограничение. Стандартного идентификатора XML‑DSig для ML‑DSA пока нет, поэтому Microsoft Word может не пометить подпись как валидную, хотя криптографически она корректна и проходит проверку через API. Это пробел в стандартах, а не дефект, и значит проверка должна происходить в вашем коде, а не у ревьюера, открывающего файл и смотрящего на баннер.

Есть также деталь платформы, не требующая действий: .NET не может читать ML‑DSA‑ключи везде, включая Linux на .NET 8. Там, где это невозможно, GroupDocs.Signature читает сертификат через движок Word, так что одна и та же сборка работает и на ноутбуке разработчика, и в Linux‑контейнере без условного кода.

Стоит ли делать это сейчас, учитывая эти ограничения?

Да, по двум причинам, не связанным с кодом. Закупка сертификатов идёт медленно — публичные УЦ всё ещё вводят выдачу ML‑DSA, поэтому организационная работа выигрывает от раннего старта. И вопрос «можем ли мы сегодня создать постквантовую подпись» уже задают команды по соответствию; возможность ответить этим документом, а не планом, стоит затраченного послеобеденного времени.

Что на самом деле доказывает пример

Четыре метода, выполняемые последовательно, с кодом выхода, зависящим от результата. Он подписывает контракт ML‑DSA‑65 и выводит субъект использованного сертификата. Затем подписывает тот же контракт на всех трёх уровнях и выводит полученные размеры. После этого дважды проверяет подписанный файл — один раз публичным сертификатом подписанта (ожидается успех), один раз сертификатом другого подписанта (ожидается провал). Затем перечисляет найденные цифровые подписи в выводе.

Вторая проверка — это та, которую стоит скопировать. Рутинный код, который был проверен только на корректных входных данных, ничего не говорит о том, отклонит ли он некорректные, а для подписей именно этот вопрос имеет значение.

Пример из реального мира: Тридцатилетний контракт

Архивы с длительным хранением — это место, где теория перестаёт быть абстракцией. Контракт, подписанный сегодня и хранящийся тридцать лет, должен оставаться проверяемым независимо от того, что произойдёт с криптографией за этот период, а «собрать сейчас, расшифровать позже» — это задокументированная модель угроз именно для такого материала.

Для такого архива практический шаг сегодня — двойной путь: сохранять RSA для форматов, которые ML‑DSA пока не покрывает, начать подписывать Word‑вывод ML‑DSA‑65 или ML‑DSA‑87, и фиксировать, какой алгоритм использовался для каждого документа, чтобы будущий аудит мог различить их без открытия файлов.

Одна вещь, которую нужно исправить в примере перед копированием

Репозиторий поставляется с самоподписанными ML‑DSA сертификатами, поэтому демонстрация работает «из коробки», что подразумевает четыре PFX‑файла и жёстко закодированный пароль в documents/. Для одноразового тестового сертификата, действующего только внутри этого примера, это приемлемо.

Однако это не шаблон для вашего собственного репозитория. Генерируйте тестовые сертификаты во время выполнения, как делает пример certificate‑validity от GroupDocs, либо храните их полностью вне системы контроля версий. Закоммиченный ключ трудно отозвать и, как правило, живёт дольше, чем сама демонстрация.

Заключение

Самые дорогие части миграции к постквантовым подписьям — сертификаты, политика и валидаторы. Код, по крайней мере для Word‑документов в .NET, представляет собой другой PFX и тот же вызов DigitalSignOptions. Склонируйте пример, укажите один из своих контрактов, и вы получите три подписанных файла, два результата проверки и сравнение размеров за несколько минут — это лучшая отправная точка для плана миграции, чем оценка.

Дополнительные ресурсы