Pular para o conteúdo
ecdsa.com
Ruby

Ruby OpenSSL (openssl gem)

A gem openssl do Ruby, a interface com o OpenSSL sobre a qual se apoiam todas as bibliotecas de criptografia e de JWT do Ruby: assinaturas DER, exceções em vez de false para entrada malformada, chaves imutáveis no OpenSSL 3 e ML-DSA pela API genérica PKey.
Licença
Ruby OR BSD-2-Clause
Versão verificada
4.0.2 ·
Código-fonte
github.com/ruby/openssl
Documentação
docs.ruby-lang.org/en/master/OpenSSL.html

Visão geral da compatibilidade

ECDSAEdDSAPós-quântica

Curvas ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Formatos de chave privada
pkcs8-pempkcs8-derencrypted-pkcs8sec1-pem

A gem openssl vem com o Ruby e encapsula o OpenSSL do sistema — o 3.5.7 na imagem ruby:4.0.7, com a gem na versão 4.0.2. OpenSSL::PKey::EC#sign(digest, data) produz ASN.1 DER com um nonce aleatório e sem normalização low-S, e verify aceita high-S. dsa_sign_asn1 assina um digest que você já calculou.

Assinaturas malformadas lançam uma exceção em vez de retornar false: uma assinatura r‖s bruta de 64 bytes ou um blob de 3 bytes lançaram OpenSSL::PKey::PKeyError: EVP_DigestVerify, enquanto uma assinatura bem formada sobre outra mensagem retornou false. Código que só confere o booleano quebra com uma assinatura no estilo JWS; por isso, capture PKeyError ou converta antes, como faz o exemplo. O conversor DER ⇄ bruto mostra as duas formas lado a lado.

As chaves são imutáveis no OpenSSL 3: atribuir private_key a uma chave EC existente lançou pkeys are immutable on OpenSSL 3.0, o que quebra tutoriais mais antigos que montam uma chave definindo o ponto ou o escalar. Em vez disso, construa as chaves a partir de DER ou PEM — um ponto SEC1 isolado precisa antes ser encapsulado em uma estrutura SPKI. Repare no que cada método grava: to_pem em uma chave privada grava EC PRIVATE KEY SEC1, incluindo o segredo, private_to_pem grava PKCS#8, e public_to_pem é o que deve ser publicado.

As curvas disponíveis acompanham o OpenSSL: da P-224 à P-521, secp256k1 e as curvas Brainpool r1, além de Ed25519 e Ed448 por meio de OpenSSL::PKey.generate_key. A mesma API genérica gerou chaves ML-DSA-44/65/87 e SLH-DSA e assinou e verificou com elas; é possível gerar chaves ML-KEM, mas PKey não oferece nenhum método de encapsulamento na versão 4.0.2. Para JWTs, a ruby-jwt se apoia nesta gem.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/ruby-openssl/run.sh executou Ruby OpenSSL (openssl gem) 4.0.2 em Ruby 4.0.7, OpenSSL 3.5.7 9 Jun 2026 na imagem de contêiner ruby:4.0.7-slim-trixie em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Codificação da assinatura ECDSA

    der

    sign(digest, data) retorna ASN.1 DER.

    Registrado pela verificação
    fact ecdsa-signature-encoding = der
  • Nonce do ECDSA

    random

    Nonce aleatório: duas assinaturas da mesma mensagem são diferentes.

    Registrado pela verificação
    fact ecdsa-nonce = random
  • Low-S ao assinar

    either

    O s não é normalizado: valores high-S apareceram entre 64 assinaturas.

    Registrado pela verificação
    fact ecdsa-low-s-sign = either
  • High-S ao verificar

    accepts-high-s

    verify aceitou a assinatura com s substituído por n − s.

    Registrado pela verificação
    fact ecdsa-low-s-verify = accepts-high-s
  • Entrada do hash na chamada de assinatura

    hashes-message-or-digest

    sign calcula o hash com o digest indicado; dsa_sign_asn1 assina um digest pré-calculado, e essa assinatura foi verificada contra a mensagem.

    Registrado pela verificação
    fact ecdsa-prehash = hashes-message-or-digest
  • Assinatura ECDSA malformada

    throws

    Uma assinatura r‖s bruta e um blob de 3 bytes lançam OpenSSL::PKey::PKeyError: EVP_DigestVerify; uma assinatura válida sobre outra mensagem retorna false.

    Registrado pela verificação
    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
  • Curvas ECDSA

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

    Assinatura e verificação funcionaram com P-224, P-256, P-384, P-521, secp256k1 e brainpoolP256r1/P384r1/P512r1.

    Registrado pela verificação
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 e Ed448 por meio de OpenSSL::PKey.generate_key.

    Registrado pela verificação
    fact eddsa = Ed25519, Ed448
  • Algoritmos pós-quânticos

    ML-DSASLH-DSA

    Geração, assinatura e verificação com ML-DSA-44/65/87 e SLH-DSA-SHA2-128s funcionaram pela API genérica PKey; chaves ML-KEM podem ser geradas, mas não há método de encapsulamento.

    Registrado pela verificação
    fact pqc = ML-DSA, SLH-DSA
    evidence pqc: ML-KEM-768 key generation, and whether PKey offers encapsulate: false
  • Formatos de chave pública

    spki-pemspki-der

    SPKI PEM e DER por meio de OpenSSL::PKey.read; um ponto SEC1 isolado só foi carregado depois de encapsulado em SPKI.

    Registrado pela verificação
    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
  • Formatos de chave privada

    pkcs8-pempkcs8-derencrypted-pkcs8sec1-pem

    PKCS#8 PEM e DER, PKCS#8 criptografado e SEC1 PEM; to_pem grava EC PRIVATE KEY, private_to_pem grava PKCS#8.

    Registrado pela verificação
    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-----
  • Objetos de chave EC mutáveis

    no

    Atribuir private_key a uma chave existente lançou pkeys are immutable on OpenSSL 3.0.

    Registrado pela verificação
    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

Uso

Assine em DER, converta para r‖s bruto, publique apenas a chave pública e veja que a verificação lança uma exceção com uma assinatura bruta.
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

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner ruby:4.0.7-slim-trixie, e a execução falha se a saída não corresponder.

Comando
ruby usage.rb
A saída deve corresponder a (expressão regular)
verified: true, raw bytes: 64\nraw signature: OpenSSL::PKey::PKeyError

Bibliotecas