Перейти к содержимому
ecdsa.com
Ruby

Ruby OpenSSL (openssl gem)

Гем openssl для Ruby — привязка к OpenSSL, на которой построены все криптографические и JWT-библиотеки Ruby: подписи в DER, исключения вместо false для некорректных входных данных, неизменяемые ключи в OpenSSL 3 и ML-DSA через общий API PKey.
Лицензия
Ruby OR BSD-2-Clause
Проверенная версия
4.0.2 ·
Исходный код
github.com/ruby/openssl
Документация
docs.ruby-lang.org/en/master/OpenSSL.html

Совместимость вкратце

ECDSAEdDSAПостквантовая криптография

Кривые ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Форматы закрытых ключей
pkcs8-pempkcs8-derencrypted-pkcs8sec1-pem

Гем openssl входит в поставку Ruby и служит обёрткой над системной библиотекой OpenSSL — в образе ruby:4.0.7 это версия 3.5.7, а версия самого гема — 4.0.2. OpenSSL::PKey::EC#sign(digest, data) выдаёт подпись ASN.1 DER со случайным nonce и без нормализации low-S, а verify принимает high-S. dsa_sign_asn1 подписывает уже вычисленный вами дайджест.

Некорректные подписи вызывают исключение, а не возвращают false: 64-байтовая подпись raw r‖s или 3-байтовый фрагмент данных вызвали OpenSSL::PKey::PKeyError: EVP_DigestVerify, тогда как корректная по форме подпись другого сообщения вернула false. Код, который проверяет только логическое значение, падает на подписи в стиле JWS, поэтому либо перехватывайте PKeyError, либо сначала преобразуйте подпись, как в примере; конвертер DER ⇄ raw показывает обе формы рядом.

В OpenSSL 3 ключи неизменяемы: присваивание private_key существующему ключу EC вызвало ошибку pkeys are immutable on OpenSSL 3.0, поэтому не работают старые руководства, в которых ключ собирают, задавая точку или скаляр. Вместо этого создавайте ключи из DER или PEM — отдельную точку SEC1 сначала нужно обернуть в структуру SPKI. Обратите внимание, какой метод что записывает: to_pem для закрытого ключа записывает SEC1 EC PRIVATE KEY вместе с секретом, private_to_pem записывает PKCS#8, а публиковать следует результат public_to_pem.

