GnuTLS обеспечивает работу с TLS, X.509 и PKCS для многих компонентов GNU и Linux и поставляется с утилитами certtool и gnutls-cli. Абстрактные интерфейсы gnutls_privkey_* и gnutls_pubkey_* одинаково работают с программными ключами, токенами PKCS#11 и TPM. Факты на этой странице проверены на пакете Alpine 3.24.2 с GnuTLS 3.8.13, которая использует Nettle для арифметики. Начиная с GnuTLS 3.8.10 постквантовые алгоритмы берутся только из необязательной библиотеки leancrypto, а этот пакет с ней не скомпонован.
gnutls_privkey_sign_data возвращает подписи ECDSA в DER со случайным nonce; флаг GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE переключает подпись на RFC 6979, и с ним тестовый вектор для P-256 из RFC воспроизвёлся точно. Значение s не нормализуется, а подписи с high-S проходят проверку. gnutls_privkey_sign_hash2 подписывает дайджест, который вы вычислили сами.
При неудачной проверке функции возвращают отрицательный код ошибки, и все виды неудач выглядели одинаково: для подписи raw r‖s, 3-байтового фрагмента данных и верной подписи других данных результат был один — GNUTLS_E_PK_SIG_VERIFY_FAILED (-89). Чтобы исключить проблему с кодированием, воспользуйтесь конвертером DER ⇄ raw.
ECDSA ограничен кривыми NIST от P-224 до P-521: gnutls_ecc_curve_get_id("SECP256K1") вернула 0, то есть кривая неизвестна. При этом Ed25519 и Ed448 работают и для подписи, и для проверки. В заголовочных файлах объявлены GNUTLS_PK_MLKEM768 и GNUTLS_PK_MLDSA65, но эта сборка не реализует ни один из них: их нет в gnutls_pk_list(), генерация ключа ML-DSA-65 завершается ошибкой GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM, а среди групп TLS, которые выводит gnutls-cli --list, есть X25519 и X448, но нет ни одной гибридной группы с ML-KEM. Код, который компилируется с этими константами, всё равно может завершиться ошибкой во время выполнения, поэтому проверяйте фактический список алгоритмов в среде выполнения. Сертификаты, созданные certtool, разбирайте в декодере сертификатов.