Набор кривых совпадает с OpenSSL: от P-224 до P-521, secp256k1 и кривые Brainpool r1, а также Ed25519 и Ed448 через OpenSSL::PKey.generate_key. Через тот же общий API удалось сгенерировать ключи ML-DSA-44/65/87 и SLH-DSA, подписать ими данные и проверить подписи; ключи ML-KEM генерируются, но метода инкапсуляции в PKey версии 4.0.2 нет. Для JWT поверх этого гема построен ruby-jwt.

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/ruby-openssl/run.sh запустила Ruby OpenSSL (openssl gem) 4.0.2 в среде Ruby 4.0.7, OpenSSL 3.5.7 9 Jun 2026 в образе контейнера ruby:4.0.7-slim-trixie; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Кодировка подписи ECDSA

    der

    sign(digest, data) возвращает ASN.1 DER.

    Что записала проверка
    fact ecdsa-signature-encoding = der
  • Nonce в ECDSA

    random

    Случайный nonce: две подписи одного сообщения различаются.

    Что записала проверка
    fact ecdsa-nonce = random
  • Low-S при подписи

    either

    s не нормализуется: среди 64 подписей встретились значения high-S.

    Что записала проверка
    fact ecdsa-low-s-sign = either
  • High-S при проверке

    accepts-high-s

    verify принял подпись, в которой s заменили на n − s.

    Что записала проверка
    fact ecdsa-low-s-verify = accepts-high-s
  • Что хеширует вызов подписи

    hashes-message-or-digest

    sign хеширует данные переданным алгоритмом дайджеста; dsa_sign_asn1 подписывает заранее вычисленный дайджест, и такая подпись прошла проверку по сообщению.

    Что записала проверка
    fact ecdsa-prehash = hashes-message-or-digest
  • Некорректная подпись ECDSA

    throws

    Подпись raw r‖s и 3-байтовый фрагмент данных вызывают OpenSSL::PKey::PKeyError: EVP_DigestVerify; для корректной подписи другого сообщения возвращается false.

    Что записала проверка
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: raw r||s: OpenSSL::PKey::PKeyError: EVP_DigestVerify
    evidence ecdsa-malformed-signature: 3 bytes: OpenSSL::PKey::PKeyError: EVP_DigestVerify
    evidence ecdsa-malformed-signature: wrong message: false
  • Кривые ECDSA

    P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    P-224, P-256, P-384, P-521, secp256k1 и brainpoolP256r1/P384r1/P512r1: подпись и проверка прошли успешно.

    Что записала проверка
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 и Ed448 через OpenSSL::PKey.generate_key.

    Что записала проверка
    fact eddsa = Ed25519, Ed448
  • Постквантовые алгоритмы

    ML-DSASLH-DSA

    Для ML-DSA-44/65/87 и SLH-DSA-SHA2-128s генерация ключей, подпись и проверка через общий API PKey прошли успешно; ключи ML-KEM генерируются, но метода инкапсуляции нет.

    Что записала проверка
    fact pqc = ML-DSA, SLH-DSA
    evidence pqc: ML-KEM-768 key generation, and whether PKey offers encapsulate: false
  • Форматы открытых ключей

    spki-pemspki-der

    SPKI в PEM и DER через OpenSSL::PKey.read; отдельная точка SEC1 загрузилась только после того, как её обернули в SPKI.

    Что записала проверка
    fact public-key-formats = spki-pem, spki-der
    evidence public-key-formats: a 65-byte SEC1 point wrapped in an SPKI structure by hand, then loaded: ok
  • Форматы закрытых ключей

    pkcs8-pempkcs8-derencrypted-pkcs8sec1-pem

    PKCS#8 в PEM и DER, зашифрованный PKCS#8 и SEC1 в PEM; to_pem записывает EC PRIVATE KEY, а private_to_pem — PKCS#8.

    Что записала проверка
    fact private-key-formats = pkcs8-pem, pkcs8-der, encrypted-pkcs8, sec1-pem
    evidence private-key-formats: key.to_pem writes -----BEGIN EC PRIVATE KEY-----; key.private_to_pem writes -----BEGIN PRIVATE KEY-----
  • Изменяемость объектов ключей EC

    no

    Присваивание private_key существующему ключу вызвало ошибку pkeys are immutable on OpenSSL 3.0.

    Что записала проверка
    fact ec-key-mutable = no
    evidence ec-key-mutable: assigning private_key on an existing EC key: OpenSSL::PKey::PKeyError: pkeys are immutable on OpenSSL 3.0

Использование

Подпишите данные в формате DER, преобразуйте подпись в raw r‖s, опубликуйте только открытый ключ и убедитесь, что verify выбрасывает исключение на подписи raw.
require "openssl"

key = OpenSSL::PKey::EC.generate("prime256v1")
digest = OpenSSL::Digest.new("SHA256")
message = "order #1042 approved"

der = key.sign(digest, message) # ASN.1 DER

# Raw r||s for JWS or WebCrypto: take the two INTEGERs and pad each to 32 bytes.
r, s = OpenSSL::ASN1.decode(der).value.map(&:value)
raw = r.to_s(2).rjust(32, "\0") + s.to_s(2).rjust(32, "\0")

# Publish only the public half: public_to_pem, never to_pem (which writes the private key).
public_key = OpenSSL::PKey.read(key.public_to_pem)
puts "verified: #{public_key.verify(digest, der, message)}, raw bytes: #{raw.bytesize}"

# A raw signature is not "false" here: verify raises OpenSSL::PKey::PKeyError.
begin
  public_key.verify(digest, raw, message)
rescue OpenSSL::PKey::PKeyError => e
  puts "raw signature: #{e.class}"
end

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера ruby:4.0.7-slim-trixie, и запуск завершается ошибкой, если вывод не совпадает.

Команда
ruby usage.rb
Вывод должен совпадать (регулярное выражение)
verified: true, raw bytes: 64\nraw signature: OpenSSL::PKey::PKeyError

Библиотеки